1 #include "TextStyleCheck.h"
2 
3 #include <ClangTidy.h>
4 #include <clang/AST/ASTContext.h>
5 #include <clang/AST/Expr.h>
6 #include <clang/AST/ExprCXX.h>
7 #include <clang/ASTMatchers/ASTMatchers.h>
8 #include <clang/ASTMatchers/ASTMatchersInternal.h>
9 #include <clang/Basic/Diagnostic.h>
10 #include <clang/Basic/SourceLocation.h>
11 #include <clang/Basic/SourceManager.h>
12 #include <cstddef>
13 #include <string>
14 
15 #include "../../src/text_style_check.h"
16 #include "StringLiteralIterator.h"
17 
18 namespace clang
19 {
20 class LangOptions;
21 class TargetInfo;
22 }  // namespace clang
23 
24 using namespace clang::ast_matchers;
25 
26 namespace clang
27 {
28 namespace tidy
29 {
30 namespace cata
31 {
32 
TextStyleCheck(StringRef Name,ClangTidyContext * Context)33 TextStyleCheck::TextStyleCheck( StringRef Name, ClangTidyContext *Context )
34     : ClangTidyCheck( Name, Context ),
35       EscapeUnicode( Options.get( "EscapeUnicode", 0 ) != 0 ) {}
36 
storeOptions(ClangTidyOptions::OptionMap & Opts)37 void TextStyleCheck::storeOptions( ClangTidyOptions::OptionMap &Opts )
38 {
39     Options.store( Opts, "EscapeUnicode", EscapeUnicode );
40 }
41 
registerMatchers(MatchFinder * Finder)42 void TextStyleCheck::registerMatchers( MatchFinder *Finder )
43 {
44     Finder->addMatcher(
45         stringLiteral(
46             anyOf(
47                 // check if the literal is used in string concat.
48                 // todo: check the side of the literal
49                 hasAncestor(
50                     cxxOperatorCallExpr(
51                         anyOf(
52                             hasOverloadedOperatorName( "+" ),
53                             hasOverloadedOperatorName( "<<" ),
54                             hasOverloadedOperatorName( "+=" )
55                         )
56                     ).bind( "concat" )
57                 ),
58                 anything()
59             ),
60             // specifically ignore mapgen strings
61             unless(
62                 hasAncestor(
63                     callExpr(
64                         callee(
65                             functionDecl(
66                                 hasAnyName( "formatted_set_simple", "ter_bind", "furn_bind" )
67                             )
68                         )
69                     )
70                 )
71             ),
72             // __func__, etc
73             unless( hasAncestor( predefinedExpr() ) )
74         ).bind( "str" ),
75         this
76     );
77 }
78 
check(const MatchFinder::MatchResult & Result)79 void TextStyleCheck::check( const MatchFinder::MatchResult &Result )
80 {
81     const StringLiteral *str = Result.Nodes.getNodeAs<StringLiteral>( "str" );
82     if( !str ) {
83         return;
84     }
85     const StringLiteral &text = *str;
86 
87     const SourceManager &SrcMgr = *Result.SourceManager;
88     for( size_t i = 0; i < text.getNumConcatenated(); ++i ) {
89         const SourceLocation &loc = text.getStrTokenLoc( i );
90         if( loc.isInvalid() ) {
91             return;
92         } else if( StringRef( SrcMgr.getPresumedLoc( SrcMgr.getSpellingLoc(
93                                   loc ) ).getFilename() ).equals( "<scratch space>" ) ) {
94             return;
95         }
96     }
97 
98     // ignore wide/u16/u32 strings
99     if( ( !text.isAscii() && !text.isUTF8() ) || text.getCharByteWidth() != 1 ) {
100         return;
101     }
102 
103     // disable fix-its for utf8 strings to avoid removing the u8 prefix
104     bool fixit = text.isAscii();
105     for( size_t i = 0; fixit && i < text.getNumConcatenated(); ++i ) {
106         const SourceLocation &loc = text.getStrTokenLoc( i );
107         if( !loc.isMacroID() && SrcMgr.getCharacterData( loc )[0] == 'R' ) {
108             // disable fix-its for raw strings to avoid removing the R prefix
109             fixit = false;
110         }
111     }
112 
113     const LangOptions &LangOpts = Result.Context->getLangOpts();
114     const TargetInfo &Info = Result.Context->getTargetInfo();
115     const auto location = [&SrcMgr, &LangOpts, &Info]
116     ( const StringLiteralIterator & it ) -> SourceLocation {
117         return SrcMgr.getSpellingLoc( it.toSourceLocation( SrcMgr, LangOpts, Info ) );
118     };
119 
120     const auto text_style_check_callback =
121         [this, fixit, location]
122         ( const text_style_fix type, const std::string & msg,
123           const StringLiteralIterator & /*beg*/, const StringLiteralIterator & /*end*/,
124           const StringLiteralIterator & at,
125           const StringLiteralIterator & from, const StringLiteralIterator & to,
126           const std::string & fix
127     ) {
128         clang::DiagnosticBuilder diags = diag( location( at ), msg );
129         if( fixit ) {
130             switch( type ) {
131                 case text_style_fix::removal: {
132                     const CharSourceRange range = CharSourceRange::getCharRange(
133                                                       location( from ), location( to ) );
134                     diags << FixItHint::CreateRemoval( range );
135                 }
136                 break;
137                 case text_style_fix::insertion:
138                     diags << FixItHint::CreateInsertion( location( from ), fix );
139                     break;
140                 case text_style_fix::replacement: {
141                     const CharSourceRange range = CharSourceRange::getCharRange(
142                                                       location( from ), location( to ) );
143                     diags << FixItHint::CreateReplacement( range, fix );
144                 }
145                 break;
146             }
147         }
148     };
149 
150     const auto beg = StringLiteralIterator::begin( text );
151     const auto end = StringLiteralIterator::end( text );
152     const bool in_concat_expr = Result.Nodes.getNodeAs<CXXOperatorCallExpr>( "concat" ) != nullptr;
153     text_style_check<StringLiteralIterator>( beg, end,
154             // treat spaces at the end of strings in concat expressions (+, += or <<) as deliberate
155             in_concat_expr ? fix_end_of_string_spaces::no : fix_end_of_string_spaces::yes,
156             EscapeUnicode ? escape_unicode::yes : escape_unicode::no,
157             text_style_check_callback );
158 }
159 
160 } // namespace cata
161 } // namespace tidy
162 } // namespace clang
163