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 "llvm/ADT/Optional.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/ASTMatchers/ASTMatchers.h" 14 #include "clang/Lex/Lexer.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 unless(cxxMethodDecl(ofClass(cxxRecordDecl(isLambda())))), 40 has(typeLoc(forEach(ConstParamDecl)))) 41 .bind("func"), 42 this); 43 } 44 45 // Re-lex the tokens to get precise location of last 'const' 46 static llvm::Optional<Token> ConstTok(CharSourceRange Range, 47 const MatchFinder::MatchResult &Result) { 48 const SourceManager &Sources = *Result.SourceManager; 49 std::pair<FileID, unsigned> LocInfo = 50 Sources.getDecomposedLoc(Range.getBegin()); 51 StringRef File = Sources.getBufferData(LocInfo.first); 52 const char *TokenBegin = File.data() + LocInfo.second; 53 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), 54 Result.Context->getLangOpts(), File.begin(), TokenBegin, 55 File.end()); 56 Token Tok; 57 llvm::Optional<Token> ConstTok; 58 while (!RawLexer.LexFromRawLexer(Tok)) { 59 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) 60 break; 61 if (Tok.is(tok::raw_identifier)) { 62 IdentifierInfo &Info = Result.Context->Idents.get(StringRef( 63 Sources.getCharacterData(Tok.getLocation()), Tok.getLength())); 64 Tok.setIdentifierInfo(&Info); 65 Tok.setKind(Info.getTokenID()); 66 } 67 if (Tok.is(tok::kw_const)) 68 ConstTok = Tok; 69 } 70 return ConstTok; 71 } 72 73 void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { 74 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); 75 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); 76 77 if (!Param->getType().isLocalConstQualified()) 78 return; 79 80 auto Diag = diag(Param->getLocStart(), 81 "parameter %0 is const-qualified in the function " 82 "declaration; const-qualification of parameters only has an " 83 "effect in function definitions"); 84 if (Param->getName().empty()) { 85 for (unsigned int i = 0; i < Func->getNumParams(); ++i) { 86 if (Param == Func->getParamDecl(i)) { 87 Diag << (i + 1); 88 break; 89 } 90 } 91 } else { 92 Diag << Param; 93 } 94 95 if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) { 96 // Do not offer a suggestion if the part of the variable declaration comes 97 // from a macro. 98 return; 99 } 100 101 CharSourceRange FileRange = Lexer::makeFileCharRange( 102 CharSourceRange::getTokenRange(getTypeRange(*Param)), 103 *Result.SourceManager, Result.Context->getLangOpts()); 104 105 if (!FileRange.isValid()) 106 return; 107 108 auto Tok = ConstTok(FileRange, Result); 109 if (!Tok) 110 return; 111 Diag << FixItHint::CreateRemoval( 112 CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation())); 113 } 114 115 } // namespace readability 116 } // namespace tidy 117 } // namespace clang 118