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