diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go index 8491e8a2a5..45e2b31c77 100644 --- a/internal/lsp/source/hover.go +++ b/internal/lsp/source/hover.go @@ -477,6 +477,13 @@ func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signatur func FindHoverContext(ctx context.Context, s Snapshot, pkg Package, obj types.Object, pkgNode ast.Node, fullDecl ast.Decl) (*HoverContext, error) { var info *HoverContext + // Type parameters get their signature from their declaration object. + if _, isTypeName := obj.(*types.TypeName); isTypeName { + if _, isTypeParam := obj.Type().(*typeparams.TypeParam); isTypeParam { + return &HoverContext{signatureSource: obj}, nil + } + } + // This is problematic for a number of reasons. We really need to have a more // general mechanism to validate the coherency of AST with type information, // but absent that we must do our best to ensure that we don't use fullNode @@ -555,7 +562,7 @@ func FindHoverContext(ctx context.Context, s Snapshot, pkg Package, obj types.Ob } } - info, err = formatGenDecl(node, spec, fullPos, obj) + info, err = hoverGenDecl(node, spec, fullPos, obj) if err != nil { return nil, err } @@ -563,7 +570,7 @@ func FindHoverContext(ctx context.Context, s Snapshot, pkg Package, obj types.Ob case *ast.TypeSpec: if obj.Parent() == types.Universe { if genDecl, ok := fullDecl.(*ast.GenDecl); ok { - info = formatTypeSpec(node, genDecl) + info = hoverTypeSpec(node, genDecl) } } case *ast.FuncDecl: @@ -620,11 +627,11 @@ func isFunctionParam(obj types.Object, node *ast.FuncDecl) bool { return false } -// formatGenDecl returns hover information an object declared via spec inside +// hoverGenDecl returns hover information an object declared via spec inside // of the GenDecl node. obj is the type-checked object corresponding to the // declaration, but may have been type-checked using a different AST than the // given nodes; fullPos is the position of obj in node's AST. -func formatGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj types.Object) (*HoverContext, error) { +func hoverGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj types.Object) (*HoverContext, error) { if spec == nil { return nil, errors.Errorf("no spec for node %v at position %v", node, fullPos) } @@ -632,12 +639,12 @@ func formatGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj type // If we have a field or method. switch obj.(type) { case *types.Var, *types.Const, *types.Func: - return formatVar(spec, fullPos, obj, node), nil + return hoverVar(spec, fullPos, obj, node), nil } // Handle types. switch spec := spec.(type) { case *ast.TypeSpec: - return formatTypeSpec(spec, node), nil + return hoverTypeSpec(spec, node), nil case *ast.ValueSpec: return &HoverContext{signatureSource: spec, Comment: spec.Doc}, nil case *ast.ImportSpec: @@ -646,7 +653,8 @@ func formatGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj type return nil, errors.Errorf("unable to format spec %v (%T)", spec, spec) } -func formatTypeSpec(spec *ast.TypeSpec, decl *ast.GenDecl) *HoverContext { +// TODO(rfindley): rename this function. +func hoverTypeSpec(spec *ast.TypeSpec, decl *ast.GenDecl) *HoverContext { comment := spec.Doc if comment == nil && decl != nil { comment = decl.Doc @@ -660,7 +668,7 @@ func formatTypeSpec(spec *ast.TypeSpec, decl *ast.GenDecl) *HoverContext { } } -func formatVar(node ast.Spec, fullPos token.Pos, obj types.Object, decl *ast.GenDecl) *HoverContext { +func hoverVar(node ast.Spec, fullPos token.Pos, obj types.Object, decl *ast.GenDecl) *HoverContext { var fieldList *ast.FieldList switch spec := node.(type) { case *ast.TypeSpec: diff --git a/internal/lsp/testdata/godef/hover_generics/hover.go b/internal/lsp/testdata/godef/hover_generics/hover.go index 61a8218751..7400e1acdd 100644 --- a/internal/lsp/testdata/godef/hover_generics/hover.go +++ b/internal/lsp/testdata/godef/hover_generics/hover.go @@ -1,11 +1,15 @@ package hover -type value[T any] struct { //@mark(value, "value"),hoverdef("value", value) +type value[T any] struct { //@mark(value, "value"),hoverdef("value", value),mark(valueTdecl, "T"),hoverdef("T",valueTdecl) val T //@mark(valueTparam, "T"),hoverdef("T", valueTparam) Q int //@mark(valueQfield, "Q"),hoverdef("Q", valueQfield) } -type Value[T any] struct { +type Value[T any] struct { //@mark(ValueTdecl, "T"),hoverdef("T",ValueTdecl) val T //@mark(ValueTparam, "T"),hoverdef("T", ValueTparam) Q int //@mark(ValueQfield, "Q"),hoverdef("Q", ValueQfield) } + +func F[P interface{ ~int | string }]() { //@mark(Pparam, "P"),hoverdef("P",Pparam) + var _ P //@mark(Pvar, "P"),hoverdef("P",Pvar) +} diff --git a/internal/lsp/testdata/godef/hover_generics/hover.go.golden b/internal/lsp/testdata/godef/hover_generics/hover.go.golden index 4de8206c33..c7df541384 100644 --- a/internal/lsp/testdata/godef/hover_generics/hover.go.golden +++ b/internal/lsp/testdata/godef/hover_generics/hover.go.golden @@ -5,26 +5,6 @@ type value[T any] struct { Q int //@mark(valueQfield, "Q"),hoverdef("Q", valueQfield) } ``` --- valueTparam-hoverdef -- -```go -type value[T any] struct { - val T //@mark(valueTparam, "T"),hoverdef("T", valueTparam) - Q int //@mark(valueQfield, "Q"),hoverdef("Q", valueQfield) -} -``` --- valueQfield-hoverdef -- -```go -field Q int -``` - -\@mark\(valueQfield, \"Q\"\),hoverdef\(\"Q\", valueQfield\) --- ValueTparam-hoverdef -- -```go -type Value[T any] struct { - val T //@mark(ValueTparam, "T"),hoverdef("T", ValueTparam) - Q int //@mark(ValueQfield, "Q"),hoverdef("Q", ValueQfield) -} -``` -- ValueQfield-hoverdef -- ```go field Q int @@ -33,3 +13,33 @@ field Q int [`(hover.Value).Q` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/hover_generics?utm_source=gopls#Value.Q) \@mark\(ValueQfield, \"Q\"\),hoverdef\(\"Q\", ValueQfield\) +-- ValueTdecl-hoverdef -- +```go +type parameter T any +``` +-- ValueTparam-hoverdef -- +```go +type parameter T any +``` +-- valueQfield-hoverdef -- +```go +field Q int +``` + +\@mark\(valueQfield, \"Q\"\),hoverdef\(\"Q\", valueQfield\) +-- valueTdecl-hoverdef -- +```go +type parameter T any +``` +-- valueTparam-hoverdef -- +```go +type parameter T any +``` +-- Pparam-hoverdef -- +```go +type parameter P interface{~int|string} +``` +-- Pvar-hoverdef -- +```go +type parameter P interface{~int|string} +``` diff --git a/internal/lsp/testdata/godef/infer_generics/inferred.go.golden b/internal/lsp/testdata/godef/infer_generics/inferred.go.golden index 081ea53dc0..4a36ff460b 100644 --- a/internal/lsp/testdata/godef/infer_generics/inferred.go.golden +++ b/internal/lsp/testdata/godef/infer_generics/inferred.go.golden @@ -1,6 +1,6 @@ -- argInfer-hoverdef -- ```go -func app(s []int, e int) []int // func[S₁ interface{~[]E₂}, E₂ interface{}](s S₁, e E₂) S₁ +func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S ``` -- constrInf-hoverdef -- ```go @@ -8,13 +8,13 @@ func app(s []int, e int) []int // func[S₁ interface{~[]E₂}, E₂ interface{} ``` -- constrInfer-hoverdef -- ```go -func app(s []int, e int) []int // func[S₁ interface{~[]E₂}, E₂ interface{}](s S₁, e E₂) S₁ +func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S ``` -- instance-hoverdef -- ```go -func app(s []int, e int) []int // func[S₁ interface{~[]E₂}, E₂ interface{}](s S₁, e E₂) S₁ +func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S ``` -- partialInfer-hoverdef -- ```go -func app(s []int, e int) []int // func[S₁ interface{~[]E₂}, E₂ interface{}](s S₁, e E₂) S₁ +func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S ``` diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden index b2706d6589..be3fbc6dd4 100644 --- a/internal/lsp/testdata/summary_go1.18.txt.golden +++ b/internal/lsp/testdata/summary_go1.18.txt.golden @@ -16,7 +16,7 @@ SemanticTokenCount = 3 SuggestedFixCount = 61 FunctionExtractionCount = 25 MethodExtractionCount = 6 -DefinitionsCount = 104 +DefinitionsCount = 108 TypeDefinitionsCount = 18 HighlightsCount = 69 ReferencesCount = 27