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 // A function that helps to tell whether a TargetDecl in a UsingDecl will be
21 // checked. Only variable, function, function template, class template, class,
22 // enum declaration and enum constant declaration are considered.
23 static bool ShouldCheckDecl(const Decl *TargetDecl) {
24   return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
25          isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
26          isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
27          isa<EnumConstantDecl>(TargetDecl);
28 }
29 
30 void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
31   Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
32   auto DeclMatcher = hasDeclaration(namedDecl().bind("used"));
33   Finder->addMatcher(loc(enumType(DeclMatcher)), this);
34   Finder->addMatcher(loc(recordType(DeclMatcher)), this);
35   Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this);
36   Finder->addMatcher(declRefExpr().bind("used"), this);
37   Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))),
38                      this);
39   Finder->addMatcher(
40       callExpr(hasDeclaration(functionDecl(hasAnyTemplateArgument(
41           anyOf(refersToTemplate(templateName().bind("used")),
42                 refersToDeclaration(functionDecl().bind("used"))))))),
43       this);
44   Finder->addMatcher(loc(templateSpecializationType(hasAnyTemplateArgument(
45                          templateArgument().bind("used")))),
46                      this);
47 }
48 
49 void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
50   if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
51     return;
52 
53   if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
54     // Ignores using-declarations defined in macros.
55     if (Using->getLocation().isMacroID())
56       return;
57 
58     // Ignores using-declarations defined in class definition.
59     if (isa<CXXRecordDecl>(Using->getDeclContext()))
60       return;
61 
62     // FIXME: We ignore using-decls defined in function definitions at the
63     // moment because of false positives caused by ADL and different function
64     // scopes.
65     if (isa<FunctionDecl>(Using->getDeclContext()))
66       return;
67 
68     UsingDeclContext Context(Using);
69     Context.UsingDeclRange = CharSourceRange::getCharRange(
70         Using->getBeginLoc(),
71         Lexer::findLocationAfterToken(
72             Using->getEndLoc(), tok::semi, *Result.SourceManager, getLangOpts(),
73             /*SkipTrailingWhitespaceAndNewLine=*/true));
74     for (const auto *UsingShadow : Using->shadows()) {
75       const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
76       if (ShouldCheckDecl(TargetDecl))
77         Context.UsingTargetDecls.insert(TargetDecl);
78     }
79     if (!Context.UsingTargetDecls.empty())
80       Contexts.push_back(Context);
81     return;
82   }
83   // Mark using declarations as used by setting FoundDecls' value to zero. As
84   // the AST is walked in order, usages are only marked after a the
85   // corresponding using declaration has been found.
86   // FIXME: This currently doesn't look at whether the type reference is
87   // actually found with the help of the using declaration.
88   if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) {
89     if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
90       removeFromFoundDecls(FD->getPrimaryTemplate());
91     } else if (const auto *Specialization =
92                    dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
93       Used = Specialization->getSpecializedTemplate();
94     }
95     removeFromFoundDecls(Used);
96     return;
97   }
98 
99   if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
100     // FIXME: Support non-type template parameters.
101     if (Used->getKind() == TemplateArgument::Template) {
102       if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
103         removeFromFoundDecls(TD);
104     } else if (Used->getKind() == TemplateArgument::Type) {
105       if (auto *RD = Used->getAsType()->getAsCXXRecordDecl())
106         removeFromFoundDecls(RD);
107     }
108     return;
109   }
110 
111   if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) {
112     removeFromFoundDecls(Used->getAsTemplateDecl());
113     return;
114   }
115 
116   if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) {
117     if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
118       if (const auto *FDT = FD->getPrimaryTemplate())
119         removeFromFoundDecls(FDT);
120       else
121         removeFromFoundDecls(FD);
122     } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
123       removeFromFoundDecls(VD);
124     } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
125       removeFromFoundDecls(ECD);
126       if (const auto *ET = ECD->getType()->getAs<EnumType>())
127         removeFromFoundDecls(ET->getDecl());
128     }
129   }
130   // Check the uninstantiated template function usage.
131   if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) {
132     for (const NamedDecl *ND : ULE->decls()) {
133       if (const auto *USD = dyn_cast<UsingShadowDecl>(ND))
134         removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
135     }
136   }
137 }
138 
139 void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) {
140   if (!D)
141     return;
142   // FIXME: Currently, we don't handle the using-decls being used in different
143   // scopes (such as different namespaces, different functions). Instead of
144   // giving an incorrect message, we mark all of them as used.
145   //
146   // FIXME: Use a more efficient way to find a matching context.
147   for (auto &Context : Contexts) {
148     if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0)
149       Context.IsUsed = true;
150   }
151 }
152 
153 void UnusedUsingDeclsCheck::onEndOfTranslationUnit() {
154   for (const auto &Context : Contexts) {
155     if (!Context.IsUsed) {
156       diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused")
157           << Context.FoundUsingDecl;
158       // Emit a fix and a fix description of the check;
159       diag(Context.FoundUsingDecl->getLocation(),
160            /*FixDescription=*/"remove the using", DiagnosticIDs::Note)
161           << FixItHint::CreateRemoval(Context.UsingDeclRange);
162     }
163   }
164   Contexts.clear();
165 }
166 
167 } // namespace misc
168 } // namespace tidy
169 } // namespace clang
170