10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
19 Finder->addMatcher(parmVarDecl().bind(
"Parm"),
this);
22 Finder->addMatcher(cxxConstructorDecl().bind(
"Ctor"),
this);
26 Finder->addMatcher(declRefExpr().bind(
"Ref"),
this);
30 stmt(anyOf(unaryOperator(hasAnyOperatorName(
"++",
"--")),
31 binaryOperator(), callExpr(), returnStmt(), cxxConstructExpr(),
32 cxxUnresolvedConstructExpr()))
35 Finder->addMatcher(varDecl(hasInitializer(anything())).bind(
"Mark"),
this);
39 if (
const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>(
"Parm")) {
40 if (
const DeclContext *D = Parm->getParentFunctionOrMethod()) {
41 if (
const auto *M = dyn_cast<CXXMethodDecl>(D)) {
42 if (M->isVirtual() || M->size_overridden_methods() != 0)
47 }
else if (
const auto *Ctor =
48 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"Ctor")) {
49 for (
const auto *Parm : Ctor->parameters())
51 for (
const auto *Init : Ctor->inits())
52 markCanNotBeConst(Init->getInit(),
true);
53 }
else if (
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>(
"Ref")) {
55 }
else if (
const auto *S = Result.Nodes.getNodeAs<Stmt>(
"Mark")) {
56 if (
const auto *B = dyn_cast<BinaryOperator>(S)) {
57 if (B->isAssignmentOp())
58 markCanNotBeConst(B,
false);
59 }
else if (
const auto *CE = dyn_cast<CallExpr>(S)) {
63 for (
const auto *Arg : CE->arguments())
64 markCanNotBeConst(Arg->IgnoreParenCasts(),
true);
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++);
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);
85 if (
const auto *CD = CE->getConstructor()) {
86 for (
const auto *Par : CD->parameters()) {
87 if (ArgNr >= CE->getNumArgs())
89 const Expr *Arg = CE->getArg(ArgNr++);
91 const Type *ParType = Par->getType().getTypePtr();
92 if (!ParType->isReferenceType() || Par->getType().isConstQualified())
94 markCanNotBeConst(Arg->IgnoreParenCasts(),
false);
97 }
else if (
const auto *CE = dyn_cast<CXXUnresolvedConstructExpr>(S)) {
98 markCanNotBeConst(CE,
true);
99 }
else if (
const auto *R = dyn_cast<ReturnStmt>(S)) {
100 markCanNotBeConst(R->getRetValue(),
true);
101 }
else if (
const auto *U = dyn_cast<UnaryOperator>(S)) {
102 markCanNotBeConst(U,
true);
104 }
else if (
const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"Mark")) {
105 const QualType T = VD->getType();
106 if (T->isDependentType())
107 markCanNotBeConst(VD->getInit(),
false);
108 else if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
109 T->isArrayType() || T->isRecordType())
110 markCanNotBeConst(VD->getInit(),
true);
111 else if (T->isLValueReferenceType() &&
112 !T->getPointeeType().isConstQualified())
113 markCanNotBeConst(VD->getInit(),
false);
117void NonConstParameterCheck::addParm(
const ParmVarDecl *Parm) {
119 const QualType T = Parm->getType();
120 if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
121 !(T->getPointeeType()->isIntegerType() ||
122 T->getPointeeType()->isFloatingType()))
125 auto [It, Inserted] = Parameters.try_emplace(Parm);
129 It->second.IsReferenced =
false;
130 It->second.CanBeConst =
true;
133void NonConstParameterCheck::setReferenced(
const DeclRefExpr *Ref) {
134 auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
135 if (It != Parameters.end())
136 It->second.IsReferenced =
true;
140 diagnoseNonConstParameters();
143void NonConstParameterCheck::diagnoseNonConstParameters() {
144 for (
const auto &It : Parameters) {
145 const ParmVarDecl *Par = It.first;
146 const ParmInfo &ParamInfo = It.second;
149 if (!ParamInfo.IsReferenced)
153 if (!ParamInfo.CanBeConst)
156 SmallVector<FixItHint, 8> Fixes;
158 dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
161 const unsigned Index = Par->getFunctionScopeIndex();
162 for (FunctionDecl *FnDecl : Function->redecls()) {
163 if (FnDecl->getNumParams() <= Index)
165 Fixes.push_back(FixItHint::CreateInsertion(
166 FnDecl->getParamDecl(Index)->getBeginLoc(),
"const "));
169 diag(Par->getLocation(),
"pointer parameter '%0' can be pointer to const")
170 << Par->getName() << Fixes;
174void NonConstParameterCheck::markCanNotBeConst(
const Expr *E,
175 bool CanNotBeConst) {
179 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
181 const QualType T = Cast->getType();
182 if (T->isPointerType() && T->getPointeeType().isConstQualified())
186 E = E->IgnoreParenCasts();
188 if (
const auto *B = dyn_cast<BinaryOperator>(E)) {
189 if (
B->isAdditiveOp()) {
191 markCanNotBeConst(
B->getLHS(), CanNotBeConst);
192 markCanNotBeConst(
B->getRHS(), CanNotBeConst);
193 }
else if (
B->isAssignmentOp()) {
194 markCanNotBeConst(
B->getLHS(),
false);
197 const QualType
T =
B->getLHS()->getType();
198 if (
T->isPointerType() && !
T->getPointeeType().isConstQualified())
199 markCanNotBeConst(
B->getRHS(),
true);
201 }
else if (
const auto *C = dyn_cast<ConditionalOperator>(E)) {
202 markCanNotBeConst(
C->getTrueExpr(), CanNotBeConst);
203 markCanNotBeConst(
C->getFalseExpr(), CanNotBeConst);
204 }
else if (
const auto *U = dyn_cast<UnaryOperator>(E)) {
205 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
206 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
207 if (
const auto *SubU =
208 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
209 markCanNotBeConst(SubU->getSubExpr(),
true);
210 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
211 }
else if (U->getOpcode() == UO_Deref) {
213 markCanNotBeConst(U->getSubExpr(),
true);
215 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
217 }
else if (
const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
218 markCanNotBeConst(
A->getBase(),
true);
219 }
else if (
const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
220 markCanNotBeConst(CLE->getInitializer(),
true);
221 }
else if (
const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
222 for (
const auto *Arg : Constr->arguments())
223 if (
const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
224 markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
226 markCanNotBeConst(Arg, CanNotBeConst);
227 }
else if (
const auto *CE = dyn_cast<CXXUnresolvedConstructExpr>(E)) {
228 for (
const auto *Arg : CE->arguments())
229 markCanNotBeConst(Arg, CanNotBeConst);
230 }
else if (
const auto *ILE = dyn_cast<InitListExpr>(E)) {
231 for (
unsigned I = 0U; I < ILE->getNumInits(); ++I)
232 markCanNotBeConst(ILE->getInit(I), CanNotBeConst);
233 }
else if (
const auto *PLE = dyn_cast<ParenListExpr>(E)) {
234 for (
unsigned I = 0U; I < PLE->getNumExprs(); ++I)
235 markCanNotBeConst(PLE->getExpr(I), CanNotBeConst);
236 }
else if (CanNotBeConst) {
238 if (
const auto *D = dyn_cast<DeclRefExpr>(E)) {
239 auto It = Parameters.find(dyn_cast<ParmVarDecl>(
D->getDecl()));
240 if (It != Parameters.end())
241 It->second.CanBeConst =
false;
void onEndOfTranslationUnit() override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override