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