brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.9 KiB · 8709050 Raw
218 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 "UnusedParametersCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/ASTLambda.h"12#include "clang/AST/Attr.h"13#include "clang/AST/Decl.h"14#include "clang/AST/RecursiveASTVisitor.h"15#include "clang/ASTMatchers/ASTMatchFinder.h"16#include "clang/Basic/SourceManager.h"17#include "clang/Lex/Lexer.h"18#include <unordered_map>19#include <unordered_set>20 21using namespace clang::ast_matchers;22 23namespace clang::tidy::misc {24 25static bool isOverrideMethod(const FunctionDecl *Function) {26  if (const auto *MD = dyn_cast<CXXMethodDecl>(Function))27    return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();28  return false;29}30 31static bool hasAttrAfterParam(const SourceManager *SourceManager,32                              const ParmVarDecl *Param) {33  for (const auto *Attr : Param->attrs()) {34    if (SourceManager->isBeforeInTranslationUnit(Param->getLocation(),35                                                 Attr->getLocation())) {36      return true;37    }38  }39  return false;40}41 42void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {43  Finder->addMatcher(functionDecl(isDefinition(), hasBody(stmt()),44                                  hasAnyParameter(decl()),45                                  unless(hasAttr(attr::Kind::Naked)))46                         .bind("function"),47                     this);48}49 50template <typename T>51static CharSourceRange removeNode(const MatchFinder::MatchResult &Result,52                                  const T *PrevNode, const T *Node,53                                  const T *NextNode) {54  if (NextNode)55    return CharSourceRange::getCharRange(Node->getBeginLoc(),56                                         NextNode->getBeginLoc());57 58  if (PrevNode)59    return CharSourceRange::getTokenRange(60        Lexer::getLocForEndOfToken(PrevNode->getEndLoc(), 0,61                                   *Result.SourceManager,62                                   Result.Context->getLangOpts()),63        Node->getEndLoc());64 65  return CharSourceRange::getTokenRange(Node->getSourceRange());66}67 68static FixItHint removeParameter(const MatchFinder::MatchResult &Result,69                                 const FunctionDecl *Function, unsigned Index) {70  return FixItHint::CreateRemoval(removeNode(71      Result, Index > 0 ? Function->getParamDecl(Index - 1) : nullptr,72      Function->getParamDecl(Index),73      Index + 1 < Function->getNumParams() ? Function->getParamDecl(Index + 1)74                                           : nullptr));75}76 77static FixItHint removeArgument(const MatchFinder::MatchResult &Result,78                                const CallExpr *Call, unsigned Index) {79  return FixItHint::CreateRemoval(removeNode(80      Result, Index > 0 ? Call->getArg(Index - 1) : nullptr,81      Call->getArg(Index),82      Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));83}84 85class UnusedParametersCheck::IndexerVisitor86    : public RecursiveASTVisitor<IndexerVisitor> {87public:88  IndexerVisitor(ASTContext &Ctx) { TraverseAST(Ctx); }89 90  const std::unordered_set<const CallExpr *> &91  getFnCalls(const FunctionDecl *Fn) {92    return Index[Fn->getCanonicalDecl()].Calls;93  }94 95  const std::unordered_set<const DeclRefExpr *> &96  getOtherRefs(const FunctionDecl *Fn) {97    return Index[Fn->getCanonicalDecl()].OtherRefs;98  }99 100  bool shouldTraversePostOrder() const { return true; }101 102  bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {103    if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {104      Fn = Fn->getCanonicalDecl();105      Index[Fn].OtherRefs.insert(DeclRef);106    }107    return true;108  }109 110  bool WalkUpFromCallExpr(CallExpr *Call) {111    if (const auto *Fn =112            dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {113      Fn = Fn->getCanonicalDecl();114      if (const auto *Ref =115              dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {116        Index[Fn].OtherRefs.erase(Ref);117      }118      Index[Fn].Calls.insert(Call);119    }120    return true;121  }122 123private:124  struct IndexEntry {125    std::unordered_set<const CallExpr *> Calls;126    std::unordered_set<const DeclRefExpr *> OtherRefs;127  };128 129  std::unordered_map<const FunctionDecl *, IndexEntry> Index;130};131 132UnusedParametersCheck::~UnusedParametersCheck() = default;133 134UnusedParametersCheck::UnusedParametersCheck(StringRef Name,135                                             ClangTidyContext *Context)136    : ClangTidyCheck(Name, Context),137      StrictMode(Options.get("StrictMode", false)),138      IgnoreVirtual(Options.get("IgnoreVirtual", false)) {}139 140void UnusedParametersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {141  Options.store(Opts, "StrictMode", StrictMode);142  Options.store(Opts, "IgnoreVirtual", IgnoreVirtual);143}144 145void UnusedParametersCheck::warnOnUnusedParameter(146    const MatchFinder::MatchResult &Result, const FunctionDecl *Function,147    unsigned ParamIndex) {148  const auto *Param = Function->getParamDecl(ParamIndex);149  // Don't bother to diagnose invalid parameters as being unused.150  if (Param->isInvalidDecl())151    return;152  auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;153 154  if (!Indexer) {155    Indexer = std::make_unique<IndexerVisitor>(*Result.Context);156  }157 158  // Cannot remove parameter for non-local functions.159  if (Function->isExternallyVisible() ||160      !Result.SourceManager->isInMainFile(Function->getLocation()) ||161      !Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function) ||162      isLambdaCallOperator(Function)) {163    // It is illegal to omit parameter name here in C code, so early-out.164    if (!Result.Context->getLangOpts().CPlusPlus)165      return;166 167    const SourceRange RemovalRange(Param->getLocation());168    // Note: We always add a space before the '/*' to not accidentally create169    // a '*/*' for pointer types, which doesn't start a comment. clang-format170    // will clean this up afterwards.171    MyDiag << FixItHint::CreateReplacement(172        RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());173    return;174  }175 176  // Fix all redeclarations.177  for (const FunctionDecl *FD : Function->redecls())178    if (FD->param_size())179      MyDiag << removeParameter(Result, FD, ParamIndex);180 181  // Fix all call sites.182  for (const CallExpr *Call : Indexer->getFnCalls(Function))183    if (ParamIndex < Call->getNumArgs()) // See PR38055 for example.184      MyDiag << removeArgument(Result, Call, ParamIndex);185}186 187void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {188  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");189  if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())190    return;191  if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {192    if (IgnoreVirtual && Method->isVirtual())193      return;194    if (Method->isLambdaStaticInvoker())195      return;196  }197  for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {198    const auto *Param = Function->getParamDecl(I);199    if (Param->isUsed() || Param->isReferenced() || !Param->getDeclName() ||200        Param->hasAttr<UnusedAttr>())201      continue;202    if (hasAttrAfterParam(Result.SourceManager, Param)) {203      // Due to how grammar works, attributes would be wrongly applied to the204      // type if we remove the preceding parameter name.205      continue;206    }207 208    // In non-strict mode ignore function definitions with empty bodies209    // (constructor initializer counts for non-empty body).210    if (StrictMode || !Function->getBody()->children().empty() ||211        (isa<CXXConstructorDecl>(Function) &&212         cast<CXXConstructorDecl>(Function)->getNumCtorInitializers() > 0))213      warnOnUnusedParameter(Result, Function, I);214  }215}216 217} // namespace clang::tidy::misc218