1 //===--- AvoidConstParamsInDecls.cpp - clang-tidy--------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "AvoidConstParamsInDecls.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 #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 
getTypeRange(const ParmVarDecl & Param)23 SourceRange getTypeRange(const ParmVarDecl &Param) {
24   return SourceRange(Param.getBeginLoc(),
25                      Param.getLocation().getLocWithOffset(-1));
26 }
27 
28 } // namespace
29 
registerMatchers(MatchFinder * Finder)30 void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) {
31   const auto ConstParamDecl =
32       parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
33   Finder->addMatcher(
34       functionDecl(unless(isDefinition()),
35                    has(typeLoc(forEach(ConstParamDecl))))
36           .bind("func"),
37       this);
38 }
39 
check(const MatchFinder::MatchResult & Result)40 void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) {
41   const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
42   const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
43 
44   if (!Param->getType().isLocalConstQualified())
45     return;
46 
47   auto Diag = diag(Param->getBeginLoc(),
48                    "parameter %0 is const-qualified in the function "
49                    "declaration; const-qualification of parameters only has an "
50                    "effect in function definitions");
51   if (Param->getName().empty()) {
52     for (unsigned int I = 0; I < Func->getNumParams(); ++I) {
53       if (Param == Func->getParamDecl(I)) {
54         Diag << (I + 1);
55         break;
56       }
57     }
58   } else {
59     Diag << Param;
60   }
61 
62   if (Param->getBeginLoc().isMacroID() != Param->getEndLoc().isMacroID()) {
63     // Do not offer a suggestion if the part of the variable declaration comes
64     // from a macro.
65     return;
66   }
67 
68   CharSourceRange FileRange = Lexer::makeFileCharRange(
69       CharSourceRange::getTokenRange(getTypeRange(*Param)),
70       *Result.SourceManager, getLangOpts());
71 
72   if (!FileRange.isValid())
73     return;
74 
75   auto Tok = tidy::utils::lexer::getQualifyingToken(
76       tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
77   if (!Tok)
78     return;
79   Diag << FixItHint::CreateRemoval(
80       CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));
81 }
82 
83 } // namespace readability
84 } // namespace tidy
85 } // namespace clang
86