internal/lsp/analysis: add quickfix for "no new vars on left side"
This change adds a quick fix for type errors of the type "no new vars on left side of :=". It will replace the ":=" with an "=".
Updates golang/go#34644
Change-Id: I91af8eb82956104229c3b4f3d0fce60fdfdbb5ea
Reviewed-on: https://go-review.googlesource.com/c/tools/+/225477
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index c2d8498..47a65cf 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -321,6 +321,24 @@
Default value: `true`.
+### **nonewvars**
+
+suggested fixes for "no new vars on left side of :="
+
+This checker provides suggested fixes for type errors of the
+type "no new vars on left side of :=". For example:
+```go
+z := 1
+z := 2
+```
+will turn into
+```go
+z := 1
+z = 2
+```
+
+Default value: `true`.
+
### **noresultvalues**
suggested fixes for "no result values expected"
diff --git a/internal/analysisinternal/analysis.go b/internal/analysisinternal/analysis.go
index 1599702..39f4bb6 100644
--- a/internal/analysisinternal/analysis.go
+++ b/internal/analysisinternal/analysis.go
@@ -29,6 +29,7 @@
type TypeErrorPass string
const (
+ NoNewVars TypeErrorPass = "nonewvars"
NoResultValues TypeErrorPass = "noresultvalues"
UndeclaredName TypeErrorPass = "undeclaredname"
)
diff --git a/internal/lsp/analysis/nonewvars/nonewvars.go b/internal/lsp/analysis/nonewvars/nonewvars.go
new file mode 100644
index 0000000..31dcd25
--- /dev/null
+++ b/internal/lsp/analysis/nonewvars/nonewvars.go
@@ -0,0 +1,91 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package nonewvars defines an Analyzer that applies suggested fixes
+// to errors of the type "no new variables on left side of :=".
+package nonewvars
+
+import (
+ "bytes"
+ "go/ast"
+ "go/format"
+ "go/token"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/analysisinternal"
+)
+
+const Doc = `suggested fixes for "no new vars on left side of :="
+
+This checker provides suggested fixes for type errors of the
+type "no new vars on left side of :=". For example:
+ z := 1
+ z := 2
+will turn into
+ z := 1
+ z = 2
+`
+
+var Analyzer = &analysis.Analyzer{
+ Name: string(analysisinternal.NoNewVars),
+ Doc: Doc,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ Run: run,
+ RunDespiteErrors: true,
+}
+
+const noNewVarsMsg = "no new variables on left side of :="
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+ errors := analysisinternal.GetTypeErrors(pass)
+
+ nodeFilter := []ast.Node{(*ast.AssignStmt)(nil)}
+ inspect.Preorder(nodeFilter, func(n ast.Node) {
+ assignStmt, _ := n.(*ast.AssignStmt)
+ // We only care about ":=".
+ if assignStmt.Tok != token.DEFINE {
+ return
+ }
+
+ var file *ast.File
+ for _, f := range pass.Files {
+ if f.Pos() <= assignStmt.Pos() && assignStmt.Pos() < f.End() {
+ file = f
+ break
+ }
+ }
+ if file == nil {
+ return
+ }
+
+ for _, err := range errors {
+ if err.Msg != noNewVarsMsg {
+ continue
+ }
+ if assignStmt.Pos() > err.Pos || err.Pos >= assignStmt.End() {
+ continue
+ }
+ var buf bytes.Buffer
+ if err := format.Node(&buf, pass.Fset, file); err != nil {
+ continue
+ }
+ pass.Report(analysis.Diagnostic{
+ Pos: err.Pos,
+ End: analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos),
+ Message: err.Msg,
+ SuggestedFixes: []analysis.SuggestedFix{{
+ Message: "Change ':=' to '='",
+ TextEdits: []analysis.TextEdit{{
+ Pos: err.Pos,
+ End: err.Pos + 1,
+ }},
+ }},
+ })
+ }
+ })
+ return nil, nil
+}
diff --git a/internal/lsp/analysis/nonewvars/nonewvars_test.go b/internal/lsp/analysis/nonewvars/nonewvars_test.go
new file mode 100644
index 0000000..3983bc5
--- /dev/null
+++ b/internal/lsp/analysis/nonewvars/nonewvars_test.go
@@ -0,0 +1,17 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package nonewvars_test
+
+import (
+ "testing"
+
+ "golang.org/x/tools/go/analysis/analysistest"
+ "golang.org/x/tools/internal/lsp/analysis/nonewvars"
+)
+
+func Test(t *testing.T) {
+ testdata := analysistest.TestData()
+ analysistest.RunWithSuggestedFixes(t, testdata, nonewvars.Analyzer, "a")
+}
diff --git a/internal/lsp/analysis/nonewvars/testdata/src/a/a.go b/internal/lsp/analysis/nonewvars/testdata/src/a/a.go
new file mode 100644
index 0000000..97d8fcd
--- /dev/null
+++ b/internal/lsp/analysis/nonewvars/testdata/src/a/a.go
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package nonewvars
+
+import "log"
+
+func x() {
+ z := 1
+ z := 2 // want "no new variables on left side of :="
+
+ _, z := 3, 100 // want "no new variables on left side of :="
+
+ log.Println(z)
+}
diff --git a/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden b/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden
new file mode 100644
index 0000000..17197e5
--- /dev/null
+++ b/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package nonewvars
+
+import "log"
+
+func x() {
+ z := 1
+ z = 2 // want "no new variables on left side of :="
+
+ _, z = 3, 100 // want "no new variables on left side of :="
+
+ log.Println(z)
+}
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 57288e1..568a20c 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -36,6 +36,7 @@
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
+ "golang.org/x/tools/internal/lsp/analysis/nonewvars"
"golang.org/x/tools/internal/lsp/analysis/noresultvalues"
"golang.org/x/tools/internal/lsp/analysis/simplifycompositelit"
"golang.org/x/tools/internal/lsp/analysis/simplifyrange"
@@ -488,6 +489,7 @@
func typeErrorAnalyzers() map[string]Analyzer {
return map[string]Analyzer{
+ nonewvars.Analyzer.Name: {Analyzer: nonewvars.Analyzer, Enabled: true},
noresultvalues.Analyzer.Name: {Analyzer: noresultvalues.Analyzer, Enabled: true},
undeclaredname.Analyzer.Name: {Analyzer: undeclaredname.Analyzer, Enabled: true},
}