237 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "NonConstParameterCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::readability {16 17void 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 unused25 // 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 37void 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 data60 // const. But sometimes the data is written even though the parameter61 // 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 114void 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 auto [It, Inserted] = Parameters.try_emplace(Parm);123 if (!Inserted)124 return;125 126 It->second.IsReferenced = false;127 It->second.CanBeConst = true;128}129 130void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {131 auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));132 if (It != Parameters.end())133 It->second.IsReferenced = true;134}135 136void NonConstParameterCheck::onEndOfTranslationUnit() {137 diagnoseNonConstParameters();138}139 140void NonConstParameterCheck::diagnoseNonConstParameters() {141 for (const auto &It : Parameters) {142 const ParmVarDecl *Par = It.first;143 const ParmInfo &ParamInfo = It.second;144 145 // Unused parameter => there are other warnings about this.146 if (!ParamInfo.IsReferenced)147 continue;148 149 // Parameter can't be const.150 if (!ParamInfo.CanBeConst)151 continue;152 153 SmallVector<FixItHint, 8> Fixes;154 auto *Function =155 dyn_cast_or_null<const FunctionDecl>(Par->getParentFunctionOrMethod());156 if (!Function)157 continue;158 const unsigned Index = Par->getFunctionScopeIndex();159 for (FunctionDecl *FnDecl : Function->redecls()) {160 if (FnDecl->getNumParams() <= Index)161 continue;162 Fixes.push_back(FixItHint::CreateInsertion(163 FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));164 }165 166 diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")167 << Par->getName() << Fixes;168 }169}170 171void NonConstParameterCheck::markCanNotBeConst(const Expr *E,172 bool CanNotBeConst) {173 if (!E)174 return;175 176 if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {177 // If expression is const then ignore usage.178 const QualType T = Cast->getType();179 if (T->isPointerType() && T->getPointeeType().isConstQualified())180 return;181 }182 183 E = E->IgnoreParenCasts();184 185 if (const auto *B = dyn_cast<BinaryOperator>(E)) {186 if (B->isAdditiveOp()) {187 // p + 2188 markCanNotBeConst(B->getLHS(), CanNotBeConst);189 markCanNotBeConst(B->getRHS(), CanNotBeConst);190 } else if (B->isAssignmentOp()) {191 markCanNotBeConst(B->getLHS(), false);192 193 // If LHS is not const then RHS can't be const.194 const QualType T = B->getLHS()->getType();195 if (T->isPointerType() && !T->getPointeeType().isConstQualified())196 markCanNotBeConst(B->getRHS(), true);197 }198 } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {199 markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);200 markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);201 } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {202 if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||203 U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {204 if (const auto *SubU =205 dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))206 markCanNotBeConst(SubU->getSubExpr(), true);207 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);208 } else if (U->getOpcode() == UO_Deref) {209 if (!CanNotBeConst)210 markCanNotBeConst(U->getSubExpr(), true);211 } else {212 markCanNotBeConst(U->getSubExpr(), CanNotBeConst);213 }214 } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {215 markCanNotBeConst(A->getBase(), true);216 } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {217 markCanNotBeConst(CLE->getInitializer(), true);218 } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {219 for (const auto *Arg : Constr->arguments()) {220 if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))221 markCanNotBeConst(cast<Expr>(M->getSubExpr()), CanNotBeConst);222 }223 } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {224 for (unsigned I = 0U; I < ILE->getNumInits(); ++I)225 markCanNotBeConst(ILE->getInit(I), true);226 } else if (CanNotBeConst) {227 // Referencing parameter.228 if (const auto *D = dyn_cast<DeclRefExpr>(E)) {229 auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));230 if (It != Parameters.end())231 It->second.CanBeConst = false;232 }233 }234}235 236} // namespace clang::tidy::readability237