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