1 //===--- UnusedUsingDeclsCheck.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
9 #include "UnusedUsingDeclsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace misc {
19
20 namespace {
21 // FIXME: Move ASTMatcher library.
AST_POLYMORPHIC_MATCHER_P(forEachTemplateArgument,AST_POLYMORPHIC_SUPPORTED_TYPES (ClassTemplateSpecializationDecl,TemplateSpecializationType,FunctionDecl),clang::ast_matchers::internal::Matcher<TemplateArgument>,InnerMatcher)22 AST_POLYMORPHIC_MATCHER_P(
23 forEachTemplateArgument,
24 AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
25 TemplateSpecializationType, FunctionDecl),
26 clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
27 ArrayRef<TemplateArgument> TemplateArgs =
28 clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
29 clang::ast_matchers::internal::BoundNodesTreeBuilder Result;
30 bool Matched = false;
31 for (const auto &Arg : TemplateArgs) {
32 clang::ast_matchers::internal::BoundNodesTreeBuilder ArgBuilder(*Builder);
33 if (InnerMatcher.matches(Arg, Finder, &ArgBuilder)) {
34 Matched = true;
35 Result.addMatch(ArgBuilder);
36 }
37 }
38 *Builder = std::move(Result);
39 return Matched;
40 }
41
AST_MATCHER_P(DeducedTemplateSpecializationType,refsToTemplatedDecl,clang::ast_matchers::internal::Matcher<NamedDecl>,DeclMatcher)42 AST_MATCHER_P(DeducedTemplateSpecializationType, refsToTemplatedDecl,
43 clang::ast_matchers::internal::Matcher<NamedDecl>, DeclMatcher) {
44 if (const auto *TD = Node.getTemplateName().getAsTemplateDecl())
45 return DeclMatcher.matches(*TD, Finder, Builder);
46 return false;
47 }
48 } // namespace
49
50 // A function that helps to tell whether a TargetDecl in a UsingDecl will be
51 // checked. Only variable, function, function template, class template, class,
52 // enum declaration and enum constant declaration are considered.
ShouldCheckDecl(const Decl * TargetDecl)53 static bool ShouldCheckDecl(const Decl *TargetDecl) {
54 return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
55 isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
56 isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
57 isa<EnumConstantDecl>(TargetDecl);
58 }
59
registerMatchers(MatchFinder * Finder)60 void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
61 Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
62 auto DeclMatcher = hasDeclaration(namedDecl().bind("used"));
63 Finder->addMatcher(loc(enumType(DeclMatcher)), this);
64 Finder->addMatcher(loc(recordType(DeclMatcher)), this);
65 Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this);
66 Finder->addMatcher(loc(deducedTemplateSpecializationType(
67 refsToTemplatedDecl(namedDecl().bind("used")))),
68 this);
69 Finder->addMatcher(declRefExpr().bind("used"), this);
70 Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))),
71 this);
72 Finder->addMatcher(
73 callExpr(hasDeclaration(functionDecl(
74 forEachTemplateArgument(templateArgument().bind("used"))))),
75 this);
76 Finder->addMatcher(loc(templateSpecializationType(forEachTemplateArgument(
77 templateArgument().bind("used")))),
78 this);
79 }
80
check(const MatchFinder::MatchResult & Result)81 void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
82 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
83 return;
84
85 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
86 // Ignores using-declarations defined in macros.
87 if (Using->getLocation().isMacroID())
88 return;
89
90 // Ignores using-declarations defined in class definition.
91 if (isa<CXXRecordDecl>(Using->getDeclContext()))
92 return;
93
94 // FIXME: We ignore using-decls defined in function definitions at the
95 // moment because of false positives caused by ADL and different function
96 // scopes.
97 if (isa<FunctionDecl>(Using->getDeclContext()))
98 return;
99
100 UsingDeclContext Context(Using);
101 Context.UsingDeclRange = CharSourceRange::getCharRange(
102 Using->getBeginLoc(),
103 Lexer::findLocationAfterToken(
104 Using->getEndLoc(), tok::semi, *Result.SourceManager, getLangOpts(),
105 /*SkipTrailingWhitespaceAndNewLine=*/true));
106 for (const auto *UsingShadow : Using->shadows()) {
107 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
108 if (ShouldCheckDecl(TargetDecl))
109 Context.UsingTargetDecls.insert(TargetDecl);
110 }
111 if (!Context.UsingTargetDecls.empty())
112 Contexts.push_back(Context);
113 return;
114 }
115
116 // Mark a corresponding using declaration as used.
117 auto RemoveNamedDecl = [&](const NamedDecl *Used) {
118 removeFromFoundDecls(Used);
119 // Also remove variants of Used.
120 if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
121 removeFromFoundDecls(FD->getPrimaryTemplate());
122 } else if (const auto *Specialization =
123 dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
124 removeFromFoundDecls(Specialization->getSpecializedTemplate());
125 } else if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
126 if (const auto *FDT = FD->getPrimaryTemplate())
127 removeFromFoundDecls(FDT);
128 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
129 if (const auto *ET = ECD->getType()->getAs<EnumType>())
130 removeFromFoundDecls(ET->getDecl());
131 }
132 };
133 // We rely on the fact that the clang AST is walked in order, usages are only
134 // marked after a corresponding using decl has been found.
135 if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) {
136 RemoveNamedDecl(Used);
137 return;
138 }
139
140 if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
141 if (Used->getKind() == TemplateArgument::Template) {
142 if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
143 removeFromFoundDecls(TD);
144 } else if (Used->getKind() == TemplateArgument::Type) {
145 if (auto *RD = Used->getAsType()->getAsCXXRecordDecl())
146 removeFromFoundDecls(RD);
147 } else if (Used->getKind() == TemplateArgument::Declaration) {
148 RemoveNamedDecl(Used->getAsDecl());
149 }
150 return;
151 }
152
153 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) {
154 RemoveNamedDecl(DRE->getDecl());
155 return;
156 }
157 // Check the uninstantiated template function usage.
158 if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) {
159 for (const NamedDecl *ND : ULE->decls()) {
160 if (const auto *USD = dyn_cast<UsingShadowDecl>(ND))
161 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
162 }
163 }
164 }
165
removeFromFoundDecls(const Decl * D)166 void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) {
167 if (!D)
168 return;
169 // FIXME: Currently, we don't handle the using-decls being used in different
170 // scopes (such as different namespaces, different functions). Instead of
171 // giving an incorrect message, we mark all of them as used.
172 //
173 // FIXME: Use a more efficient way to find a matching context.
174 for (auto &Context : Contexts) {
175 if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0)
176 Context.IsUsed = true;
177 }
178 }
179
onEndOfTranslationUnit()180 void UnusedUsingDeclsCheck::onEndOfTranslationUnit() {
181 for (const auto &Context : Contexts) {
182 if (!Context.IsUsed) {
183 diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused")
184 << Context.FoundUsingDecl;
185 // Emit a fix and a fix description of the check;
186 diag(Context.FoundUsingDecl->getLocation(),
187 /*Description=*/"remove the using", DiagnosticIDs::Note)
188 << FixItHint::CreateRemoval(Context.UsingDeclRange);
189 }
190 }
191 Contexts.clear();
192 }
193
194 } // namespace misc
195 } // namespace tidy
196 } // namespace clang
197