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
9 #include "NonConstParameterCheck.h"
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
registerMatchers(MatchFinder * Finder)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
check(const MatchFinder::MatchResult & Result)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 // Data passed by nonconst reference should not be made const.
87 unsigned ArgNr = 0U;
88 if (const auto *CD = CE->getConstructor()) {
89 for (const auto *Par : CD->parameters()) {
90 if (ArgNr >= CE->getNumArgs())
91 break;
92 const Expr *Arg = CE->getArg(ArgNr++);
93 // Is this a non constant reference parameter?
94 const Type *ParType = Par->getType().getTypePtr();
95 if (!ParType->isReferenceType() || Par->getType().isConstQualified())
96 continue;
97 markCanNotBeConst(Arg->IgnoreParenCasts(), false);
98 }
99 }
100 } else if (const auto *R = dyn_cast<ReturnStmt>(S)) {
101 markCanNotBeConst(R->getRetValue(), true);
102 } else if (const auto *U = dyn_cast<UnaryOperator>(S)) {
103 markCanNotBeConst(U, true);
104 }
105 } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
106 const QualType T = VD->getType();
107 if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
108 T->isArrayType())
109 markCanNotBeConst(VD->getInit(), true);
110 else if (T->isLValueReferenceType() &&
111 !T->getPointeeType().isConstQualified())
112 markCanNotBeConst(VD->getInit(), false);
113 }
114 }
115
addParm(const ParmVarDecl * Parm)116 void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
117 // Only add nonconst integer/float pointer parameters.
118 const QualType T = Parm->getType();
119 if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
120 !(T->getPointeeType()->isIntegerType() ||
121 T->getPointeeType()->isFloatingType()))
122 return;
123
124 if (Parameters.find(Parm) != Parameters.end())
125 return;
126
127 ParmInfo PI;
128 PI.IsReferenced = false;
129 PI.CanBeConst = true;
130 Parameters[Parm] = PI;
131 }
132
setReferenced(const DeclRefExpr * Ref)133 void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
134 auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
135 if (It != Parameters.end())
136 It->second.IsReferenced = true;
137 }
138
onEndOfTranslationUnit()139 void NonConstParameterCheck::onEndOfTranslationUnit() {
140 diagnoseNonConstParameters();
141 }
142
diagnoseNonConstParameters()143 void NonConstParameterCheck::diagnoseNonConstParameters() {
144 for (const auto &It : Parameters) {
145 const ParmVarDecl *Par = It.first;
146 const ParmInfo &ParamInfo = It.second;
147
148 // Unused parameter => there are other warnings about this.
149 if (!ParamInfo.IsReferenced)
150 continue;
151
152 // Parameter can't be const.
153 if (!ParamInfo.CanBeConst)
154 continue;
155
156 SmallVector<FixItHint, 8> Fixes;
157 auto *Function =
158 dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
159 if (!Function)
160 continue;
161 unsigned Index = Par->getFunctionScopeIndex();
162 for (FunctionDecl *FnDecl : Function->redecls())
163 Fixes.push_back(FixItHint::CreateInsertion(
164 FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
165
166 diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
167 << Par->getName() << Fixes;
168 }
169 }
170
markCanNotBeConst(const Expr * E,bool CanNotBeConst)171 void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
172 bool CanNotBeConst) {
173 if (!E)
174 return;
175
176 if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
177 // If expression is const then ignore usage.
178 const QualType T = Cast->getType();
179 if (T->isPointerType() && T->getPointeeType().isConstQualified())
180 return;
181 }
182
183 E = E->IgnoreParenCasts();
184
185 if (const auto *B = dyn_cast<BinaryOperator>(E)) {
186 if (B->isAdditiveOp()) {
187 // p + 2
188 markCanNotBeConst(B->getLHS(), CanNotBeConst);
189 markCanNotBeConst(B->getRHS(), CanNotBeConst);
190 } else if (B->isAssignmentOp()) {
191 markCanNotBeConst(B->getLHS(), false);
192
193 // If LHS is not const then RHS can't be const.
194 const QualType T = B->getLHS()->getType();
195 if (T->isPointerType() && !T->getPointeeType().isConstQualified())
196 markCanNotBeConst(B->getRHS(), true);
197 }
198 } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
199 markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
200 markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
201 } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
202 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
203 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
204 if (const auto *SubU =
205 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
206 markCanNotBeConst(SubU->getSubExpr(), true);
207 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
208 } else if (U->getOpcode() == UO_Deref) {
209 if (!CanNotBeConst)
210 markCanNotBeConst(U->getSubExpr(), true);
211 } else {
212 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
213 }
214 } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
215 markCanNotBeConst(A->getBase(), true);
216 } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
217 markCanNotBeConst(CLE->getInitializer(), true);
218 } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
219 for (const auto *Arg : Constr->arguments()) {
220 if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
221 markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
222 }
223 } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
224 for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
225 markCanNotBeConst(ILE->getInit(I), true);
226 } else if (CanNotBeConst) {
227 // Referencing parameter.
228 if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
229 auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
230 if (It != Parameters.end())
231 It->second.CanBeConst = false;
232 }
233 }
234 }
235
236 } // namespace readability
237 } // namespace tidy
238 } // namespace clang
239