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