@@ -138,6 +138,28 @@ func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, positi
138138 }, nil
139139}
140140
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+
141163// hover computes hover information at the given position. If we do not support
142164// hovering at the position, it returns _, nil, nil: an error is only returned
143165// 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
404426 _ , isTypeName := obj .(* types.TypeName )
405427 _ , isTypeParam := types .Unalias (obj .Type ()).(* types.TypeParam )
406428 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
419433 }
420434
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 )
446441 }
442+ typeDecl = typeDecl [:nl ] + " // " + sizeOffset + typeDecl [nl :]
447443 }
448444
449445 // Promoted fields
@@ -478,7 +474,7 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
478474 // already been displayed when the node was formatted
479475 // above. Don't list these again.
480476 var skip map [string ]bool
481- if iface , ok := spec .Type .(* ast.InterfaceType ); ok {
477+ if iface , ok := spec1 .Type .(* ast.InterfaceType ); ok {
482478 if iface .Methods .List != nil {
483479 for _ , m := range iface .Methods .List {
484480 if len (m .Names ) == 1 {
@@ -520,6 +516,12 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
520516 }
521517 }
522518
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+
523525 // Compute link data (on pkg.go.dev or other documentation host).
524526 //
525527 // 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
640642 }, nil
641643}
642644
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+
643678// hoverBuiltin computes hover information when hovering over a builtin
644679// identifier.
645680func hoverBuiltin (ctx context.Context , snapshot * cache.Snapshot , obj types.Object ) (* hoverResult , error ) {
0 commit comments