Skip to content

Commit 9663999

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/golang: hover: show size/offset info
This change causes Hover to report size information for types and struct fields when hovering over the declaring identifier, plus offset information for struct fields. Some tests needed tweaks to make them CPU-independent, or to disable them on 32-bit machines. Also, add the first release notes using the new git+gerrit workflow (and backfill a missing item for "View package documentation"). Change-Id: Ibe8ac5937912c3802f3ad79e3d9f92ba24eb51de Reviewed-on: https://go-review.googlesource.com/c/tools/+/573076 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 509ed1c commit 9663999

File tree

13 files changed

+291
-39
lines changed

13 files changed

+291
-39
lines changed

gopls/doc/release/README

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This directory contains the draft release notes for each upcoming release.
2+
3+
Be sure to update the file for the forthcoming release in the same CL
4+
that you add new features or fix noteworthy bugs.
5+
6+
See https://github.com/golang/tools/releases for all past releases.
7+
8+
Tip: when reviewing edits to markdown files in Gerrit, to see the
9+
rendered form, click the "Open in Code Search" link (magnifying glass
10+
in blue square) then click "View in > gitiles" (shortcut: `v g`).

gopls/doc/release/v0.16.0.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
gopls/v0.16.0
2+
3+
```
4+
go install golang.org/x/tools/[email protected]
5+
```
6+
7+
## New features
8+
9+
### Integrated documentation viewer
10+
11+
Gopls now offers a "View package documentation" code action that opens
12+
a local web page displaying the generated documentation for the
13+
current Go package in a form similar to https://pkg.go.dev. Use it to
14+
preview the marked-up documentation as you prepare API changes, or to
15+
read the documentation for locally edited packages, even ones that
16+
have not yet been saved. Reload the page after an edit to see updated
17+
documentation.
18+
19+
TODO: demo of VS Code `Source action > View package documentation`.
20+
21+
Clicking on the source-code link associated with a declaration will
22+
cause your editor to navigate to the declaration.
23+
24+
TODO: demo of source linking.
25+
TODO: appeal to VS Code users to upvote https://github.com/microsoft/vscode/issues/208093?
26+
27+
### Hover shows size/offset info
28+
29+
Hovering over the identifier that declares a type or struct field now
30+
displays the size information for the type, and the offset information
31+
for the field. This information may be helpful when making space
32+
optimizations to your data structures, or when reading assembly code.
33+
34+
TODO: example hover image.
35+
36+
## Bugs fixed
37+
38+
## Thank you to our contributors!

gopls/internal/cache/check.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1464,9 +1464,10 @@ func (b *typeCheckBatch) checkPackage(ctx context.Context, ph *packageHandle) (*
14641464
defer done()
14651465

14661466
pkg := &syntaxPackage{
1467-
id: inputs.id,
1468-
fset: b.fset, // must match parse call below
1469-
types: types.NewPackage(string(inputs.pkgPath), string(inputs.name)),
1467+
id: inputs.id,
1468+
fset: b.fset, // must match parse call below
1469+
types: types.NewPackage(string(inputs.pkgPath), string(inputs.name)),
1470+
typesSizes: inputs.sizes,
14701471
typesInfo: &types.Info{
14711472
Types: make(map[ast.Expr]types.TypeAndValue),
14721473
Defs: make(map[*ast.Ident]types.Object),

gopls/internal/cache/package.go

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type syntaxPackage struct {
5252
typeErrors []types.Error
5353
types *types.Package
5454
typesInfo *types.Info
55+
typesSizes types.Sizes
5556
importMap map[PackagePath]*types.Package
5657

5758
xrefsOnce sync.Once
@@ -155,6 +156,11 @@ func (p *Package) TypesInfo() *types.Info {
155156
return p.pkg.typesInfo
156157
}
157158

159+
// TypesSizes returns the sizing function used for types in this package.
160+
func (p *Package) TypesSizes() types.Sizes {
161+
return p.pkg.typesSizes
162+
}
163+
158164
// DependencyTypes returns the type checker's symbol for the specified
159165
// package. It returns nil if path is not among the transitive
160166
// dependencies of p, or if no symbols from that package were

gopls/internal/golang/hover.go

+89
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,80 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
244244
}
245245
}
246246

247+
// Compute size information for types,
248+
// and (size, offset) for struct fields.
249+
//
250+
// This information is useful when debugging crashes or
251+
// optimizing layout. To reduce distraction, we show it only
252+
// when hovering over the declaring identifier,
253+
// but not referring identifiers.
254+
//
255+
// Size and alignment vary across OS/ARCH.
256+
// Gopls will select the appropriate build configuration when
257+
// viewing a type declaration in a build-tagged file, but will
258+
// use the default build config for all other types, even
259+
// if they embed platform-variant types.
260+
//
261+
var sizeOffset string // optional size/offset description
262+
if def, ok := pkg.TypesInfo().Defs[ident]; ok && ident.Pos() == def.Pos() {
263+
// This is the declaring identifier.
264+
// (We can't simply use ident.Pos() == obj.Pos() because
265+
// referencedObject prefers the TypeName for an embedded field).
266+
267+
// format returns the decimal and hex representation of x.
268+
format := func(x int64) string {
269+
if x < 10 {
270+
return fmt.Sprintf("%d", x)
271+
}
272+
return fmt.Sprintf("%[1]d (%#[1]x)", x)
273+
}
274+
275+
var data []string // {size, offset}, both optional
276+
277+
// If the type has free type parameters, its size cannot be
278+
// computed. For now, we capture panics from go/types.Sizes.
279+
// TODO(adonovan): use newly factored typeparams.Free.
280+
try := func(f func()) bool {
281+
defer func() { recover() }()
282+
f()
283+
return true
284+
}
285+
286+
// size (types and fields)
287+
if v, ok := obj.(*types.Var); ok && v.IsField() || is[*types.TypeName](obj) {
288+
var sz int64
289+
if try(func() { sz = pkg.TypesSizes().Sizeof(obj.Type()) }) {
290+
data = append(data, "size="+format(sz))
291+
}
292+
}
293+
294+
// offset (fields)
295+
if v, ok := obj.(*types.Var); ok && v.IsField() {
296+
for _, n := range pathEnclosingObjNode(pgf.File, pos) {
297+
if n, ok := n.(*ast.StructType); ok {
298+
t := pkg.TypesInfo().TypeOf(n).(*types.Struct)
299+
var fields []*types.Var
300+
for i := 0; i < t.NumFields(); i++ {
301+
f := t.Field(i)
302+
fields = append(fields, f)
303+
if f == v {
304+
var offsets []int64
305+
if try(func() { offsets = pkg.TypesSizes().Offsetsof(fields) }) {
306+
if n := len(offsets); n > 0 {
307+
data = append(data, "offset="+format(offsets[n-1]))
308+
}
309+
}
310+
break
311+
}
312+
}
313+
break
314+
}
315+
}
316+
}
317+
318+
sizeOffset = strings.Join(data, ", ")
319+
}
320+
247321
var typeDecl, methods, fields string
248322

249323
// For "objects defined by a type spec", the signature produced by
@@ -284,6 +358,16 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
284358
return protocol.Range{}, nil, err
285359
}
286360
typeDecl = b.String()
361+
362+
// Splice in size/offset at end of first line.
363+
// "type T struct { // size=..."
364+
if sizeOffset != "" {
365+
nl := strings.IndexByte(typeDecl, '\n')
366+
if nl < 0 {
367+
nl = len(typeDecl)
368+
}
369+
typeDecl = typeDecl[:nl] + " // " + sizeOffset + typeDecl[nl:]
370+
}
287371
}
288372

289373
// Promoted fields
@@ -353,6 +437,11 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
353437
methods = b.String()
354438

355439
signature = typeDecl + "\n" + methods
440+
} else {
441+
// Non-types
442+
if sizeOffset != "" {
443+
signature += " // " + sizeOffset
444+
}
356445
}
357446

358447
// Compute link data (on pkg.go.dev or other documentation host).

gopls/internal/test/marker/doc.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ treatment by the test runner:
4949
in these directories before running the test.
5050
-skip_goos=a,b,c instructs the test runner to skip the test for the
5151
listed GOOS values.
52+
-skip_goarch=a,b,c does the same for GOARCH.
5253
-ignore_extra_diags suppresses errors for unmatched diagnostics
53-
TODO(rfindley): using build constraint expressions for -skip_goos would
54+
TODO(rfindley): using build constraint expressions for -skip_go{os,arch} would
5455
be clearer.
5556
-filter_builtins=false disables the filtering of builtins from
5657
completion results.

gopls/internal/test/marker/marker_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,11 @@ func Test(t *testing.T) {
115115
if test.skipReason != "" {
116116
t.Skip(test.skipReason)
117117
}
118-
for _, goos := range test.skipGOOS {
119-
if runtime.GOOS == goos {
120-
t.Skipf("skipping on %s due to -skip_goos", runtime.GOOS)
121-
}
118+
if slices.Contains(test.skipGOOS, runtime.GOOS) {
119+
t.Skipf("skipping on %s due to -skip_goos", runtime.GOOS)
120+
}
121+
if slices.Contains(test.skipGOARCH, runtime.GOARCH) {
122+
t.Skipf("skipping on %s due to -skip_goos", runtime.GOOS)
122123
}
123124

124125
// TODO(rfindley): it may be more useful to have full support for build
@@ -500,6 +501,7 @@ type markerTest struct {
500501
cgo bool
501502
writeGoSum []string // comma separated dirs to write go sum for
502503
skipGOOS []string // comma separated GOOS values to skip
504+
skipGOARCH []string // comma separated GOARCH values to skip
503505
ignoreExtraDiags bool
504506
filterBuiltins bool
505507
filterKeywords bool
@@ -513,6 +515,7 @@ func (t *markerTest) flagSet() *flag.FlagSet {
513515
flags.BoolVar(&t.cgo, "cgo", false, "if set, requires cgo (both the cgo tool and CGO_ENABLED=1)")
514516
flags.Var((*stringListValue)(&t.writeGoSum), "write_sumfile", "if set, write the sumfile for these directories")
515517
flags.Var((*stringListValue)(&t.skipGOOS), "skip_goos", "if set, skip this test on these GOOS values")
518+
flags.Var((*stringListValue)(&t.skipGOARCH), "skip_goarch", "if set, skip this test on these GOARCH values")
516519
flags.BoolVar(&t.ignoreExtraDiags, "ignore_extra_diags", false, "if set, suppress errors for unmatched diagnostics")
517520
flags.BoolVar(&t.filterBuiltins, "filter_builtins", true, "if set, filter builtins from completion results")
518521
flags.BoolVar(&t.filterKeywords, "filter_keywords", true, "if set, filter keywords from completion results")

gopls/internal/test/marker/testdata/definition/embed.txt

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
This test checks definition and hover operations over embedded fields and methods.
22

3+
Its size expectations assume a 64-bit machine,
4+
and correct sizes information requires go1.21.
5+
6+
-- flags --
7+
-skip_goarch=386
8+
-min_go=go1.21
9+
310
-- go.mod --
411
module mod.com
512

@@ -203,7 +210,7 @@ field S2 S2
203210
[`(b.S1).S2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1.S2)
204211
-- @S2 --
205212
```go
206-
type S2 struct {
213+
type S2 struct { // size=32 (0x20)
207214
F1 string //@loc(S2F1, "F1")
208215
F2 int //@loc(S2F2, "F2")
209216
*a.A //@def("A", AString),def("a",AImport)
@@ -244,7 +251,7 @@ field Field int
244251
[`(a.S).Field` on pkg.go.dev](https://pkg.go.dev/mod.com/a#S.Field)
245252
-- @aA --
246253
```go
247-
type A string
254+
type A string // size=16 (0x10)
248255
```
249256

250257
@loc(AString, "A")
@@ -257,7 +264,7 @@ func (a.A) Hi()
257264
[`a.A` on pkg.go.dev](https://pkg.go.dev/mod.com/a#A)
258265
-- @aAlias --
259266
```go
260-
type aAlias = a.A
267+
type aAlias = a.A // size=16 (0x10)
261268
```
262269

263270
@loc(aAlias, "aAlias")

gopls/internal/test/marker/testdata/definition/misc.txt

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
This test exercises miscellaneous definition and hover requests.
22

3+
Its size expectations assume a 64-bit machine.
4+
35
-- go.mod --
46
module mod.com
57

68
go 1.16
9+
10+
-- flags --
11+
-skip_goarch=386
12+
713
-- a.go --
814
package a //@loc(aPackage, re"package (a)"),hover(aPackage, aPackage, aPackage)
915

@@ -122,35 +128,35 @@ func _() {
122128
-- @aPackage --
123129
-- @hoverDeclBlocka --
124130
```go
125-
type a struct {
131+
type a struct { // size=16 (0x10)
126132
x string
127133
}
128134
```
129135

130136
1st type declaration block
131137
-- @hoverDeclBlockb --
132138
```go
133-
type b struct{}
139+
type b struct{} // size=0
134140
```
135141

136142
b has a comment
137143
-- @hoverDeclBlockc --
138144
```go
139-
type c struct {
145+
type c struct { // size=16 (0x10)
140146
f string
141147
}
142148
```
143149

144150
c is a struct
145151
-- @hoverDeclBlockd --
146152
```go
147-
type d string
153+
type d string // size=16 (0x10)
148154
```
149155

150156
3rd type declaration block
151157
-- @hoverDeclBlocke --
152158
```go
153-
type e struct {
159+
type e struct { // size=8
154160
f float64
155161
}
156162
```

gopls/internal/test/marker/testdata/hover/generics.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ package generics
1818

1919
type value[T any] struct { //hover("lue", "value", value),hover("T", "T", valueT)
2020
val T //@hover("T", "T", valuevalT)
21-
Q int //@hover("Q", "Q", valueQ)
21+
Q int64 //@hover("Q", "Q", valueQ)
2222
}
2323

2424
type Value[T any] struct { //@hover("T", "T", ValueT)
2525
val T //@hover("T", "T", ValuevalT)
26-
Q int //@hover("Q", "Q", ValueQ)
26+
Q int64 //@hover("Q", "Q", ValueQ)
2727
}
2828

2929
func F[P interface{ ~int | string }]() { //@hover("P", "P", Ptparam)
@@ -46,7 +46,7 @@ func _() {
4646

4747
-- @ValueQ --
4848
```go
49-
field Q int
49+
field Q int64 // size=8
5050
```
5151

5252
@hover("Q", "Q", ValueQ)
@@ -67,7 +67,7 @@ func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e
6767
```
6868
-- @valueQ --
6969
```go
70-
field Q int
70+
field Q int64 // size=8
7171
```
7272

7373
@hover("Q", "Q", valueQ)

gopls/internal/test/marker/testdata/hover/goprivate.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ package lib
1616
type L struct{} //@hover("L", "L", L)
1717
-- @L --
1818
```go
19-
type L struct{}
19+
type L struct{} // size=0
2020
```
2121

2222
GOPRIVATE should also match nested packages.
2323
-- @T --
2424
```go
25-
type T struct{}
25+
type T struct{} // size=0
2626
```
2727

2828
T should not be linked, as it is private.

0 commit comments

Comments
 (0)