forked from OSchip/llvm-project
[clangd] show layout info when hovering on a class/field definition.
Summary: This triggers only on the definition itself, not on references (probably too noisy). Inspecting the definition seems like a decent hint for being interested in layout. Reviewers: kadircet Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D77355
This commit is contained in:
parent
c77887e4d1
commit
c1a00b89ad
|
@ -651,6 +651,28 @@ bool isHardLineBreakAfter(llvm::StringRef Line, llvm::StringRef Rest) {
|
|||
return punctuationIndicatesLineBreak(Line) || isHardLineBreakIndicator(Rest);
|
||||
}
|
||||
|
||||
void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) {
|
||||
const auto &Ctx = ND.getASTContext();
|
||||
|
||||
if (auto *RD = llvm::dyn_cast<RecordDecl>(&ND)) {
|
||||
if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RD->getTypeForDecl()))
|
||||
HI.Size = Size->getQuantity();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *FD = llvm::dyn_cast<FieldDecl>(&ND)) {
|
||||
const auto *Record = FD->getParent()->getDefinition();
|
||||
if (Record && !Record->isDependentType()) {
|
||||
uint64_t OffsetBits = Ctx.getFieldOffset(FD);
|
||||
if (auto Size = Ctx.getTypeSizeInCharsIfKnown(FD->getType())) {
|
||||
HI.Size = Size->getQuantity();
|
||||
HI.Offset = OffsetBits / 8;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
|
||||
|
@ -707,6 +729,9 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
|
|||
auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Alias);
|
||||
if (!Decls.empty()) {
|
||||
HI = getHoverContents(Decls.front(), Index);
|
||||
// Layout info only shown when hovering on the field/class itself.
|
||||
if (Decls.front() == N->ASTNode.get<Decl>())
|
||||
addLayoutInfo(*Decls.front(), *HI);
|
||||
// Look for a close enclosing expression to show the value of.
|
||||
if (!HI->Value)
|
||||
HI->Value = printExprValue(N, AST.getASTContext());
|
||||
|
@ -782,6 +807,13 @@ markup::Document HoverInfo::present() const {
|
|||
P.appendCode(*Value);
|
||||
}
|
||||
|
||||
if (Offset)
|
||||
Output.addParagraph().appendText(
|
||||
llvm::formatv("Offset: {0} byte{1}", *Offset, *Offset == 1 ? "" : "s"));
|
||||
if (Size)
|
||||
Output.addParagraph().appendText(
|
||||
llvm::formatv("Size: {0} byte{1}", *Size, *Size == 1 ? "" : "s"));
|
||||
|
||||
if (!Documentation.empty())
|
||||
parseDocumentation(Documentation, Output);
|
||||
|
||||
|
|
|
@ -70,6 +70,10 @@ struct HoverInfo {
|
|||
llvm::Optional<std::vector<Param>> TemplateParameters;
|
||||
/// Contains the evaluated value of the symbol if available.
|
||||
llvm::Optional<std::string> Value;
|
||||
/// Contains the byte-size of fields and types where it's interesting.
|
||||
llvm::Optional<uint64_t> Size;
|
||||
/// Contains the offset of fields within the enclosing class.
|
||||
llvm::Optional<uint64_t> Offset;
|
||||
|
||||
/// Produce a user-readable information.
|
||||
markup::Document present() const;
|
||||
|
|
|
@ -66,7 +66,7 @@ TEST(Hover, Structured) {
|
|||
{R"cpp(
|
||||
namespace ns1 { namespace ns2 {
|
||||
struct Foo {
|
||||
int [[b^ar]];
|
||||
char [[b^ar]];
|
||||
};
|
||||
}}
|
||||
)cpp",
|
||||
|
@ -75,8 +75,10 @@ TEST(Hover, Structured) {
|
|||
HI.LocalScope = "Foo::";
|
||||
HI.Name = "bar";
|
||||
HI.Kind = index::SymbolKind::Field;
|
||||
HI.Definition = "int bar";
|
||||
HI.Type = "int";
|
||||
HI.Definition = "char bar";
|
||||
HI.Type = "char";
|
||||
HI.Offset = 0;
|
||||
HI.Size = 1;
|
||||
}},
|
||||
// Local to class method.
|
||||
{R"cpp(
|
||||
|
@ -100,7 +102,7 @@ TEST(Hover, Structured) {
|
|||
{R"cpp(
|
||||
namespace ns1 { namespace {
|
||||
struct {
|
||||
int [[b^ar]];
|
||||
char [[b^ar]];
|
||||
} T;
|
||||
}}
|
||||
)cpp",
|
||||
|
@ -109,8 +111,21 @@ TEST(Hover, Structured) {
|
|||
HI.LocalScope = "(anonymous struct)::";
|
||||
HI.Name = "bar";
|
||||
HI.Kind = index::SymbolKind::Field;
|
||||
HI.Definition = "int bar";
|
||||
HI.Type = "int";
|
||||
HI.Definition = "char bar";
|
||||
HI.Type = "char";
|
||||
HI.Offset = 0;
|
||||
HI.Size = 1;
|
||||
}},
|
||||
// Struct definition shows size.
|
||||
{R"cpp(
|
||||
struct [[^X]]{};
|
||||
)cpp",
|
||||
[](HoverInfo &HI) {
|
||||
HI.NamespaceScope = "";
|
||||
HI.Name = "X";
|
||||
HI.Kind = index::SymbolKind::Struct;
|
||||
HI.Definition = "struct X {}";
|
||||
HI.Size = 1;
|
||||
}},
|
||||
// Variable with template type
|
||||
{R"cpp(
|
||||
|
@ -698,6 +713,8 @@ class Foo {})cpp";
|
|||
EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters);
|
||||
EXPECT_EQ(H->SymRange, Expected.SymRange);
|
||||
EXPECT_EQ(H->Value, Expected.Value);
|
||||
EXPECT_EQ(H->Size, Expected.Size);
|
||||
EXPECT_EQ(H->Offset, Expected.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1862,6 +1879,7 @@ TEST(Hover, Present) {
|
|||
{
|
||||
[](HoverInfo &HI) {
|
||||
HI.Kind = index::SymbolKind::Class;
|
||||
HI.Size = 10;
|
||||
HI.TemplateParameters = {
|
||||
{std::string("typename"), std::string("T"), llvm::None},
|
||||
{std::string("typename"), std::string("C"),
|
||||
|
@ -1875,6 +1893,7 @@ TEST(Hover, Present) {
|
|||
},
|
||||
R"(class foo
|
||||
|
||||
Size: 10 bytes
|
||||
documentation
|
||||
|
||||
template <typename T, typename C = bool> class Foo {})",
|
||||
|
@ -1911,19 +1930,23 @@ ret_type foo(params) {})",
|
|||
},
|
||||
{
|
||||
[](HoverInfo &HI) {
|
||||
HI.Kind = index::SymbolKind::Variable;
|
||||
HI.LocalScope = "test::bar::";
|
||||
HI.Kind = index::SymbolKind::Field;
|
||||
HI.LocalScope = "test::Bar::";
|
||||
HI.Value = "value";
|
||||
HI.Name = "foo";
|
||||
HI.Type = "type";
|
||||
HI.Definition = "def";
|
||||
HI.Size = 4;
|
||||
HI.Offset = 12;
|
||||
},
|
||||
R"(variable foo
|
||||
R"(field foo
|
||||
|
||||
Type: type
|
||||
Value = value
|
||||
Offset: 12 bytes
|
||||
Size: 4 bytes
|
||||
|
||||
// In test::bar
|
||||
// In test::Bar
|
||||
def)",
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue