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