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