Skip to content

Commit

Permalink
simplify slice expr (avoid len)
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Jul 19, 2024
1 parent 905eca2 commit 2816f68
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 2 deletions.
17 changes: 15 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,18 @@ jobs:
- name: Test
run: go test -v ./...

- name: Golangci-lint
uses: golangci/golangci-lint-action@v6.0.1
lint:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.22'
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.56.0
args: --timeout 3m --config .golangci.yaml
1 change: 1 addition & 0 deletions formatter/simplify_slice_expr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package formatter
44 changes: 44 additions & 0 deletions internal/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,47 @@ func (e *Engine) detectUnusedFunctions(filename string) ([]Issue, error) {

return issues, nil
}

type SimplifySliceExprRule struct{}

func (r *SimplifySliceExprRule) Check(filename string) ([]Issue, error) {
engine := &Engine{}
return engine.detectUnnecessarySliceLength(filename)
}

func (e *Engine) detectUnnecessarySliceLength(filename string) ([]Issue, error) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
return nil, err
}

var issues []Issue
ast.Inspect(node, func(n ast.Node) bool {
sliceExpr, ok := n.(*ast.SliceExpr)
if !ok {
return true
}

if callExpr, ok := sliceExpr.High.(*ast.CallExpr); ok {
if ident, ok := callExpr.Fun.(*ast.Ident); ok && ident.Name == "len" {
if arg, ok := callExpr.Args[0].(*ast.Ident); ok {
if sliceExpr.X.(*ast.Ident).Name == arg.Name {
issue := Issue{
Rule: "unnecessary-slice-length",
Filename: filename,
Start: fset.Position(sliceExpr.Pos()),
End: fset.Position(sliceExpr.End()),
Message: "unnecessary use of len() in slice expression, can be simplified to a[b:]",
}
issues = append(issues, issue)
}
}
}
}

return true
})

return issues, nil
}
60 changes: 60 additions & 0 deletions internal/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,63 @@ func unused2() {
})
}
}

func TestDetectUnnecessarySliceLength(t *testing.T) {
tests := []struct {
name string
code string
expected int
}{
{
name: "No unnecessary slice length",
code: `
package main
func main() {
slice := []int{1, 2, 3}
_ = slice[:len(slice)]
}`,
expected: 1,
},
{
name: "Unnecessary slice length",
code: `
package main
func main() {
slice := []int{1, 2, 3}
_ = slice[:]
}`,
expected: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "lint-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)

tmpfile := filepath.Join(tmpDir, "test.go")
err = os.WriteFile(tmpfile, []byte(tt.code), 0o644)
require.NoError(t, err)

engine := &Engine{}
issues, err := engine.detectUnnecessarySliceLength(tmpfile)
require.NoError(t, err)

assert.Equal(t, tt.expected, len(issues), "Number of detected unnecessary slice length doesn't match expected")

if len(issues) > 0 {
for _, issue := range issues {
assert.Equal(t, "unnecessary-slice-length", issue.Rule)
assert.Equal(
t,
"unnecessary use of len() in slice expression, can be simplified to a[b:]",
issue.Message,
)
}
}
})
}
}

0 comments on commit 2816f68

Please sign in to comment.