blob: 03196c8a94f1f3854b729f7d37988b06138a6f21 [file] [log] [blame]
Daniel Jasper9c6df882015-07-20 01:06:44 +00001//===--- UnusedParametersCheck.cpp - clang-tidy----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "UnusedParametersCheck.h"
11#include "clang/AST/ASTContext.h"
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +000012#include "clang/AST/RecursiveASTVisitor.h"
Daniel Jasper9c6df882015-07-20 01:06:44 +000013#include "clang/ASTMatchers/ASTMatchFinder.h"
Samuel Benzaquenb4396272015-11-11 18:40:36 +000014#include "clang/Lex/Lexer.h"
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +000015#include <unordered_set>
Daniel Jasper9c6df882015-07-20 01:06:44 +000016
17using namespace clang::ast_matchers;
18
19namespace clang {
20namespace tidy {
Etienne Bergeron456177b2016-05-02 18:00:29 +000021namespace misc {
22
Haojian Wu7d158532016-04-01 07:57:30 +000023namespace {
24bool isOverrideMethod(const FunctionDecl *Function) {
25 if (const auto *MD = dyn_cast<CXXMethodDecl>(Function))
26 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
27 return false;
28}
29} // namespace
Daniel Jasper9c6df882015-07-20 01:06:44 +000030
31void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +000032 Finder->addMatcher(
33 functionDecl(isDefinition(), hasBody(stmt()), hasAnyParameter(decl()))
34 .bind("function"),
35 this);
Daniel Jasper9c6df882015-07-20 01:06:44 +000036}
37
Samuel Benzaquenb4396272015-11-11 18:40:36 +000038template <typename T>
39static CharSourceRange removeNode(const MatchFinder::MatchResult &Result,
40 const T *PrevNode, const T *Node,
41 const T *NextNode) {
42 if (NextNode)
43 return CharSourceRange::getCharRange(Node->getLocStart(),
44 NextNode->getLocStart());
Daniel Jasper82efabb2015-07-20 03:42:38 +000045
Samuel Benzaquenb4396272015-11-11 18:40:36 +000046 if (PrevNode)
47 return CharSourceRange::getTokenRange(
48 Lexer::getLocForEndOfToken(PrevNode->getLocEnd(), 0,
49 *Result.SourceManager,
50 Result.Context->getLangOpts()),
51 Node->getLocEnd());
Daniel Jasper82efabb2015-07-20 03:42:38 +000052
Samuel Benzaquenb4396272015-11-11 18:40:36 +000053 return CharSourceRange::getTokenRange(Node->getSourceRange());
Daniel Jasper82efabb2015-07-20 03:42:38 +000054}
55
Samuel Benzaquenb4396272015-11-11 18:40:36 +000056static FixItHint removeParameter(const MatchFinder::MatchResult &Result,
57 const FunctionDecl *Function, unsigned Index) {
58 return FixItHint::CreateRemoval(removeNode(
59 Result, Index > 0 ? Function->getParamDecl(Index - 1) : nullptr,
60 Function->getParamDecl(Index),
61 Index + 1 < Function->getNumParams() ? Function->getParamDecl(Index + 1)
62 : nullptr));
63}
64
65static FixItHint removeArgument(const MatchFinder::MatchResult &Result,
66 const CallExpr *Call, unsigned Index) {
67 return FixItHint::CreateRemoval(removeNode(
68 Result, Index > 0 ? Call->getArg(Index - 1) : nullptr,
69 Call->getArg(Index),
70 Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));
Daniel Jasper82efabb2015-07-20 03:42:38 +000071}
72
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +000073class UnusedParametersCheck::IndexerVisitor
74 : public RecursiveASTVisitor<IndexerVisitor> {
75public:
76 IndexerVisitor(TranslationUnitDecl *Top) { TraverseDecl(Top); }
77
78 const std::unordered_set<const CallExpr *> &
79 getFnCalls(const FunctionDecl *Fn) {
80 return Index[Fn->getCanonicalDecl()].Calls;
81 }
82
83 const std::unordered_set<const DeclRefExpr *> &
84 getOtherRefs(const FunctionDecl *Fn) {
85 return Index[Fn->getCanonicalDecl()].OtherRefs;
86 }
87
88 bool shouldTraversePostOrder() const { return true; }
89
90 bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {
91 if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
92 Fn = Fn->getCanonicalDecl();
93 Index[Fn].OtherRefs.insert(DeclRef);
94 }
95 return true;
96 }
97
98 bool WalkUpFromCallExpr(CallExpr *Call) {
99 if (const auto *Fn =
100 dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
101 Fn = Fn->getCanonicalDecl();
102 if (const auto *Ref =
103 dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {
104 Index[Fn].OtherRefs.erase(Ref);
105 }
106 Index[Fn].Calls.insert(Call);
107 }
108 return true;
109 }
110
111private:
112 struct IndexEntry {
113 std::unordered_set<const CallExpr *> Calls;
114 std::unordered_set<const DeclRefExpr *> OtherRefs;
115 };
116
117 std::unordered_map<const FunctionDecl *, IndexEntry> Index;
118};
119
120UnusedParametersCheck::~UnusedParametersCheck() = default;
121
122UnusedParametersCheck::UnusedParametersCheck(StringRef Name,
123 ClangTidyContext *Context)
124 : ClangTidyCheck(Name, Context) {}
125
Daniel Jasper9fe55a322015-07-27 13:46:37 +0000126void UnusedParametersCheck::warnOnUnusedParameter(
127 const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
128 unsigned ParamIndex) {
129 const auto *Param = Function->getParamDecl(ParamIndex);
Benjamin Kramera62e2232016-04-07 14:55:25 +0000130 auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;
Daniel Jasper9c6df882015-07-20 01:06:44 +0000131
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +0000132 if (!Indexer) {
133 Indexer = llvm::make_unique<IndexerVisitor>(
134 Result.Context->getTranslationUnitDecl());
135 }
Daniel Jasper82efabb2015-07-20 03:42:38 +0000136
137 // Comment out parameter name for non-local functions.
Daniel Jasper542482e2015-07-28 13:19:12 +0000138 if (Function->isExternallyVisible() ||
139 !Result.SourceManager->isInMainFile(Function->getLocation()) ||
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +0000140 !Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function)) {
Alexander Kornienko7b9f3e22017-09-15 11:28:28 +0000141 SourceRange RemovalRange(Param->getLocation());
Daniel Jasper718029b2015-07-28 10:39:25 +0000142 // Note: We always add a space before the '/*' to not accidentally create a
143 // '*/*' for pointer types, which doesn't start a comment. clang-format will
144 // clean this up afterwards.
Daniel Jasper82efabb2015-07-20 03:42:38 +0000145 MyDiag << FixItHint::CreateReplacement(
146 RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
147 return;
148 }
149
Daniel Jasper82efabb2015-07-20 03:42:38 +0000150 // Fix all redeclarations.
151 for (const FunctionDecl *FD : Function->redecls())
Daniel Jasperea223a72015-08-14 13:39:57 +0000152 if (FD->param_size())
Samuel Benzaquenb4396272015-11-11 18:40:36 +0000153 MyDiag << removeParameter(Result, FD, ParamIndex);
Daniel Jasper82efabb2015-07-20 03:42:38 +0000154
155 // Fix all call sites.
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +0000156 for (const auto *Call : Indexer->getFnCalls(Function))
157 MyDiag << removeArgument(Result, Call, ParamIndex);
Daniel Jasper9c6df882015-07-20 01:06:44 +0000158}
159
Daniel Jasper9fe55a322015-07-27 13:46:37 +0000160void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
161 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
Alexander Kornienko4d5dd3b2017-05-17 02:25:11 +0000162 if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
Daniel Jasper9fe55a322015-07-27 13:46:37 +0000163 return;
Daniel Jasperf6311912015-09-22 09:20:20 +0000164 if (const auto *Method = dyn_cast<CXXMethodDecl>(Function))
165 if (Method->isLambdaStaticInvoker())
166 return;
Daniel Jasper9fe55a322015-07-27 13:46:37 +0000167 for (unsigned i = 0, e = Function->getNumParams(); i != e; ++i) {
168 const auto *Param = Function->getParamDecl(i);
169 if (Param->isUsed() || Param->isReferenced() || !Param->getDeclName() ||
170 Param->hasAttr<UnusedAttr>())
171 continue;
172 warnOnUnusedParameter(Result, Function, i);
173 }
174}
175
Etienne Bergeron456177b2016-05-02 18:00:29 +0000176} // namespace misc
Daniel Jasper9c6df882015-07-20 01:06:44 +0000177} // namespace tidy
178} // namespace clang