clang-tools  14.0.0git
NonConstParameterCheck.cpp
Go to the documentation of this file.
1 //===--- NonConstParameterCheck.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 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
19 void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
20  // Add parameters to Parameters.
21  Finder->addMatcher(parmVarDecl().bind("Parm"), this);
22 
23  // C++ constructor.
24  Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
25 
26  // Track unused parameters, there is Wunused-parameter about unused
27  // parameters.
28  Finder->addMatcher(declRefExpr().bind("Ref"), this);
29 
30  // Analyse parameter usage in function.
31  Finder->addMatcher(stmt(anyOf(unaryOperator(hasAnyOperatorName("++", "--")),
32  binaryOperator(), callExpr(), returnStmt(),
33  cxxConstructExpr()))
34  .bind("Mark"),
35  this);
36  Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
37 }
38 
39 void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) {
40  if (const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>("Parm")) {
41  if (const DeclContext *D = Parm->getParentFunctionOrMethod()) {
42  if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
43  if (M->isVirtual() || M->size_overridden_methods() != 0)
44  return;
45  }
46  }
47  addParm(Parm);
48  } else if (const auto *Ctor =
49  Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) {
50  for (const auto *Parm : Ctor->parameters())
51  addParm(Parm);
52  for (const auto *Init : Ctor->inits())
53  markCanNotBeConst(Init->getInit(), true);
54  } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) {
55  setReferenced(Ref);
56  } else if (const auto *S = Result.Nodes.getNodeAs<Stmt>("Mark")) {
57  if (const auto *B = dyn_cast<BinaryOperator>(S)) {
58  if (B->isAssignmentOp())
59  markCanNotBeConst(B, false);
60  } else if (const auto *CE = dyn_cast<CallExpr>(S)) {
61  // Typically, if a parameter is const then it is fine to make the data
62  // const. But sometimes the data is written even though the parameter
63  // is const. Mark all data passed by address to the function.
64  for (const auto *Arg : CE->arguments()) {
65  markCanNotBeConst(Arg->IgnoreParenCasts(), true);
66  }
67 
68  // Data passed by nonconst reference should not be made const.
69  if (const FunctionDecl *FD = CE->getDirectCallee()) {
70  unsigned ArgNr = 0U;
71  for (const auto *Par : FD->parameters()) {
72  if (ArgNr >= CE->getNumArgs())
73  break;
74  const Expr *Arg = CE->getArg(ArgNr++);
75  // Is this a non constant reference parameter?
76  const Type *ParType = Par->getType().getTypePtr();
77  if (!ParType->isReferenceType() || Par->getType().isConstQualified())
78  continue;
79  markCanNotBeConst(Arg->IgnoreParenCasts(), false);
80  }
81  }
82  } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
83  for (const auto *Arg : CE->arguments()) {
84  markCanNotBeConst(Arg->IgnoreParenCasts(), true);
85  }
86  } else if (const auto *R = dyn_cast<ReturnStmt>(S)) {
87  markCanNotBeConst(R->getRetValue(), true);
88  } else if (const auto *U = dyn_cast<UnaryOperator>(S)) {
89  markCanNotBeConst(U, true);
90  }
91  } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
92  const QualType T = VD->getType();
93  if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
94  T->isArrayType())
95  markCanNotBeConst(VD->getInit(), true);
96  }
97 }
98 
99 void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
100  // Only add nonconst integer/float pointer parameters.
101  const QualType T = Parm->getType();
102  if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
103  !(T->getPointeeType()->isIntegerType() ||
104  T->getPointeeType()->isFloatingType()))
105  return;
106 
107  if (Parameters.find(Parm) != Parameters.end())
108  return;
109 
110  ParmInfo PI;
111  PI.IsReferenced = false;
112  PI.CanBeConst = true;
113  Parameters[Parm] = PI;
114 }
115 
116 void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
117  auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
118  if (It != Parameters.end())
119  It->second.IsReferenced = true;
120 }
121 
122 void NonConstParameterCheck::onEndOfTranslationUnit() {
123  diagnoseNonConstParameters();
124 }
125 
126 void NonConstParameterCheck::diagnoseNonConstParameters() {
127  for (const auto &It : Parameters) {
128  const ParmVarDecl *Par = It.first;
129  const ParmInfo &ParamInfo = It.second;
130 
131  // Unused parameter => there are other warnings about this.
132  if (!ParamInfo.IsReferenced)
133  continue;
134 
135  // Parameter can't be const.
136  if (!ParamInfo.CanBeConst)
137  continue;
138 
139  SmallVector<FixItHint, 8> Fixes;
140  auto *Function =
141  dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
142  if (!Function)
143  continue;
144  unsigned Index = Par->getFunctionScopeIndex();
145  for (FunctionDecl *FnDecl : Function->redecls())
146  Fixes.push_back(FixItHint::CreateInsertion(
147  FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
148 
149  diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
150  << Par->getName() << Fixes;
151  }
152 }
153 
154 void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
155  bool CanNotBeConst) {
156  if (!E)
157  return;
158 
159  if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
160  // If expression is const then ignore usage.
161  const QualType T = Cast->getType();
162  if (T->isPointerType() && T->getPointeeType().isConstQualified())
163  return;
164  }
165 
166  E = E->IgnoreParenCasts();
167 
168  if (const auto *B = dyn_cast<BinaryOperator>(E)) {
169  if (B->isAdditiveOp()) {
170  // p + 2
171  markCanNotBeConst(B->getLHS(), CanNotBeConst);
172  markCanNotBeConst(B->getRHS(), CanNotBeConst);
173  } else if (B->isAssignmentOp()) {
174  markCanNotBeConst(B->getLHS(), false);
175 
176  // If LHS is not const then RHS can't be const.
177  const QualType T = B->getLHS()->getType();
178  if (T->isPointerType() && !T->getPointeeType().isConstQualified())
179  markCanNotBeConst(B->getRHS(), true);
180  }
181  } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
182  markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
183  markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
184  } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
185  if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
186  U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
187  if (const auto *SubU =
188  dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
189  markCanNotBeConst(SubU->getSubExpr(), true);
190  markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
191  } else if (U->getOpcode() == UO_Deref) {
192  if (!CanNotBeConst)
193  markCanNotBeConst(U->getSubExpr(), true);
194  } else {
195  markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
196  }
197  } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
198  markCanNotBeConst(A->getBase(), true);
199  } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
200  markCanNotBeConst(CLE->getInitializer(), true);
201  } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
202  for (const auto *Arg : Constr->arguments()) {
203  if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
204  markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
205  }
206  } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
207  for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
208  markCanNotBeConst(ILE->getInit(I), true);
209  } else if (CanNotBeConst) {
210  // Referencing parameter.
211  if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
212  auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
213  if (It != Parameters.end())
214  It->second.CanBeConst = false;
215  }
216  }
217 }
218 
219 } // namespace readability
220 } // namespace tidy
221 } // namespace clang
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::ast_matchers
Definition: AbseilMatcher.h:14
M
const google::protobuf::Message & M
Definition: Server.cpp:309
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Parameters
std::vector< Parameter > Parameters
Definition: ExtractFunction.cpp:347
Parm
Params Parm
Definition: ConfigCompileTests.cpp:41
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:258
Index
const SymbolIndex * Index
Definition: Dexp.cpp:99
CE
CaptureExpr CE
Definition: AvoidBindCheck.cpp:67
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
NonConstParameterCheck.h
ns1::ns2::B
@ B
Definition: CategoricalFeature.h:3