From: Sam McCall Date: Thu, 11 Mar 2021 00:20:36 +0000 (+0100) Subject: [clangd] Show padding following a field on field hover. X-Git-Tag: llvmorg-14-init~2599 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b447445eaa6f8ff826a7eab276c10bc6f133aeb0;p=platform%2Fupstream%2Fllvm.git [clangd] Show padding following a field on field hover. This displays as: `Size: 4 bytes (+4 padding)` Also stop showing (byte) offset/size for bitfields. They're not meaningful and using them to calculate padding is dangerous! Differential Revision: https://reviews.llvm.org/D98377 --- diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 8b14777..c71a8c4 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -28,6 +28,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/SourceLocation.h" @@ -770,10 +771,30 @@ void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) { const auto *Record = FD->getParent(); if (Record) Record = Record->getDefinition(); - if (Record && !Record->isInvalidDecl() && !Record->isDependentType()) { - HI.Offset = Ctx.getFieldOffset(FD) / 8; - if (auto Size = Ctx.getTypeSizeInCharsIfKnown(FD->getType())) - HI.Size = Size->getQuantity(); + if (Record && !Record->isInvalidDecl() && !Record->isDependentType() && + !FD->isBitField()) { + 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(); + 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; + 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; + } + } + // Offset in a union is always zero, so not really useful to report. + if (Record->isUnion()) + HI.Offset.reset(); } return; } @@ -1013,9 +1034,12 @@ markup::Document HoverInfo::present() const { Output.addParagraph().appendText( llvm::formatv("Offset: {0} byte{1}", *Offset, *Offset == 1 ? "" : "s") .str()); - if (Size) - Output.addParagraph().appendText( + 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()); + } if (CalleeArgInfo) { assert(CallPassType); diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h index 2f2afbf..44ee9b7 100644 --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -77,6 +77,8 @@ struct HoverInfo { llvm::Optional Size; /// Contains the offset of fields within the enclosing class. llvm::Optional Offset; + /// Contains the padding following a field within the enclosing class. + llvm::Optional Padding; // Set when symbol is inside function call. Contains information extracted // from the callee definition about the argument this is passed as. llvm::Optional CalleeArgInfo; diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index c2645b9..9089a48 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -68,8 +68,9 @@ TEST(Hover, Structured) { // Field {R"cpp( namespace ns1 { namespace ns2 { - struct Foo { + class Foo { char [[b^ar]]; + double y[2]; }; }} )cpp", @@ -82,6 +83,41 @@ TEST(Hover, Structured) { HI.Type = "char"; HI.Offset = 0; HI.Size = 1; + HI.Padding = 7; + HI.AccessSpecifier = "private"; + }}, + // Union field + {R"cpp( + union Foo { + char [[b^ar]]; + double y[2]; + }; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "Foo::"; + HI.Name = "bar"; + HI.Kind = index::SymbolKind::Field; + HI.Definition = "char bar"; + HI.Type = "char"; + HI.Size = 1; + HI.Padding = 15; + HI.AccessSpecifier = "public"; + }}, + // Bitfield + {R"cpp( + struct Foo { + int [[^x]] : 1; + int y : 1; + }; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.LocalScope = "Foo::"; + HI.Name = "x"; + HI.Kind = index::SymbolKind::Field; + HI.Definition = "int x : 1"; + HI.Type = "int"; HI.AccessSpecifier = "public"; }}, // Local to class method. @@ -2558,13 +2594,14 @@ template class Foo {})", HI.Definition = "def"; HI.Size = 4; HI.Offset = 12; + HI.Padding = 4; }, R"(field foo Type: type Value = value Offset: 12 bytes -Size: 4 bytes +Size: 4 bytes (+4 padding) // In test::Bar def)",