|  | //===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "NonConstParameterCheck.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang { | 
|  | namespace tidy { | 
|  | namespace readability { | 
|  |  | 
|  | void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) { | 
|  | // Add parameters to Parameters. | 
|  | Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this); | 
|  |  | 
|  | // C++ constructor. | 
|  | Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this); | 
|  |  | 
|  | // Track unused parameters, there is Wunused-parameter about unused | 
|  | // parameters. | 
|  | Finder->addMatcher(declRefExpr().bind("Ref"), this); | 
|  |  | 
|  | // Analyse parameter usage in function. | 
|  | Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName("++"), | 
|  | hasOperatorName("--"))), | 
|  | binaryOperator(), callExpr(), returnStmt(), | 
|  | cxxConstructExpr())) | 
|  | .bind("Mark"), | 
|  | this); | 
|  | Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this); | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) { | 
|  | if (const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>("Parm")) { | 
|  | if (const DeclContext *D = Parm->getParentFunctionOrMethod()) { | 
|  | if (const auto *M = dyn_cast<CXXMethodDecl>(D)) { | 
|  | if (M->isVirtual() || M->size_overridden_methods() != 0) | 
|  | return; | 
|  | } | 
|  | } | 
|  | addParm(Parm); | 
|  | } else if (const auto *Ctor = | 
|  | Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) { | 
|  | for (const auto *Parm : Ctor->parameters()) | 
|  | addParm(Parm); | 
|  | for (const auto *Init : Ctor->inits()) | 
|  | markCanNotBeConst(Init->getInit(), true); | 
|  | } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) { | 
|  | setReferenced(Ref); | 
|  | } else if (const auto *S = Result.Nodes.getNodeAs<Stmt>("Mark")) { | 
|  | if (const auto *B = dyn_cast<BinaryOperator>(S)) { | 
|  | if (B->isAssignmentOp()) | 
|  | markCanNotBeConst(B, false); | 
|  | } else if (const auto *CE = dyn_cast<CallExpr>(S)) { | 
|  | // Typically, if a parameter is const then it is fine to make the data | 
|  | // const. But sometimes the data is written even though the parameter | 
|  | // is const. Mark all data passed by address to the function. | 
|  | for (const auto *Arg : CE->arguments()) { | 
|  | markCanNotBeConst(Arg->IgnoreParenCasts(), true); | 
|  | } | 
|  |  | 
|  | // Data passed by nonconst reference should not be made const. | 
|  | if (const FunctionDecl *FD = CE->getDirectCallee()) { | 
|  | unsigned ArgNr = 0U; | 
|  | for (const auto *Par : FD->parameters()) { | 
|  | if (ArgNr >= CE->getNumArgs()) | 
|  | break; | 
|  | const Expr *Arg = CE->getArg(ArgNr++); | 
|  | // Is this a non constant reference parameter? | 
|  | const Type *ParType = Par->getType().getTypePtr(); | 
|  | if (!ParType->isReferenceType() || Par->getType().isConstQualified()) | 
|  | continue; | 
|  | markCanNotBeConst(Arg->IgnoreParenCasts(), false); | 
|  | } | 
|  | } | 
|  | } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) { | 
|  | for (const auto *Arg : CE->arguments()) { | 
|  | markCanNotBeConst(Arg->IgnoreParenCasts(), true); | 
|  | } | 
|  | } else if (const auto *R = dyn_cast<ReturnStmt>(S)) { | 
|  | markCanNotBeConst(R->getRetValue(), true); | 
|  | } else if (const auto *U = dyn_cast<UnaryOperator>(S)) { | 
|  | markCanNotBeConst(U, true); | 
|  | } | 
|  | } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) { | 
|  | const QualType T = VD->getType(); | 
|  | if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) || | 
|  | T->isArrayType()) | 
|  | markCanNotBeConst(VD->getInit(), true); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) { | 
|  | // Only add nonconst integer/float pointer parameters. | 
|  | const QualType T = Parm->getType(); | 
|  | if (!T->isPointerType() || T->getPointeeType().isConstQualified() || | 
|  | !(T->getPointeeType()->isIntegerType() || | 
|  | T->getPointeeType()->isFloatingType())) | 
|  | return; | 
|  |  | 
|  | if (Parameters.find(Parm) != Parameters.end()) | 
|  | return; | 
|  |  | 
|  | ParmInfo PI; | 
|  | PI.IsReferenced = false; | 
|  | PI.CanBeConst = true; | 
|  | Parameters[Parm] = PI; | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) { | 
|  | auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl())); | 
|  | if (It != Parameters.end()) | 
|  | It->second.IsReferenced = true; | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::onEndOfTranslationUnit() { | 
|  | diagnoseNonConstParameters(); | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::diagnoseNonConstParameters() { | 
|  | for (const auto &It : Parameters) { | 
|  | const ParmVarDecl *Par = It.first; | 
|  | const ParmInfo &ParamInfo = It.second; | 
|  |  | 
|  | // Unused parameter => there are other warnings about this. | 
|  | if (!ParamInfo.IsReferenced) | 
|  | continue; | 
|  |  | 
|  | // Parameter can't be const. | 
|  | if (!ParamInfo.CanBeConst) | 
|  | continue; | 
|  |  | 
|  | diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const") | 
|  | << Par->getName() | 
|  | << FixItHint::CreateInsertion(Par->getLocStart(), "const "); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NonConstParameterCheck::markCanNotBeConst(const Expr *E, | 
|  | bool CanNotBeConst) { | 
|  | if (!E) | 
|  | return; | 
|  |  | 
|  | if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) { | 
|  | // If expression is const then ignore usage. | 
|  | const QualType T = Cast->getType(); | 
|  | if (T->isPointerType() && T->getPointeeType().isConstQualified()) | 
|  | return; | 
|  | } | 
|  |  | 
|  | E = E->IgnoreParenCasts(); | 
|  |  | 
|  | if (const auto *B = dyn_cast<BinaryOperator>(E)) { | 
|  | if (B->isAdditiveOp()) { | 
|  | // p + 2 | 
|  | markCanNotBeConst(B->getLHS(), CanNotBeConst); | 
|  | markCanNotBeConst(B->getRHS(), CanNotBeConst); | 
|  | } else if (B->isAssignmentOp()) { | 
|  | markCanNotBeConst(B->getLHS(), false); | 
|  |  | 
|  | // If LHS is not const then RHS can't be const. | 
|  | const QualType T = B->getLHS()->getType(); | 
|  | if (T->isPointerType() && !T->getPointeeType().isConstQualified()) | 
|  | markCanNotBeConst(B->getRHS(), true); | 
|  | } | 
|  | } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) { | 
|  | markCanNotBeConst(C->getTrueExpr(), CanNotBeConst); | 
|  | markCanNotBeConst(C->getFalseExpr(), CanNotBeConst); | 
|  | } else if (const auto *U = dyn_cast<UnaryOperator>(E)) { | 
|  | if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec || | 
|  | U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) { | 
|  | if (const auto *SubU = | 
|  | dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts())) | 
|  | markCanNotBeConst(SubU->getSubExpr(), true); | 
|  | markCanNotBeConst(U->getSubExpr(), CanNotBeConst); | 
|  | } else if (U->getOpcode() == UO_Deref) { | 
|  | if (!CanNotBeConst) | 
|  | markCanNotBeConst(U->getSubExpr(), true); | 
|  | } else { | 
|  | markCanNotBeConst(U->getSubExpr(), CanNotBeConst); | 
|  | } | 
|  | } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) { | 
|  | markCanNotBeConst(A->getBase(), true); | 
|  | } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) { | 
|  | markCanNotBeConst(CLE->getInitializer(), true); | 
|  | } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) { | 
|  | for (const auto *Arg : Constr->arguments()) { | 
|  | if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg)) | 
|  | markCanNotBeConst(cast<Expr>(M->getTemporary()), CanNotBeConst); | 
|  | } | 
|  | } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) { | 
|  | for (unsigned I = 0U; I < ILE->getNumInits(); ++I) | 
|  | markCanNotBeConst(ILE->getInit(I), true); | 
|  | } else if (CanNotBeConst) { | 
|  | // Referencing parameter. | 
|  | if (const auto *D = dyn_cast<DeclRefExpr>(E)) { | 
|  | auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl())); | 
|  | if (It != Parameters.end()) | 
|  | It->second.CanBeConst = false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace readability | 
|  | } // namespace tidy | 
|  | } // namespace clang |