From: Sam McCall Date: Fri, 3 Apr 2020 01:07:10 +0000 (+0200) Subject: [clangd] show layout info when hovering on a class/field definition. X-Git-Tag: llvmorg-12-init~9694 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c1a00b89add8f4f9658f631a4fa2cb8a068eef57;p=platform%2Fupstream%2Fllvm.git [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 --- diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index d94c528..ba8fcd4 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -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(&ND)) { + if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RD->getTypeForDecl())) + HI.Size = Size->getQuantity(); + return; + } + + if (const auto *FD = llvm::dyn_cast(&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 getHover(ParsedAST &AST, Position Pos, @@ -707,6 +729,9 @@ llvm::Optional 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()) + 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); diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h index 8fd2e6d..4476ed8 100644 --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -70,6 +70,10 @@ struct HoverInfo { llvm::Optional> TemplateParameters; /// Contains the evaluated value of the symbol if available. llvm::Optional Value; + /// Contains the byte-size of fields and types where it's interesting. + llvm::Optional Size; + /// Contains the offset of fields within the enclosing class. + llvm::Optional Offset; /// Produce a user-readable information. markup::Document present() const; diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index b51329e..4b98848 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -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 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)", }, };