diff --git a/gopls/doc/release/v0.17.0.md b/gopls/doc/release/v0.17.0.md index fff579844d6..65b835d6737 100644 --- a/gopls/doc/release/v0.17.0.md +++ b/gopls/doc/release/v0.17.0.md @@ -12,7 +12,7 @@ displayed by v0.16.0 and its diagnostics were confusing. ## Extract declarations to new file Gopls now offers another code action, "Extract declarations to new file", which moves selected code sections to a newly created file within the -same package. The created filename is chosen as the first {function, type, +same package. The created filename is chosen as the first {function, type, const, var} name encountered. In addition, import declarations are added or removed as needed. @@ -22,3 +22,9 @@ or by selecting a whole declaration or multiple declrations. In order to avoid ambiguity and surprise about what to extract, some kinds of paritial selection of a declration cannot invoke this code action. + +## Standard library version information in Hover + +Hovering over a standard library symbol now displays information about the first +Go release containing the symbol. For example, hovering over `errors.As` shows +"Added in go1.13". diff --git a/gopls/internal/golang/hover.go b/gopls/internal/golang/hover.go index 089b73e43df..2edb8a99d34 100644 --- a/gopls/internal/golang/hover.go +++ b/gopls/internal/golang/hover.go @@ -1209,18 +1209,19 @@ func formatHover(h *hoverJSON, options *settings.Options, pkgURL func(path Packa // StdSymbolOf returns the std lib symbol information of the given obj. // It returns nil if the input obj is not an exported standard library symbol. func StdSymbolOf(obj types.Object) *stdlib.Symbol { - if !obj.Exported() { + if !obj.Exported() || obj.Pkg() == nil { + return nil + } + + // Symbols that not defined in standard library should return early. + // TODO(hxjiang): The returned slices is binary searchable. + symbols := stdlib.PackageSymbols[obj.Pkg().Path()] + if symbols == nil { return nil } // Handle Function, Type, Const & Var. if isPackageLevel(obj) { - // Symbols defined not in std lib package should return early. - symbols := stdlib.PackageSymbols[obj.Pkg().Path()] - if symbols == nil { - return nil - } - // TODO(hxjiang): This is binary searchable. for _, s := range symbols { if s.Kind == stdlib.Method || s.Kind == stdlib.Field { continue @@ -1236,7 +1237,7 @@ func StdSymbolOf(obj types.Object) *stdlib.Symbol { if fn, _ := obj.(*types.Func); fn != nil { isPtr, named := typesinternal.ReceiverNamed(fn.Type().(*types.Signature).Recv()) if isPackageLevel(named.Obj()) { - for _, s := range stdlib.PackageSymbols[obj.Pkg().Path()] { + for _, s := range symbols { if s.Kind != stdlib.Method { continue } @@ -1249,7 +1250,30 @@ func StdSymbolOf(obj types.Object) *stdlib.Symbol { } } - // TODO(hxjiang): handle exported fields of package level types. + // Handle Field. + if v, _ := obj.(*types.Var); v != nil && v.IsField() { + for _, s := range symbols { + if s.Kind != stdlib.Field { + continue + } + + typeName, fieldName := s.SplitField() + if fieldName != v.Name() { + continue + } + + typeObj := obj.Pkg().Scope().Lookup(typeName) + if typeObj == nil { + continue + } + + if fieldObj, _, _ := types.LookupFieldOrMethod(typeObj.Type(), true, obj.Pkg(), fieldName); obj == fieldObj { + return &s + } + } + return nil + } + return nil } diff --git a/gopls/internal/test/integration/misc/hover_test.go b/gopls/internal/test/integration/misc/hover_test.go index e2dd037093c..9c679f02d53 100644 --- a/gopls/internal/test/integration/misc/hover_test.go +++ b/gopls/internal/test/integration/misc/hover_test.go @@ -670,6 +670,7 @@ import "fmt" import "context" import "crypto" import "regexp" +import "go/doc/comment" type testRegexp = *regexp.Regexp @@ -686,6 +687,9 @@ func _() { copy := re.Copy() var testRE testRegexp testRE.Longest() + + var pr comment.Printer + pr.HeadingID = func(*comment.Heading) string { return "" } } ` @@ -702,7 +706,7 @@ func _() { {"SHA512_224", true, "go1.5"}, // package-level const {"Copy", true, "go1.6"}, // method {"Longest", true, "go1.1"}, // method with alias receiver - // TODO(hxjiang): add test for symbol type Field. + {"HeadingID", true, "go1.19"}, // field } Run(t, src, func(t *testing.T, env *Env) {