1 //===--- DumpAST.cpp ---------------------------------------------*- C++-*-===//
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
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 //===----------------------------------------------------------------------===//
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"
25 /// Dumps the AST of the selected node.
31 /// |-DeclRefExpr fcall
32 /// `-StringLiteral "foo"
33 class DumpAST
: public Tweak
{
35 const char *id() const final
;
37 bool prepare(const Selection
&Inputs
) override
{
38 for (auto *N
= Inputs
.ASTSelection
.commonAncestor(); N
&& !Node
;
40 if (dumpable(N
->ASTNode
))
42 return Node
.has_value();
44 Expected
<Effect
> apply(const Selection
&Inputs
) override
;
45 std::string
title() const override
{
47 llvm::formatv("Dump {0} AST", Node
->getNodeKind().asStringRef()));
49 llvm::StringLiteral
kind() const override
{ return CodeAction::INFO_KIND
; }
50 bool hidden() const override
{ return true; }
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
>();
59 std::optional
<DynTypedNode
> Node
;
61 REGISTER_TWEAK(DumpAST
)
63 llvm::Expected
<Tweak::Effect
> DumpAST::apply(const Selection
&Inputs
) {
65 llvm::raw_string_ostream
OS(Str
);
66 Node
->dump(OS
, Inputs
.AST
->getASTContext());
67 return Effect::showMessage(std::move(OS
.str()));
70 /// Dumps the SelectionTree.
78 /// TranslationUnitDecl
79 /// FunctionDecl void foo()
81 /// .CallExpr fcall(2 + 2)
82 /// ImplicitCastExpr fcall
83 /// .DeclRefExpr fcall
84 /// BinaryOperator 2 + 2
86 class ShowSelectionTree
: public Tweak
{
88 const char *id() const final
;
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
));
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; }
98 REGISTER_TWEAK(ShowSelectionTree
)
100 /// Dumps the symbol under the cursor.
106 /// {"containerName":null,"id":"CA2EBE44A1D76D2A","name":"foo","usr":"c:@F@foo#"}
107 class 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
{
112 llvm::raw_string_ostream
Out(Storage
);
114 for (auto &Sym
: getSymbolInfo(
115 *Inputs
.AST
, sourceLocToPosition(Inputs
.AST
->getSourceManager(),
118 return Effect::showMessage(Out
.str());
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; }
124 REGISTER_TWEAK(DumpSymbol
)
126 /// Shows the layout of the RecordDecl under the cursor.
128 /// struct X { int foo; };
133 /// | [sizeof=4, dsize=4, align=4,
134 /// | nvsize=4, nvalign=4]
135 class DumpRecordLayout
: public Tweak
{
137 const char *id() const final
;
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();
146 Expected
<Effect
> apply(const Selection
&Inputs
) override
{
148 llvm::raw_string_ostream
OS(Str
);
149 Inputs
.AST
->getASTContext().DumpRecordLayout(Record
, OS
);
150 return Effect::showMessage(std::move(OS
.str()));
152 std::string
title() const override
{
153 return std::string(llvm::formatv(
155 TypeWithKeyword::getTagTypeKindName(Record
->getTagKind())));
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; }
165 const RecordDecl
*Record
= nullptr;
167 REGISTER_TWEAK(DumpRecordLayout
)
170 } // namespace clangd