2019-06-18 21:37:54 +08:00
|
|
|
//===--- 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.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-10-28 23:27:42 +08:00
|
|
|
#include "XRefs.h"
|
2019-06-18 21:37:54 +08:00
|
|
|
#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 {
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(
|
|
|
|
llvm::formatv("Dump {0} AST", Node->getNodeKind().asStringRef()));
|
2019-06-18 21:37:54 +08:00
|
|
|
}
|
|
|
|
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);
|
2019-12-16 22:46:40 +08:00
|
|
|
Node->dump(OS, Inputs.AST->getSourceManager());
|
2019-06-18 21:37:54 +08:00
|
|
|
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;
|
|
|
|
|
[clangd] SelectionTree treats TranslationUnitDecl (mostly) consistently with other containers.
Summary:
Previously TranslationUnitDecl would never be selected.
This means root() is never null, and returns a reference.
commonAncestor() is in principle never null also, but returning TUDecl
here requires tweaks to be careful not to traverse it (this was already
possible when selecting multiple top-level decls, and there are associated bugs!)
Instead, never allow commonAncestor() to return TUDecl, return null instead.
Reviewers: hokein
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D65101
llvm-svn: 366893
2019-07-24 20:14:56 +08:00
|
|
|
bool prepare(const Selection &Inputs) override { return true; }
|
2019-06-18 21:37:54 +08:00
|
|
|
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; }
|
|
|
|
};
|
2019-06-18 21:52:00 +08:00
|
|
|
REGISTER_TWEAK(ShowSelectionTree)
|
2019-06-18 21:37:54 +08:00
|
|
|
|
2019-10-28 23:27:42 +08:00
|
|
|
/// Dumps the symbol under the cursor.
|
|
|
|
/// Inputs:
|
|
|
|
/// void foo();
|
|
|
|
/// ^^^
|
|
|
|
/// Message:
|
|
|
|
/// foo -
|
|
|
|
/// {"containerName":null,"id":"CA2EBE44A1D76D2A","name":"foo","usr":"c:@F@foo#"}
|
|
|
|
class DumpSymbol : public Tweak {
|
|
|
|
const char *id() const override final;
|
|
|
|
bool prepare(const Selection &Inputs) override { return true; }
|
|
|
|
Expected<Effect> apply(const Selection &Inputs) override {
|
|
|
|
std::string Storage;
|
|
|
|
llvm::raw_string_ostream Out(Storage);
|
|
|
|
|
|
|
|
for (auto &Sym : getSymbolInfo(
|
2019-12-16 22:46:40 +08:00
|
|
|
*Inputs.AST, sourceLocToPosition(Inputs.AST->getSourceManager(),
|
|
|
|
Inputs.Cursor)))
|
2019-10-28 23:27:42 +08:00
|
|
|
Out << Sym;
|
|
|
|
return Effect::showMessage(Out.str());
|
|
|
|
}
|
|
|
|
std::string title() const override { return "Dump symbol under the cursor"; }
|
|
|
|
Intent intent() const override { return Info; }
|
|
|
|
bool hidden() const override { return true; }
|
|
|
|
};
|
|
|
|
REGISTER_TWEAK(DumpSymbol)
|
|
|
|
|
2019-06-18 21:37:54 +08:00
|
|
|
/// 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);
|
2019-12-16 22:46:40 +08:00
|
|
|
Inputs.AST->getASTContext().DumpRecordLayout(Record, OS);
|
2019-06-18 21:37:54 +08:00
|
|
|
return Effect::showMessage(std::move(OS.str()));
|
|
|
|
}
|
|
|
|
std::string title() const override {
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(llvm::formatv(
|
2019-06-18 21:37:54 +08:00
|
|
|
"Show {0} layout",
|
2020-01-29 03:23:46 +08:00
|
|
|
TypeWithKeyword::getTagTypeKindName(Record->getTagKind())));
|
2019-06-18 21:37:54 +08:00
|
|
|
}
|
|
|
|
Intent intent() const override { return Info; }
|
2019-07-18 23:00:38 +08:00
|
|
|
// 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; }
|
2019-06-18 21:37:54 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
const RecordDecl *Record = nullptr;
|
|
|
|
};
|
2019-06-18 21:52:00 +08:00
|
|
|
REGISTER_TWEAK(DumpRecordLayout)
|
2019-06-18 21:37:54 +08:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|