forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			197 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- UnusedParametersCheck.cpp - clang-tidy----------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "UnusedParametersCheck.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/AST/RecursiveASTVisitor.h"
 | |
| #include "clang/ASTMatchers/ASTMatchFinder.h"
 | |
| #include "clang/Lex/Lexer.h"
 | |
| #include "llvm/ADT/STLExtras.h"
 | |
| #include <unordered_set>
 | |
| 
 | |
| using namespace clang::ast_matchers;
 | |
| 
 | |
| namespace clang {
 | |
| namespace tidy {
 | |
| namespace misc {
 | |
| 
 | |
| namespace {
 | |
| bool isOverrideMethod(const FunctionDecl *Function) {
 | |
|   if (const auto *MD = dyn_cast<CXXMethodDecl>(Function))
 | |
|     return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
 | |
|   return false;
 | |
| }
 | |
| } // namespace
 | |
| 
 | |
| void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {
 | |
|   Finder->addMatcher(
 | |
|       functionDecl(isDefinition(), hasBody(stmt()), hasAnyParameter(decl()))
 | |
|           .bind("function"),
 | |
|       this);
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| static CharSourceRange removeNode(const MatchFinder::MatchResult &Result,
 | |
|                                   const T *PrevNode, const T *Node,
 | |
|                                   const T *NextNode) {
 | |
|   if (NextNode)
 | |
|     return CharSourceRange::getCharRange(Node->getBeginLoc(),
 | |
|                                          NextNode->getBeginLoc());
 | |
| 
 | |
|   if (PrevNode)
 | |
|     return CharSourceRange::getTokenRange(
 | |
|         Lexer::getLocForEndOfToken(PrevNode->getEndLoc(), 0,
 | |
|                                    *Result.SourceManager,
 | |
|                                    Result.Context->getLangOpts()),
 | |
|         Node->getEndLoc());
 | |
| 
 | |
|   return CharSourceRange::getTokenRange(Node->getSourceRange());
 | |
| }
 | |
| 
 | |
| static FixItHint removeParameter(const MatchFinder::MatchResult &Result,
 | |
|                                  const FunctionDecl *Function, unsigned Index) {
 | |
|   return FixItHint::CreateRemoval(removeNode(
 | |
|       Result, Index > 0 ? Function->getParamDecl(Index - 1) : nullptr,
 | |
|       Function->getParamDecl(Index),
 | |
|       Index + 1 < Function->getNumParams() ? Function->getParamDecl(Index + 1)
 | |
|                                            : nullptr));
 | |
| }
 | |
| 
 | |
| static FixItHint removeArgument(const MatchFinder::MatchResult &Result,
 | |
|                                 const CallExpr *Call, unsigned Index) {
 | |
|   return FixItHint::CreateRemoval(removeNode(
 | |
|       Result, Index > 0 ? Call->getArg(Index - 1) : nullptr,
 | |
|       Call->getArg(Index),
 | |
|       Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));
 | |
| }
 | |
| 
 | |
| class UnusedParametersCheck::IndexerVisitor
 | |
|     : public RecursiveASTVisitor<IndexerVisitor> {
 | |
| public:
 | |
|   IndexerVisitor(ASTContext &Ctx) { TraverseAST(Ctx); }
 | |
| 
 | |
|   const std::unordered_set<const CallExpr *> &
 | |
|   getFnCalls(const FunctionDecl *Fn) {
 | |
|     return Index[Fn->getCanonicalDecl()].Calls;
 | |
|   }
 | |
| 
 | |
|   const std::unordered_set<const DeclRefExpr *> &
 | |
|   getOtherRefs(const FunctionDecl *Fn) {
 | |
|     return Index[Fn->getCanonicalDecl()].OtherRefs;
 | |
|   }
 | |
| 
 | |
|   bool shouldTraversePostOrder() const { return true; }
 | |
| 
 | |
|   bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {
 | |
|     if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
 | |
|       Fn = Fn->getCanonicalDecl();
 | |
|       Index[Fn].OtherRefs.insert(DeclRef);
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool WalkUpFromCallExpr(CallExpr *Call) {
 | |
|     if (const auto *Fn =
 | |
|             dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
 | |
|       Fn = Fn->getCanonicalDecl();
 | |
|       if (const auto *Ref =
 | |
|               dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {
 | |
|         Index[Fn].OtherRefs.erase(Ref);
 | |
|       }
 | |
|       Index[Fn].Calls.insert(Call);
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   struct IndexEntry {
 | |
|     std::unordered_set<const CallExpr *> Calls;
 | |
|     std::unordered_set<const DeclRefExpr *> OtherRefs;
 | |
|   };
 | |
| 
 | |
|   std::unordered_map<const FunctionDecl *, IndexEntry> Index;
 | |
| };
 | |
| 
 | |
| UnusedParametersCheck::~UnusedParametersCheck() = default;
 | |
| 
 | |
| UnusedParametersCheck::UnusedParametersCheck(StringRef Name,
 | |
|                                              ClangTidyContext *Context)
 | |
|     : ClangTidyCheck(Name, Context),
 | |
|       StrictMode(Options.getLocalOrGlobal("StrictMode", 0) != 0) {}
 | |
| 
 | |
| void UnusedParametersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
 | |
|   Options.store(Opts, "StrictMode", StrictMode);
 | |
| }
 | |
| 
 | |
| void UnusedParametersCheck::warnOnUnusedParameter(
 | |
|     const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
 | |
|     unsigned ParamIndex) {
 | |
|   const auto *Param = Function->getParamDecl(ParamIndex);
 | |
|   auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;
 | |
| 
 | |
|   if (!Indexer) {
 | |
|     Indexer = std::make_unique<IndexerVisitor>(*Result.Context);
 | |
|   }
 | |
| 
 | |
|   // Cannot remove parameter for non-local functions.
 | |
|   if (Function->isExternallyVisible() ||
 | |
|       !Result.SourceManager->isInMainFile(Function->getLocation()) ||
 | |
|       !Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function)) {
 | |
| 
 | |
|     // It is illegal to omit parameter name here in C code, so early-out.
 | |
|     if (!Result.Context->getLangOpts().CPlusPlus)
 | |
|       return;
 | |
| 
 | |
|     SourceRange RemovalRange(Param->getLocation());
 | |
|     // Note: We always add a space before the '/*' to not accidentally create
 | |
|     // a '*/*' for pointer types, which doesn't start a comment. clang-format
 | |
|     // will clean this up afterwards.
 | |
|     MyDiag << FixItHint::CreateReplacement(
 | |
|         RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Fix all redeclarations.
 | |
|   for (const FunctionDecl *FD : Function->redecls())
 | |
|     if (FD->param_size())
 | |
|       MyDiag << removeParameter(Result, FD, ParamIndex);
 | |
| 
 | |
|   // Fix all call sites.
 | |
|   for (const CallExpr *Call : Indexer->getFnCalls(Function))
 | |
|     if (ParamIndex < Call->getNumArgs()) // See PR38055 for example.
 | |
|       MyDiag << removeArgument(Result, Call, ParamIndex);
 | |
| }
 | |
| 
 | |
| void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
 | |
|   const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
 | |
|   if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
 | |
|     return;
 | |
|   if (const auto *Method = dyn_cast<CXXMethodDecl>(Function))
 | |
|     if (Method->isLambdaStaticInvoker())
 | |
|       return;
 | |
|   for (unsigned i = 0, e = Function->getNumParams(); i != e; ++i) {
 | |
|     const auto *Param = Function->getParamDecl(i);
 | |
|     if (Param->isUsed() || Param->isReferenced() || !Param->getDeclName() ||
 | |
|         Param->hasAttr<UnusedAttr>())
 | |
|       continue;
 | |
| 
 | |
|     // In non-strict mode ignore function definitions with empty bodies
 | |
|     // (constructor initializer counts for non-empty body).
 | |
|     if (StrictMode ||
 | |
|         (Function->getBody()->child_begin() !=
 | |
|          Function->getBody()->child_end()) ||
 | |
|         (isa<CXXConstructorDecl>(Function) &&
 | |
|          cast<CXXConstructorDecl>(Function)->getNumCtorInitializers() > 0))
 | |
|       warnOnUnusedParameter(Result, Function, i);
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace misc
 | |
| } // namespace tidy
 | |
| } // namespace clang
 |