1 //===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "NonConstParameterCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::readability
{
17 void NonConstParameterCheck::registerMatchers(MatchFinder
*Finder
) {
18 // Add parameters to Parameters.
19 Finder
->addMatcher(parmVarDecl().bind("Parm"), this);
22 Finder
->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
24 // Track unused parameters, there is Wunused-parameter about unused
26 Finder
->addMatcher(declRefExpr().bind("Ref"), this);
28 // Analyse parameter usage in function.
29 Finder
->addMatcher(stmt(anyOf(unaryOperator(hasAnyOperatorName("++", "--")),
30 binaryOperator(), callExpr(), returnStmt(),
34 Finder
->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
37 void NonConstParameterCheck::check(const MatchFinder::MatchResult
&Result
) {
38 if (const auto *Parm
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("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)
46 } else if (const auto *Ctor
=
47 Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("Ctor")) {
48 for (const auto *Parm
: Ctor
->parameters())
50 for (const auto *Init
: Ctor
->inits())
51 markCanNotBeConst(Init
->getInit(), true);
52 } else if (const auto *Ref
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("Ref")) {
54 } else if (const auto *S
= Result
.Nodes
.getNodeAs
<Stmt
>("Mark")) {
55 if (const auto *B
= dyn_cast
<BinaryOperator
>(S
)) {
56 if (B
->isAssignmentOp())
57 markCanNotBeConst(B
, false);
58 } else if (const auto *CE
= dyn_cast
<CallExpr
>(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(Arg
->IgnoreParenCasts(), true);
66 // Data passed by nonconst reference should not be made const.
67 if (const FunctionDecl
*FD
= CE
->getDirectCallee()) {
69 for (const auto *Par
: FD
->parameters()) {
70 if (ArgNr
>= CE
->getNumArgs())
72 const Expr
*Arg
= CE
->getArg(ArgNr
++);
73 // Is this a non constant reference parameter?
74 const Type
*ParType
= Par
->getType().getTypePtr();
75 if (!ParType
->isReferenceType() || Par
->getType().isConstQualified())
77 markCanNotBeConst(Arg
->IgnoreParenCasts(), false);
80 } else if (const auto *CE
= dyn_cast
<CXXConstructExpr
>(S
)) {
81 for (const auto *Arg
: CE
->arguments()) {
82 markCanNotBeConst(Arg
->IgnoreParenCasts(), true);
84 // Data passed by nonconst reference should not be made const.
86 if (const auto *CD
= CE
->getConstructor()) {
87 for (const auto *Par
: CD
->parameters()) {
88 if (ArgNr
>= CE
->getNumArgs())
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())
95 markCanNotBeConst(Arg
->IgnoreParenCasts(), false);
98 } else if (const auto *R
= dyn_cast
<ReturnStmt
>(S
)) {
99 markCanNotBeConst(R
->getRetValue(), true);
100 } else if (const auto *U
= dyn_cast
<UnaryOperator
>(S
)) {
101 markCanNotBeConst(U
, true);
103 } else if (const auto *VD
= Result
.Nodes
.getNodeAs
<VarDecl
>("Mark")) {
104 const QualType T
= VD
->getType();
105 if ((T
->isPointerType() && !T
->getPointeeType().isConstQualified()) ||
106 T
->isArrayType() || T
->isRecordType())
107 markCanNotBeConst(VD
->getInit(), true);
108 else if (T
->isLValueReferenceType() &&
109 !T
->getPointeeType().isConstQualified())
110 markCanNotBeConst(VD
->getInit(), false);
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()))
122 if (Parameters
.find(Parm
) != Parameters
.end())
126 PI
.IsReferenced
= false;
127 PI
.CanBeConst
= true;
128 Parameters
[Parm
] = PI
;
131 void NonConstParameterCheck::setReferenced(const DeclRefExpr
*Ref
) {
132 auto It
= Parameters
.find(dyn_cast
<ParmVarDecl
>(Ref
->getDecl()));
133 if (It
!= Parameters
.end())
134 It
->second
.IsReferenced
= true;
137 void NonConstParameterCheck::onEndOfTranslationUnit() {
138 diagnoseNonConstParameters();
141 void NonConstParameterCheck::diagnoseNonConstParameters() {
142 for (const auto &It
: Parameters
) {
143 const ParmVarDecl
*Par
= It
.first
;
144 const ParmInfo
&ParamInfo
= It
.second
;
146 // Unused parameter => there are other warnings about this.
147 if (!ParamInfo
.IsReferenced
)
150 // Parameter can't be const.
151 if (!ParamInfo
.CanBeConst
)
154 SmallVector
<FixItHint
, 8> Fixes
;
156 dyn_cast_or_null
<const FunctionDecl
>(Par
->getParentFunctionOrMethod());
159 unsigned Index
= Par
->getFunctionScopeIndex();
160 for (FunctionDecl
*FnDecl
: Function
->redecls()) {
161 if (FnDecl
->getNumParams() <= Index
)
163 Fixes
.push_back(FixItHint::CreateInsertion(
164 FnDecl
->getParamDecl(Index
)->getBeginLoc(), "const "));
167 diag(Par
->getLocation(), "pointer parameter '%0' can be pointer to const")
168 << Par
->getName() << Fixes
;
172 void NonConstParameterCheck::markCanNotBeConst(const Expr
*E
,
173 bool CanNotBeConst
) {
177 if (const auto *Cast
= dyn_cast
<ImplicitCastExpr
>(E
)) {
178 // If expression is const then ignore usage.
179 const QualType T
= Cast
->getType();
180 if (T
->isPointerType() && T
->getPointeeType().isConstQualified())
184 E
= E
->IgnoreParenCasts();
186 if (const auto *B
= dyn_cast
<BinaryOperator
>(E
)) {
187 if (B
->isAdditiveOp()) {
189 markCanNotBeConst(B
->getLHS(), CanNotBeConst
);
190 markCanNotBeConst(B
->getRHS(), CanNotBeConst
);
191 } else if (B
->isAssignmentOp()) {
192 markCanNotBeConst(B
->getLHS(), false);
194 // If LHS is not const then RHS can't be const.
195 const QualType T
= B
->getLHS()->getType();
196 if (T
->isPointerType() && !T
->getPointeeType().isConstQualified())
197 markCanNotBeConst(B
->getRHS(), true);
199 } else if (const auto *C
= dyn_cast
<ConditionalOperator
>(E
)) {
200 markCanNotBeConst(C
->getTrueExpr(), CanNotBeConst
);
201 markCanNotBeConst(C
->getFalseExpr(), CanNotBeConst
);
202 } else if (const auto *U
= dyn_cast
<UnaryOperator
>(E
)) {
203 if (U
->getOpcode() == UO_PreInc
|| U
->getOpcode() == UO_PreDec
||
204 U
->getOpcode() == UO_PostInc
|| U
->getOpcode() == UO_PostDec
) {
205 if (const auto *SubU
=
206 dyn_cast
<UnaryOperator
>(U
->getSubExpr()->IgnoreParenCasts()))
207 markCanNotBeConst(SubU
->getSubExpr(), true);
208 markCanNotBeConst(U
->getSubExpr(), CanNotBeConst
);
209 } else if (U
->getOpcode() == UO_Deref
) {
211 markCanNotBeConst(U
->getSubExpr(), true);
213 markCanNotBeConst(U
->getSubExpr(), CanNotBeConst
);
215 } else if (const auto *A
= dyn_cast
<ArraySubscriptExpr
>(E
)) {
216 markCanNotBeConst(A
->getBase(), true);
217 } else if (const auto *CLE
= dyn_cast
<CompoundLiteralExpr
>(E
)) {
218 markCanNotBeConst(CLE
->getInitializer(), true);
219 } else if (const auto *Constr
= dyn_cast
<CXXConstructExpr
>(E
)) {
220 for (const auto *Arg
: Constr
->arguments()) {
221 if (const auto *M
= dyn_cast
<MaterializeTemporaryExpr
>(Arg
))
222 markCanNotBeConst(cast
<Expr
>(M
->getSubExpr()), CanNotBeConst
);
224 } else if (const auto *ILE
= dyn_cast
<InitListExpr
>(E
)) {
225 for (unsigned I
= 0U; I
< ILE
->getNumInits(); ++I
)
226 markCanNotBeConst(ILE
->getInit(I
), true);
227 } else if (CanNotBeConst
) {
228 // Referencing parameter.
229 if (const auto *D
= dyn_cast
<DeclRefExpr
>(E
)) {
230 auto It
= Parameters
.find(dyn_cast
<ParmVarDecl
>(D
->getDecl()));
231 if (It
!= Parameters
.end())
232 It
->second
.CanBeConst
= false;
237 } // namespace clang::tidy::readability