diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -70,6 +70,10 @@ 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/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -651,6 +651,28 @@ 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 @@ 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 @@ 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/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -66,7 +66,7 @@ {R"cpp( namespace ns1 { namespace ns2 { struct Foo { - int [[b^ar]]; + char [[b^ar]]; }; }} )cpp", @@ -75,8 +75,10 @@ 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 @@ {R"cpp( namespace ns1 { namespace { struct { - int [[b^ar]]; + char [[b^ar]]; } T; }} )cpp", @@ -109,8 +111,21 @@ 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 @@ 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 @@ { [](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 @@ }, R"(class foo +Size: 10 bytes documentation template class Foo {})", @@ -1911,19 +1930,23 @@ }, { [](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)", }, };