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);
29 Finder->addMatcher(stmt(anyOf(unaryOperator(hasAnyOperatorName(
"++",
"--")),
30 binaryOperator(), callExpr(), returnStmt(),
34 Finder->addMatcher(varDecl(hasInitializer(anything())).bind(
"Mark"),
this);
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)) {
62 for (
const auto *Arg :
CE->arguments()) {
63 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);
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++);
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);
114void NonConstParameterCheck::addParm(
const ParmVarDecl *
Parm) {
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;
131void 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;
138 diagnoseNonConstParameters();
141void NonConstParameterCheck::diagnoseNonConstParameters() {
142 for (
const auto &It : Parameters) {
143 const ParmVarDecl *Par = It.first;
144 const ParmInfo &ParamInfo = It.second;
147 if (!ParamInfo.IsReferenced)
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 Fixes.push_back(FixItHint::CreateInsertion(
162 FnDecl->getParamDecl(Index)->getBeginLoc(),
"const "));
164 diag(Par->getLocation(),
"pointer parameter '%0' can be pointer to const")
165 << Par->getName() << Fixes;
169void NonConstParameterCheck::markCanNotBeConst(
const Expr *
E,
170 bool CanNotBeConst) {
174 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(
E)) {
176 const QualType T = Cast->getType();
177 if (T->isPointerType() && T->getPointeeType().isConstQualified())
181 E =
E->IgnoreParenCasts();
183 if (
const auto *B = dyn_cast<BinaryOperator>(
E)) {
184 if (B->isAdditiveOp()) {
186 markCanNotBeConst(B->getLHS(), CanNotBeConst);
187 markCanNotBeConst(B->getRHS(), CanNotBeConst);
188 }
else if (
B->isAssignmentOp()) {
189 markCanNotBeConst(
B->getLHS(),
false);
192 const QualType T =
B->getLHS()->getType();
193 if (T->isPointerType() && !T->getPointeeType().isConstQualified())
194 markCanNotBeConst(
B->getRHS(),
true);
196 }
else if (
const auto *
C = dyn_cast<ConditionalOperator>(
E)) {
197 markCanNotBeConst(
C->getTrueExpr(), CanNotBeConst);
198 markCanNotBeConst(
C->getFalseExpr(), CanNotBeConst);
199 }
else if (
const auto *U = dyn_cast<UnaryOperator>(
E)) {
200 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
201 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
202 if (
const auto *SubU =
203 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
204 markCanNotBeConst(SubU->getSubExpr(),
true);
205 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
206 }
else if (U->getOpcode() == UO_Deref) {
208 markCanNotBeConst(U->getSubExpr(),
true);
210 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
212 }
else if (
const auto *A = dyn_cast<ArraySubscriptExpr>(
E)) {
213 markCanNotBeConst(
A->getBase(),
true);
214 }
else if (
const auto *CLE = dyn_cast<CompoundLiteralExpr>(
E)) {
215 markCanNotBeConst(CLE->getInitializer(),
true);
216 }
else if (
const auto *Constr = dyn_cast<CXXConstructExpr>(
E)) {
217 for (
const auto *Arg : Constr->arguments()) {
218 if (
const auto *
M = dyn_cast<MaterializeTemporaryExpr>(Arg))
219 markCanNotBeConst(cast<Expr>(
M->getSubExpr()), CanNotBeConst);
221 }
else if (
const auto *ILE = dyn_cast<InitListExpr>(
E)) {
222 for (
unsigned I = 0U; I < ILE->getNumInits(); ++I)
223 markCanNotBeConst(ILE->getInit(I),
true);
224 }
else if (CanNotBeConst) {
226 if (
const auto *D = dyn_cast<DeclRefExpr>(
E)) {
227 auto It =
Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
229 It->second.CanBeConst =
false;
ArrayRef< const ParmVarDecl * > Parameters
const google::protobuf::Message & M
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void onEndOfTranslationUnit() override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.