1 //===--- AvoidConstParamsInDecls.cpp - clang-tidy--------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "AvoidConstParamsInDecls.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Lex/Lexer.h" 14 #include "llvm/ADT/Optional.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace readability { 21 namespace { 22 23 SourceRange getTypeRange(const ParmVarDecl &Param) { 24 if (Param.getIdentifier() != nullptr) 25 return SourceRange(Param.getLocStart(), 26 Param.getLocEnd().getLocWithOffset(-1)); 27 return Param.getSourceRange(); 28 } 29 30 } // namespace 31 32 void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) { 33 const auto ConstParamDecl = 34 parmVarDecl(hasType(qualType(isConstQualified()))).bind("param"); 35 Finder->addMatcher( 36 functionDecl(unless(isDefinition()), 37 // Lambdas are always their own definition, but they 38 // generate a non-definition FunctionDecl too. Ignore those. 39 // Class template instantiations have a non-definition 40 // CXXMethodDecl for methods that aren't used in this 41 // translation unit. Ignore those, as the template will have 42 // already been checked. 43 unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf( 44 isLambda(), ast_matchers::isTemplateInstantiation()))))), 45 has(typeLoc(forEach(ConstParamDecl)))) 46 .bind("func"), 47 this); 48 } 49 50 // Re-lex the tokens to get precise location of last 'const' 51 static llvm::Optional<Token> ConstTok(CharSourceRange Range, 52 const MatchFinder::MatchResult &Result) { 53 const SourceManager &Sources = *Result.SourceManager; 54 std::pair<FileID, unsigned> LocInfo = 55 Sources.getDecomposedLoc(Range.getBegin()); 56 StringRef File = Sources.getBufferData(LocInfo.first); 57 const char *TokenBegin = File.data() + LocInfo.second; 58 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), 59 Result.Context->getLangOpts(), File.begin(), TokenBegin, 60 File.end()); 61 Token Tok; 62 llvm::Optional<Token> ConstTok; 63 while (!RawLexer.LexFromRawLexer(Tok)) { 64 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) 65 break; 66 if (Tok.is(tok::raw_identifier)) { 67 IdentifierInfo &Info = Result.Context->Idents.get(StringRef( 68 Sources.getCharacterData(Tok.getLocation()), Tok.getLength())); 69 Tok.setIdentifierInfo(&Info); 70 Tok.setKind(Info.getTokenID()); 71 } 72 if (Tok.is(tok::kw_const)) 73 ConstTok = Tok; 74 } 75 return ConstTok; 76 } 77 78 void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { 79 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); 80 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); 81 82 if (!Param->getType().isLocalConstQualified()) 83 return; 84 85 auto Diag = diag(Param->getLocStart(), 86 "parameter %0 is const-qualified in the function " 87 "declaration; const-qualification of parameters only has an " 88 "effect in function definitions"); 89 if (Param->getName().empty()) { 90 for (unsigned int i = 0; i < Func->getNumParams(); ++i) { 91 if (Param == Func->getParamDecl(i)) { 92 Diag << (i + 1); 93 break; 94 } 95 } 96 } else { 97 Diag << Param; 98 } 99 100 if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) { 101 // Do not offer a suggestion if the part of the variable declaration comes 102 // from a macro. 103 return; 104 } 105 106 CharSourceRange FileRange = Lexer::makeFileCharRange( 107 CharSourceRange::getTokenRange(getTypeRange(*Param)), 108 *Result.SourceManager, getLangOpts()); 109 110 if (!FileRange.isValid()) 111 return; 112 113 auto Tok = ConstTok(FileRange, Result); 114 if (!Tok) 115 return; 116 Diag << FixItHint::CreateRemoval( 117 CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation())); 118 } 119 120 } // namespace readability 121 } // namespace tidy 122 } // namespace clang 123