mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Unnecessary Slice-to-Slice Conversion analyzer (#7321)
* analyzer with tests * fix bazel file * modify analyzer to fix build issues * add analyzer to tool chain * remove arrays from inspections * fix redundant [:] operator * Merge branch 'master' into use-slice-directly * Merge branch 'master' into use-slice-directly * fix another inspection * add package-level comment
This commit is contained in:
28
tools/analyzers/slicedirect/BUILD.bazel
Normal file
28
tools/analyzers/slicedirect/BUILD.bazel
Normal file
@@ -0,0 +1,28 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_tool_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["analyzer.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/slicedirect",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@org_golang_x_tools//go/analysis:go_default_library",
|
||||
"@org_golang_x_tools//go/analysis/passes/inspect:go_default_library",
|
||||
"@org_golang_x_tools//go/ast/inspector:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_tool_library(
|
||||
name = "go_tool_library",
|
||||
srcs = ["analyzer.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/slicedirect",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@org_golang_x_tools//go/analysis:go_tool_library",
|
||||
"@org_golang_x_tools//go/analysis/passes/inspect:go_tool_library",
|
||||
"@org_golang_x_tools//go/ast/inspector:go_tool_library",
|
||||
],
|
||||
)
|
||||
|
||||
# gazelle:exclude analyzer_test.go
|
||||
63
tools/analyzers/slicedirect/analyzer.go
Normal file
63
tools/analyzers/slicedirect/analyzer.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Package slicedirect implements a static analyzer to ensure that code does not contain
|
||||
// applications of [:] on expressions which are already slices.
|
||||
package slicedirect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// Doc explaining the tool.
|
||||
const Doc = "Tool to detect unnecessary slice-to-slice conversion by applying [:] to a slice expression."
|
||||
|
||||
// Analyzer runs static analysis.
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "slicedirect",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
if !ok {
|
||||
return nil, errors.New("analyzer is not type *inspector.Inspector")
|
||||
}
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.SliceExpr)(nil),
|
||||
}
|
||||
|
||||
typeInfo := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
|
||||
|
||||
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||
sliceExpr, ok := node.(*ast.SliceExpr)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if err := types.CheckExpr(pass.Fset, pass.Pkg, sliceExpr.X.Pos(), sliceExpr.X, &typeInfo); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sliceExpr.Low != nil || sliceExpr.High != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := typeInfo.Types[sliceExpr.X].Type.(type) {
|
||||
case *types.Slice:
|
||||
pass.Reportf(sliceExpr.Pos(), "Expression is already a slice.")
|
||||
case *types.Basic:
|
||||
if x.String() == "string" {
|
||||
pass.Reportf(sliceExpr.Pos(), "Expression is already a slice.")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
11
tools/analyzers/slicedirect/analyzer_test.go
Normal file
11
tools/analyzers/slicedirect/analyzer_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package slicedirect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
)
|
||||
|
||||
func TestAnalyzer(t *testing.T) {
|
||||
analysistest.Run(t, analysistest.TestData(), Analyzer)
|
||||
}
|
||||
8
tools/analyzers/slicedirect/testdata/BUILD.bazel
vendored
Normal file
8
tools/analyzers/slicedirect/testdata/BUILD.bazel
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["slice.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/slicedirect/testdata",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
46
tools/analyzers/slicedirect/testdata/slice.go
vendored
Normal file
46
tools/analyzers/slicedirect/testdata/slice.go
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package testdata
|
||||
|
||||
func NoIndexProvided() {
|
||||
x := []byte{'f', 'o', 'o'}
|
||||
y := x[:] // want "Expression is already a slice."
|
||||
if len(y) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func StartIndexProvided_NoDiagnostic() {
|
||||
x := []byte{'f', 'o', 'o'}
|
||||
y := x[1:]
|
||||
if len(y) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func EndIndexProvided_NoDiagnostic() {
|
||||
x := []byte{'f', 'o', 'o'}
|
||||
y := x[:2]
|
||||
if len(y) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func BothIndicesProvided_NoDiagnostic() {
|
||||
x := []byte{'f', 'o', 'o'}
|
||||
y := x[1:2]
|
||||
if len(y) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func StringSlice() {
|
||||
x := "foo"
|
||||
y := x[:] // want "Expression is already a slice."
|
||||
if len(y) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func SliceFromFunction() {
|
||||
x := getSlice()[:] // want "Expression is already a slice."
|
||||
if len(x) == 3 {
|
||||
}
|
||||
}
|
||||
|
||||
func getSlice() []string {
|
||||
return []string{"bar"}
|
||||
}
|
||||
Reference in New Issue
Block a user