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