From 4cb5e436ae713579cfee00cfc8e516cf965b91e9 Mon Sep 17 00:00:00 2001 From: SR_team Date: Mon, 5 Jun 2023 17:52:06 +0200 Subject: [PATCH] [clangd] Show size, offset and padding for bit fields on hover Examle: ``` struct test { char a; char b : 3; char c : 5; int d; int e : 27; }; ``` {F27617774} {F27617776} {F27617777} {F27617780} Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D151128 --- clang-tools-extra/clangd/Hover.cpp | 52 +++++++++++------ clang-tools-extra/clangd/Hover.h | 2 +- clang-tools-extra/clangd/unittests/HoverTests.cpp | 68 +++++++++++++++++++---- 3 files changed, 92 insertions(+), 30 deletions(-) diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index d1f6f3a..0ae70dd1 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -866,7 +866,7 @@ HoverInfo getStringLiteralContents(const StringLiteral *SL, HoverInfo HI; HI.Name = "string-literal"; - HI.Size = (SL->getLength() + 1) * SL->getCharByteWidth(); + HI.Size = (SL->getLength() + 1) * SL->getCharByteWidth() * 8; HI.Type = SL->getType().getAsString(PP).c_str(); return HI; @@ -1000,7 +1000,7 @@ 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(); + HI.Size = Size->getQuantity() * 8; return; } @@ -1008,25 +1008,26 @@ void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) { const auto *Record = FD->getParent(); if (Record) Record = Record->getDefinition(); - if (Record && !Record->isInvalidDecl() && !Record->isDependentType() && - !FD->isBitField()) { + if (Record && !Record->isInvalidDecl() && !Record->isDependentType()) { const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(Record); - HI.Offset = Layout.getFieldOffset(FD->getFieldIndex()) / 8; - if (auto Size = Ctx.getTypeSizeInCharsIfKnown(FD->getType())) { - HI.Size = FD->isZeroSize(Ctx) ? 0 : Size->getQuantity(); + HI.Offset = Layout.getFieldOffset(FD->getFieldIndex()); + if (FD->isBitField()) + HI.Size = FD->getBitWidthValue(Ctx); + else if (auto Size = Ctx.getTypeSizeInCharsIfKnown(FD->getType())) + HI.Size = FD->isZeroSize(Ctx) ? 0 : Size->getQuantity() * 8; + if (HI.Size) { unsigned EndOfField = *HI.Offset + *HI.Size; // Calculate padding following the field. if (!Record->isUnion() && FD->getFieldIndex() + 1 < Layout.getFieldCount()) { // Measure padding up to the next class field. - unsigned NextOffset = - Layout.getFieldOffset(FD->getFieldIndex() + 1) / 8; + unsigned NextOffset = Layout.getFieldOffset(FD->getFieldIndex() + 1); if (NextOffset >= EndOfField) // next field could be a bitfield! HI.Padding = NextOffset - EndOfField; } else { // Measure padding up to the end of the object. - HI.Padding = Layout.getSize().getQuantity() - EndOfField; + HI.Padding = Layout.getSize().getQuantity() * 8 - EndOfField; } } // Offset in a union is always zero, so not really useful to report. @@ -1401,6 +1402,24 @@ std::optional getHover(ParsedAST &AST, Position Pos, return HI; } +// Sizes (and padding) are shown in bytes if possible, otherwise in bits. +static std::string formatSize(uint64_t SizeInBits) { + uint64_t Value = SizeInBits % 8 == 0 ? SizeInBits / 8 : SizeInBits; + const char *Unit = Value != 0 && Value == SizeInBits ? "bit" : "byte"; + return llvm::formatv("{0} {1}{2}", Value, Unit, Value == 1 ? "" : "s").str(); +} + +// Offsets are shown in bytes + bits, so offsets of different fields +// can always be easily compared. +static std::string formatOffset(uint64_t OffsetInBits) { + const auto Bytes = OffsetInBits / 8; + const auto Bits = OffsetInBits % 8; + auto Offset = formatSize(Bytes * 8); + if (Bits != 0) + Offset += " and " + formatSize(Bits); + return Offset; +} + markup::Document HoverInfo::present() const { markup::Document Output; @@ -1464,14 +1483,13 @@ markup::Document HoverInfo::present() const { } if (Offset) - Output.addParagraph().appendText( - llvm::formatv("Offset: {0} byte{1}", *Offset, *Offset == 1 ? "" : "s") - .str()); + Output.addParagraph().appendText("Offset: " + formatOffset(*Offset)); if (Size) { - auto &P = Output.addParagraph().appendText( - llvm::formatv("Size: {0} byte{1}", *Size, *Size == 1 ? "" : "s").str()); - if (Padding && *Padding != 0) - P.appendText(llvm::formatv(" (+{0} padding)", *Padding).str()); + auto &P = Output.addParagraph().appendText("Size: " + formatSize(*Size)); + if (Padding && *Padding != 0) { + P.appendText( + llvm::formatv(" (+{0} padding)", formatSize(*Padding)).str()); + } } if (CalleeArgInfo) { diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h index 891a9d9..6a61100 100644 --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -91,7 +91,7 @@ struct HoverInfo { std::optional> TemplateParameters; /// Contains the evaluated value of the symbol if available. std::optional Value; - /// Contains the byte-size of fields and types where it's interesting. + /// Contains the bit-size of fields and types where it's interesting. std::optional Size; /// Contains the offset of fields within the enclosing class. std::optional Offset; diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 135fdfc..583310f 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -89,8 +89,8 @@ TEST(Hover, Structured) { HI.Definition = "char bar"; HI.Type = "char"; HI.Offset = 0; - HI.Size = 1; - HI.Padding = 7; + HI.Size = 8; + HI.Padding = 56; HI.AccessSpecifier = "private"; }}, // Union field @@ -107,8 +107,8 @@ TEST(Hover, Structured) { HI.Kind = index::SymbolKind::Field; HI.Definition = "char bar"; HI.Type = "char"; - HI.Size = 1; - HI.Padding = 15; + HI.Size = 8; + HI.Padding = 120; HI.AccessSpecifier = "public"; }}, // Bitfield @@ -125,6 +125,8 @@ TEST(Hover, Structured) { HI.Kind = index::SymbolKind::Field; HI.Definition = "int x : 1"; HI.Type = "int"; + HI.Offset = 0; + HI.Size = 1; HI.AccessSpecifier = "public"; }}, // Local to class method. @@ -188,7 +190,7 @@ TEST(Hover, Structured) { HI.Definition = "char bar"; HI.Type = "char"; HI.Offset = 0; - HI.Size = 1; + HI.Size = 8; HI.AccessSpecifier = "public"; }}, // Struct definition shows size. @@ -200,7 +202,7 @@ TEST(Hover, Structured) { HI.Name = "X"; HI.Kind = index::SymbolKind::Struct; HI.Definition = "struct X {}"; - HI.Size = 1; + HI.Size = 8; }}, // Variable with template type {R"cpp( @@ -1285,6 +1287,26 @@ class Foo final {})cpp"; HI.Kind = index::SymbolKind::Field; HI.Definition = "m_int arr[Size]"; HI.Type = {"m_int[Size]", "int[Size]"}; + }}, + {// Bitfield offset, size and padding + R"cpp( + struct Foo { + char x; + char [[^y]] : 1; + int z; + }; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "Foo::"; + HI.Name = "y"; + HI.Kind = index::SymbolKind::Field; + HI.Definition = "char y : 1"; + HI.Type = "char"; + HI.Offset = 8; + HI.Size = 1; + HI.Padding = 23; + HI.AccessSpecifier = "public"; }}}; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Code); @@ -1508,7 +1530,7 @@ TEST(Hover, All) { {"auto s = ^[[\"Hello, world!\"]]; // string literal", [](HoverInfo &HI) { HI.Name = "string-literal"; - HI.Size = 14; + HI.Size = 112; HI.Type = "const char[14]"; }}, { @@ -3218,7 +3240,7 @@ TEST(Hover, Present) { { [](HoverInfo &HI) { HI.Kind = index::SymbolKind::Class; - HI.Size = 10; + HI.Size = 80; HI.TemplateParameters = { {{"typename"}, std::string("T"), std::nullopt}, {{"typename"}, std::string("C"), std::string("bool")}, @@ -3274,16 +3296,38 @@ template class Foo {})", HI.Name = "foo"; HI.Type = {"type", "can_type"}; HI.Definition = "def"; - HI.Size = 4; - HI.Offset = 12; - HI.Padding = 4; + HI.Size = 32; + HI.Offset = 96; + HI.Padding = 32; }, R"(field foo Type: type (aka can_type) Value = value Offset: 12 bytes -Size: 4 bytes (+4 padding) +Size: 4 bytes (+4 bytes padding) + +// In test::Bar +def)", + }, + { + [](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Field; + HI.LocalScope = "test::Bar::"; + HI.Value = "value"; + HI.Name = "foo"; + HI.Type = {"type", "can_type"}; + HI.Definition = "def"; + HI.Size = 25; + HI.Offset = 35; + HI.Padding = 4; + }, + R"(field foo + +Type: type (aka can_type) +Value = value +Offset: 4 bytes and 3 bits +Size: 25 bits (+4 bits padding) // In test::Bar def)", -- 2.7.4