From adcfb65266f68497f21c7757d5ee2db61d345578 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 10 Feb 2022 20:15:56 -0500 Subject: [PATCH] internal/lsp/source: use the object as the hover source for type params Use the type name object as the signature source for type parametes, as it it efficiently formatted as "type parameter ". Fixes golang/go#51116 Change-Id: I3042ff248402dae833e3197c6ac320c2bd103c07 Reviewed-on: https://go-review.googlesource.com/c/tools/+/385018 Trust: Robert Findley Run-TryBot: Robert Findley Reviewed-by: Heschi Kreinick gopls-CI: kokoro TryBot-Result: Gopher Robot --- internal/lsp/source/hover.go | 24 ++++++--- .../testdata/godef/hover_generics/hover.go | 8 ++- .../godef/hover_generics/hover.go.golden | 50 +++++++++++-------- .../godef/infer_generics/inferred.go.golden | 8 +-- .../lsp/testdata/summary_go1.18.txt.golden | 2 +- 5 files changed, 57 insertions(+), 35 deletions(-) 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