UnusedParametersCheck.cpp 7.2 KB

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