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