1 #include "AlmostNeverAutoCheck.h"
2
3 #include <cassert>
4 #include <clang/AST/Decl.h>
5 #include <clang/AST/DeclCXX.h>
6 #include <clang/AST/Expr.h>
7 #include <clang/AST/ExprCXX.h>
8 #include <clang/ASTMatchers/ASTMatchFinder.h>
9 #include <clang/ASTMatchers/ASTMatchers.h>
10 #include <clang/ASTMatchers/ASTMatchersInternal.h>
11 #include <clang/Basic/Diagnostic.h>
12 #include <clang/Basic/LLVM.h>
13 #include <clang/Basic/SourceLocation.h>
14 #include <clang/Lex/Lexer.h>
15 #include <llvm/ADT/APInt.h>
16 #include <llvm/Support/Casting.h>
17 #include <map>
18 #include <string>
19 #include <tuple>
20 #include <utility>
21
22 #include "Utils.h"
23 #include "clang/AST/OperationKinds.h"
24 #include "../../src/cata_assert.h"
25
26 using namespace clang::ast_matchers;
27
28 namespace clang
29 {
30 namespace tidy
31 {
32 namespace cata
33 {
34
registerMatchers(MatchFinder * Finder)35 void AlmostNeverAutoCheck::registerMatchers( MatchFinder *Finder )
36 {
37 Finder->addMatcher(
38 varDecl(
39 hasType( autoType() )
40 ).bind( "decl" ),
41 this
42 );
43 }
44
CheckDecl(AlmostNeverAutoCheck & Check,const MatchFinder::MatchResult & Result)45 static void CheckDecl( AlmostNeverAutoCheck &Check,
46 const MatchFinder::MatchResult &Result )
47 {
48 const VarDecl *AutoVarDecl = Result.Nodes.getNodeAs<VarDecl>( "decl" );
49
50 if( !AutoVarDecl ) {
51 return;
52 }
53
54 if( AutoVarDecl->isImplicit() ) {
55 // This is some compiler-generated VarDecl, such as in a range for
56 // loop; skip it
57 return;
58 }
59
60 SourceRange RangeToReplace = AutoVarDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange();
61
62 const SourceManager *SM = Result.SourceManager;
63 SourceLocation ExpansionBegin = SM->getFileLoc( RangeToReplace.getBegin() );
64 if( ExpansionBegin != RangeToReplace.getBegin() ) {
65 // This means we're inside a macro expansion
66 return;
67 }
68
69 PrintingPolicy Policy( LangOptions{} );
70 Policy.adjustForCPlusPlus();
71 const Expr *Initializer = AutoVarDecl->getAnyInitializer();
72 if( !Initializer ) {
73 // This happens for one range for loop in CDDA (in test_statistics.h) and I'm not sure why
74 // it's different from the others.
75 Check.diag(
76 RangeToReplace.getBegin(),
77 "Avoid auto in declaration of %0."
78 ) << AutoVarDecl;
79 return;
80 }
81 QualType AutoTp = Initializer->getType();
82 bool WasConst = AutoVarDecl->getType().isLocalConstQualified();
83 AutoTp.removeLocalConst();
84 std::string TypeStr = AutoTp.getAsString( Policy );
85
86 if( WasConst && AutoTp.getTypePtr()->isPointerType() ) {
87 // In the case of 'const auto' we need to bring the beginning forwards
88 // to the start of the 'const'.
89 RangeToReplace.setBegin( AutoVarDecl->getBeginLoc() );
90 AutoTp.addConst();
91 TypeStr = AutoTp.getAsString( Policy );
92 // In the case of 'auto const' we need to push the end back to the end
93 // of the 'const'. Couldn't find a nice way to do that, so using this
94 // super hacky way.
95 std::string TextFromEnd( SM->getCharacterData( RangeToReplace.getEnd() ), 10 );
96 if( TextFromEnd == "auto const" ) {
97 RangeToReplace.setEnd( RangeToReplace.getEnd().getLocWithOffset( 10 ) );
98 }
99 }
100
101 if( TypeStr == "auto" ) {
102 // Not sure what causes this; usually something that returns "auto"
103 // like this is a thing we're relatively happy to be auto. Can revisit
104 // if necessary.
105 return;
106 }
107
108 // Ignore lambdas and "<dependent type>"
109 if( TypeStr.find_first_of( "(<" ) != std::string::npos ) {
110 return;
111 }
112
113 // Heuristic to ignore certain types (e.g. iterators, implementation
114 // details) based on their names. Skipping the first character of each
115 // word to avoid worrying about capitalization.
116 for( std::string Fragment : {
117 "terator", "nternal"
118 } ) {
119 if( std::search( TypeStr.begin(), TypeStr.end(), Fragment.begin(), Fragment.end() ) !=
120 TypeStr.end() ) {
121 return;
122 }
123 }
124
125 const FunctionDecl *ContainingFunc = getContainingFunction( Result, AutoVarDecl );
126 if( ContainingFunc &&
127 ContainingFunc->getTemplateSpecializationKind() == TSK_ImplicitInstantiation ) {
128 return;
129 }
130
131 Check.diag(
132 RangeToReplace.getBegin(),
133 "Avoid auto in declaration of %0."
134 ) << AutoVarDecl <<
135 FixItHint::CreateReplacement( RangeToReplace, TypeStr );
136 }
137
check(const MatchFinder::MatchResult & Result)138 void AlmostNeverAutoCheck::check( const MatchFinder::MatchResult &Result )
139 {
140 CheckDecl( *this, Result );
141 }
142
143 } // namespace cata
144 } // namespace tidy
145 } // namespace clang
146