| //===--- AvoidConstParamsInDecls.cpp - clang-tidy--------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "AvoidConstParamsInDecls.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Lex/Lexer.h" |
| #include "llvm/ADT/Optional.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace readability { |
| namespace { |
| |
| SourceRange getTypeRange(const ParmVarDecl &Param) { |
| if (Param.getIdentifier() != nullptr) |
| return SourceRange(Param.getLocStart(), |
| Param.getLocEnd().getLocWithOffset(-1)); |
| return Param.getSourceRange(); |
| } |
| |
| } // namespace |
| |
| void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) { |
| const auto ConstParamDecl = |
| parmVarDecl(hasType(qualType(isConstQualified()))).bind("param"); |
| Finder->addMatcher( |
| functionDecl(unless(isDefinition()), |
| // Lambdas are always their own definition, but they |
| // generate a non-definition FunctionDecl too. Ignore those. |
| // Class template instantiations have a non-definition |
| // CXXMethodDecl for methods that aren't used in this |
| // translation unit. Ignore those, as the template will have |
| // already been checked. |
| unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf( |
| isLambda(), ast_matchers::isTemplateInstantiation()))))), |
| has(typeLoc(forEach(ConstParamDecl)))) |
| .bind("func"), |
| this); |
| } |
| |
| // Re-lex the tokens to get precise location of last 'const' |
| static llvm::Optional<Token> ConstTok(CharSourceRange Range, |
| const MatchFinder::MatchResult &Result) { |
| const SourceManager &Sources = *Result.SourceManager; |
| std::pair<FileID, unsigned> LocInfo = |
| Sources.getDecomposedLoc(Range.getBegin()); |
| StringRef File = Sources.getBufferData(LocInfo.first); |
| const char *TokenBegin = File.data() + LocInfo.second; |
| Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), |
| Result.Context->getLangOpts(), File.begin(), TokenBegin, |
| File.end()); |
| Token Tok; |
| llvm::Optional<Token> ConstTok; |
| while (!RawLexer.LexFromRawLexer(Tok)) { |
| if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) |
| break; |
| if (Tok.is(tok::raw_identifier)) { |
| IdentifierInfo &Info = Result.Context->Idents.get(StringRef( |
| Sources.getCharacterData(Tok.getLocation()), Tok.getLength())); |
| Tok.setIdentifierInfo(&Info); |
| Tok.setKind(Info.getTokenID()); |
| } |
| if (Tok.is(tok::kw_const)) |
| ConstTok = Tok; |
| } |
| return ConstTok; |
| } |
| |
| void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { |
| const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); |
| const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); |
| |
| if (!Param->getType().isLocalConstQualified()) |
| return; |
| |
| auto Diag = diag(Param->getLocStart(), |
| "parameter %0 is const-qualified in the function " |
| "declaration; const-qualification of parameters only has an " |
| "effect in function definitions"); |
| if (Param->getName().empty()) { |
| for (unsigned int i = 0; i < Func->getNumParams(); ++i) { |
| if (Param == Func->getParamDecl(i)) { |
| Diag << (i + 1); |
| break; |
| } |
| } |
| } else { |
| Diag << Param; |
| } |
| |
| if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) { |
| // Do not offer a suggestion if the part of the variable declaration comes |
| // from a macro. |
| return; |
| } |
| |
| CharSourceRange FileRange = Lexer::makeFileCharRange( |
| CharSourceRange::getTokenRange(getTypeRange(*Param)), |
| *Result.SourceManager, getLangOpts()); |
| |
| if (!FileRange.isValid()) |
| return; |
| |
| auto Tok = ConstTok(FileRange, Result); |
| if (!Tok) |
| return; |
| Diag << FixItHint::CreateRemoval( |
| CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation())); |
| } |
| |
| } // namespace readability |
| } // namespace tidy |
| } // namespace clang |