1
0
mirror of https://github.com/golang/go synced 2024-09-30 12:28:35 -06:00

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

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

A slice expression of the form:
	s[a:len(s)]
will be simplified to:
	s[a:]

Updates golang/go#37221

Change-Id: Ibd4dedaadc9b129d5d39524f0c1ccc8a95bf7e0d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/223659
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Rohan Challa 2020-03-15 13:01:52 -04:00
parent e428a8eca3
commit 5d86d385bf
6 changed files with 272 additions and 0 deletions

View File

@ -364,6 +364,23 @@ This is one of the simplifications that "gofmt -s" applies.
Default value: `true`.
### **simplifyslice**
check for slice simplifications
A slice expression of the form:
```go
s[a:len(s)]
```
will be simplified to:
```go
s[a:]
```
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,96 @@
// 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 simplifyslice defines an Analyzer that simplifies slice statements.
// https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go
// https://golang.org/cmd/gofmt/#hdr-The_simplify_command
package simplifyslice
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"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 slice simplifications
A slice expression of the form:
s[a:len(s)]
will be simplified to:
s[a:]
This is one of the simplifications that "gofmt -s" applies.`
var Analyzer = &analysis.Analyzer{
Name: "simplifyslice",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
// Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
// but we leave them as is since sometimes we want to be very explicit
// about the lower bound.
// An example where the 0 helps:
// x, y, z := b[0:2], b[2:4], b[4:6]
// An example where it does not:
// x, y := b[:n], b[n:]
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.SliceExpr)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
expr := n.(*ast.SliceExpr)
// - 3-index slices always require the 2nd and 3rd index
if expr.Max != nil {
return
}
s, ok := expr.X.(*ast.Ident)
// the array/slice object is a single, resolved identifier
if !ok || s.Obj == nil {
return
}
call, ok := expr.High.(*ast.CallExpr)
// the high expression is a function call with a single argument
if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() {
return
}
fun, ok := call.Fun.(*ast.Ident)
// the function called is "len" and it is not locally defined; and
// because we don't have dot imports, it must be the predefined len()
if !ok || fun.Name != "len" || fun.Obj != nil {
return
}
arg, ok := call.Args[0].(*ast.Ident)
// the len argument is the array/slice object
if !ok || arg.Obj != s.Obj {
return
}
old := expr.High
var b bytes.Buffer
printer.Fprint(&b, pass.Fset, old)
pass.Report(analysis.Diagnostic{
Pos: old.Pos(),
End: old.End(),
Message: fmt.Sprintf("unneeded: %s", b.String()),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("Remove '%s'", b.String()),
TextEdits: []analysis.TextEdit{{
Pos: old.Pos(),
End: old.End(),
NewText: []byte{},
}},
}},
})
expr.High = old
})
return nil, nil
}

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

View File

@ -0,0 +1,70 @@
// 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
var (
a [10]byte
b [20]float32
s []int
t struct {
s []byte
}
_ = a[0:]
_ = a[1:10]
_ = a[2:len(a)] // want "unneeded: len\\(a\\)"
_ = a[3:(len(a))]
_ = a[len(a)-1 : len(a)] // want "unneeded: len\\(a\\)"
_ = a[2:len(a):len(a)]
_ = a[:]
_ = a[:10]
_ = a[:len(a)] // want "unneeded: len\\(a\\)"
_ = a[:(len(a))]
_ = a[:len(a)-1]
_ = a[:len(a):len(a)]
_ = s[0:]
_ = s[1:10]
_ = s[2:len(s)] // want "unneeded: len\\(s\\)"
_ = s[3:(len(s))]
_ = s[len(a) : len(s)-1]
_ = s[0:len(b)]
_ = s[2:len(s):len(s)]
_ = s[:]
_ = s[:10]
_ = s[:len(s)] // want "unneeded: len\\(s\\)"
_ = s[:(len(s))]
_ = s[:len(s)-1]
_ = s[:len(b)]
_ = s[:len(s):len(s)]
_ = t.s[0:]
_ = t.s[1:10]
_ = t.s[2:len(t.s)]
_ = t.s[3:(len(t.s))]
_ = t.s[len(a) : len(t.s)-1]
_ = t.s[0:len(b)]
_ = t.s[2:len(t.s):len(t.s)]
_ = t.s[:]
_ = t.s[:10]
_ = t.s[:len(t.s)]
_ = t.s[:(len(t.s))]
_ = t.s[:len(t.s)-1]
_ = t.s[:len(b)]
_ = t.s[:len(t.s):len(t.s)]
)
func _() {
s := s[0:len(s)] // want "unneeded: len\\(s\\)"
_ = s
}
func m() {
maps := []int{}
_ = maps[1:len(maps)] // want "unneeded: len\\(maps\\)"
}

View File

@ -0,0 +1,70 @@
// 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
var (
a [10]byte
b [20]float32
s []int
t struct {
s []byte
}
_ = a[0:]
_ = a[1:10]
_ = a[2:] // want "unneeded: len\\(a\\)"
_ = a[3:(len(a))]
_ = a[len(a)-1:] // want "unneeded: len\\(a\\)"
_ = a[2:len(a):len(a)]
_ = a[:]
_ = a[:10]
_ = a[:] // want "unneeded: len\\(a\\)"
_ = a[:(len(a))]
_ = a[:len(a)-1]
_ = a[:len(a):len(a)]
_ = s[0:]
_ = s[1:10]
_ = s[2:] // want "unneeded: len\\(s\\)"
_ = s[3:(len(s))]
_ = s[len(a) : len(s)-1]
_ = s[0:len(b)]
_ = s[2:len(s):len(s)]
_ = s[:]
_ = s[:10]
_ = s[:] // want "unneeded: len\\(s\\)"
_ = s[:(len(s))]
_ = s[:len(s)-1]
_ = s[:len(b)]
_ = s[:len(s):len(s)]
_ = t.s[0:]
_ = t.s[1:10]
_ = t.s[2:len(t.s)]
_ = t.s[3:(len(t.s))]
_ = t.s[len(a) : len(t.s)-1]
_ = t.s[0:len(b)]
_ = t.s[2:len(t.s):len(t.s)]
_ = t.s[:]
_ = t.s[:10]
_ = t.s[:len(t.s)]
_ = t.s[:(len(t.s))]
_ = t.s[:len(t.s)-1]
_ = t.s[:len(b)]
_ = t.s[:len(t.s):len(t.s)]
)
func _() {
s := s[0:] // want "unneeded: len\\(s\\)"
_ = s
}
func m() {
maps := []int{}
_ = maps[1:] // want "unneeded: len\\(maps\\)"
}

View File

@ -38,6 +38,7 @@ import (
"golang.org/x/tools/go/analysis/passes/unusedresult"
"golang.org/x/tools/internal/lsp/analysis/simplifycompositelit"
"golang.org/x/tools/internal/lsp/analysis/simplifyrange"
"golang.org/x/tools/internal/lsp/analysis/simplifyslice"
"golang.org/x/tools/internal/lsp/analysis/unusedparams"
"golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/diff"
@ -517,5 +518,6 @@ func defaultAnalyzers() map[string]Analyzer {
// gofmt -s suite:
simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, Enabled: true, HighConfidence: true},
simplifyrange.Analyzer.Name: {Analyzer: simplifyrange.Analyzer, Enabled: true, HighConfidence: true},
simplifyslice.Analyzer.Name: {Analyzer: simplifyslice.Analyzer, Enabled: true, HighConfidence: true},
}
}