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
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::readability {
16
17void 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
37void 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
114void 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
131void 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
137void NonConstParameterCheck::onEndOfTranslationUnit() {
138 diagnoseNonConstParameters();
139}
140
141void 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
169void 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

source code of clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp