clang-tools 8.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) {
76 SymbolInfo::SymbolKind Type;
77 if (llvm::isa<VarDecl>(ND)) {
78 Type = SymbolInfo::SymbolKind::Variable;
79 } else if (llvm::isa<FunctionDecl>(ND)) {
80 Type = SymbolInfo::SymbolKind::Function;
81 } else if (llvm::isa<TypedefNameDecl>(ND)) {
82 Type = SymbolInfo::SymbolKind::TypedefName;
83 } else if (llvm::isa<EnumConstantDecl>(ND)) {
84 Type = SymbolInfo::SymbolKind::EnumConstantDecl;
85 } else if (llvm::isa<EnumDecl>(ND)) {
86 Type = SymbolInfo::SymbolKind::EnumDecl;
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;
98 Type = SymbolInfo::SymbolKind::Class;
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
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::matchers::AST_MATCHER
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
clang::find_all_symbols::SymbolInfo::SymbolKind::Function
clang::find_all_symbols::SymbolInfo::SymbolKind::Class
clang::ast_matchers::AST_POLYMORPHIC_MATCHER
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
Definition: AbseilMatcher.h:32
clang::find_all_symbols::SymbolInfo::Signals::Seen
unsigned Seen
Definition: SymbolInfo.h:59
clang::find_all_symbols::SymbolInfo::SymbolKind::EnumConstantDecl
HeaderMapCollector.h
clang::find_all_symbols::getIncludePath
std::string getIncludePath(const SourceManager &SM, SourceLocation Loc, const HeaderMapCollector *Collector)
This calculates the include path for Loc.
Definition: PathConfig.cpp:17
SymbolInfo.h
clang::find_all_symbols::SymbolInfo::Signals
Definition: SymbolInfo.h:54
Filename
std::string Filename
Filename as a string.
Definition: IncludeOrderCheck.cpp:39
SymbolInfo
clang::find_all_symbols::SymbolInfo SymbolInfo
Definition: FindAllSymbolsMain.cpp:39
clang::find_all_symbols::SymbolInfo::SymbolKind::Variable
FindAllSymbols.h
clang::find_all_symbols::SymbolInfo::SymbolKind::EnumDecl
clang::find_all_symbols::SymbolInfo::SymbolKind
SymbolKind
The SymbolInfo Type.
Definition: SymbolInfo.h:31
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang::find_all_symbols::SymbolInfo::ContextType::Namespace
clang::find_all_symbols::SymbolInfo::SymbolKind::TypedefName
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::find_all_symbols::SymbolInfo::ContextType::Record
clang::find_all_symbols::SymbolInfo::Signals::Used
unsigned Used
Definition: SymbolInfo.h:62
clang::find_all_symbols::SymbolInfo::ContextType::EnumDecl
PathConfig.h