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