1
0
mirror of https://github.com/golang/go synced 2024-09-30 14:18:32 -06:00

internal/lsp/analysis: add simplify-range pass from "gofmt -s"

This is one of the simplifications that "gofmt -s" applies.
https://golang.org/cmd/gofmt/#hdr-The_simplify_command

A range of the form:
	for x, _ = range v {...}
will be simplified to:
	for x = range v {...}

A range of the form:
	for _ = range v {...}
will be simplified to:
	for range v {...}

Updates golang/go#37221

Change-Id: Ic6babbd0b8ab961ebb4d0d6df72df52d9acde6e7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/223661
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rohan Challa 2020-03-15 12:51:33 -04:00
parent e46a7b92c0
commit 94fe02cb5c
6 changed files with 191 additions and 0 deletions

View File

@ -321,6 +321,32 @@ errors is discouraged.
Default value: `true`.
### **simplifyrange**
check for range statement simplifications
A range of the form:
```go
for x, _ = range v {...}
```
will be simplified to:
```go
for x = range v {...}
```
A range of the form:
```go
for _ = range v {...}
```
will be simplified to:
```go
for range v {...}
```
This is one of the simplifications that "gofmt -s" applies.
Default value: `true`.
### **sortslice**
check the argument type of sort.Slice

View File

@ -0,0 +1,114 @@
// 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 simplifyrange defines an Analyzer that simplifies range statements.
// https://golang.org/cmd/gofmt/#hdr-The_simplify_command
// https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go
package simplifyrange
import (
"bytes"
"go/ast"
"go/printer"
"go/token"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for range statement simplifications
A range of the form:
for x, _ = range v {...}
will be simplified to:
for x = range v {...}
A range of the form:
for _ = range v {...}
will be simplified to:
for range v {...}
This is one of the simplifications that "gofmt -s" applies.`
var Analyzer = &analysis.Analyzer{
Name: "simplifyrange",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.RangeStmt)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
stmt := n.(*ast.RangeStmt)
end := newlineIndex(pass.Fset, stmt)
var old ast.Expr
// Range statements of the form: for i, _ := range x {}
if isBlank(stmt.Value) {
old = stmt.Value
defer func() {
stmt.Value = old
}()
stmt.Value = nil
}
// Range statements of the form: for _ := range x {}
if isBlank(stmt.Key) && stmt.Value == nil {
old = stmt.Key
defer func() {
stmt.Key = old
}()
stmt.Key = nil
}
// Return early if neither if condition is met.
if old == nil {
return
}
pass.Report(analysis.Diagnostic{
Pos: old.Pos(),
End: old.End(),
Message: "simplify range expression",
SuggestedFixes: suggestedFixes(pass.Fset, stmt, end),
})
})
return nil, nil
}
func suggestedFixes(fset *token.FileSet, rng *ast.RangeStmt, end token.Pos) []analysis.SuggestedFix {
var b bytes.Buffer
printer.Fprint(&b, fset, rng)
stmt := b.Bytes()
index := bytes.Index(stmt, []byte("\n"))
// If there is a new line character, then don't replace the body.
if index != -1 {
stmt = stmt[:index]
}
return []analysis.SuggestedFix{{
Message: "Remove empty value",
TextEdits: []analysis.TextEdit{{
Pos: rng.Pos(),
End: end,
NewText: stmt[:index],
}},
}}
}
func newlineIndex(fset *token.FileSet, rng *ast.RangeStmt) token.Pos {
var b bytes.Buffer
printer.Fprint(&b, fset, rng)
contents := b.Bytes()
index := bytes.Index(contents, []byte("\n"))
if index == -1 {
return rng.End()
}
return rng.Pos() + token.Pos(index)
}
func isBlank(x ast.Expr) bool {
ident, ok := x.(*ast.Ident)
return ok && ident.Name == "_"
}

View File

@ -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 simplifyrange_test
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/internal/lsp/analysis/simplifyrange"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.RunWithSuggestedFixes(t, testdata, simplifyrange.Analyzer, "a")
}

View File

@ -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 testdata
import "log"
func m() {
maps := make(map[string]string)
for k, _ := range maps { // want "simplify range expression"
log.Println(k)
}
for _ = range maps { // want "simplify range expression"
}
}

View File

@ -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 testdata
import "log"
func m() {
maps := make(map[string]string)
for k := range maps { // want "simplify range expression"
log.Println(k)
}
for range maps { // want "simplify range expression"
}
}

View File

@ -36,6 +36,7 @@ import (
"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/simplifyrange"
"golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/diff/myers"
@ -511,5 +512,6 @@ func defaultAnalyzers() map[string]Analyzer {
testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true},
// gofmt -s suite:
simplifyrange.Analyzer.Name: {Analyzer: simplifyrange.Analyzer, Enabled: true, HighConfidence: true},
}
}