clang-tools  10.0.0svn
FindAllSymbols.cpp
Go to the documentation of this file.
1 //===-- FindAllSymbols.cpp - find all symbols--------------------*- C++ -*-===//
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 "FindAllSymbols.h"
10 #include "HeaderMapCollector.h"
11 #include "PathConfig.h"
12 #include "SymbolInfo.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclCXX.h"
15 #include "clang/AST/Type.h"
16 #include "clang/ASTMatchers/ASTMatchFinder.h"
17 #include "clang/ASTMatchers/ASTMatchers.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/Support/FileSystem.h"
21 
22 using namespace clang::ast_matchers;
23 
24 namespace clang {
25 namespace find_all_symbols {
26 namespace {
27 
28 AST_MATCHER(EnumConstantDecl, isInScopedEnum) {
29  if (const auto *ED = dyn_cast<EnumDecl>(Node.getDeclContext()))
30  return ED->isScoped();
31  return false;
32 }
33 
34 AST_POLYMORPHIC_MATCHER(isFullySpecialized,
35  AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
36  CXXRecordDecl)) {
37  if (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
38  bool IsPartialSpecialization =
39  llvm::isa<VarTemplatePartialSpecializationDecl>(Node) ||
40  llvm::isa<ClassTemplatePartialSpecializationDecl>(Node);
41  return !IsPartialSpecialization;
42  }
43  return false;
44 }
45 
46 std::vector<SymbolInfo::Context> GetContexts(const NamedDecl *ND) {
47  std::vector<SymbolInfo::Context> Contexts;
48  for (const auto *Context = ND->getDeclContext(); Context;
49  Context = Context->getParent()) {
50  if (llvm::isa<TranslationUnitDecl>(Context) ||
51  llvm::isa<LinkageSpecDecl>(Context))
52  break;
53 
54  assert(llvm::isa<NamedDecl>(Context) &&
55  "Expect Context to be a NamedDecl");
56  if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {
57  if (!NSD->isInlineNamespace())
58  Contexts.emplace_back(SymbolInfo::ContextType::Namespace,
59  NSD->getName().str());
60  } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {
61  Contexts.emplace_back(SymbolInfo::ContextType::EnumDecl,
62  ED->getName().str());
63  } else {
64  const auto *RD = cast<RecordDecl>(Context);
65  Contexts.emplace_back(SymbolInfo::ContextType::Record,
66  RD->getName().str());
67  }
68  }
69  return Contexts;
70 }
71 
72 llvm::Optional<SymbolInfo>
73 CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,
74  const HeaderMapCollector *Collector) {
76  if (llvm::isa<VarDecl>(ND)) {
78  } else if (llvm::isa<FunctionDecl>(ND)) {
80  } else if (llvm::isa<TypedefNameDecl>(ND)) {
82  } else if (llvm::isa<EnumConstantDecl>(ND)) {
84  } else if (llvm::isa<EnumDecl>(ND)) {
86  // Ignore anonymous enum declarations.
87  if (ND->getName().empty())
88  return llvm::None;
89  } else {
90  assert(llvm::isa<RecordDecl>(ND) &&
91  "Matched decl must be one of VarDecl, "
92  "FunctionDecl, TypedefNameDecl, EnumConstantDecl, "
93  "EnumDecl and RecordDecl!");
94  // C-style record decl can have empty name, e.g "struct { ... } var;".
95  if (ND->getName().empty())
96  return llvm::None;
98  }
99 
100  SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());
101  if (!Loc.isValid()) {
102  llvm::errs() << "Declaration " << ND->getNameAsString() << "("
103  << ND->getDeclKindName()
104  << ") has invalid declaration location.";
105  return llvm::None;
106  }
107 
108  std::string FilePath = getIncludePath(SM, Loc, Collector);
109  if (FilePath.empty()) return llvm::None;
110 
111  return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND));
112 }
113 
114 } // namespace
115 
116 void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
117  // FIXME: Handle specialization.
118  auto IsInSpecialization = hasAncestor(
119  decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
120  functionDecl(isExplicitTemplateSpecialization()))));
121 
122  // Matchers for both C and C++.
123  // We only match symbols from header files, i.e. not from main files (see
124  // function's comment for detailed explanation).
125  auto CommonFilter =
126  allOf(unless(isImplicit()), unless(isExpansionInMainFile()));
127 
128  auto HasNSOrTUCtxMatcher =
129  hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
130 
131  // We need seperate rules for C record types and C++ record types since some
132  // template related matchers are inapplicable on C record declarations.
133  //
134  // Matchers specific to C++ code.
135  // All declarations should be in namespace or translation unit.
136  auto CCMatcher =
137  allOf(HasNSOrTUCtxMatcher, unless(IsInSpecialization),
138  unless(ast_matchers::isTemplateInstantiation()),
139  unless(isInstantiated()), unless(isFullySpecialized()));
140 
141  // Matchers specific to code in extern "C" {...}.
142  auto ExternCMatcher = hasDeclContext(linkageSpecDecl());
143 
144  // Matchers for variable declarations.
145  //
146  // In most cases, `ParmVarDecl` is filtered out by hasDeclContext(...)
147  // matcher since the declaration context is usually `MethodDecl`. However,
148  // this assumption does not hold for parameters of a function pointer
149  // parameter.
150  // For example, consider a function declaration:
151  // void Func(void (*)(float), int);
152  // The float parameter of the function pointer has an empty name, and its
153  // declaration context is an anonymous namespace; therefore, it won't be
154  // filtered out by our matchers above.
155  auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
156  unless(parmVarDecl()));
157 
158  // Matchers for C-style record declarations in extern "C" {...}.
159  auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
160  // Matchers for C++ record declarations.
161  auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
162 
163  // Matchers for function declarations.
164  // We want to exclude friend declaration, but the `DeclContext` of a friend
165  // function declaration is not the class in which it is declared, so we need
166  // to explicitly check if the parent is a `friendDecl`.
167  auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
168  anyOf(ExternCMatcher, CCMatcher));
169 
170  // Matcher for typedef and type alias declarations.
171  //
172  // typedef and type alias can come from C-style headers and C++ headers.
173  // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
174  // or `LinkageSpecDecl`.
175  // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
176  // or `NamespaceDecl`.
177  // With the following context matcher, we can match `typedefNameDecl` from
178  // both C-style headers and C++ headers (except for those in classes).
179  // "cc_matchers" are not included since template-related matchers are not
180  // applicable on `TypedefNameDecl`.
181  auto Typedefs =
182  typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
183  hasDeclContext(linkageSpecDecl())));
184 
185  // Matchers for enum declarations.
186  auto Enums = enumDecl(CommonFilter, isDefinition(),
187  anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
188 
189  // Matchers for enum constant declarations.
190  // We only match the enum constants in non-scoped enum declarations which are
191  // inside toplevel translation unit or a namespace.
192  auto EnumConstants = enumConstantDecl(
193  CommonFilter, unless(isInScopedEnum()),
194  anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
195 
196  // Most of the time we care about all matchable decls, or all types.
197  auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums));
198  auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
199  EnumConstants, Functions));
200 
201  // We want eligible decls bound to "decl"...
202  MatchFinder->addMatcher(Decls.bind("decl"), this);
203 
204  // ... and all uses of them bound to "use". These have many cases:
205  // Uses of values/functions: these generate a declRefExpr.
206  MatchFinder->addMatcher(
207  declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
208  // Uses of function templates:
209  MatchFinder->addMatcher(
210  declRefExpr(isExpansionInMainFile(),
211  to(functionDecl(hasParent(
212  functionTemplateDecl(has(Functions.bind("use"))))))),
213  this);
214 
215  // Uses of most types: just look at what the typeLoc refers to.
216  MatchFinder->addMatcher(
217  typeLoc(isExpansionInMainFile(),
218  loc(qualType(hasDeclaration(Types.bind("use"))))),
219  this);
220  // Uses of typedefs: these are often transparent to hasDeclaration, so we need
221  // to handle them explicitly.
222  MatchFinder->addMatcher(
223  typeLoc(isExpansionInMainFile(),
224  loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
225  this);
226  // Uses of class templates:
227  // The typeLoc names the templateSpecializationType. Its declaration is the
228  // ClassTemplateDecl, which contains the CXXRecordDecl we want.
229  MatchFinder->addMatcher(
230  typeLoc(isExpansionInMainFile(),
231  loc(templateSpecializationType(hasDeclaration(
232  classTemplateSpecializationDecl(hasSpecializedTemplate(
233  classTemplateDecl(has(CXXRecords.bind("use"))))))))),
234  this);
235 }
236 
237 void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
238  // Ignore Results in failing TUs.
239  if (Result.Context->getDiagnostics().hasErrorOccurred()) {
240  return;
241  }
242 
243  SymbolInfo::Signals Signals;
244  const NamedDecl *ND;
245  if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))
246  Signals.Used = 1;
247  else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))
248  Signals.Seen = 1;
249  else
250  assert(false && "Must match a NamedDecl!");
251 
252  const SourceManager *SM = Result.SourceManager;
253  if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
254  Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
255  FileSymbols[*Symbol] += Signals;
256  }
257 }
258 
259 void FindAllSymbols::onEndOfTranslationUnit() {
260  if (Filename != "") {
261  Reporter->reportSymbols(Filename, FileSymbols);
262  FileSymbols.clear();
263  Filename = "";
264  }
265 }
266 
267 } // namespace find_all_symbols
268 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
Definition: AbseilMatcher.h:31
std::string getIncludePath(const SourceManager &SM, SourceLocation Loc, const HeaderMapCollector *Collector)
This calculates the include path for Loc.
Definition: PathConfig.cpp:16
std::string Filename
Filename as a string.
clang::find_all_symbols::SymbolInfo SymbolInfo
SymbolKind
The SymbolInfo Type.
Definition: SymbolInfo.h:30
std::shared_ptr< SymbolCollector > Collector
llvm::SmallDenseMap< const Decl *, RelSet > Decls
Definition: FindTarget.cpp:88
bool isExplicitTemplateSpecialization(const NamedDecl *D)
Indicates if D is an explicit template specialization, e.g.
Definition: AST.cpp:74
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
NodeType Type