From c643c5675e65b2856e05c60658dbaa5d6d012fd7 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 3 Feb 2024 17:01:23 +0800 Subject: [PATCH 1/7] gopls/internal/lsp/source/completion: unimport check gop index overload --- .../lsp/source/completion/completion.go | 37 ++++++++------ .../lsp/source/completion/completion_gox.go | 49 ++++++++++++++++++- .../lsp/source/completion/format_gox.go | 11 +++++ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go index 541f5a6c381..9398da502ef 100644 --- a/gopls/internal/lsp/source/completion/completion.go +++ b/gopls/internal/lsp/source/completion/completion.go @@ -3310,7 +3310,7 @@ func forEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, } // goxls: quickParse -func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough *int32, selName string, relevances map[string]float64, needImport bool) func(uri span.URI, m *source.Metadata) error { +func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough *int32, selName string, relevances map[string]float64, needImport bool, recheck *unimportChecked) func(uri span.URI, m *source.Metadata) error { return func(uri span.URI, m *source.Metadata) error { if atomic.LoadInt32(enough) != 0 { return nil @@ -3420,23 +3420,32 @@ func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough * } cMu.Lock() - c.items = append(c.items, item) - // goxls func alias + if tok == token.CONST && id.Name == "GopPackage" { + recheck.pkgs[m.PkgPath] = true + } if tok == token.FUNC { - if alias, ok := hasAliasName(id.Name); ok { - var noSnip bool - switch len(fn.Type.Params.List) { - case 0: - noSnip = true - case 1: - if fn.Recv != nil { - if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { - noSnip = true - } + var noSnip bool + switch len(fn.Type.Params.List) { + case 0: + noSnip = true + case 1: + if fn.Recv != nil { + if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { + noSnip = true } } - c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) } + if maybleIndexOverload(id.Name) { + recheck.items[m.PkgPath] = append(recheck.items[m.PkgPath], recheckItem{item, noSnip}) + } else { + c.items = append(c.items, item) + // goxls func alias + if alias, ok := hasAliasName(id.Name); ok { + c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) + } + } + } else { + c.items = append(c.items, item) } if len(c.items) >= unimportedMemberTarget { atomic.StoreInt32(enough, 1) diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index f137be3478c..dc98c3fc418 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -1310,12 +1310,20 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return nil } + // recheck is gop index overload check + recheck := &unimportChecked{ + make(map[source.PackagePath]bool), + make(map[source.PackagePath][]recheckItem), + } + for _, path := range paths { + recheck.pkgs[source.PackagePath(path)] = true + } // Extract the package-level candidates using a quick parse. - quickParseGo := c.quickParse(ctx, &cMu, &enough, sel.Sel.Name, relevances, needImport) + quickParseGo := c.quickParse(ctx, &cMu, &enough, sel.Sel.Name, relevances, needImport, recheck) var g errgroup.Group for _, path := range paths { m := known[source.PackagePath(path)] - for _, uri := range m.CompiledGopFiles { // goxls: TODO - how to handle Go files? + for _, uri := range m.CompiledGopFiles { uri := uri g.Go(func() error { return quickParse(uri, m) @@ -1331,6 +1339,33 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro if err := g.Wait(); err != nil { return err } + // check gop packages index overload + for pkg, items := range recheck.items { + if recheck.pkgs[pkg] { + names := make(map[string]bool) + sort.Slice(items, func(i, j int) bool { + return items[i].Label < items[j].Label + }) + for _, item := range items { + id := item.Label[:len(item.Label)-3] + if !names[id] { + names[id] = true + item.isOverload = true + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false)) + if alias, ok := hasAliasName(id); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } else { + for _, item := range items { + c.items = append(c.items, item.CompletionItem) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } // In addition, we search in the module cache using goimports. ctx, cancel := context.WithCancel(ctx) @@ -1368,6 +1403,16 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return nil } +type recheckItem struct { + CompletionItem + noSnip bool +} + +type unimportChecked struct { + pkgs map[source.PackagePath]bool // gop package + items map[source.PackagePath][]recheckItem +} + func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) { scope := pkg.Scope() for _, name := range scope.Names() { diff --git a/gopls/internal/lsp/source/completion/format_gox.go b/gopls/internal/lsp/source/completion/format_gox.go index 9078fc8b000..529b635dba8 100644 --- a/gopls/internal/lsp/source/completion/format_gox.go +++ b/gopls/internal/lsp/source/completion/format_gox.go @@ -295,6 +295,17 @@ func isIndexOverload(fn string, name string) bool { return false } +func maybleIndexOverload(fn string) bool { + n := len(fn) + if (n > 3) && (fn[n-3] == '_') && (fn[n-2] == '_') { + c := fn[n-1] + if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') { + return true + } + } + return false +} + func (c *gopCompleter) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) { obj := cand.obj item := CompletionItem{ From 12e1bd3d893adc79eaf04886746de0fad758cf5b Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 20 Mar 2024 19:54:35 +0800 Subject: [PATCH 2/7] gopls/internal/lsp/source/completion: gopCompleter.quickParse check gopo overload --- .../lsp/source/completion/completion.go | 59 +++++++++++++++---- .../lsp/source/completion/completion_gox.go | 34 ++++++----- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go index 9398da502ef..668e5016678 100644 --- a/gopls/internal/lsp/source/completion/completion.go +++ b/gopls/internal/lsp/source/completion/completion.go @@ -3420,21 +3420,33 @@ func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough * } cMu.Lock() - if tok == token.CONST && id.Name == "GopPackage" { - recheck.pkgs[m.PkgPath] = true - } - if tok == token.FUNC { - var noSnip bool - switch len(fn.Type.Params.List) { - case 0: - noSnip = true - case 1: - if fn.Recv != nil { - if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { - noSnip = true + if tok == token.CONST { + if id.Name == "GopPackage" { + recheck.pkgs[m.PkgPath] = true + } else if strings.HasPrefix(id.Name, "Gopo_") { + md := checkTypeMethod(id.Name[5:]) + if md.typ == "" && ast.IsExported(md.name) { + item := CompletionItem{ + Label: md.name, + Detail: fmt.Sprintf("func (from %q)", m.PkgPath), + InsertText: md.name, + Score: unimportedScore(relevances[path]), + Kind: protocol.FunctionCompletion, + isOverload: true, + } + if needImport { + imp := &importInfo{importPath: path} + if imports.ImportPathToAssumedName(path) != string(m.Name) { + imp.name = string(m.Name) + } + item.AdditionalTextEdits, _ = c.importEdits(imp) } + recheck.gopo[m.PkgPath] = append(recheck.gopo[m.PkgPath], item) } } + } + if tok == token.FUNC { + noSnip := len(fn.Type.Params.List) == 0 if maybleIndexOverload(id.Name) { recheck.items[m.PkgPath] = append(recheck.items[m.PkgPath], recheckItem{item, noSnip}) } else { @@ -3455,3 +3467,26 @@ func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough * return nil } } + +type mthd struct { + typ string + name string +} + +// Func (no _ func name) +// _Func (with _ func name) +// TypeName_Method (no _ method name) +// _TypeName__Method (with _ method name) +func checkTypeMethod(name string) mthd { + if pos := strings.IndexByte(name, '_'); pos >= 0 { + if pos == 0 { + t := name[1:] + if pos = strings.Index(t, "__"); pos <= 0 { + return mthd{"", t} // _Func + } + return mthd{t[:pos], t[pos+2:]} // _TypeName__Method + } + return mthd{name[:pos], name[pos+1:]} // TypeName_Method + } + return mthd{"", name} // Func +} diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index dc98c3fc418..293811e53e2 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -1288,17 +1288,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro // goxls func alias if tok == token.FUNC { if alias, ok := hasAliasName(id.Name); ok { - var noSnip bool - switch len(fn.Type.Params.List) { - case 0: - noSnip = true - case 1: - if fn.Recv != nil { - if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { - noSnip = true - } - } - } + noSnip := len(fn.Type.Params.List) == 0 c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) } } @@ -1314,9 +1304,13 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro recheck := &unimportChecked{ make(map[source.PackagePath]bool), make(map[source.PackagePath][]recheckItem), + make(map[source.PackagePath][]CompletionItem), } for _, path := range paths { - recheck.pkgs[source.PackagePath(path)] = true + m := known[source.PackagePath(path)] + if len(m.CompiledGopFiles) > 0 { + recheck.pkgs[source.PackagePath(path)] = true + } } // Extract the package-level candidates using a quick parse. quickParseGo := c.quickParse(ctx, &cMu, &enough, sel.Sel.Name, relevances, needImport, recheck) @@ -1366,6 +1360,17 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro } } } + // check gop packages gopo overload + for pkg, items := range recheck.gopo { + if recheck.pkgs[pkg] { + for _, item := range items { + c.items = append(c.items, item) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item, item.Label, alias, 0.0001, true)) + } + } + } + } // In addition, we search in the module cache using goimports. ctx, cancel := context.WithCancel(ctx) @@ -1409,8 +1414,9 @@ type recheckItem struct { } type unimportChecked struct { - pkgs map[source.PackagePath]bool // gop package - items map[source.PackagePath][]recheckItem + pkgs map[source.PackagePath]bool // gop package + items map[source.PackagePath][]recheckItem // index overload funcs + gopo map[source.PackagePath][]CompletionItem // gopo overload funcs } func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) { From 3e4c31c27f86eea6cf47594b280b22a63e06afee Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 20 Mar 2024 21:38:22 +0800 Subject: [PATCH 3/7] gopls/internal/lsp/source/completion: gopForEachPackageMember check ast.OverloadFuncDecl --- .../lsp/source/completion/completion_gox.go | 140 +++++++++++------- .../lsp/source/completion/format_gox.go | 2 +- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index 293811e53e2..9a4f9e686d4 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -1189,7 +1189,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return err } path := string(m.PkgPath) - gopForEachPackageMember(content, func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl) { + gopForEachPackageMember(content, func(tok token.Token, id *ast.Ident, fnType *ast.FuncType, isOverload bool) { if atomic.LoadInt32(&enough) != 0 { return } @@ -1237,7 +1237,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro } // For functions, add a parameter snippet. - if fn != nil { + if fnType != nil { var sn snippet.Builder sn.WriteText(id.Name) @@ -1277,18 +1277,23 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro } } - paramList("[", "]", typeparams.ForFuncType(fn.Type)) - paramList("(", ")", fn.Type.Params) + paramList("[", "]", typeparams.ForFuncType(fnType)) + paramList("(", ")", fnType.Params) item.snippet = &sn } + if isOverload { + // ast.OverloadFuncDecl + item.isOverload = true + item.Detail = "Go+ overload func\n\n" + item.Detail + } cMu.Lock() c.items = append(c.items, item) // goxls func alias if tok == token.FUNC { if alias, ok := hasAliasName(id.Name); ok { - noSnip := len(fn.Type.Params.List) == 0 + noSnip := !isOverload && len(fnType.Params.List) == 0 c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) } } @@ -1333,45 +1338,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro if err := g.Wait(); err != nil { return err } - // check gop packages index overload - for pkg, items := range recheck.items { - if recheck.pkgs[pkg] { - names := make(map[string]bool) - sort.Slice(items, func(i, j int) bool { - return items[i].Label < items[j].Label - }) - for _, item := range items { - id := item.Label[:len(item.Label)-3] - if !names[id] { - names[id] = true - item.isOverload = true - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false)) - if alias, ok := hasAliasName(id); ok { - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) - } - } - } - } else { - for _, item := range items { - c.items = append(c.items, item.CompletionItem) - if alias, ok := hasAliasName(item.Label); ok { - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) - } - } - } - } - // check gop packages gopo overload - for pkg, items := range recheck.gopo { - if recheck.pkgs[pkg] { - for _, item := range items { - c.items = append(c.items, item) - if alias, ok := hasAliasName(item.Label); ok { - c.items = append(c.items, cloneAliasItem(item, item.Label, alias, 0.0001, true)) - } - } - } - } - + recheck.checkOverload(c) // In addition, we search in the module cache using goimports. ctx, cancel := context.WithCancel(ctx) var mu sync.Mutex @@ -1413,12 +1380,6 @@ type recheckItem struct { noSnip bool } -type unimportChecked struct { - pkgs map[source.PackagePath]bool // gop package - items map[source.PackagePath][]recheckItem // index overload funcs - gopo map[source.PackagePath][]CompletionItem // gopo overload funcs -} - func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) { scope := pkg.Scope() for _, name := range scope.Names() { @@ -2478,7 +2439,7 @@ Nodes: // TYPE/VAR/CONST/FUNC declaration in the Go source file, based on a // quick partial parse. fn is non-nil only for function declarations. // The AST position information is garbage. -func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl)) { +func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, fnType *ast.FuncType, isOverload bool)) { purged := goxlsastutil.PurgeFuncBodies(content) file, _ := parserutil.ParseFile(token.NewFileSet(), "", purged, 0) for _, decl := range file.Decls { @@ -2488,15 +2449,86 @@ func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ide switch spec := spec.(type) { case *ast.ValueSpec: // var/const for _, id := range spec.Names { - f(decl.Tok, id, nil) + f(decl.Tok, id, nil, false) } case *ast.TypeSpec: - f(decl.Tok, spec.Name, nil) + f(decl.Tok, spec.Name, nil, false) } } case *ast.FuncDecl: if decl.Recv == nil { - f(token.FUNC, decl.Name, decl) + f(token.FUNC, decl.Name, decl.Type, false) + } + case *ast.OverloadFuncDecl: + if decl.Recv == nil && ast.IsExported(decl.Name.Name) { + var typ *ast.FuncType + FindType: + for _, expr := range decl.Funcs { + switch expr := expr.(type) { + case *ast.Ident: + if !ast.IsExported(expr.Name) { + continue + } + if obj := file.Scope.Lookup(expr.Name); obj != nil { + if d, ok := obj.Decl.(*ast.FuncDecl); ok { + typ = d.Type + break FindType + } + } + case *ast.FuncLit: + typ = expr.Type + break FindType + } + } + f(token.FUNC, decl.Name, typ, false) + } + } + } +} + +type unimportChecked struct { + pkgs map[source.PackagePath]bool // gop package + items map[source.PackagePath][]recheckItem // index overload funcs + gopo map[source.PackagePath][]CompletionItem // gopo overload funcs +} + +func (recheck *unimportChecked) checkOverload(c *gopCompleter) { + // check gop package index overload + for pkg, items := range recheck.items { + if recheck.pkgs[pkg] { + names := make(map[string]bool) + sort.Slice(items, func(i, j int) bool { + return items[i].Label < items[j].Label + }) + for _, item := range items { + id := item.Label[:len(item.Label)-3] + if !names[id] { + names[id] = true + item.isOverload = true + item.Detail = "Go+ overload func\n\n" + item.Detail + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false)) + if alias, ok := hasAliasName(id); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } else { + for _, item := range items { + c.items = append(c.items, item.CompletionItem) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } + // check gop package gopo overload + for pkg, items := range recheck.gopo { + if recheck.pkgs[pkg] { + for _, item := range items { + c.items = append(c.items, item) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item, item.Label, alias, 0.0001, true)) + } } } } diff --git a/gopls/internal/lsp/source/completion/format_gox.go b/gopls/internal/lsp/source/completion/format_gox.go index 529b635dba8..6a142df4938 100644 --- a/gopls/internal/lsp/source/completion/format_gox.go +++ b/gopls/internal/lsp/source/completion/format_gox.go @@ -142,7 +142,7 @@ Suffixes: if _, objs := gogen.CheckSigFuncExObjects(sig); len(objs) > 0 { isOverload = true var buf bytes.Buffer - buf.WriteString("Go+ overload funcs\n") + buf.WriteString("Go+ overload func\n") for _, o := range objs { if isIndexOverload(o.Name(), obj.Name()) { c.seen[o] = true From 9d79a89419682c49bf012188521de1442602f014 Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 20 Mar 2024 21:49:29 +0800 Subject: [PATCH 4/7] gopls/internal/goxls/testdata: update overload info --- .../internal/goxls/testdata/overload/overload_rank.gop.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gopls/internal/goxls/testdata/overload/overload_rank.gop.in b/gopls/internal/goxls/testdata/overload/overload_rank.gop.in index ebe907bb451..6ff7610593f 100644 --- a/gopls/internal/goxls/testdata/overload/overload_rank.gop.in +++ b/gopls/internal/goxls/testdata/overload/overload_rank.gop.in @@ -3,15 +3,15 @@ import ( ) func _() { - demo //@item(apkgDemo1, "demo", "Go+ overload funcs\n\n- func()\n- func(n int) int\n- func(n1 int, n2 int)\n- func(s ...string)", "func") - Demo //@item(apkgDemo2, "Demo", "Go+ overload funcs\n\n- func()\n- func(n int) int\n- func(n1 int, n2 int)\n- func(s ...string)", "func") + demo //@item(apkgDemo1, "demo", "Go+ overload func\n\n- func()\n- func(n int) int\n- func(n1 int, n2 int)\n- func(s ...string)", "func") + Demo //@item(apkgDemo2, "Demo", "Go+ overload func\n\n- func()\n- func(n int) int\n- func(n1 int, n2 int)\n- func(s ...string)", "func") a.demo //@rank(" //", apkgDemo1, apkgDemo2) } func _() { - add //@item(apkgAdd1, "add", "Go+ overload funcs\n\n- func(a int)\n- func(a ...string)", "method") - Add //@item(apkgAdd2, "Add", "Go+ overload funcs\n\n- func(a int)\n- func(a ...string)", "method") + add //@item(apkgAdd1, "add", "Go+ overload func\n\n- func(a int)\n- func(a ...string)", "method") + Add //@item(apkgAdd2, "Add", "Go+ overload func\n\n- func(a int)\n- func(a ...string)", "method") var n N n.add //@rank(" //", apkgAdd1, apkgAdd2) From 4c67aac2dba5cdf0b636d7e8b90eb226458c79ca Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 20 Mar 2024 22:21:24 +0800 Subject: [PATCH 5/7] gopls/internal/goxls/testdata: update testdata --- gopls/internal/goxls/testdata/bar/bar.go.in | 47 +++++++++++++++++++ gopls/internal/goxls/testdata/baz/baz.go.in | 33 +++++++++++++ .../goxls/testdata/foo/{foo.gop => foo.go} | 3 +- .../goxls/testdata/foo/gop_autogen.go | 3 -- .../goxls/testdata/summary.txt.golden | 4 +- .../goxls/testdata/summary_go1.18.txt.golden | 4 +- .../goxls/testdata/summary_go1.21.txt.golden | 4 +- .../goxls/testdata/unimported/export_test.gop | 3 ++ .../testdata/unimported/unimported.gop.in | 23 +++++++++ .../unimported/unimported_cand_type.gop | 16 +++++++ .../goxls/testdata/unimported/x_test.gop | 9 ++++ 11 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 gopls/internal/goxls/testdata/bar/bar.go.in create mode 100644 gopls/internal/goxls/testdata/baz/baz.go.in rename gopls/internal/goxls/testdata/foo/{foo.gop => foo.go} (93%) delete mode 100644 gopls/internal/goxls/testdata/foo/gop_autogen.go create mode 100644 gopls/internal/goxls/testdata/unimported/export_test.gop create mode 100644 gopls/internal/goxls/testdata/unimported/unimported.gop.in create mode 100644 gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop create mode 100644 gopls/internal/goxls/testdata/unimported/x_test.gop diff --git a/gopls/internal/goxls/testdata/bar/bar.go.in b/gopls/internal/goxls/testdata/bar/bar.go.in new file mode 100644 index 00000000000..502bdf74060 --- /dev/null +++ b/gopls/internal/goxls/testdata/bar/bar.go.in @@ -0,0 +1,47 @@ +// +build go1.11 + +package bar + +import ( + "golang.org/lsptests/foo" //@item(foo, "foo", "\"golang.org/lsptests/foo\"", "package") +) + +func helper(i foo.IntFoo) {} //@item(helper, "helper", "func(i foo.IntFoo)", "func") + +func _() { + help //@complete("l", helper) + _ = foo.StructFoo{} //@complete("S", IntFoo, StructFoo) +} + +// Bar is a function. +func Bar() { //@item(Bar, "Bar", "func()", "func", "Bar is a function.") + foo.Foo() //@complete("F", Foo, IntFoo, StructFoo) + var _ foo.IntFoo //@complete("I", IntFoo, StructFoo) + foo.() //@complete("(", Foo, IntFoo, StructFoo) +} + +func _() { + var Valentine int //@item(Valentine, "Valentine", "int", "var") + + _ = foo.StructFoo{ + Valu //@complete(" //", Value) + } + _ = foo.StructFoo{ + Va //@complete("a", Value, Valentine) + } + _ = foo.StructFoo{ + Value: 5, //@complete("a", Value) + } + _ = foo.StructFoo{ + //@complete("", Value, Valentine, foo, helper, Bar) + } + _ = foo.StructFoo{ + Value: Valen //@complete("le", Valentine) + } + _ = foo.StructFoo{ + Value: //@complete(" //", Valentine, foo, helper, Bar) + } + _ = foo.StructFoo{ + Value: //@complete(" ", Valentine, foo, helper, Bar) + } +} diff --git a/gopls/internal/goxls/testdata/baz/baz.go.in b/gopls/internal/goxls/testdata/baz/baz.go.in new file mode 100644 index 00000000000..94952e1267b --- /dev/null +++ b/gopls/internal/goxls/testdata/baz/baz.go.in @@ -0,0 +1,33 @@ +// +build go1.11 + +package baz + +import ( + "golang.org/lsptests/bar" + + f "golang.org/lsptests/foo" +) + +var FooStruct f.StructFoo + +func Baz() { + defer bar.Bar() //@complete("B", Bar) + // TODO(rstambler): Test completion here. + defer bar.B + var x f.IntFoo //@complete("n", IntFoo),typdef("x", IntFoo) + bar.Bar() //@complete("B", Bar) +} + +func _() { + bob := f.StructFoo{Value: 5} + if x := bob. //@complete(" //", Value) + switch true == false { + case true: + if x := bob. //@complete(" //", Value) + case false: + } + if x := bob.Va //@complete("a", Value) + switch true == true { + default: + } +} diff --git a/gopls/internal/goxls/testdata/foo/foo.gop b/gopls/internal/goxls/testdata/foo/foo.go similarity index 93% rename from gopls/internal/goxls/testdata/foo/foo.gop rename to gopls/internal/goxls/testdata/foo/foo.go index 3e4c12a6c0b..490ff2e657c 100644 --- a/gopls/internal/goxls/testdata/foo/foo.gop +++ b/gopls/internal/goxls/testdata/foo/foo.go @@ -23,9 +23,8 @@ func _() { shadowed := 123 { shadowed := "hi" //@item(shadowed, "shadowed", "string", "var") - _ = shadowed //@complete("a", shadowed) + sha //@complete("a", shadowed) } - _ = shadowed } type IntFoo int //@item(IntFoo, "IntFoo", "int", "type") diff --git a/gopls/internal/goxls/testdata/foo/gop_autogen.go b/gopls/internal/goxls/testdata/foo/gop_autogen.go deleted file mode 100644 index 3d4911c1e9a..00000000000 --- a/gopls/internal/goxls/testdata/foo/gop_autogen.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by gop (Go+); DO NOT EDIT. - -package foo diff --git a/gopls/internal/goxls/testdata/summary.txt.golden b/gopls/internal/goxls/testdata/summary.txt.golden index 70d47218cfc..03b9aba21ba 100644 --- a/gopls/internal/goxls/testdata/summary.txt.golden +++ b/gopls/internal/goxls/testdata/summary.txt.golden @@ -1,7 +1,7 @@ -- summary -- CallHierarchyCount = 0 CodeLensCount = 4 -CompletionsCount = 18 +CompletionsCount = 36 CompletionSnippetCount = 12 UnimportedCompletionsCount = 0 DeepCompletionsCount = 0 @@ -14,7 +14,7 @@ SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 DefinitionsCount = 23 -TypeDefinitionsCount = 1 +TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 RenamesCount = 0 diff --git a/gopls/internal/goxls/testdata/summary_go1.18.txt.golden b/gopls/internal/goxls/testdata/summary_go1.18.txt.golden index 70d47218cfc..03b9aba21ba 100644 --- a/gopls/internal/goxls/testdata/summary_go1.18.txt.golden +++ b/gopls/internal/goxls/testdata/summary_go1.18.txt.golden @@ -1,7 +1,7 @@ -- summary -- CallHierarchyCount = 0 CodeLensCount = 4 -CompletionsCount = 18 +CompletionsCount = 36 CompletionSnippetCount = 12 UnimportedCompletionsCount = 0 DeepCompletionsCount = 0 @@ -14,7 +14,7 @@ SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 DefinitionsCount = 23 -TypeDefinitionsCount = 1 +TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 RenamesCount = 0 diff --git a/gopls/internal/goxls/testdata/summary_go1.21.txt.golden b/gopls/internal/goxls/testdata/summary_go1.21.txt.golden index 70d47218cfc..03b9aba21ba 100644 --- a/gopls/internal/goxls/testdata/summary_go1.21.txt.golden +++ b/gopls/internal/goxls/testdata/summary_go1.21.txt.golden @@ -1,7 +1,7 @@ -- summary -- CallHierarchyCount = 0 CodeLensCount = 4 -CompletionsCount = 18 +CompletionsCount = 36 CompletionSnippetCount = 12 UnimportedCompletionsCount = 0 DeepCompletionsCount = 0 @@ -14,7 +14,7 @@ SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 DefinitionsCount = 23 -TypeDefinitionsCount = 1 +TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 RenamesCount = 0 diff --git a/gopls/internal/goxls/testdata/unimported/export_test.gop b/gopls/internal/goxls/testdata/unimported/export_test.gop new file mode 100644 index 00000000000..707768e1da2 --- /dev/null +++ b/gopls/internal/goxls/testdata/unimported/export_test.gop @@ -0,0 +1,3 @@ +package unimported + +var TestExport int //@item(testexport, "TestExport", "var (from \"golang.org/lsptests/unimported\")", "var") diff --git a/gopls/internal/goxls/testdata/unimported/unimported.gop.in b/gopls/internal/goxls/testdata/unimported/unimported.gop.in new file mode 100644 index 00000000000..74d51ffe82a --- /dev/null +++ b/gopls/internal/goxls/testdata/unimported/unimported.gop.in @@ -0,0 +1,23 @@ +package unimported + +func _() { + http //@unimported("p", nethttp) + // container/ring is extremely unlikely to be imported by anything, so shouldn't have type information. + ring.Ring //@unimported("Ring", ringring) + signature.Foo //@unimported("Foo", signaturefoo) + + context.Bac //@unimported(" //", contextBackground) +} + +// Create markers for unimported std lib packages. Only for use by this test. +/* http */ //@item(nethttp, "http", "\"net/http\"", "package") + +/* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var") + +/* signature.Foo */ //@item(signaturefoo, "Foo", "func (from \"golang.org/lsptests/signature\")", "func") + +/* context.Background */ //@item(contextBackground, "Background", "func (from \"context\")", "func") + +// Now that we no longer type-check imported completions, +// we don't expect the context.Background().Err method (see golang/go#58663). +/* context.Background().Err */ //@item(contextBackgroundErr, "Background().Err", "func (from \"context\")", "method") diff --git a/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop b/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop new file mode 100644 index 00000000000..554c426a998 --- /dev/null +++ b/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop @@ -0,0 +1,16 @@ +package unimported + +import ( + _ "context" + + "golang.org/lsptests/baz" + _ "golang.org/lsptests/signature" // provide type information for unimported completions in the other file +) + +func _() { + foo.StructFoo{} //@item(litFooStructFoo, "foo.StructFoo{}", "struct{...}", "struct") + + // We get the literal completion for "foo.StructFoo{}" even though we haven't + // imported "foo" yet. + baz.FooStruct = f //@snippet(" //", litFooStructFoo, "foo.StructFoo{$0\\}", "foo.StructFoo{$0\\}") +} diff --git a/gopls/internal/goxls/testdata/unimported/x_test.gop b/gopls/internal/goxls/testdata/unimported/x_test.gop new file mode 100644 index 00000000000..681dcb2536d --- /dev/null +++ b/gopls/internal/goxls/testdata/unimported/x_test.gop @@ -0,0 +1,9 @@ +package unimported_test + +import ( + "testing" +) + +func TestSomething(t *testing.T) { + _ = unimported.TestExport //@unimported("TestExport", testexport) +} From 17f3133c430762279c73fe1e7dcbfdee9f520939 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 21 Mar 2024 16:33:29 +0800 Subject: [PATCH 6/7] gopls/internal/lsp/source/completion: recheckOverload support found gopo overload decl from go --- .../lsp/source/completion/completion.go | 182 ----------- .../lsp/source/completion/completion_gox.go | 59 +--- .../lsp/source/completion/unimport_gox.go | 300 ++++++++++++++++++ 3 files changed, 301 insertions(+), 240 deletions(-) create mode 100644 gopls/internal/lsp/source/completion/unimport_gox.go diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go index 668e5016678..a3bb9bfdea3 100644 --- a/gopls/internal/lsp/source/completion/completion.go +++ b/gopls/internal/lsp/source/completion/completion.go @@ -3308,185 +3308,3 @@ func forEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, } } } - -// goxls: quickParse -func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough *int32, selName string, relevances map[string]float64, needImport bool, recheck *unimportChecked) func(uri span.URI, m *source.Metadata) error { - return func(uri span.URI, m *source.Metadata) error { - if atomic.LoadInt32(enough) != 0 { - return nil - } - - fh, err := c.snapshot.ReadFile(ctx, uri) - if err != nil { - return err - } - content, err := fh.Content() - if err != nil { - return err - } - path := string(m.PkgPath) - forEachPackageMember(content, func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl) { - if atomic.LoadInt32(enough) != 0 { - return - } - - if !id.IsExported() { - return - } - - cMu.Lock() - score := c.matcher.Score(id.Name) - cMu.Unlock() - - if selName != "_" && score == 0 { - return // not a match; avoid constructing the completion item below - } - - // The only detail is the kind and package: `var (from "example.com/foo")` - // TODO(adonovan): pretty-print FuncDecl.FuncType or TypeSpec.Type? - // TODO(adonovan): should this score consider the actual c.matcher.Score - // of the item? How does this compare with the deepState.enqueue path? - item := CompletionItem{ - Label: id.Name, - Detail: fmt.Sprintf("%s (from %q)", strings.ToLower(tok.String()), m.PkgPath), - InsertText: id.Name, - Score: unimportedScore(relevances[path]), - } - switch tok { - case token.FUNC: - item.Kind = protocol.FunctionCompletion - case token.VAR: - item.Kind = protocol.VariableCompletion - case token.CONST: - item.Kind = protocol.ConstantCompletion - case token.TYPE: - // Without types, we can't distinguish Class from Interface. - item.Kind = protocol.ClassCompletion - } - - if needImport { - imp := &importInfo{importPath: path} - if imports.ImportPathToAssumedName(path) != string(m.Name) { - imp.name = string(m.Name) - } - item.AdditionalTextEdits, _ = c.importEdits(imp) - } - - // For functions, add a parameter snippet. - if fn != nil { - var sn snippet.Builder - sn.WriteText(id.Name) - - paramList := func(open, close string, list *ast.FieldList) { - if list != nil { - var cfg printer.Config // slight overkill - var nparams int - param := func(name string, typ ast.Expr) { - if nparams > 0 { - sn.WriteText(", ") - } - nparams++ - if c.opts.placeholders { - sn.WritePlaceholder(func(b *snippet.Builder) { - var buf strings.Builder - buf.WriteString(name) - buf.WriteByte(' ') - cfg.Fprint(&buf, token.NewFileSet(), typ) - b.WriteText(buf.String()) - }) - } else { - sn.WriteText(name) - } - } - - sn.WriteText(open) - for _, field := range list.List { - if field.Names != nil { - for _, name := range field.Names { - param(name.Name, field.Type) - } - } else { - param("_", field.Type) - } - } - sn.WriteText(close) - } - } - - paramList("[", "]", typeparams.ForFuncType(fn.Type)) - paramList("(", ")", fn.Type.Params) - - item.snippet = &sn - } - - cMu.Lock() - if tok == token.CONST { - if id.Name == "GopPackage" { - recheck.pkgs[m.PkgPath] = true - } else if strings.HasPrefix(id.Name, "Gopo_") { - md := checkTypeMethod(id.Name[5:]) - if md.typ == "" && ast.IsExported(md.name) { - item := CompletionItem{ - Label: md.name, - Detail: fmt.Sprintf("func (from %q)", m.PkgPath), - InsertText: md.name, - Score: unimportedScore(relevances[path]), - Kind: protocol.FunctionCompletion, - isOverload: true, - } - if needImport { - imp := &importInfo{importPath: path} - if imports.ImportPathToAssumedName(path) != string(m.Name) { - imp.name = string(m.Name) - } - item.AdditionalTextEdits, _ = c.importEdits(imp) - } - recheck.gopo[m.PkgPath] = append(recheck.gopo[m.PkgPath], item) - } - } - } - if tok == token.FUNC { - noSnip := len(fn.Type.Params.List) == 0 - if maybleIndexOverload(id.Name) { - recheck.items[m.PkgPath] = append(recheck.items[m.PkgPath], recheckItem{item, noSnip}) - } else { - c.items = append(c.items, item) - // goxls func alias - if alias, ok := hasAliasName(id.Name); ok { - c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) - } - } - } else { - c.items = append(c.items, item) - } - if len(c.items) >= unimportedMemberTarget { - atomic.StoreInt32(enough, 1) - } - cMu.Unlock() - }) - return nil - } -} - -type mthd struct { - typ string - name string -} - -// Func (no _ func name) -// _Func (with _ func name) -// TypeName_Method (no _ method name) -// _TypeName__Method (with _ method name) -func checkTypeMethod(name string) mthd { - if pos := strings.IndexByte(name, '_'); pos >= 0 { - if pos == 0 { - t := name[1:] - if pos = strings.Index(t, "__"); pos <= 0 { - return mthd{"", t} // _Func - } - return mthd{t[:pos], t[pos+2:]} // _TypeName__Method - } - return mthd{name[:pos], name[pos+1:]} // TypeName_Method - } - return mthd{"", name} // Func -} diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index 9a4f9e686d4..4c01a547f8e 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -1306,11 +1306,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro } // recheck is gop index overload check - recheck := &unimportChecked{ - make(map[source.PackagePath]bool), - make(map[source.PackagePath][]recheckItem), - make(map[source.PackagePath][]CompletionItem), - } + recheck := newRecheckOverload() for _, path := range paths { m := known[source.PackagePath(path)] if len(m.CompiledGopFiles) > 0 { @@ -1375,11 +1371,6 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return nil } -type recheckItem struct { - CompletionItem - noSnip bool -} - func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) { scope := pkg.Scope() for _, name := range scope.Names() { @@ -2485,51 +2476,3 @@ func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ide } } } - -type unimportChecked struct { - pkgs map[source.PackagePath]bool // gop package - items map[source.PackagePath][]recheckItem // index overload funcs - gopo map[source.PackagePath][]CompletionItem // gopo overload funcs -} - -func (recheck *unimportChecked) checkOverload(c *gopCompleter) { - // check gop package index overload - for pkg, items := range recheck.items { - if recheck.pkgs[pkg] { - names := make(map[string]bool) - sort.Slice(items, func(i, j int) bool { - return items[i].Label < items[j].Label - }) - for _, item := range items { - id := item.Label[:len(item.Label)-3] - if !names[id] { - names[id] = true - item.isOverload = true - item.Detail = "Go+ overload func\n\n" + item.Detail - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false)) - if alias, ok := hasAliasName(id); ok { - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) - } - } - } - } else { - for _, item := range items { - c.items = append(c.items, item.CompletionItem) - if alias, ok := hasAliasName(item.Label); ok { - c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) - } - } - } - } - // check gop package gopo overload - for pkg, items := range recheck.gopo { - if recheck.pkgs[pkg] { - for _, item := range items { - c.items = append(c.items, item) - if alias, ok := hasAliasName(item.Label); ok { - c.items = append(c.items, cloneAliasItem(item, item.Label, alias, 0.0001, true)) - } - } - } - } -} diff --git a/gopls/internal/lsp/source/completion/unimport_gox.go b/gopls/internal/lsp/source/completion/unimport_gox.go new file mode 100644 index 00000000000..493896fdb18 --- /dev/null +++ b/gopls/internal/lsp/source/completion/unimport_gox.go @@ -0,0 +1,300 @@ +package completion + +import ( + "context" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + + goplsastutil "golang.org/x/tools/gopls/internal/astutil" + "golang.org/x/tools/gopls/internal/lsp/protocol" + "golang.org/x/tools/gopls/internal/lsp/snippet" + "golang.org/x/tools/gopls/internal/lsp/source" + "golang.org/x/tools/gopls/internal/span" + "golang.org/x/tools/internal/imports" + "golang.org/x/tools/internal/typeparams" +) + +type recheckItem struct { + CompletionItem + noSnip bool +} + +type recheckOverload struct { + pkgs map[source.PackagePath]bool // gop package + items map[source.PackagePath][]recheckItem // index overload funcs + gopo map[source.PackagePath][]recheckItem // gopo overload funcs +} + +func newRecheckOverload() *recheckOverload { + return &recheckOverload{ + make(map[source.PackagePath]bool), + make(map[source.PackagePath][]recheckItem), + make(map[source.PackagePath][]recheckItem), + } +} + +func (recheck *recheckOverload) checkOverload(c *gopCompleter) { + // check gop package index overload + for pkg, items := range recheck.items { + if recheck.pkgs[pkg] { + names := make(map[string]bool) + sort.Slice(items, func(i, j int) bool { + return items[i].Label < items[j].Label + }) + for _, item := range items { + id := item.Label[:len(item.Label)-3] + if !names[id] { + names[id] = true + item.isOverload = true + item.Detail = "Go+ overload func\n\n" + item.Detail + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false)) + if alias, ok := hasAliasName(id); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } else { + for _, item := range items { + c.items = append(c.items, item.CompletionItem) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } + // check gop package gopo overload + for pkg, items := range recheck.gopo { + if recheck.pkgs[pkg] { + for _, item := range items { + item.isOverload = true + item.Detail = "Go+ overload func\n\n" + item.Detail + c.items = append(c.items, item.CompletionItem) + if alias, ok := hasAliasName(item.Label); ok { + c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip)) + } + } + } + } +} + +// goxls: quickParse +func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough *int32, selName string, relevances map[string]float64, needImport bool, recheck *recheckOverload) func(uri span.URI, m *source.Metadata) error { + return func(uri span.URI, m *source.Metadata) error { + if atomic.LoadInt32(enough) != 0 { + return nil + } + + fh, err := c.snapshot.ReadFile(ctx, uri) + if err != nil { + return err + } + content, err := fh.Content() + if err != nil { + return err + } + path := string(m.PkgPath) + goForEachPackageMember(content, func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl, gopo bool) { + if atomic.LoadInt32(enough) != 0 { + return + } + + if !id.IsExported() { + return + } + + cMu.Lock() + score := c.matcher.Score(id.Name) + cMu.Unlock() + + if selName != "_" && score == 0 { + return // not a match; avoid constructing the completion item below + } + + // The only detail is the kind and package: `var (from "example.com/foo")` + // TODO(adonovan): pretty-print FuncDecl.FuncType or TypeSpec.Type? + // TODO(adonovan): should this score consider the actual c.matcher.Score + // of the item? How does this compare with the deepState.enqueue path? + item := CompletionItem{ + Label: id.Name, + Detail: fmt.Sprintf("%s (from %q)", strings.ToLower(tok.String()), m.PkgPath), + InsertText: id.Name, + Score: unimportedScore(relevances[path]), + } + switch tok { + case token.FUNC: + item.Kind = protocol.FunctionCompletion + case token.VAR: + item.Kind = protocol.VariableCompletion + case token.CONST: + item.Kind = protocol.ConstantCompletion + case token.TYPE: + // Without types, we can't distinguish Class from Interface. + item.Kind = protocol.ClassCompletion + } + + if needImport { + imp := &importInfo{importPath: path} + if imports.ImportPathToAssumedName(path) != string(m.Name) { + imp.name = string(m.Name) + } + item.AdditionalTextEdits, _ = c.importEdits(imp) + } + + // For functions, add a parameter snippet. + if fn != nil { + var sn snippet.Builder + sn.WriteText(id.Name) + + paramList := func(open, close string, list *ast.FieldList) { + if list != nil { + var cfg printer.Config // slight overkill + var nparams int + param := func(name string, typ ast.Expr) { + if nparams > 0 { + sn.WriteText(", ") + } + nparams++ + if c.opts.placeholders { + sn.WritePlaceholder(func(b *snippet.Builder) { + var buf strings.Builder + buf.WriteString(name) + buf.WriteByte(' ') + cfg.Fprint(&buf, token.NewFileSet(), typ) + b.WriteText(buf.String()) + }) + } else { + sn.WriteText(name) + } + } + + sn.WriteText(open) + for _, field := range list.List { + if field.Names != nil { + for _, name := range field.Names { + param(name.Name, field.Type) + } + } else { + param("_", field.Type) + } + } + sn.WriteText(close) + } + } + + paramList("[", "]", typeparams.ForFuncType(fn.Type)) + paramList("(", ")", fn.Type.Params) + + item.snippet = &sn + } + + cMu.Lock() + if tok == token.CONST { + if id.Name == "GopPackage" { + recheck.pkgs[m.PkgPath] = true + } + } + if tok == token.FUNC { + noSnip := len(fn.Type.Params.List) == 0 + if maybleIndexOverload(id.Name) { + recheck.items[m.PkgPath] = append(recheck.items[m.PkgPath], recheckItem{item, noSnip}) + } else if gopo { + recheck.gopo[m.PkgPath] = append(recheck.gopo[m.PkgPath], recheckItem{item, noSnip}) + } else { + c.items = append(c.items, item) + // goxls func alias + if alias, ok := hasAliasName(id.Name); ok { + c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) + } + } + } else { + c.items = append(c.items, item) + } + if len(c.items) >= unimportedMemberTarget { + atomic.StoreInt32(enough, 1) + } + cMu.Unlock() + }) + return nil + } +} + +// goForEachPackageMember calls f(tok, id, fnType, gopo) for each package-level +// TYPE/VAR/CONST/FUNC declaration in the Go source file, based on a +// quick partial parse. fn is non-nil only for function declarations. +// The AST position information is garbage. +func goForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl, gopo bool)) { + purged := goplsastutil.PurgeFuncBodies(content) + file, _ := parser.ParseFile(token.NewFileSet(), "", purged, 0) + for _, decl := range file.Decls { + switch decl := decl.(type) { + case *ast.GenDecl: + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.ValueSpec: // var/const + for _, id := range spec.Names { + if decl.Tok == token.CONST && strings.HasPrefix(id.Name, "Gopo_") { + if md := checkTypeMethod(id.Name[5:]); md.typ == "" && ast.IsExported(md.name) { + if fn := foundGopoExportOverload(file, spec); fn != nil { + f(token.FUNC, ast.NewIdent(md.name), fn, true) + } + } + } + f(decl.Tok, id, nil, false) + } + case *ast.TypeSpec: + f(decl.Tok, spec.Name, nil, false) + } + } + case *ast.FuncDecl: + if decl.Recv == nil { + f(token.FUNC, decl.Name, decl, false) + } + } + } +} + +func foundGopoExportOverload(f *ast.File, spec *ast.ValueSpec) (fn *ast.FuncDecl) { + if lit, ok := spec.Values[0].(*ast.BasicLit); ok { + if s, err := strconv.Unquote(lit.Value); err == nil { + for _, v := range strings.Split(s, ",") { + if obj := f.Scope.Lookup(v); obj != nil && ast.IsExported(obj.Name) { + if fn, ok := obj.Decl.(*ast.FuncDecl); ok { + return fn + } + } + } + } + } + return nil +} + +type mthd struct { + typ string + name string +} + +// Func (no _ func name) +// _Func (with _ func name) +// TypeName_Method (no _ method name) +// _TypeName__Method (with _ method name) +func checkTypeMethod(name string) mthd { + if pos := strings.IndexByte(name, '_'); pos >= 0 { + if pos == 0 { + t := name[1:] + if pos = strings.Index(t, "__"); pos <= 0 { + return mthd{"", t} // _Func + } + return mthd{t[:pos], t[pos+2:]} // _TypeName__Method + } + return mthd{name[:pos], name[pos+1:]} // TypeName_Method + } + return mthd{"", name} // Func +} From 9cda23ad0515ac3a65b54de3e2280662013eec20 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 21 Mar 2024 22:18:36 +0800 Subject: [PATCH 7/7] gopls/internal/goxls/testdata: add overload unimported test --- .../goxls/testdata/overload/c/add.gop | 10 ++ .../goxls/testdata/overload/c/gop_autogen.go | 3 + .../goxls/testdata/overload/c/method.gop | 19 ++++ .../goxls/testdata/overload/c/mul.gop | 14 +++ .../goxls/testdata/overload/d/utils.go | 33 +++++++ .../goxls/testdata/overload/gop_autogen.go | 2 + .../goxls/testdata/overload/overload.gop | 22 +++++ .../testdata/overload/overload.gop.golden | 96 +++++++++++++++++++ .../overload/overload_unimport.gop.in | 24 +++++ .../goxls/testdata/summary.txt.golden | 4 +- .../goxls/testdata/summary_go1.18.txt.golden | 4 +- .../goxls/testdata/summary_go1.21.txt.golden | 4 +- .../goxls/testdata/unimported/export_test.gop | 3 - .../testdata/unimported/unimported.gop.in | 23 ----- .../unimported/unimported_cand_type.gop | 16 ---- .../goxls/testdata/unimported/x_test.gop | 9 -- .../lsp/source/completion/completion_gox.go | 2 +- 17 files changed, 230 insertions(+), 58 deletions(-) create mode 100644 gopls/internal/goxls/testdata/overload/c/add.gop create mode 100644 gopls/internal/goxls/testdata/overload/c/gop_autogen.go create mode 100644 gopls/internal/goxls/testdata/overload/c/method.gop create mode 100644 gopls/internal/goxls/testdata/overload/c/mul.gop create mode 100644 gopls/internal/goxls/testdata/overload/d/utils.go create mode 100644 gopls/internal/goxls/testdata/overload/overload_unimport.gop.in delete mode 100644 gopls/internal/goxls/testdata/unimported/export_test.gop delete mode 100644 gopls/internal/goxls/testdata/unimported/unimported.gop.in delete mode 100644 gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop delete mode 100644 gopls/internal/goxls/testdata/unimported/x_test.gop diff --git a/gopls/internal/goxls/testdata/overload/c/add.gop b/gopls/internal/goxls/testdata/overload/c/add.gop new file mode 100644 index 00000000000..1ccda63f069 --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/c/add.gop @@ -0,0 +1,10 @@ +package c + +func Add = ( + func(a, b int) int { + return a + b + } + func(a, b string) string { + return a + b + } +) diff --git a/gopls/internal/goxls/testdata/overload/c/gop_autogen.go b/gopls/internal/goxls/testdata/overload/c/gop_autogen.go new file mode 100644 index 00000000000..111e6a11eab --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/c/gop_autogen.go @@ -0,0 +1,3 @@ +// Code generated by gop (Go+); DO NOT EDIT. + +package c diff --git a/gopls/internal/goxls/testdata/overload/c/method.gop b/gopls/internal/goxls/testdata/overload/c/method.gop new file mode 100644 index 00000000000..e53f489ed90 --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/c/method.gop @@ -0,0 +1,19 @@ +package c + +type Foo struct { +} + +func (a *Foo) MulInt(b int) *Foo { //@mark(c_foo_mulInt,"MulInt") + println "mulInt" + return a +} + +func (a *Foo) MulFoo(b *Foo) *Foo { //@mark(c_foo_mulFoo,"MulFoo") + println "mulFoo" + return a +} + +func (Foo).Mul = ( + (Foo).MulInt + (Foo).MulFoo +) diff --git a/gopls/internal/goxls/testdata/overload/c/mul.gop b/gopls/internal/goxls/testdata/overload/c/mul.gop new file mode 100644 index 00000000000..8ca7de64ea6 --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/c/mul.gop @@ -0,0 +1,14 @@ +package c + +func MulInt(a, b int) int { //@mark(c_mulInt,"MulInt") + return a * b +} + +func MulFloat(a, b float64) float64 { //@mark(c_mulFloat,"MulFloat") + return a * b +} + +func Mul = ( + MulInt + MulFloat +) diff --git a/gopls/internal/goxls/testdata/overload/d/utils.go b/gopls/internal/goxls/testdata/overload/d/utils.go new file mode 100644 index 00000000000..e466ad0b3f9 --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/d/utils.go @@ -0,0 +1,33 @@ +package d + +const GopPackage = true + +type Foo struct { +} + +const Gopo_Foo_Mul = ".MulInt,.MulFoo" +const Gopo_Mul = "MulInt,MulFloat" + +func Add__0(a int, b int) int { //@mark(d_add__0,"Add__0") + return a + b +} + +func Add__1(a string, b string) string { //@mark(d_add__1,"Add__1") + return a + b +} + +func (a *Foo) MulInt(b int) *Foo { //@mark(d_foo_mulInt,"MulInt") + return a +} + +func (a *Foo) MulFoo(b *Foo) *Foo { //@mark(d_foo_mulFoo,"MulFoo") + return a +} + +func MulInt(a int, b int) int { //@mark(d_mulInt,"MulInt") + return a * b +} + +func MulFloat(a float64, b float64) float64 { //@mark(d_mulFloat,"MulFloat") + return a * b +} diff --git a/gopls/internal/goxls/testdata/overload/gop_autogen.go b/gopls/internal/goxls/testdata/overload/gop_autogen.go index 26751cdc203..79cb0375e98 100644 --- a/gopls/internal/goxls/testdata/overload/gop_autogen.go +++ b/gopls/internal/goxls/testdata/overload/gop_autogen.go @@ -5,4 +5,6 @@ package main import ( "golang.org/lsptests/overload/a" "golang.org/lsptests/overload/b" + "golang.org/lsptests/overload/c" + "golang.org/lsptests/overload/d" ) diff --git a/gopls/internal/goxls/testdata/overload/overload.gop b/gopls/internal/goxls/testdata/overload/overload.gop index 32a305086a3..ccc0faec882 100644 --- a/gopls/internal/goxls/testdata/overload/overload.gop +++ b/gopls/internal/goxls/testdata/overload/overload.gop @@ -1,6 +1,8 @@ import ( "golang.org/lsptests/overload/a" "golang.org/lsptests/overload/b" + "golang.org/lsptests/overload/c" + "golang.org/lsptests/overload/d" ) Demo //@godef("Demo",Demo__0) @@ -30,3 +32,23 @@ b.demo "hello" //@godef("demo",b_demo__3) var n2 b.N n2.add 100 //@godef("add",b_add__0) n2.add "hello" //@godef("add",b_add__1) + +c.mul 100, 200 //@godef("mul",c_mulInt) +c.mul 100.1, 200 //@godef("mul",c_mulFloat) + +c.add 100, 200 +c.add "hello", "world" + +var foo c.Foo +foo.mul 100 //@godef("mul",c_foo_mulInt) +foo.mul new(c.Foo) //@godef("mul",c_foo_mulFoo) + +d.mul 100, 200 //@godef("mul",d_mulInt) +d.mul 100.1, 200 //@godef("mul",d_mulFloat) + +d.add 100, 200 //@godef("add",d_add__0) +d.add "hello", "world" //@godef("add",d_add__1) + +var foo2 d.Foo +foo2.mul 100 //@godef("mul",d_foo_mulInt) +foo2.mul new(d.Foo) //@godef("mul",d_foo_mulFoo) diff --git a/gopls/internal/goxls/testdata/overload/overload.gop.golden b/gopls/internal/goxls/testdata/overload/overload.gop.golden index c190441b1a6..6f4d4cf0639 100644 --- a/gopls/internal/goxls/testdata/overload/overload.gop.golden +++ b/gopls/internal/goxls/testdata/overload/overload.gop.golden @@ -108,6 +108,102 @@ func b.GopDemo() ``` [`b.GopDemo` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/b#GopDemo) +-- c_add-hoverdef -- +```go +func c.Add__1(a string, b string) string +``` + +[`c.Add__1` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Add__1) +-- c_add__0-hoverdef -- +```go +func c.Add__0(a int, b int) int +``` + +[`c.Add__0` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Add__0) +-- c_foo_mulFoo-hoverdef -- +```go +func (*c.Foo).MulFoo(b *c.Foo) *c.Foo +``` + +[`(c.Foo).MulFoo` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Foo.MulFoo) +-- c_foo_mulInt-hoverdef -- +```go +func (*c.Foo).MulInt(b int) *c.Foo +``` + +[`(c.Foo).MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Foo.MulInt) +-- c_foo_mul__0-hoverdef -- +```go +func (*c.Foo).MulInt(b int) *c.Foo +``` + +[`(c.Foo).MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Foo.MulInt) +-- c_foo_mul__1-hoverdef -- +```go +func (*c.Foo).MulFoo(b *c.Foo) *c.Foo +``` + +[`(c.Foo).MulFoo` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#Foo.MulFoo) +-- c_mulFloat-hoverdef -- +```go +func c.MulFloat(a float64, b float64) float64 +``` + +[`c.MulFloat` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#MulFloat) +-- c_mulInt-hoverdef -- +```go +func c.MulInt(a int, b int) int +``` + +[`c.MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#MulInt) +-- c_mul__0-hoverdef -- +```go +func c.MulInt(a int, b int) int +``` + +[`c.MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#MulInt) +-- c_mul__1-hoverdef -- +```go +func c.MulFloat(a float64, b float64) float64 +``` + +[`c.MulFloat` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/c#MulFloat) +-- d_add__0-hoverdef -- +```go +func d.Add__0(a int, b int) int +``` + +[`d.Add__0` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#Add__0) +-- d_add__1-hoverdef -- +```go +func d.Add__1(a string, b string) string +``` + +[`d.Add__1` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#Add__1) +-- d_foo_mulFoo-hoverdef -- +```go +func (*d.Foo).MulFoo(b *d.Foo) *d.Foo +``` + +[`(d.Foo).MulFoo` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#Foo.MulFoo) +-- d_foo_mulInt-hoverdef -- +```go +func (*d.Foo).MulInt(b int) *d.Foo +``` + +[`(d.Foo).MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#Foo.MulInt) +-- d_mulFloat-hoverdef -- +```go +func d.MulFloat(a float64, b float64) float64 +``` + +[`d.MulFloat` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#MulFloat) +-- d_mulInt-hoverdef -- +```go +func d.MulInt(a int, b int) int +``` + +[`d.MulInt` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/overload/d#MulInt) -- pkg_add__0-hoverdef -- ```go func (*pkg.N).Add__0(a int) diff --git a/gopls/internal/goxls/testdata/overload/overload_unimport.gop.in b/gopls/internal/goxls/testdata/overload/overload_unimport.gop.in new file mode 100644 index 00000000000..502f6aeb9a0 --- /dev/null +++ b/gopls/internal/goxls/testdata/overload/overload_unimport.gop.in @@ -0,0 +1,24 @@ +func _() { + c.add //@unimported("add", c_add_unimported) + c.mul //@unimported("mul", c_mul_unimported) + c.mulInt //@unimported("mulInt", c_mulInt_unimported) + c.MulInt //@unimported("MulInt", c_MulInt_unimported) +} + +func _() { + d.add //@unimported("add", d_add_unimported) + d.mul //@unimported("mul", d_mul_unimported) + d.mulInt //@unimported("mulInt", d_mulInt_unimported) + d.MulInt //@unimported("MulInt", d_MulInt_unimported) +} + + +/* c.add */ //@item(c_add_unimported, "add", "Go+ overload func\n\nfunc (from \"golang.org/lsptests/overload/c\")", "func") +/* c.mul */ //@item(c_mul_unimported, "mul", "Go+ overload func\n\nfunc (from \"golang.org/lsptests/overload/c\")", "func") +/* c.mulInt */ //@item(c_mulInt_unimported, "mulInt", "Go+ alias func\n\nfunc (from \"golang.org/lsptests/overload/c\")", "func") +/* c.MulInt */ //@item(c_MulInt_unimported, "MulInt", "func (from \"golang.org/lsptests/overload/c\")", "func") + +/* d.add */ //@item(d_add_unimported, "add", "Go+ overload func\n\nfunc (from \"golang.org/lsptests/overload/d\")", "func") +/* d.mul */ //@item(d_mul_unimported, "mul", "Go+ overload func\n\nfunc (from \"golang.org/lsptests/overload/d\")", "func") +/* d.mulInt */ //@item(d_mulInt_unimported, "mulInt", "Go+ alias func\n\nfunc (from \"golang.org/lsptests/overload/d\")", "func") +/* d.MulInt */ //@item(d_MulInt_unimported, "MulInt", "func (from \"golang.org/lsptests/overload/d\")", "func") diff --git a/gopls/internal/goxls/testdata/summary.txt.golden b/gopls/internal/goxls/testdata/summary.txt.golden index 03b9aba21ba..7ee2de39051 100644 --- a/gopls/internal/goxls/testdata/summary.txt.golden +++ b/gopls/internal/goxls/testdata/summary.txt.golden @@ -3,7 +3,7 @@ CallHierarchyCount = 0 CodeLensCount = 4 CompletionsCount = 36 CompletionSnippetCount = 12 -UnimportedCompletionsCount = 0 +UnimportedCompletionsCount = 8 DeepCompletionsCount = 0 FuzzyCompletionsCount = 1 RankedCompletionsCount = 15 @@ -13,7 +13,7 @@ FoldingRangesCount = 0 SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 -DefinitionsCount = 23 +DefinitionsCount = 33 TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 diff --git a/gopls/internal/goxls/testdata/summary_go1.18.txt.golden b/gopls/internal/goxls/testdata/summary_go1.18.txt.golden index 03b9aba21ba..7ee2de39051 100644 --- a/gopls/internal/goxls/testdata/summary_go1.18.txt.golden +++ b/gopls/internal/goxls/testdata/summary_go1.18.txt.golden @@ -3,7 +3,7 @@ CallHierarchyCount = 0 CodeLensCount = 4 CompletionsCount = 36 CompletionSnippetCount = 12 -UnimportedCompletionsCount = 0 +UnimportedCompletionsCount = 8 DeepCompletionsCount = 0 FuzzyCompletionsCount = 1 RankedCompletionsCount = 15 @@ -13,7 +13,7 @@ FoldingRangesCount = 0 SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 -DefinitionsCount = 23 +DefinitionsCount = 33 TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 diff --git a/gopls/internal/goxls/testdata/summary_go1.21.txt.golden b/gopls/internal/goxls/testdata/summary_go1.21.txt.golden index 03b9aba21ba..7ee2de39051 100644 --- a/gopls/internal/goxls/testdata/summary_go1.21.txt.golden +++ b/gopls/internal/goxls/testdata/summary_go1.21.txt.golden @@ -3,7 +3,7 @@ CallHierarchyCount = 0 CodeLensCount = 4 CompletionsCount = 36 CompletionSnippetCount = 12 -UnimportedCompletionsCount = 0 +UnimportedCompletionsCount = 8 DeepCompletionsCount = 0 FuzzyCompletionsCount = 1 RankedCompletionsCount = 15 @@ -13,7 +13,7 @@ FoldingRangesCount = 0 SemanticTokenCount = 0 SuggestedFixCount = 0 MethodExtractionCount = 0 -DefinitionsCount = 23 +DefinitionsCount = 33 TypeDefinitionsCount = 2 HighlightsCount = 0 InlayHintsCount = 0 diff --git a/gopls/internal/goxls/testdata/unimported/export_test.gop b/gopls/internal/goxls/testdata/unimported/export_test.gop deleted file mode 100644 index 707768e1da2..00000000000 --- a/gopls/internal/goxls/testdata/unimported/export_test.gop +++ /dev/null @@ -1,3 +0,0 @@ -package unimported - -var TestExport int //@item(testexport, "TestExport", "var (from \"golang.org/lsptests/unimported\")", "var") diff --git a/gopls/internal/goxls/testdata/unimported/unimported.gop.in b/gopls/internal/goxls/testdata/unimported/unimported.gop.in deleted file mode 100644 index 74d51ffe82a..00000000000 --- a/gopls/internal/goxls/testdata/unimported/unimported.gop.in +++ /dev/null @@ -1,23 +0,0 @@ -package unimported - -func _() { - http //@unimported("p", nethttp) - // container/ring is extremely unlikely to be imported by anything, so shouldn't have type information. - ring.Ring //@unimported("Ring", ringring) - signature.Foo //@unimported("Foo", signaturefoo) - - context.Bac //@unimported(" //", contextBackground) -} - -// Create markers for unimported std lib packages. Only for use by this test. -/* http */ //@item(nethttp, "http", "\"net/http\"", "package") - -/* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var") - -/* signature.Foo */ //@item(signaturefoo, "Foo", "func (from \"golang.org/lsptests/signature\")", "func") - -/* context.Background */ //@item(contextBackground, "Background", "func (from \"context\")", "func") - -// Now that we no longer type-check imported completions, -// we don't expect the context.Background().Err method (see golang/go#58663). -/* context.Background().Err */ //@item(contextBackgroundErr, "Background().Err", "func (from \"context\")", "method") diff --git a/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop b/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop deleted file mode 100644 index 554c426a998..00000000000 --- a/gopls/internal/goxls/testdata/unimported/unimported_cand_type.gop +++ /dev/null @@ -1,16 +0,0 @@ -package unimported - -import ( - _ "context" - - "golang.org/lsptests/baz" - _ "golang.org/lsptests/signature" // provide type information for unimported completions in the other file -) - -func _() { - foo.StructFoo{} //@item(litFooStructFoo, "foo.StructFoo{}", "struct{...}", "struct") - - // We get the literal completion for "foo.StructFoo{}" even though we haven't - // imported "foo" yet. - baz.FooStruct = f //@snippet(" //", litFooStructFoo, "foo.StructFoo{$0\\}", "foo.StructFoo{$0\\}") -} diff --git a/gopls/internal/goxls/testdata/unimported/x_test.gop b/gopls/internal/goxls/testdata/unimported/x_test.gop deleted file mode 100644 index 681dcb2536d..00000000000 --- a/gopls/internal/goxls/testdata/unimported/x_test.gop +++ /dev/null @@ -1,9 +0,0 @@ -package unimported_test - -import ( - "testing" -) - -func TestSomething(t *testing.T) { - _ = unimported.TestExport //@unimported("TestExport", testexport) -} diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index 4c01a547f8e..08489c96e38 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -2471,7 +2471,7 @@ func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ide break FindType } } - f(token.FUNC, decl.Name, typ, false) + f(token.FUNC, decl.Name, typ, true) } } }