@@ -138,6 +138,28 @@ func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, positi
138
138
}, nil
139
139
}
140
140
141
+ // findRhsTypeDecl finds an alias's rhs type and returns its declaration.
142
+ // The rhs of an alias might be an alias as well, but we feel this is a rare case.
143
+ // It returns an empty string if the given obj is not an alias.
144
+ func findRhsTypeDecl (ctx context.Context , snapshot * cache.Snapshot , pkg * cache.Package , obj types.Object ) (string , error ) {
145
+ if alias , ok := obj .Type ().(* types.Alias ); ok {
146
+ // we choose Rhs instead of types.Unalias to make the connection between original alias
147
+ // and the corresponding aliased type clearer.
148
+ // types.Unalias brings confusion because it breaks the connection from A to C given
149
+ // the alias chain like 'type ( A = B; B =C ; )' except we show all transitive alias
150
+ // from start to the end. As it's rare, we don't do so.
151
+ t := alias .Rhs ()
152
+ switch o := t .(type ) {
153
+ case * types.Named :
154
+ obj = o .Obj ()
155
+ declPGF1 , declPos1 , _ := parseFull (ctx , snapshot , pkg .FileSet (), obj .Pos ())
156
+ realTypeDecl , _ , err := typeDeclContent (declPGF1 , declPos1 , obj )
157
+ return realTypeDecl , err
158
+ }
159
+ }
160
+ return "" , nil
161
+ }
162
+
141
163
// hover computes hover information at the given position. If we do not support
142
164
// hovering at the position, it returns _, nil, nil: an error is only returned
143
165
// if the position is valid but we fail to compute hover information.
@@ -404,46 +426,20 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
404
426
_ , isTypeName := obj .(* types.TypeName )
405
427
_ , isTypeParam := types .Unalias (obj .Type ()).(* types.TypeParam )
406
428
if isTypeName && ! isTypeParam {
407
- spec , ok := spec .(* ast.TypeSpec )
408
- if ! ok {
409
- // We cannot find a TypeSpec for this type or alias declaration
410
- // (that is not a type parameter or a built-in).
411
- // This should be impossible even for ill-formed trees;
412
- // we suspect that AST repair may be creating inconsistent
413
- // positions. Don't report a bug in that case. (#64241)
414
- errorf := fmt .Errorf
415
- if ! declPGF .Fixed () {
416
- errorf = bug .Errorf
417
- }
418
- return protocol.Range {}, nil , errorf ("type name %q without type spec" , obj .Name ())
429
+ var spec1 * ast.TypeSpec
430
+ typeDecl , spec1 , err = typeDeclContent (declPGF , declPos , obj )
431
+ if err != nil {
432
+ return protocol.Range {}, nil , err
419
433
}
420
434
421
- // Format the type's declaration syntax.
422
- {
423
- // Don't duplicate comments.
424
- spec2 := * spec
425
- spec2 .Doc = nil
426
- spec2 .Comment = nil
427
-
428
- var b strings.Builder
429
- b .WriteString ("type " )
430
- fset := tokeninternal .FileSetFor (declPGF .Tok )
431
- // TODO(adonovan): use a smarter formatter that omits
432
- // inaccessible fields (non-exported ones from other packages).
433
- if err := format .Node (& b , fset , & spec2 ); err != nil {
434
- return protocol.Range {}, nil , err
435
- }
436
- typeDecl = b .String ()
437
-
438
- // Splice in size/offset at end of first line.
439
- // "type T struct { // size=..."
440
- if sizeOffset != "" {
441
- nl := strings .IndexByte (typeDecl , '\n' )
442
- if nl < 0 {
443
- nl = len (typeDecl )
444
- }
445
- typeDecl = typeDecl [:nl ] + " // " + sizeOffset + typeDecl [nl :]
435
+ // Splice in size/offset at end of first line.
436
+ // "type T struct { // size=..."
437
+ if sizeOffset != "" {
438
+ nl := strings .IndexByte (typeDecl , '\n' )
439
+ if nl < 0 {
440
+ nl = len (typeDecl )
446
441
}
442
+ typeDecl = typeDecl [:nl ] + " // " + sizeOffset + typeDecl [nl :]
447
443
}
448
444
449
445
// Promoted fields
@@ -478,7 +474,7 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
478
474
// already been displayed when the node was formatted
479
475
// above. Don't list these again.
480
476
var skip map [string ]bool
481
- if iface , ok := spec .Type .(* ast.InterfaceType ); ok {
477
+ if iface , ok := spec1 .Type .(* ast.InterfaceType ); ok {
482
478
if iface .Methods .List != nil {
483
479
for _ , m := range iface .Methods .List {
484
480
if len (m .Names ) == 1 {
@@ -520,6 +516,12 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
520
516
}
521
517
}
522
518
519
+ // realTypeDecl is defined to store the underlying definition of an alias.
520
+ realTypeDecl , _ := findRhsTypeDecl (ctx , snapshot , pkg , obj ) // tolerate the error
521
+ if realTypeDecl != "" {
522
+ typeDecl += fmt .Sprintf ("\n \n %s" , realTypeDecl )
523
+ }
524
+
523
525
// Compute link data (on pkg.go.dev or other documentation host).
524
526
//
525
527
// If linkPath is empty, the symbol is not linkable.
@@ -640,6 +642,39 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
640
642
}, nil
641
643
}
642
644
645
+ // typeDeclContent returns a well formatted type definition.
646
+ func typeDeclContent (declPGF * parsego.File , declPos token.Pos , obj types.Object ) (string , * ast.TypeSpec , error ) {
647
+ _ , spec , _ := findDeclInfo ([]* ast.File {declPGF .File }, declPos ) // may be nil^3
648
+ // Don't duplicate comments.
649
+ spec1 , ok := spec .(* ast.TypeSpec )
650
+ if ! ok {
651
+ // We cannot find a TypeSpec for this type or alias declaration
652
+ // (that is not a type parameter or a built-in).
653
+ // This should be impossible even for ill-formed trees;
654
+ // we suspect that AST repair may be creating inconsistent
655
+ // positions. Don't report a bug in that case. (#64241)
656
+ errorf := fmt .Errorf
657
+ if ! declPGF .Fixed () {
658
+ errorf = bug .Errorf
659
+ }
660
+ return "" , nil , errorf ("type name %q without type spec" , obj .Name ())
661
+ }
662
+ spec2 := * spec1
663
+ spec2 .Doc = nil
664
+ spec2 .Comment = nil
665
+
666
+ var b strings.Builder
667
+ b .WriteString ("type " )
668
+ fset := tokeninternal .FileSetFor (declPGF .Tok )
669
+ // TODO(adonovan): use a smarter formatter that omits
670
+ // inaccessible fields (non-exported ones from other packages).
671
+ if err := format .Node (& b , fset , & spec2 ); err != nil {
672
+ return "" , nil , err
673
+ }
674
+ typeDecl := b .String ()
675
+ return typeDecl , spec1 , nil
676
+ }
677
+
643
678
// hoverBuiltin computes hover information when hovering over a builtin
644
679
// identifier.
645
680
func hoverBuiltin (ctx context.Context , snapshot * cache.Snapshot , obj types.Object ) (* hoverResult , error ) {
0 commit comments