blob: 878eab8b95650875c5f8ca5f999dd52c84b132a0 [file] [log] [blame]
Daniel Marjamaki9e4ecfa2016-08-23 10:09:08 +00001//===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Daniel Marjamaki9e4ecfa2016-08-23 10:09:08 +00006//
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 {
16namespace tidy {
17namespace readability {
18
19void 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
40void 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
100void 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
117void 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
123void NonConstParameterCheck::onEndOfTranslationUnit() {
124 diagnoseNonConstParameters();
125}
126
127void 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
Alexander Kornienko93327142017-11-27 12:42:04 +0000140 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(
Stephen Kelly43465bf2018-08-09 22:42:26 +0000148 FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
Alexander Kornienko93327142017-11-27 12:42:04 +0000149
Daniel Marjamaki9e4ecfa2016-08-23 10:09:08 +0000150 diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
Alexander Kornienko93327142017-11-27 12:42:04 +0000151 << Par->getName() << Fixes;
Daniel Marjamaki9e4ecfa2016-08-23 10:09:08 +0000152 }
153}
154
155void 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))
Tykerb0561b32019-11-17 11:41:55 +0100205 markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
Daniel Marjamaki9e4ecfa2016-08-23 10:09:08 +0000206 }
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