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