Skip to content

Commit efd951d

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/analysis/stubmethods: merge into CodeAction
This change removes the stubmethods analyzer, a roundabout implementation, and instead computes it directly from the protocol.QuickFix code action producer. This is simpler, more efficient, and has noticeably lower latency (being type-based not analysis based). We should consider this for the other type-error analyzers. However, the documentation, formerly generated to analyzers.md, is now maintained in the Quick Fixes section of diagnostics.md. More importantly, the `analyzers[stubmethods]` config setting no longer exists. Also: - addEditAction et al: pass Diagnostics as a parameter instead of returning a pointer to a mutable CodeAction. - protocol.Intersect: clarify how its treatment differs from mathematical convention in its handling of empty ranges, and fix a bug where by [1,2) and [2,3) were considered to intersect. (Only abutting empty ranges intersect by our definition.) - Upgrade a marker test from @diag to @suggestedfixerr, now that the latter exists. Change-Id: I010b2d4730596cac6f376c631839bfda159bf433 Reviewed-on: https://go-review.googlesource.com/c/tools/+/617035 Auto-Submit: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent d0d0d9e commit efd951d

File tree

18 files changed

+158
-249
lines changed

18 files changed

+158
-249
lines changed

gopls/doc/analyzers.md

-36
Original file line numberDiff line numberDiff line change
@@ -796,42 +796,6 @@ Default: on.
796796

797797
Package documentation: [structtag](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/structtag)
798798

799-
<a id='stubmethods'></a>
800-
## `stubmethods`: detect missing methods and fix with stub implementations
801-
802-
803-
This analyzer detects type-checking errors due to missing methods
804-
in assignments from concrete types to interface types, and offers
805-
a suggested fix that will create a set of stub methods so that
806-
the concrete type satisfies the interface.
807-
808-
For example, this function will not compile because the value
809-
NegativeErr{} does not implement the "error" interface:
810-
811-
func sqrt(x float64) (float64, error) {
812-
if x < 0 {
813-
return 0, NegativeErr{} // error: missing method
814-
}
815-
...
816-
}
817-
818-
type NegativeErr struct{}
819-
820-
This analyzer will suggest a fix to declare this method:
821-
822-
// Error implements error.Error.
823-
func (NegativeErr) Error() string {
824-
panic("unimplemented")
825-
}
826-
827-
(At least, it appears to behave that way, but technically it
828-
doesn't use the SuggestedFix mechanism and the stub is created by
829-
logic in gopls's golang.stub function.)
830-
831-
Default: on.
832-
833-
Package documentation: [stubmethods](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/stubmethods)
834-
835799
<a id='testinggoroutine'></a>
836800
## `testinggoroutine`: report calls to (*testing.T).Fatal from goroutines started by a test
837801

gopls/doc/features/diagnostics.md

+46
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,52 @@ Client support:
119119
- **Vim + coc.nvim**: ??
120120
- **CLI**: `gopls check file.go`
121121

122+
123+
<!-- Below we list any quick fixes (by their internal fix name)
124+
that aren't analyzers. -->
125+
126+
### `stubMethods`: Declare missing methods of type
127+
128+
When a value of a concrete type is assigned to a variable of an
129+
interface type, but the concrete type does not possess all the
130+
necessary methods, the type checker will report a "missing method"
131+
error.
132+
133+
In this situation, gopls offers a quick fix to add stub declarations
134+
of all the missing methods to the concrete type so that it implements
135+
the interface.
136+
137+
For example, this function will not compile because the value
138+
`NegativeErr{}` does not implement the "error" interface:
139+
140+
```go
141+
func sqrt(x float64) (float64, error) {
142+
if x < 0 {
143+
return 0, NegativeErr{} // error: missing method
144+
}
145+
...
146+
}
147+
148+
type NegativeErr struct{}
149+
```
150+
151+
Gopls will offer a quick fix to declare this method:
152+
153+
```go
154+
155+
// Error implements error.Error.
156+
func (NegativeErr) Error() string {
157+
panic("unimplemented")
158+
}
159+
```
160+
161+
Beware that the new declarations appear alongside the concrete type,
162+
which may be in a different file or even package from the cursor
163+
position.
164+
(Perhaps gopls should send a `showDocument` request to navigate the
165+
client there, or a progress notification indicating that something
166+
happened.)
167+
122168
<!--
123169
124170
dorky details and deletia:

gopls/doc/features/transformation.md

-3
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,6 @@ The `refactor.extract` family of code actions all return commands that
275275
replace the selected expression or statements with a reference to a
276276
newly created declaration that contains the selected code:
277277

278-
<!-- See TODO comments in settings/codeactionkind.go about splitting
279-
up "refactor.extract" into finer grained categories. -->
280-
281278
- **`refactor.extract.function`** replaces one or more complete statements by a
282279
call to a new function named `newFunction` whose body contains the
283280
statements. The selection must enclose fewer statements than the

gopls/internal/analysis/stubmethods/doc.go

-38
This file was deleted.

gopls/internal/analysis/stubmethods/stubmethods.go

+3-88
Original file line numberDiff line numberDiff line change
@@ -4,101 +4,16 @@
44

55
package stubmethods
66

7+
// TODO(adonovan): rename package to golang/stubmethods or move
8+
// functions to golang/stub.go.
9+
710
import (
8-
"bytes"
9-
_ "embed"
1011
"fmt"
1112
"go/ast"
12-
"go/format"
1313
"go/token"
1414
"go/types"
15-
"strings"
16-
17-
"golang.org/x/tools/go/analysis"
18-
"golang.org/x/tools/go/ast/astutil"
19-
"golang.org/x/tools/gopls/internal/util/typesutil"
20-
"golang.org/x/tools/internal/analysisinternal"
21-
"golang.org/x/tools/internal/typesinternal"
2215
)
2316

24-
//go:embed doc.go
25-
var doc string
26-
27-
var Analyzer = &analysis.Analyzer{
28-
Name: "stubmethods",
29-
Doc: analysisinternal.MustExtractDoc(doc, "stubmethods"),
30-
Run: run,
31-
RunDespiteErrors: true,
32-
URL: "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/stubmethods",
33-
}
34-
35-
// TODO(rfindley): remove this thin wrapper around the stubmethods refactoring,
36-
// and eliminate the stubmethods analyzer.
37-
//
38-
// Previous iterations used the analysis framework for computing refactorings,
39-
// which proved inefficient.
40-
func run(pass *analysis.Pass) (interface{}, error) {
41-
for _, err := range pass.TypeErrors {
42-
var file *ast.File
43-
for _, f := range pass.Files {
44-
if f.Pos() <= err.Pos && err.Pos < f.End() {
45-
file = f
46-
break
47-
}
48-
}
49-
// Get the end position of the error.
50-
_, _, end, ok := typesinternal.ReadGo116ErrorData(err)
51-
if !ok {
52-
var buf bytes.Buffer
53-
if err := format.Node(&buf, pass.Fset, file); err != nil {
54-
continue
55-
}
56-
end = analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos)
57-
}
58-
if diag, ok := DiagnosticForError(pass.Fset, file, err.Pos, end, err.Msg, pass.TypesInfo); ok {
59-
pass.Report(diag)
60-
}
61-
}
62-
63-
return nil, nil
64-
}
65-
66-
// MatchesMessage reports whether msg matches the error message sought after by
67-
// the stubmethods fix.
68-
func MatchesMessage(msg string) bool {
69-
return strings.Contains(msg, "missing method") || strings.HasPrefix(msg, "cannot convert") || strings.Contains(msg, "not implement")
70-
}
71-
72-
// DiagnosticForError computes a diagnostic suggesting to implement an
73-
// interface to fix the type checking error defined by (start, end, msg).
74-
//
75-
// If no such fix is possible, the second result is false.
76-
func DiagnosticForError(fset *token.FileSet, file *ast.File, start, end token.Pos, msg string, info *types.Info) (analysis.Diagnostic, bool) {
77-
if !MatchesMessage(msg) {
78-
return analysis.Diagnostic{}, false
79-
}
80-
81-
path, _ := astutil.PathEnclosingInterval(file, start, end)
82-
si := GetStubInfo(fset, info, path, start)
83-
if si == nil {
84-
return analysis.Diagnostic{}, false
85-
}
86-
qf := typesutil.FileQualifier(file, si.Concrete.Obj().Pkg(), info)
87-
iface := types.TypeString(si.Interface.Type(), qf)
88-
return analysis.Diagnostic{
89-
Pos: start,
90-
End: end,
91-
Message: msg,
92-
Category: FixCategory,
93-
SuggestedFixes: []analysis.SuggestedFix{{
94-
Message: fmt.Sprintf("Declare missing methods of %s", iface),
95-
// No TextEdits => computed later by gopls.
96-
}},
97-
}, true
98-
}
99-
100-
const FixCategory = "stubmethods" // recognized by gopls ApplyFix
101-
10217
// StubInfo represents a concrete type
10318
// that wants to stub out an interface type
10419
type StubInfo struct {

gopls/internal/analysis/stubmethods/stubmethods_test.go

-17
This file was deleted.

gopls/internal/analysis/stubmethods/testdata/src/typeparams/implement.go

-15
This file was deleted.

gopls/internal/doc/api.json

-11
Original file line numberDiff line numberDiff line change
@@ -567,11 +567,6 @@
567567
"Doc": "check that struct field tags conform to reflect.StructTag.Get\n\nAlso report certain struct tags (json, xml) used with unexported fields.",
568568
"Default": "true"
569569
},
570-
{
571-
"Name": "\"stubmethods\"",
572-
"Doc": "detect missing methods and fix with stub implementations\n\nThis analyzer detects type-checking errors due to missing methods\nin assignments from concrete types to interface types, and offers\na suggested fix that will create a set of stub methods so that\nthe concrete type satisfies the interface.\n\nFor example, this function will not compile because the value\nNegativeErr{} does not implement the \"error\" interface:\n\n\tfunc sqrt(x float64) (float64, error) {\n\t\tif x \u003c 0 {\n\t\t\treturn 0, NegativeErr{} // error: missing method\n\t\t}\n\t\t...\n\t}\n\n\ttype NegativeErr struct{}\n\nThis analyzer will suggest a fix to declare this method:\n\n\t// Error implements error.Error.\n\tfunc (NegativeErr) Error() string {\n\t\tpanic(\"unimplemented\")\n\t}\n\n(At least, it appears to behave that way, but technically it\ndoesn't use the SuggestedFix mechanism and the stub is created by\nlogic in gopls's golang.stub function.)",
573-
"Default": "true"
574-
},
575570
{
576571
"Name": "\"testinggoroutine\"",
577572
"Doc": "report calls to (*testing.T).Fatal from goroutines started by a test\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\n\tfunc TestFoo(t *testing.T) {\n\t go func() {\n\t t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n\t }()\n\t}",
@@ -1238,12 +1233,6 @@
12381233
"URL": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/structtag",
12391234
"Default": true
12401235
},
1241-
{
1242-
"Name": "stubmethods",
1243-
"Doc": "detect missing methods and fix with stub implementations\n\nThis analyzer detects type-checking errors due to missing methods\nin assignments from concrete types to interface types, and offers\na suggested fix that will create a set of stub methods so that\nthe concrete type satisfies the interface.\n\nFor example, this function will not compile because the value\nNegativeErr{} does not implement the \"error\" interface:\n\n\tfunc sqrt(x float64) (float64, error) {\n\t\tif x \u003c 0 {\n\t\t\treturn 0, NegativeErr{} // error: missing method\n\t\t}\n\t\t...\n\t}\n\n\ttype NegativeErr struct{}\n\nThis analyzer will suggest a fix to declare this method:\n\n\t// Error implements error.Error.\n\tfunc (NegativeErr) Error() string {\n\t\tpanic(\"unimplemented\")\n\t}\n\n(At least, it appears to behave that way, but technically it\ndoesn't use the SuggestedFix mechanism and the stub is created by\nlogic in gopls's golang.stub function.)",
1244-
"URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/stubmethods",
1245-
"Default": true
1246-
},
12471236
{
12481237
"Name": "testinggoroutine",
12491238
"Doc": "report calls to (*testing.T).Fatal from goroutines started by a test\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\n\tfunc TestFoo(t *testing.T) {\n\t go func() {\n\t t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n\t }()\n\t}",

gopls/internal/golang/change_quote.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,5 @@ func convertStringLiteral(req *codeActionsRequest) {
6868
bug.Reportf("failed to convert diff.Edit to protocol.TextEdit:%v", err)
6969
return
7070
}
71-
req.addEditAction(title, protocol.DocumentChangeEdit(req.fh, textedits))
71+
req.addEditAction(title, nil, protocol.DocumentChangeEdit(req.fh, textedits))
7272
}

0 commit comments

Comments
 (0)