10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
18AST_MATCHER_P(VarDecl, hasOwnInitializer, ast_matchers::internal::Matcher<Expr>,
20 const Expr *Initializer = Node.getInit();
21 return Initializer !=
nullptr &&
22 InnerMatcher.matches(*Initializer, Finder, Builder);
27 unsigned ParamIndex) {
28 ASTContext &Context = Function.getASTContext();
29 const auto *Proto = Function.getType()->getAs<FunctionProtoType>();
35 ParamTypes[ParamIndex] = Context.getPointerType(
36 ParamTypes[ParamIndex]->getPointeeType().withConst());
39 Function.getParent()->lookup(Function.getDeclName()), [&](
const Decl *D) {
40 if (const auto *Using = dyn_cast<UsingShadowDecl>(D))
41 D = Using->getTargetDecl();
42 const FunctionDecl *Overload = D->getAsFunction();
44 Overload->getCanonicalDecl() == Function.getCanonicalDecl())
47 const QualType ConstParamFunctionType = Context.getFunctionType(
48 Overload->getReturnType(), ParamTypes, Proto->getExtProtoInfo());
49 return Context.hasSameFunctionTypeIgnoringExceptionSpec(
50 ConstParamFunctionType, Overload->getType());
56 Finder->addMatcher(parmVarDecl().bind(
"Parm"),
this);
59 Finder->addMatcher(cxxConstructorDecl().bind(
"Ctor"),
this);
63 Finder->addMatcher(declRefExpr().bind(
"Ref"),
this);
67 stmt(anyOf(unaryOperator(hasAnyOperatorName(
"++",
"--")),
68 binaryOperator(), callExpr(), returnStmt(), cxxConstructExpr(),
69 cxxUnresolvedConstructExpr(), atomicExpr()))
72 Finder->addMatcher(varDecl(hasOwnInitializer(anything())).bind(
"Mark"),
this);
76 if (
const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>(
"Parm")) {
77 if (
const DeclContext *D = Parm->getParentFunctionOrMethod()) {
78 if (
const auto *M = dyn_cast<CXXMethodDecl>(D);
79 M && (M->isVirtual() || M->size_overridden_methods() != 0))
83 }
else if (
const auto *Ctor =
84 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"Ctor")) {
85 for (
const auto *Parm : Ctor->parameters())
87 for (
const auto *Init : Ctor->inits())
88 markCanNotBeConst(Init->getInit(),
true);
89 }
else if (
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>(
"Ref")) {
91 }
else if (
const auto *S = Result.Nodes.getNodeAs<Stmt>(
"Mark")) {
92 if (
const auto *B = dyn_cast<BinaryOperator>(S)) {
93 if (B->isAssignmentOp())
94 markCanNotBeConst(B,
false);
95 }
else if (
const auto *CE = dyn_cast<CallExpr>(S)) {
99 for (
const auto *Arg : CE->arguments())
100 markCanNotBeConst(Arg->IgnoreParenCasts(),
true);
103 if (
const FunctionDecl *FD = CE->getDirectCallee()) {
105 for (
const auto *Par : FD->parameters()) {
106 if (ArgNr >= CE->getNumArgs())
108 const Expr *Arg = CE->getArg(ArgNr++);
110 const Type *ParType = Par->getType().getTypePtr();
111 if (!ParType->isReferenceType() || Par->getType().isConstQualified())
113 markCanNotBeConst(Arg->IgnoreParenCasts(),
false);
116 }
else if (
const auto *AE = dyn_cast<AtomicExpr>(S)) {
120 for (
const Expr *SubExpr :
121 llvm::ArrayRef(AE->getSubExprs(), AE->getNumSubExprs()))
122 markCanNotBeConst(SubExpr->IgnoreParenCasts(),
true);
123 }
else if (
const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
124 for (
const auto *Arg : CE->arguments())
125 markCanNotBeConst(Arg->IgnoreParenCasts(),
true);
128 if (
const auto *CD = CE->getConstructor()) {
129 for (
const auto *Par : CD->parameters()) {
130 if (ArgNr >= CE->getNumArgs())
132 const Expr *Arg = CE->getArg(ArgNr++);
134 const Type *ParType = Par->getType().getTypePtr();
135 if (!ParType->isReferenceType() || Par->getType().isConstQualified())
137 markCanNotBeConst(Arg->IgnoreParenCasts(),
false);
140 }
else if (
const auto *CE = dyn_cast<CXXUnresolvedConstructExpr>(S)) {
141 markCanNotBeConst(CE,
true);
142 }
else if (
const auto *R = dyn_cast<ReturnStmt>(S)) {
143 markCanNotBeConst(R->getRetValue(),
true);
144 }
else if (
const auto *U = dyn_cast<UnaryOperator>(S)) {
145 markCanNotBeConst(U,
true);
147 }
else if (
const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"Mark")) {
148 const QualType T = VD->getType();
149 if (T->isDependentType()) {
150 const Expr *Init = VD->getInit()->IgnoreParenCasts();
151 if (
const auto *U = dyn_cast<UnaryOperator>(Init);
152 U && U->getOpcode() == UO_Deref) {
153 markCanNotBeConst(U->getSubExpr(),
true);
154 }
else if (
const auto *PLE = dyn_cast<ParenListExpr>(Init)) {
155 for (
const Expr *E : PLE->exprs()) {
156 E = E->IgnoreParenCasts();
157 if (
const auto *U = dyn_cast<UnaryOperator>(E);
158 U && U->getOpcode() == UO_Deref)
159 markCanNotBeConst(U->getSubExpr(),
true);
161 markCanNotBeConst(E,
true);
164 markCanNotBeConst(Init,
true);
166 }
else if ((T->isPointerType() &&
167 !T->getPointeeType().isConstQualified()) ||
168 T->isArrayType() || T->isRecordType()) {
169 markCanNotBeConst(VD->getInit(),
true);
170 }
else if (T->isLValueReferenceType() &&
171 !T->getPointeeType().isConstQualified()) {
172 markCanNotBeConst(VD->getInit(),
false);
177void NonConstParameterCheck::addParm(
const ParmVarDecl *Parm) {
179 const QualType T = Parm->getType();
180 if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
181 !(T->getPointeeType()->isIntegerType() ||
182 T->getPointeeType()->isFloatingType()))
185 auto [It, Inserted] = Parameters.try_emplace(Parm);
189 It->second.IsReferenced =
false;
190 It->second.CanBeConst =
true;
193void NonConstParameterCheck::setReferenced(
const DeclRefExpr *Ref) {
194 const auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
195 if (It != Parameters.end())
196 It->second.IsReferenced =
true;
200 diagnoseNonConstParameters();
203void NonConstParameterCheck::diagnoseNonConstParameters() {
204 for (
const auto &It : Parameters) {
205 const ParmVarDecl *Par = It.first;
206 const ParmInfo &ParamInfo = It.second;
209 if (!ParamInfo.IsReferenced)
213 if (!ParamInfo.CanBeConst)
218 dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
221 const unsigned Index = Par->getFunctionScopeIndex();
225 for (FunctionDecl *FnDecl : Function->redecls()) {
226 if (FnDecl->getNumParams() <= Index)
228 Fixes.push_back(FixItHint::CreateInsertion(
229 FnDecl->getParamDecl(Index)->getBeginLoc(),
"const "));
232 diag(Par->getLocation(),
"pointer parameter '%0' can be pointer to const")
233 << Par->getName() << Fixes;
237void NonConstParameterCheck::markCanNotBeConst(
const Expr *E,
238 bool CanNotBeConst) {
242 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
244 const QualType T = Cast->getType();
245 if (T->isPointerType() && T->getPointeeType().isConstQualified())
249 E = E->IgnoreParenCasts();
251 if (
const auto *B = dyn_cast<BinaryOperator>(E)) {
252 if (
B->isAdditiveOp()) {
254 markCanNotBeConst(
B->getLHS(), CanNotBeConst);
255 markCanNotBeConst(
B->getRHS(), CanNotBeConst);
256 }
else if (
B->isAssignmentOp()) {
257 markCanNotBeConst(
B->getLHS(),
false);
260 const QualType
T =
B->getLHS()->getType();
261 if (
T->isPointerType() && !
T->getPointeeType().isConstQualified())
262 markCanNotBeConst(
B->getRHS(),
true);
264 }
else if (
const auto *C = dyn_cast<ConditionalOperator>(E)) {
265 markCanNotBeConst(
C->getTrueExpr(), CanNotBeConst);
266 markCanNotBeConst(
C->getFalseExpr(), CanNotBeConst);
267 }
else if (
const auto *U = dyn_cast<UnaryOperator>(E)) {
268 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
269 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
270 if (
const auto *SubU =
271 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
272 markCanNotBeConst(SubU->getSubExpr(),
true);
273 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
274 }
else if (U->getOpcode() == UO_Deref) {
276 markCanNotBeConst(U->getSubExpr(),
true);
278 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
280 }
else if (
const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
281 if (
A->isInstantiationDependent()) {
282 markCanNotBeConst(
A->getLHS(),
true);
283 markCanNotBeConst(
A->getRHS(),
true);
285 markCanNotBeConst(
A->getBase(),
true);
287 }
else if (
const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
288 markCanNotBeConst(CLE->getInitializer(),
true);
289 }
else if (
const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
290 for (
const auto *Arg : Constr->arguments())
291 if (
const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
292 markCanNotBeConst(M->getSubExpr(), CanNotBeConst);
294 markCanNotBeConst(Arg, CanNotBeConst);
295 }
else if (
const auto *CE = dyn_cast<CXXUnresolvedConstructExpr>(E)) {
296 for (
const auto *Arg : CE->arguments())
297 markCanNotBeConst(Arg, CanNotBeConst);
298 }
else if (
const auto *ILE = dyn_cast<InitListExpr>(E)) {
299 for (
unsigned I = 0U; I < ILE->getNumInits(); ++I)
300 markCanNotBeConst(ILE->getInit(I), CanNotBeConst);
301 }
else if (
const auto *PLE = dyn_cast<ParenListExpr>(E)) {
302 for (
unsigned I = 0U; I < PLE->getNumExprs(); ++I)
303 markCanNotBeConst(PLE->getExpr(I), CanNotBeConst);
304 }
else if (CanNotBeConst) {
306 if (
const auto *D = dyn_cast<DeclRefExpr>(E)) {
307 const auto It = Parameters.find(dyn_cast<ParmVarDecl>(
D->getDecl()));
308 if (It != Parameters.end())
309 It->second.CanBeConst =
false;
void onEndOfTranslationUnit() override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
static bool wouldConflictWithExistingDecl(const FunctionDecl &Function, unsigned ParamIndex)