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