Skip to content

Commit c64bb76

Browse files
committed
gopls/internal/lsp/source: make infertypeargs a convenience analyzer
Fixes golang/go#57930 Change-Id: I49fee92e92f29912a1f83f68dd3c1bf14f5dcfe2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/472181 gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Run-TryBot: Robert Findley <[email protected]>
1 parent 91b7a8e commit c64bb76

File tree

6 files changed

+136
-69
lines changed

6 files changed

+136
-69
lines changed

gopls/doc/analyzers.md

+16-16
Original file line numberDiff line numberDiff line change
@@ -215,22 +215,6 @@ name but different signatures. Example:
215215
The Read method in v has a different signature than the Read method in
216216
io.Reader, so this assertion cannot succeed.
217217

218-
**Enabled by default.**
219-
220-
## **infertypeargs**
221-
222-
check for unnecessary type arguments in call expressions
223-
224-
Explicit type arguments may be omitted from call expressions if they can be
225-
inferred from function arguments, or from other type arguments:
226-
227-
func f[T any](T) {}
228-
229-
func _() {
230-
f[string]("foo") // string could be inferred
231-
}
232-
233-
234218
**Enabled by default.**
235219

236220
## **loopclosure**
@@ -753,6 +737,22 @@ expensive to compute, callers should compute it separately, using the
753737
SuggestedFix function below.
754738

755739

740+
**Enabled by default.**
741+
742+
## **infertypeargs**
743+
744+
check for unnecessary type arguments in call expressions
745+
746+
Explicit type arguments may be omitted from call expressions if they can be
747+
inferred from function arguments, or from other type arguments:
748+
749+
func f[T any](T) {}
750+
751+
func _() {
752+
f[string]("foo") // string could be inferred
753+
}
754+
755+
756756
**Enabled by default.**
757757

758758
## **stubmethods**

gopls/internal/lsp/regtest/marker.go

+66-40
Original file line numberDiff line numberDiff line change
@@ -1335,41 +1335,51 @@ func rename(env *Env, loc protocol.Location, newName string) (map[string][]byte,
13351335
return nil, err
13361336
}
13371337

1338-
return applyDocumentChanges(env, editMap.DocumentChanges)
1338+
fileChanges := make(map[string][]byte)
1339+
if err := applyDocumentChanges(env, editMap.DocumentChanges, fileChanges); err != nil {
1340+
return nil, fmt.Errorf("applying document changes: %v", err)
1341+
}
1342+
return fileChanges, nil
13391343
}
13401344

1341-
// applyDocumentChanges returns the effect of applying the document
1342-
// changes to the contents of the Editor buffers. The actual editor
1343-
// buffers are unchanged.
1344-
func applyDocumentChanges(env *Env, changes []protocol.DocumentChanges) (map[string][]byte, error) {
1345-
result := make(map[string][]byte)
1345+
// applyDocumentChanges applies the given document changes to the editor buffer
1346+
// content, recording the resulting contents in the fileChanges map. It is an
1347+
// error for a change to an edit a file that is already present in the
1348+
// fileChanges map.
1349+
func applyDocumentChanges(env *Env, changes []protocol.DocumentChanges, fileChanges map[string][]byte) error {
1350+
getMapper := func(path string) (*protocol.Mapper, error) {
1351+
if _, ok := fileChanges[path]; ok {
1352+
return nil, fmt.Errorf("internal error: %s is already edited", path)
1353+
}
1354+
return env.Editor.Mapper(path)
1355+
}
1356+
13461357
for _, change := range changes {
13471358
if change.RenameFile != nil {
13481359
// rename
13491360
oldFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.OldURI)
1350-
newFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.NewURI)
1351-
mapper, err := env.Editor.Mapper(oldFile)
1361+
mapper, err := getMapper(oldFile)
13521362
if err != nil {
1353-
return nil, err
1363+
return err
13541364
}
1355-
result[newFile] = mapper.Content
1356-
1365+
newFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.NewURI)
1366+
fileChanges[newFile] = mapper.Content
13571367
} else {
13581368
// edit
13591369
filename := env.Sandbox.Workdir.URIToPath(change.TextDocumentEdit.TextDocument.URI)
1360-
mapper, err := env.Editor.Mapper(filename)
1370+
mapper, err := getMapper(filename)
13611371
if err != nil {
1362-
return nil, err
1372+
return err
13631373
}
13641374
patched, _, err := source.ApplyProtocolEdits(mapper, change.TextDocumentEdit.Edits)
13651375
if err != nil {
1366-
return nil, err
1376+
return err
13671377
}
1368-
result[filename] = patched
1378+
fileChanges[filename] = patched
13691379
}
13701380
}
13711381

1372-
return result, nil
1382+
return nil
13731383
}
13741384

13751385
func codeActionMarker(mark marker, actionKind string, start, end protocol.Location, golden *Golden) {
@@ -1453,40 +1463,56 @@ func codeAction(env *Env, uri protocol.DocumentURI, rng protocol.Range, actionKi
14531463
}
14541464
action := candidates[0]
14551465

1466+
// Apply the codeAction.
1467+
//
1468+
// Spec:
1469+
// "If a code action provides an edit and a command, first the edit is
1470+
// executed and then the command."
1471+
fileChanges := make(map[string][]byte)
14561472
// An action may specify an edit and/or a command, to be
14571473
// applied in that order. But since applyDocumentChanges(env,
14581474
// action.Edit.DocumentChanges) doesn't compose, for now we
14591475
// assert that all commands used in the @suggestedfix tests
14601476
// return only a command.
1461-
if action.Edit != nil && action.Edit.DocumentChanges != nil {
1462-
env.T.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.DocumentChanges", action.Kind, action.Title)
1463-
}
1464-
if action.Command == nil {
1465-
return nil, fmt.Errorf("missing CodeAction{Kind=%s, Title=%q}.Command", action.Kind, action.Title)
1477+
if action.Edit != nil {
1478+
if action.Edit.Changes != nil {
1479+
env.T.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes", action.Kind, action.Title)
1480+
}
1481+
if action.Edit.DocumentChanges != nil {
1482+
if err := applyDocumentChanges(env, action.Edit.DocumentChanges, fileChanges); err != nil {
1483+
return nil, fmt.Errorf("applying document changes: %v", err)
1484+
}
1485+
}
14661486
}
14671487

1468-
// This is a typical CodeAction command:
1469-
//
1470-
// Title: "Implement error"
1471-
// Command: gopls.apply_fix
1472-
// Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}]
1473-
//
1474-
// The client makes an ExecuteCommand RPC to the server,
1475-
// which dispatches it to the ApplyFix handler.
1476-
// ApplyFix dispatches to the "stub_methods" suggestedfix hook (the meat).
1477-
// The server then makes an ApplyEdit RPC to the client,
1478-
// whose Awaiter hook gathers the edits instead of applying them.
1479-
1480-
_ = env.Awaiter.takeDocumentChanges() // reset (assuming Env is confined to this thread)
1488+
if action.Command != nil {
1489+
// This is a typical CodeAction command:
1490+
//
1491+
// Title: "Implement error"
1492+
// Command: gopls.apply_fix
1493+
// Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}]
1494+
//
1495+
// The client makes an ExecuteCommand RPC to the server,
1496+
// which dispatches it to the ApplyFix handler.
1497+
// ApplyFix dispatches to the "stub_methods" suggestedfix hook (the meat).
1498+
// The server then makes an ApplyEdit RPC to the client,
1499+
// whose Awaiter hook gathers the edits instead of applying them.
1500+
1501+
_ = env.Awaiter.takeDocumentChanges() // reset (assuming Env is confined to this thread)
1502+
1503+
if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
1504+
Command: action.Command.Command,
1505+
Arguments: action.Command.Arguments,
1506+
}); err != nil {
1507+
env.T.Fatalf("error converting command %q to edits: %v", action.Command.Command, err)
1508+
}
14811509

1482-
if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
1483-
Command: action.Command.Command,
1484-
Arguments: action.Command.Arguments,
1485-
}); err != nil {
1486-
env.T.Fatalf("error converting command %q to edits: %v", action.Command.Command, err)
1510+
if err := applyDocumentChanges(env, env.Awaiter.takeDocumentChanges(), fileChanges); err != nil {
1511+
return nil, fmt.Errorf("applying document changes from command: %v", err)
1512+
}
14871513
}
14881514

1489-
return applyDocumentChanges(env, env.Awaiter.takeDocumentChanges())
1515+
return fileChanges, nil
14901516
}
14911517

14921518
// TODO(adonovan): suggestedfixerr

gopls/internal/lsp/source/api_json.go

+10-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gopls/internal/lsp/source/options.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,11 @@ func convenienceAnalyzers() map[string]*Analyzer {
14021402
Fix: StubMethods,
14031403
Enabled: true,
14041404
},
1405+
infertypeargs.Analyzer.Name: {
1406+
Analyzer: infertypeargs.Analyzer,
1407+
Enabled: true,
1408+
ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite},
1409+
},
14051410
}
14061411
}
14071412

@@ -1445,7 +1450,6 @@ func defaultAnalyzers() map[string]*Analyzer {
14451450
unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false},
14461451
unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false},
14471452
useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false},
1448-
infertypeargs.Analyzer.Name: {Analyzer: infertypeargs.Analyzer, Enabled: true},
14491453
embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true},
14501454
timeformat.Analyzer.Name: {Analyzer: timeformat.Analyzer, Enabled: true},
14511455

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
This test verifies the infertypeargs refactoring.
2+
3+
-- flags --
4+
-min_go=go1.18
5+
6+
-- go.mod --
7+
module mod.test/infertypeargs
8+
9+
go 1.18
10+
11+
-- p.go --
12+
package infertypeargs
13+
14+
func app[S interface{ ~[]E }, E interface{}](s S, e E) S {
15+
return append(s, e)
16+
}
17+
18+
func _() {
19+
_ = app[[]int]
20+
_ = app[[]int, int]
21+
_ = app[[]int]([]int{}, 0) //@codeaction("refactor.rewrite", "app", ")", infer)
22+
_ = app([]int{}, 0)
23+
}
24+
25+
-- @infer/p.go --
26+
package infertypeargs
27+
28+
func app[S interface{ ~[]E }, E interface{}](s S, e E) S {
29+
return append(s, e)
30+
}
31+
32+
func _() {
33+
_ = app[[]int]
34+
_ = app[[]int, int]
35+
_ = app([]int{}, 0) //@codeaction("refactor.rewrite", "app", ")", infer)
36+
_ = app([]int{}, 0)
37+
}
38+

gopls/internal/regtest/marker/testdata/hover/generics.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ func app[S interface{ ~[]E }, E interface{}](s S, e E) S {
3939
func _() {
4040
_ = app[[]int] //@hover("app", "app", appint)
4141
_ = app[[]int, int] //@hover("app", "app", appint)
42-
// TODO(rfindley): eliminate this diagnostic.
43-
_ = app[[]int]([]int{}, 0) //@hover("app", "app", appint),diag("[[]int]", re"unnecessary type arguments")
42+
_ = app[[]int]([]int{}, 0) //@hover("app", "app", appint)
4443
_ = app([]int{}, 0) //@hover("app", "app", appint)
4544
}
4645

0 commit comments

Comments
 (0)