llvm-project
447 строк · 17.0 Кб
1//===--- DumpAST.cpp - Serialize clang AST to LSP -------------------------===//
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 "DumpAST.h"
10#include "Protocol.h"
11#include "SourceCode.h"
12#include "support/Logger.h"
13#include "clang/AST/ASTTypeTraits.h"
14#include "clang/AST/Expr.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/NestedNameSpecifier.h"
17#include "clang/AST/PrettyPrinter.h"
18#include "clang/AST/RecursiveASTVisitor.h"
19#include "clang/AST/TextNodeDumper.h"
20#include "clang/AST/Type.h"
21#include "clang/AST/TypeLoc.h"
22#include "clang/Basic/Specifiers.h"
23#include "clang/Tooling/Syntax/Tokens.h"
24#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/raw_ostream.h"
26#include <optional>
27
28namespace clang {
29namespace clangd {
30namespace {
31
32using llvm::raw_ostream;
33template <typename Print> std::string toString(const Print &C) {
34std::string Result;
35llvm::raw_string_ostream OS(Result);
36C(OS);
37return std::move(OS.str());
38}
39
40bool isInjectedClassName(Decl *D) {
41if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
42return CRD->isInjectedClassName();
43return false;
44}
45
46class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
47using Base = RecursiveASTVisitor<DumpVisitor>;
48
49const syntax::TokenBuffer &Tokens;
50const ASTContext &Ctx;
51
52// Pointers are into 'children' vector.
53// They remain valid because while a node is on the stack we only add
54// descendants, not siblings.
55std::vector<ASTNode *> Stack;
56
57// Generic logic used to handle traversal of all node kinds.
58
59template <typename T>
60bool traverseNodePre(llvm::StringRef Role, const T &Node) {
61if (Stack.empty()) {
62assert(Root.role.empty());
63Stack.push_back(&Root);
64} else {
65Stack.back()->children.emplace_back();
66Stack.push_back(&Stack.back()->children.back());
67}
68auto &N = *Stack.back();
69N.role = Role.str();
70N.kind = getKind(Node);
71N.detail = getDetail(Node);
72N.range = getRange(Node);
73N.arcana = getArcana(Node);
74return true;
75}
76bool traverseNodePost() {
77assert(!Stack.empty());
78Stack.pop_back();
79return true;
80}
81template <typename T, typename Callable>
82bool traverseNode(llvm::StringRef Role, const T &Node, const Callable &Body) {
83traverseNodePre(Role, Node);
84Body();
85return traverseNodePost();
86}
87
88// Range: most nodes have getSourceRange(), with a couple of exceptions.
89// We only return it if it's valid at both ends and there are no macros.
90
91template <typename T> std::optional<Range> getRange(const T &Node) {
92SourceRange SR = getSourceRange(Node);
93auto Spelled = Tokens.spelledForExpanded(Tokens.expandedTokens(SR));
94if (!Spelled)
95return std::nullopt;
96return halfOpenToRange(
97Tokens.sourceManager(),
98CharSourceRange::getCharRange(Spelled->front().location(),
99Spelled->back().endLocation()));
100}
101template <typename T, typename = decltype(std::declval<T>().getSourceRange())>
102SourceRange getSourceRange(const T &Node) {
103return Node.getSourceRange();
104}
105template <typename T,
106typename = decltype(std::declval<T *>()->getSourceRange())>
107SourceRange getSourceRange(const T *Node) {
108return Node->getSourceRange();
109}
110// TemplateName doesn't have a real Loc node type.
111SourceRange getSourceRange(const TemplateName &Node) { return SourceRange(); }
112// Attr just uses a weird method name. Maybe we should fix it instead?
113SourceRange getSourceRange(const Attr *Node) { return Node->getRange(); }
114
115// Kind is usually the class name, without the suffix ("Type" etc).
116// Where there's a set of variants instead, we use the 'Kind' enum values.
117
118std::string getKind(const Decl *D) { return D->getDeclKindName(); }
119std::string getKind(const Stmt *S) {
120std::string Result = S->getStmtClassName();
121if (llvm::StringRef(Result).ends_with("Stmt") ||
122llvm::StringRef(Result).ends_with("Expr"))
123Result.resize(Result.size() - 4);
124return Result;
125}
126std::string getKind(const TypeLoc &TL) {
127std::string Result;
128if (TL.getTypeLocClass() == TypeLoc::Qualified)
129return "Qualified";
130return TL.getType()->getTypeClassName();
131}
132std::string getKind(const TemplateArgumentLoc &TAL) {
133switch (TAL.getArgument().getKind()) {
134#define TEMPLATE_ARGUMENT_KIND(X) \
135case TemplateArgument::X: \
136return #X
137TEMPLATE_ARGUMENT_KIND(Null);
138TEMPLATE_ARGUMENT_KIND(NullPtr);
139TEMPLATE_ARGUMENT_KIND(Expression);
140TEMPLATE_ARGUMENT_KIND(Integral);
141TEMPLATE_ARGUMENT_KIND(Pack);
142TEMPLATE_ARGUMENT_KIND(Type);
143TEMPLATE_ARGUMENT_KIND(Declaration);
144TEMPLATE_ARGUMENT_KIND(Template);
145TEMPLATE_ARGUMENT_KIND(TemplateExpansion);
146TEMPLATE_ARGUMENT_KIND(StructuralValue);
147#undef TEMPLATE_ARGUMENT_KIND
148}
149llvm_unreachable("Unhandled ArgKind enum");
150}
151std::string getKind(const NestedNameSpecifierLoc &NNSL) {
152assert(NNSL.getNestedNameSpecifier());
153switch (NNSL.getNestedNameSpecifier()->getKind()) {
154#define NNS_KIND(X) \
155case NestedNameSpecifier::X: \
156return #X
157NNS_KIND(Identifier);
158NNS_KIND(Namespace);
159NNS_KIND(TypeSpec);
160NNS_KIND(TypeSpecWithTemplate);
161NNS_KIND(Global);
162NNS_KIND(Super);
163NNS_KIND(NamespaceAlias);
164#undef NNS_KIND
165}
166llvm_unreachable("Unhandled SpecifierKind enum");
167}
168std::string getKind(const CXXCtorInitializer *CCI) {
169if (CCI->isBaseInitializer())
170return "BaseInitializer";
171if (CCI->isDelegatingInitializer())
172return "DelegatingInitializer";
173if (CCI->isAnyMemberInitializer())
174return "MemberInitializer";
175llvm_unreachable("Unhandled CXXCtorInitializer type");
176}
177std::string getKind(const TemplateName &TN) {
178switch (TN.getKind()) {
179#define TEMPLATE_KIND(X) \
180case TemplateName::X: \
181return #X;
182TEMPLATE_KIND(Template);
183TEMPLATE_KIND(OverloadedTemplate);
184TEMPLATE_KIND(AssumedTemplate);
185TEMPLATE_KIND(QualifiedTemplate);
186TEMPLATE_KIND(DependentTemplate);
187TEMPLATE_KIND(SubstTemplateTemplateParm);
188TEMPLATE_KIND(SubstTemplateTemplateParmPack);
189TEMPLATE_KIND(UsingTemplate);
190#undef TEMPLATE_KIND
191}
192llvm_unreachable("Unhandled NameKind enum");
193}
194std::string getKind(const Attr *A) {
195switch (A->getKind()) {
196#define ATTR(X) \
197case attr::X: \
198return #X;
199#include "clang/Basic/AttrList.inc"
200#undef ATTR
201}
202llvm_unreachable("Unhandled attr::Kind enum");
203}
204std::string getKind(const CXXBaseSpecifier &CBS) {
205// There aren't really any variants of CXXBaseSpecifier.
206// To avoid special cases in the API/UI, use public/private as the kind.
207return getAccessSpelling(CBS.getAccessSpecifier()).str();
208}
209std::string getKind(const ConceptReference *CR) {
210// Again there are no variants here.
211// Kind is "Concept", role is "reference"
212return "Concept";
213}
214
215// Detail is the single most important fact about the node.
216// Often this is the name, sometimes a "kind" enum like operators or casts.
217// We should avoid unbounded text, like dumping parameter lists.
218
219std::string getDetail(const Decl *D) {
220const auto *ND = dyn_cast<NamedDecl>(D);
221if (!ND || llvm::isa_and_nonnull<CXXConstructorDecl>(ND->getAsFunction()) ||
222isa<CXXDestructorDecl>(ND))
223return "";
224std::string Name = toString([&](raw_ostream &OS) { ND->printName(OS); });
225if (Name.empty())
226return "(anonymous)";
227return Name;
228}
229std::string getDetail(const Stmt *S) {
230if (const auto *DRE = dyn_cast<DeclRefExpr>(S))
231return DRE->getNameInfo().getAsString();
232if (const auto *DSDRE = dyn_cast<DependentScopeDeclRefExpr>(S))
233return DSDRE->getNameInfo().getAsString();
234if (const auto *ME = dyn_cast<MemberExpr>(S))
235return ME->getMemberNameInfo().getAsString();
236if (const auto *CE = dyn_cast<CastExpr>(S))
237return CE->getCastKindName();
238if (const auto *BO = dyn_cast<BinaryOperator>(S))
239return BO->getOpcodeStr().str();
240if (const auto *UO = dyn_cast<UnaryOperator>(S))
241return UnaryOperator::getOpcodeStr(UO->getOpcode()).str();
242if (const auto *CCO = dyn_cast<CXXConstructExpr>(S))
243return CCO->getConstructor()->getNameAsString();
244if (const auto *CTE = dyn_cast<CXXThisExpr>(S)) {
245bool Const = CTE->getType()->getPointeeType().isLocalConstQualified();
246if (CTE->isImplicit())
247return Const ? "const, implicit" : "implicit";
248if (Const)
249return "const";
250return "";
251}
252if (isa<IntegerLiteral, FloatingLiteral, FixedPointLiteral,
253CharacterLiteral, ImaginaryLiteral, CXXBoolLiteralExpr>(S))
254return toString([&](raw_ostream &OS) {
255S->printPretty(OS, nullptr, Ctx.getPrintingPolicy());
256});
257if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S))
258return MTE->isBoundToLvalueReference() ? "lvalue" : "rvalue";
259return "";
260}
261std::string getDetail(const TypeLoc &TL) {
262if (TL.getType().hasLocalQualifiers())
263return TL.getType().getLocalQualifiers().getAsString(
264Ctx.getPrintingPolicy());
265if (const auto *TT = dyn_cast<TagType>(TL.getTypePtr()))
266return getDetail(TT->getDecl());
267if (const auto *DT = dyn_cast<DeducedType>(TL.getTypePtr()))
268if (DT->isDeduced())
269return DT->getDeducedType().getAsString(Ctx.getPrintingPolicy());
270if (const auto *BT = dyn_cast<BuiltinType>(TL.getTypePtr()))
271return BT->getName(Ctx.getPrintingPolicy()).str();
272if (const auto *TTPT = dyn_cast<TemplateTypeParmType>(TL.getTypePtr()))
273return getDetail(TTPT->getDecl());
274if (const auto *TT = dyn_cast<TypedefType>(TL.getTypePtr()))
275return getDetail(TT->getDecl());
276return "";
277}
278std::string getDetail(const NestedNameSpecifierLoc &NNSL) {
279const auto &NNS = *NNSL.getNestedNameSpecifier();
280switch (NNS.getKind()) {
281case NestedNameSpecifier::Identifier:
282return NNS.getAsIdentifier()->getName().str() + "::";
283case NestedNameSpecifier::Namespace:
284return NNS.getAsNamespace()->getNameAsString() + "::";
285case NestedNameSpecifier::NamespaceAlias:
286return NNS.getAsNamespaceAlias()->getNameAsString() + "::";
287default:
288return "";
289}
290}
291std::string getDetail(const CXXCtorInitializer *CCI) {
292if (FieldDecl *FD = CCI->getAnyMember())
293return getDetail(FD);
294if (TypeLoc TL = CCI->getBaseClassLoc())
295return getDetail(TL);
296return "";
297}
298std::string getDetail(const TemplateArgumentLoc &TAL) {
299if (TAL.getArgument().getKind() == TemplateArgument::Integral)
300return toString(TAL.getArgument().getAsIntegral(), 10);
301return "";
302}
303std::string getDetail(const TemplateName &TN) {
304return toString([&](raw_ostream &OS) {
305TN.print(OS, Ctx.getPrintingPolicy(), TemplateName::Qualified::None);
306});
307}
308std::string getDetail(const Attr *A) {
309return A->getAttrName() ? A->getNormalizedFullName() : A->getSpelling();
310}
311std::string getDetail(const CXXBaseSpecifier &CBS) {
312return CBS.isVirtual() ? "virtual" : "";
313}
314std::string getDetail(const ConceptReference *CR) {
315return CR->getNamedConcept()->getNameAsString();
316}
317
318/// Arcana is produced by TextNodeDumper, for the types it supports.
319
320template <typename Dump> std::string dump(const Dump &D) {
321return toString([&](raw_ostream &OS) {
322TextNodeDumper Dumper(OS, Ctx, /*ShowColors=*/false);
323D(Dumper);
324});
325}
326template <typename T> std::string getArcana(const T &N) {
327return dump([&](TextNodeDumper &D) { D.Visit(N); });
328}
329std::string getArcana(const NestedNameSpecifierLoc &NNS) { return ""; }
330std::string getArcana(const TemplateName &NNS) { return ""; }
331std::string getArcana(const CXXBaseSpecifier &CBS) { return ""; }
332std::string getArcana(const TemplateArgumentLoc &TAL) {
333return dump([&](TextNodeDumper &D) {
334D.Visit(TAL.getArgument(), TAL.getSourceRange());
335});
336}
337std::string getArcana(const TypeLoc &TL) {
338return dump([&](TextNodeDumper &D) { D.Visit(TL.getType()); });
339}
340
341public:
342ASTNode Root;
343DumpVisitor(const syntax::TokenBuffer &Tokens, const ASTContext &Ctx)
344: Tokens(Tokens), Ctx(Ctx) {}
345
346// Override traversal to record the nodes we care about.
347// Generally, these are nodes with position information (TypeLoc, not Type).
348
349bool TraverseDecl(Decl *D) {
350return !D || isInjectedClassName(D) ||
351traverseNode("declaration", D, [&] { Base::TraverseDecl(D); });
352}
353bool TraverseTypeLoc(TypeLoc TL) {
354return !TL || traverseNode("type", TL, [&] { Base::TraverseTypeLoc(TL); });
355}
356bool TraverseTemplateName(const TemplateName &TN) {
357return traverseNode("template name", TN,
358[&] { Base::TraverseTemplateName(TN); });
359}
360bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &TAL) {
361return traverseNode("template argument", TAL,
362[&] { Base::TraverseTemplateArgumentLoc(TAL); });
363}
364bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSL) {
365return !NNSL || traverseNode("specifier", NNSL, [&] {
366Base::TraverseNestedNameSpecifierLoc(NNSL);
367});
368}
369bool TraverseConstructorInitializer(CXXCtorInitializer *CCI) {
370return !CCI || traverseNode("constructor initializer", CCI, [&] {
371Base::TraverseConstructorInitializer(CCI);
372});
373}
374bool TraverseAttr(Attr *A) {
375return !A || traverseNode("attribute", A, [&] { Base::TraverseAttr(A); });
376}
377bool TraverseConceptReference(ConceptReference *C) {
378return !C || traverseNode("reference", C,
379[&] { Base::TraverseConceptReference(C); });
380}
381bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier &CBS) {
382return traverseNode("base", CBS,
383[&] { Base::TraverseCXXBaseSpecifier(CBS); });
384}
385// Stmt is the same, but this form allows the data recursion optimization.
386bool dataTraverseStmtPre(Stmt *S) {
387return S && traverseNodePre(isa<Expr>(S) ? "expression" : "statement", S);
388}
389bool dataTraverseStmtPost(Stmt *X) { return traverseNodePost(); }
390
391// QualifiedTypeLoc is handled strangely in RecursiveASTVisitor: the derived
392// TraverseTypeLoc is not called for the inner UnqualTypeLoc.
393// This means we'd never see 'int' in 'const int'! Work around that here.
394// (The reason for the behavior is to avoid traversing the nested Type twice,
395// but we ignore TraverseType anyway).
396bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QTL) {
397return TraverseTypeLoc(QTL.getUnqualifiedLoc());
398}
399// Uninteresting parts of the AST that don't have locations within them.
400bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; }
401bool TraverseType(QualType) { return true; }
402
403// OpaqueValueExpr blocks traversal, we must explicitly traverse it.
404bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
405return TraverseStmt(E->getSourceExpr());
406}
407// We only want to traverse the *syntactic form* to understand the selection.
408bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
409return TraverseStmt(E->getSyntacticForm());
410}
411};
412
413} // namespace
414
415ASTNode dumpAST(const DynTypedNode &N, const syntax::TokenBuffer &Tokens,
416const ASTContext &Ctx) {
417DumpVisitor V(Tokens, Ctx);
418// DynTypedNode only works with const, RecursiveASTVisitor only non-const :-(
419if (const auto *D = N.get<Decl>())
420V.TraverseDecl(const_cast<Decl *>(D));
421else if (const auto *S = N.get<Stmt>())
422V.TraverseStmt(const_cast<Stmt *>(S));
423else if (const auto *NNSL = N.get<NestedNameSpecifierLoc>())
424V.TraverseNestedNameSpecifierLoc(
425*const_cast<NestedNameSpecifierLoc *>(NNSL));
426else if (const auto *NNS = N.get<NestedNameSpecifier>())
427V.TraverseNestedNameSpecifier(const_cast<NestedNameSpecifier *>(NNS));
428else if (const auto *TL = N.get<TypeLoc>())
429V.TraverseTypeLoc(*const_cast<TypeLoc *>(TL));
430else if (const auto *QT = N.get<QualType>())
431V.TraverseType(*const_cast<QualType *>(QT));
432else if (const auto *CCI = N.get<CXXCtorInitializer>())
433V.TraverseConstructorInitializer(const_cast<CXXCtorInitializer *>(CCI));
434else if (const auto *TAL = N.get<TemplateArgumentLoc>())
435V.TraverseTemplateArgumentLoc(*const_cast<TemplateArgumentLoc *>(TAL));
436else if (const auto *CBS = N.get<CXXBaseSpecifier>())
437V.TraverseCXXBaseSpecifier(*const_cast<CXXBaseSpecifier *>(CBS));
438else if (const auto *CR = N.get<ConceptReference>())
439V.TraverseConceptReference(const_cast<ConceptReference *>(CR));
440else
441elog("dumpAST: unhandled DynTypedNode kind {0}",
442N.getNodeKind().asStringRef());
443return std::move(V.Root);
444}
445
446} // namespace clangd
447} // namespace clang
448