blob: 9baa8fa4ad6a8409f696e3d69310cf95ac7f9e64 [file] [log] [blame]
//===--- DumpAST.cpp ---------------------------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Defines a few tweaks that expose AST and related information.
// Some of these are fairly clang-specific and hidden (e.g. textual AST dumps).
// Others are more generally useful (class layout) and are exposed by default.
//===----------------------------------------------------------------------===//
#include "refactor/Tweak.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Type.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace clangd {
namespace {
/// Dumps the AST of the selected node.
/// Input:
/// fcall("foo");
/// ^^^^^
/// Message:
/// CallExpr
/// |-DeclRefExpr fcall
/// `-StringLiteral "foo"
class DumpAST : public Tweak {
public:
const char *id() const override final;
bool prepare(const Selection &Inputs) override {
for (auto N = Inputs.ASTSelection.commonAncestor(); N && !Node;
N = N->Parent)
if (dumpable(N->ASTNode))
Node = N->ASTNode;
return Node.hasValue();
}
Expected<Effect> apply(const Selection &Inputs) override;
std::string title() const override {
return llvm::formatv("Dump {0} AST", Node->getNodeKind().asStringRef());
}
Intent intent() const override { return Info; }
bool hidden() const override { return true; }
private:
static bool dumpable(const ast_type_traits::DynTypedNode &N) {
// Sadly not all node types can be dumped, and there's no API to check.
// See DynTypedNode::dump().
return N.get<Decl>() || N.get<Stmt>() || N.get<Type>();
}
llvm::Optional<ast_type_traits::DynTypedNode> Node;
};
REGISTER_TWEAK(DumpAST)
llvm::Expected<Tweak::Effect> DumpAST::apply(const Selection &Inputs) {
std::string Str;
llvm::raw_string_ostream OS(Str);
Node->dump(OS, Inputs.AST.getASTContext().getSourceManager());
return Effect::showMessage(std::move(OS.str()));
}
/// Dumps the SelectionTree.
/// Input:
/// int fcall(int);
/// void foo() {
/// fcall(2 + 2);
/// ^^^^^
/// }
/// Message:
/// TranslationUnitDecl
/// FunctionDecl void foo()
/// CompoundStmt {}
/// .CallExpr fcall(2 + 2)
/// ImplicitCastExpr fcall
/// .DeclRefExpr fcall
/// BinaryOperator 2 + 2
/// *IntegerLiteral 2
class ShowSelectionTree : public Tweak {
public:
const char *id() const override final;
bool prepare(const Selection &Inputs) override { return true; }
Expected<Effect> apply(const Selection &Inputs) override {
return Effect::showMessage(llvm::to_string(Inputs.ASTSelection));
}
std::string title() const override { return "Show selection tree"; }
Intent intent() const override { return Info; }
bool hidden() const override { return true; }
};
REGISTER_TWEAK(ShowSelectionTree)
/// Shows the layout of the RecordDecl under the cursor.
/// Input:
/// struct X { int foo; };
/// ^^^^^^^^
/// Message:
/// 0 | struct S
/// 0 | int foo
/// | [sizeof=4, dsize=4, align=4,
/// | nvsize=4, nvalign=4]
class DumpRecordLayout : public Tweak {
public:
const char *id() const override final;
bool prepare(const Selection &Inputs) override {
if (auto *Node = Inputs.ASTSelection.commonAncestor())
if (auto *D = Node->ASTNode.get<Decl>())
Record = dyn_cast<RecordDecl>(D);
return Record && Record->isThisDeclarationADefinition() &&
!Record->isDependentType();
}
Expected<Effect> apply(const Selection &Inputs) override {
std::string Str;
llvm::raw_string_ostream OS(Str);
Inputs.AST.getASTContext().DumpRecordLayout(Record, OS);
return Effect::showMessage(std::move(OS.str()));
}
std::string title() const override {
return llvm::formatv(
"Show {0} layout",
TypeWithKeyword::getTagTypeKindName(Record->getTagKind()));
}
Intent intent() const override { return Info; }
// FIXME: this is interesting to most users. However:
// - triggering is too broad (e.g. triggers on comments within a class)
// - showMessage has inconsistent UX (e.g. newlines are stripped in VSCode)
// - the output itself is a bit hard to decipher.
bool hidden() const override { return true; }
private:
const RecordDecl *Record = nullptr;
};
REGISTER_TWEAK(DumpRecordLayout)
} // namespace
} // namespace clangd
} // namespace clang