brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.0 KiB · 9fbe3ba Raw
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