11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers;
18 namespace readability {
20 void NonConstParameterCheck::registerMatchers(MatchFinder *
Finder) {
22 Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind(
"Parm"),
this);
25 Finder->addMatcher(cxxConstructorDecl().bind(
"Ctor"),
this);
29 Finder->addMatcher(declRefExpr().bind(
"Ref"),
this);
32 Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName(
"++"),
33 hasOperatorName(
"--"))),
34 binaryOperator(), callExpr(), returnStmt(),
38 Finder->addMatcher(varDecl(hasInitializer(anything())).bind(
"Mark"),
this);
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)
50 }
else if (
const auto *Ctor =
51 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"Ctor")) {
52 for (
const auto *Parm : Ctor->parameters())
54 for (
const auto *Init : Ctor->inits())
55 markCanNotBeConst(Init->getInit(),
true);
56 }
else if (
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>(
"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)) {
66 for (
const auto *Arg : CE->arguments()) {
67 markCanNotBeConst(Arg->IgnoreParenCasts(),
true);
71 if (
const FunctionDecl *FD = CE->getDirectCallee()) {
73 for (
const auto *Par : FD->parameters()) {
74 if (ArgNr >= CE->getNumArgs())
76 const Expr *Arg = CE->getArg(ArgNr++);
78 const Type *ParType = Par->getType().getTypePtr();
79 if (!ParType->isReferenceType() || Par->getType().isConstQualified())
81 markCanNotBeConst(Arg->IgnoreParenCasts(),
false);
84 }
else if (
const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
85 for (
const auto *Arg : CE->arguments()) {
86 markCanNotBeConst(Arg->IgnoreParenCasts(),
true);
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);
93 }
else if (
const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"Mark")) {
94 const QualType T = VD->getType();
95 if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
97 markCanNotBeConst(VD->getInit(),
true);
101 void NonConstParameterCheck::addParm(
const ParmVarDecl *Parm) {
103 const QualType T = Parm->getType();
104 if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
105 !(T->getPointeeType()->isIntegerType() ||
106 T->getPointeeType()->isFloatingType()))
109 if (Parameters.find(Parm) != Parameters.end())
113 PI.IsReferenced =
false;
114 PI.CanBeConst =
true;
115 Parameters[Parm] = PI;
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;
124 void NonConstParameterCheck::onEndOfTranslationUnit() {
125 diagnoseNonConstParameters();
128 void NonConstParameterCheck::diagnoseNonConstParameters() {
129 for (
const auto &It : Parameters) {
130 const ParmVarDecl *Par = It.first;
131 const ParmInfo &ParamInfo = It.second;
134 if (!ParamInfo.IsReferenced)
138 if (!ParamInfo.CanBeConst)
141 diag(Par->getLocation(),
"pointer parameter '%0' can be pointer to const")
143 << FixItHint::CreateInsertion(Par->getLocStart(),
"const ");
147 void NonConstParameterCheck::markCanNotBeConst(
const Expr *E,
148 bool CanNotBeConst) {
152 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
154 const QualType T = Cast->getType();
155 if (T->isPointerType() && T->getPointeeType().isConstQualified())
159 E = E->IgnoreParenCasts();
161 if (
const auto *B = dyn_cast<BinaryOperator>(E)) {
162 if (B->isAdditiveOp()) {
164 markCanNotBeConst(B->getLHS(), CanNotBeConst);
165 markCanNotBeConst(B->getRHS(), CanNotBeConst);
166 }
else if (B->isAssignmentOp()) {
167 markCanNotBeConst(B->getLHS(),
false);
170 const QualType T = B->getLHS()->getType();
171 if (T->isPointerType() && !T->getPointeeType().isConstQualified())
172 markCanNotBeConst(B->getRHS(),
true);
174 }
else if (
const auto *C = dyn_cast<ConditionalOperator>(E)) {
175 markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
176 markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
177 }
else if (
const auto *U = dyn_cast<UnaryOperator>(E)) {
178 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
179 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
180 if (
const auto *SubU =
181 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
182 markCanNotBeConst(SubU->getSubExpr(),
true);
183 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
184 }
else if (U->getOpcode() == UO_Deref) {
186 markCanNotBeConst(U->getSubExpr(),
true);
188 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
190 }
else if (
const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
191 markCanNotBeConst(A->getBase(),
true);
192 }
else if (
const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
193 markCanNotBeConst(CLE->getInitializer(),
true);
194 }
else if (
const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
195 for (
const auto *Arg : Constr->arguments()) {
196 if (
const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
197 markCanNotBeConst(cast<Expr>(M->getTemporary()), CanNotBeConst);
199 }
else if (
const auto *ILE = dyn_cast<InitListExpr>(E)) {
200 for (
unsigned I = 0U; I < ILE->getNumInits(); ++I)
201 markCanNotBeConst(ILE->getInit(I),
true);
202 }
else if (CanNotBeConst) {
204 if (
const auto *D = dyn_cast<DeclRefExpr>(E)) {
205 auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
206 if (It != Parameters.end())
207 It->second.CanBeConst =
false;
std::unique_ptr< ast_matchers::MatchFinder > Finder