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