brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.8 KiB · cd409a2 Raw
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