brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · e126e72 Raw
172 lines · cpp
1//===--- DumpAST.cpp ---------------------------------------------*- C++-*-===//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// Defines a few tweaks that expose AST and related information.9// Some of these are fairly clang-specific and hidden (e.g. textual AST dumps).10// Others are more generally useful (class layout) and are exposed by default.11//===----------------------------------------------------------------------===//12#include "XRefs.h"13#include "refactor/Tweak.h"14#include "clang/AST/ASTTypeTraits.h"15#include "clang/AST/Type.h"16#include "llvm/Support/FormatVariadic.h"17#include "llvm/Support/ScopedPrinter.h"18#include "llvm/Support/raw_ostream.h"19#include <optional>20 21namespace clang {22namespace clangd {23namespace {24 25/// Dumps the AST of the selected node.26/// Input:27///   fcall("foo");28///   ^^^^^29/// Message:30///   CallExpr31///   |-DeclRefExpr fcall32///   `-StringLiteral "foo"33class DumpAST : public Tweak {34public:35  const char *id() const final;36 37  bool prepare(const Selection &Inputs) override {38    for (auto *N = Inputs.ASTSelection.commonAncestor(); N && !Node;39         N = N->Parent)40      if (dumpable(N->ASTNode))41        Node = N->ASTNode;42    return Node.has_value();43  }44  Expected<Effect> apply(const Selection &Inputs) override;45  std::string title() const override {46    return std::string(47        llvm::formatv("Dump {0} AST", Node->getNodeKind().asStringRef()));48  }49  llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; }50  bool hidden() const override { return true; }51 52private:53  static bool dumpable(const DynTypedNode &N) {54    // Sadly not all node types can be dumped, and there's no API to check.55    // See DynTypedNode::dump().56    return N.get<Decl>() || N.get<Stmt>() || N.get<Type>();57  }58 59  std::optional<DynTypedNode> Node;60};61REGISTER_TWEAK(DumpAST)62 63llvm::Expected<Tweak::Effect> DumpAST::apply(const Selection &Inputs) {64  std::string Str;65  llvm::raw_string_ostream OS(Str);66  Node->dump(OS, Inputs.AST->getASTContext());67  return Effect::showMessage(std::move(OS.str()));68}69 70/// Dumps the SelectionTree.71/// Input:72/// int fcall(int);73/// void foo() {74///   fcall(2 + 2);75///     ^^^^^76/// }77/// Message:78/// TranslationUnitDecl79///   FunctionDecl void foo()80///     CompoundStmt {}81///      .CallExpr fcall(2 + 2)82///        ImplicitCastExpr fcall83///         .DeclRefExpr fcall84///        BinaryOperator 2 + 285///          *IntegerLiteral 286class ShowSelectionTree : public Tweak {87public:88  const char *id() const final;89 90  bool prepare(const Selection &Inputs) override { return true; }91  Expected<Effect> apply(const Selection &Inputs) override {92    return Effect::showMessage(llvm::to_string(Inputs.ASTSelection));93  }94  std::string title() const override { return "Show selection tree"; }95  llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; }96  bool hidden() const override { return true; }97};98REGISTER_TWEAK(ShowSelectionTree)99 100/// Dumps the symbol under the cursor.101/// Inputs:102/// void foo();103///      ^^^104/// Message:105///  foo -106///  {"containerName":null,"id":"CA2EBE44A1D76D2A","name":"foo","usr":"c:@F@foo#"}107class DumpSymbol : public Tweak {108  const char *id() const final;109  bool prepare(const Selection &Inputs) override { return true; }110  Expected<Effect> apply(const Selection &Inputs) override {111    std::string Storage;112    llvm::raw_string_ostream Out(Storage);113 114    for (auto &Sym : getSymbolInfo(115             *Inputs.AST, sourceLocToPosition(Inputs.AST->getSourceManager(),116                                              Inputs.Cursor)))117      Out << Sym;118    return Effect::showMessage(Out.str());119  }120  std::string title() const override { return "Dump symbol under the cursor"; }121  llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; }122  bool hidden() const override { return true; }123};124REGISTER_TWEAK(DumpSymbol)125 126/// Shows the layout of the RecordDecl under the cursor.127/// Input:128/// struct X { int foo; };129/// ^^^^^^^^130/// Message:131///        0 | struct S132///        0 |   int foo133///          | [sizeof=4, dsize=4, align=4,134///          |  nvsize=4, nvalign=4]135class DumpRecordLayout : public Tweak {136public:137  const char *id() const final;138 139  bool prepare(const Selection &Inputs) override {140    if (auto *Node = Inputs.ASTSelection.commonAncestor())141      if (auto *D = Node->ASTNode.get<Decl>())142        Record = dyn_cast<RecordDecl>(D);143    return Record && Record->isThisDeclarationADefinition() &&144           !Record->isDependentType();145  }146  Expected<Effect> apply(const Selection &Inputs) override {147    std::string Str;148    llvm::raw_string_ostream OS(Str);149    Inputs.AST->getASTContext().DumpRecordLayout(Record, OS);150    return Effect::showMessage(std::move(OS.str()));151  }152  std::string title() const override {153    return std::string(llvm::formatv(154        "Show {0} layout",155        TypeWithKeyword::getTagTypeKindName(Record->getTagKind())));156  }157  llvm::StringLiteral kind() const override { return CodeAction::INFO_KIND; }158  // FIXME: this is interesting to most users. However:159  //  - triggering is too broad (e.g. triggers on comments within a class)160  //  - showMessage has inconsistent UX (e.g. newlines are stripped in VSCode)161  //  - the output itself is a bit hard to decipher.162  bool hidden() const override { return true; }163 164private:165  const RecordDecl *Record = nullptr;166};167REGISTER_TWEAK(DumpRecordLayout)168 169} // namespace170} // namespace clangd171} // namespace clang172