1 //===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NonConstParameterCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
registerMatchers(MatchFinder * Finder)19 void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
20   // Add parameters to Parameters.
21   Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this);
22 
23   // C++ constructor.
24   Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
25 
26   // Track unused parameters, there is Wunused-parameter about unused
27   // parameters.
28   Finder->addMatcher(declRefExpr().bind("Ref"), this);
29 
30   // Analyse parameter usage in function.
31   Finder->addMatcher(
32       traverse(TK_AsIs,
33                stmt(anyOf(unaryOperator(hasAnyOperatorName("++", "--")),
34                           binaryOperator(), callExpr(), returnStmt(),
35                           cxxConstructExpr()))
36                    .bind("Mark")),
37       this);
38   Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
39 }
40 
check(const MatchFinder::MatchResult & Result)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)
46           return;
47       }
48     }
49     addParm(Parm);
50   } else if (const auto *Ctor =
51                  Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) {
52     for (const auto *Parm : Ctor->parameters())
53       addParm(Parm);
54     for (const auto *Init : Ctor->inits())
55       markCanNotBeConst(Init->getInit(), true);
56   } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) {
57     setReferenced(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)) {
63       // Typically, if a parameter is const then it is fine to make the data
64       // const. But sometimes the data is written even though the parameter
65       // is const. Mark all data passed by address to the function.
66       for (const auto *Arg : CE->arguments()) {
67         markCanNotBeConst(Arg->IgnoreParenCasts(), true);
68       }
69 
70       // Data passed by nonconst reference should not be made const.
71       if (const FunctionDecl *FD = CE->getDirectCallee()) {
72         unsigned ArgNr = 0U;
73         for (const auto *Par : FD->parameters()) {
74           if (ArgNr >= CE->getNumArgs())
75             break;
76           const Expr *Arg = CE->getArg(ArgNr++);
77           // Is this a non constant reference parameter?
78           const Type *ParType = Par->getType().getTypePtr();
79           if (!ParType->isReferenceType() || Par->getType().isConstQualified())
80             continue;
81           markCanNotBeConst(Arg->IgnoreParenCasts(), false);
82         }
83       }
84     } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
85       for (const auto *Arg : CE->arguments()) {
86         markCanNotBeConst(Arg->IgnoreParenCasts(), true);
87       }
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);
92     }
93   } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
94     const QualType T = VD->getType();
95     if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
96         T->isArrayType())
97       markCanNotBeConst(VD->getInit(), true);
98   }
99 }
100 
addParm(const ParmVarDecl * Parm)101 void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
102   // Only add nonconst integer/float pointer parameters.
103   const QualType T = Parm->getType();
104   if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
105       !(T->getPointeeType()->isIntegerType() ||
106         T->getPointeeType()->isFloatingType()))
107     return;
108 
109   if (Parameters.find(Parm) != Parameters.end())
110     return;
111 
112   ParmInfo PI;
113   PI.IsReferenced = false;
114   PI.CanBeConst = true;
115   Parameters[Parm] = PI;
116 }
117 
setReferenced(const DeclRefExpr * Ref)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;
122 }
123 
onEndOfTranslationUnit()124 void NonConstParameterCheck::onEndOfTranslationUnit() {
125   diagnoseNonConstParameters();
126 }
127 
diagnoseNonConstParameters()128 void NonConstParameterCheck::diagnoseNonConstParameters() {
129   for (const auto &It : Parameters) {
130     const ParmVarDecl *Par = It.first;
131     const ParmInfo &ParamInfo = It.second;
132 
133     // Unused parameter => there are other warnings about this.
134     if (!ParamInfo.IsReferenced)
135       continue;
136 
137     // Parameter can't be const.
138     if (!ParamInfo.CanBeConst)
139       continue;
140 
141     SmallVector<FixItHint, 8> Fixes;
142     auto *Function =
143         dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());
144     if (!Function)
145       continue;
146     unsigned Index = Par->getFunctionScopeIndex();
147     for (FunctionDecl *FnDecl : Function->redecls())
148       Fixes.push_back(FixItHint::CreateInsertion(
149           FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
150 
151     diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
152         << Par->getName() << Fixes;
153   }
154 }
155 
markCanNotBeConst(const Expr * E,bool CanNotBeConst)156 void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
157                                                bool CanNotBeConst) {
158   if (!E)
159     return;
160 
161   if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
162     // If expression is const then ignore usage.
163     const QualType T = Cast->getType();
164     if (T->isPointerType() && T->getPointeeType().isConstQualified())
165       return;
166   }
167 
168   E = E->IgnoreParenCasts();
169 
170   if (const auto *B = dyn_cast<BinaryOperator>(E)) {
171     if (B->isAdditiveOp()) {
172       // p + 2
173       markCanNotBeConst(B->getLHS(), CanNotBeConst);
174       markCanNotBeConst(B->getRHS(), CanNotBeConst);
175     } else if (B->isAssignmentOp()) {
176       markCanNotBeConst(B->getLHS(), false);
177 
178       // If LHS is not const then RHS can't be const.
179       const QualType T = B->getLHS()->getType();
180       if (T->isPointerType() && !T->getPointeeType().isConstQualified())
181         markCanNotBeConst(B->getRHS(), true);
182     }
183   } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
184     markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
185     markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
186   } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
187     if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
188         U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
189       if (const auto *SubU =
190               dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
191         markCanNotBeConst(SubU->getSubExpr(), true);
192       markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
193     } else if (U->getOpcode() == UO_Deref) {
194       if (!CanNotBeConst)
195         markCanNotBeConst(U->getSubExpr(), true);
196     } else {
197       markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
198     }
199   } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
200     markCanNotBeConst(A->getBase(), true);
201   } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
202     markCanNotBeConst(CLE->getInitializer(), true);
203   } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
204     for (const auto *Arg : Constr->arguments()) {
205       if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
206         markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);
207     }
208   } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
209     for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
210       markCanNotBeConst(ILE->getInit(I), true);
211   } else if (CanNotBeConst) {
212     // Referencing parameter.
213     if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
214       auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
215       if (It != Parameters.end())
216         It->second.CanBeConst = false;
217     }
218   }
219 }
220 
221 } // namespace readability
222 } // namespace tidy
223 } // namespace clang
224