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-exception
6//
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/// CallExpr
31/// |-DeclRefExpr fcall
32/// `-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(S: 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/// TranslationUnitDecl
79/// FunctionDecl void foo()
80/// CompoundStmt {}
81/// .CallExpr fcall(2 + 2)
82/// ImplicitCastExpr fcall
83/// .DeclRefExpr fcall
84/// BinaryOperator 2 + 2
85/// *IntegerLiteral 2
86class 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(S: llvm::to_string(Value: 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 AST&: *Inputs.AST, Pos: sourceLocToPosition(SM: Inputs.AST->getSourceManager(),
116 Loc: Inputs.Cursor)))
117 Out << Sym;
118 return Effect::showMessage(S: 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 S
132/// 0 | int foo
133/// | [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(RD: Record, OS);
150 return Effect::showMessage(S: std::move(OS.str()));
151 }
152 std::string title() const override {
153 return std::string(llvm::formatv(
154 "Show {0} layout",
155 TypeWithKeyword::getTagTypeKindName(Kind: 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} // namespace
170} // namespace clangd
171} // namespace clang
172

source code of clang-tools-extra/clangd/refactor/tweaks/DumpAST.cpp