99 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "AvoidConstParamsInDeclsCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Lex/Lexer.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::readability {18 19static SourceRange getTypeRange(const ParmVarDecl &Param) {20 return {Param.getBeginLoc(), Param.getLocation().getLocWithOffset(-1)};21}22 23// Finds the location of the qualifying `const` token in the `ParmValDecl`'s24// return type. Returns `std::nullopt` when the parm type is not25// `const`-qualified like when the type is an alias or a macro.26static std::optional<Token>27findConstToRemove(const ParmVarDecl &Param,28 const MatchFinder::MatchResult &Result) {29 const CharSourceRange FileRange = Lexer::makeFileCharRange(30 CharSourceRange::getTokenRange(getTypeRange(Param)),31 *Result.SourceManager, Result.Context->getLangOpts());32 33 if (FileRange.isInvalid())34 return std::nullopt;35 36 return tidy::utils::lexer::getQualifyingToken(37 tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);38}39 40void AvoidConstParamsInDeclsCheck::storeOptions(41 ClangTidyOptions::OptionMap &Opts) {42 Options.store(Opts, "IgnoreMacros", IgnoreMacros);43}44 45void AvoidConstParamsInDeclsCheck::registerMatchers(MatchFinder *Finder) {46 const auto ConstParamDecl =47 parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");48 Finder->addMatcher(functionDecl(unless(isDefinition()),49 has(typeLoc(forEach(ConstParamDecl))))50 .bind("func"),51 this);52}53 54void AvoidConstParamsInDeclsCheck::check(55 const MatchFinder::MatchResult &Result) {56 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");57 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");58 59 if (!Param->getType().isLocalConstQualified())60 return;61 62 if (IgnoreMacros &&63 (Param->getBeginLoc().isMacroID() || Param->getEndLoc().isMacroID())) {64 // Suppress the check if macros are involved.65 return;66 }67 68 const auto Tok = findConstToRemove(*Param, Result);69 const auto ConstLocation = Tok ? Tok->getLocation() : Param->getBeginLoc();70 71 auto Diag = diag(ConstLocation,72 "parameter %0 is const-qualified in the function "73 "declaration; const-qualification of parameters only has an "74 "effect in function definitions");75 if (Param->getName().empty()) {76 for (unsigned int I = 0; I < Func->getNumParams(); ++I) {77 if (Param == Func->getParamDecl(I)) {78 Diag << (I + 1);79 break;80 }81 }82 } else {83 Diag << Param;84 }85 86 if (Param->getBeginLoc().isMacroID() != Param->getEndLoc().isMacroID()) {87 // Do not offer a suggestion if the part of the variable declaration comes88 // from a macro.89 return;90 }91 if (!Tok)92 return;93 94 Diag << FixItHint::CreateRemoval(95 CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));96}97 98} // namespace clang::tidy::readability99