From 11649658f184fb5eecef9e24b00aaaa859bbe421 Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:03:56 +0200 Subject: [PATCH] empty: added empty and zero length detection (#109) Fixes #108 Co-authored-by: Anton Telyshev --- README.md | 4 +++ .../src/checkers-default/empty/empty_test.go | 8 +++++ .../empty/empty_test.go.golden | 8 +++++ internal/checkers/empty.go | 34 +++++++++++++++++-- internal/testgen/gen_empty.go | 6 +++- 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9555334f..9866dbba 100644 --- a/README.md +++ b/README.md @@ -227,11 +227,15 @@ assert.Less(t, len(arr), 0) assert.Greater(t, 0, len(arr)) assert.Less(t, len(arr), 1) assert.Greater(t, 1, len(arr)) +assert.Zero(t, len(arr)) +assert.Empty(t, len(arr)) assert.NotEqual(t, 0, len(arr)) assert.NotEqualValues(t, 0, len(arr)) assert.Less(t, 0, len(arr)) assert.Greater(t, len(arr), 0) +assert.NotZero(t, len(arr)) +assert.NotEmpty(t, len(arr)) ✅ assert.Empty(t, arr) diff --git a/analyzer/testdata/src/checkers-default/empty/empty_test.go b/analyzer/testdata/src/checkers-default/empty/empty_test.go index d0c32d87..cdca4f84 100644 --- a/analyzer/testdata/src/checkers-default/empty/empty_test.go +++ b/analyzer/testdata/src/checkers-default/empty/empty_test.go @@ -36,6 +36,10 @@ func TestEmptyChecker(t *testing.T) { assert.Lessf(t, len(elems), 1, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" assert.Greater(t, 1, len(elems)) // want "empty: use assert\\.Empty" assert.Greaterf(t, 1, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" + assert.Zero(t, len(elems)) // want "empty: use assert\\.Empty" + assert.Zerof(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" + assert.Empty(t, len(elems)) // want "empty: use assert\\.Empty" + assert.Emptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" assert.Less(t, len(elems), 0) // want "empty: use assert\\.Empty" assert.Lessf(t, len(elems), 0, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" assert.Greater(t, 0, len(elems)) // want "empty: use assert\\.Empty" @@ -65,6 +69,10 @@ func TestEmptyChecker(t *testing.T) { assert.Greaterf(t, len(elems), 0, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" assert.Less(t, 0, len(elems)) // want "empty: use assert\\.NotEmpty" assert.Lessf(t, 0, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" + assert.NotZero(t, len(elems)) // want "empty: use assert\\.NotEmpty" + assert.NotZerof(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" + assert.NotEmpty(t, len(elems)) // want "empty: use assert\\.NotEmpty" + assert.NotEmptyf(t, len(elems), "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" // Valid. assert.NotEmpty(t, elems) diff --git a/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden b/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden index 3e0bc53d..574e3577 100644 --- a/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden +++ b/analyzer/testdata/src/checkers-default/empty/empty_test.go.golden @@ -44,6 +44,10 @@ func TestEmptyChecker(t *testing.T) { assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" assert.Empty(t, elems) // want "empty: use assert\\.Empty" assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" + assert.Empty(t, elems) // want "empty: use assert\\.Empty" + assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" + assert.Empty(t, elems) // want "empty: use assert\\.Empty" + assert.Emptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.Emptyf" // Valid. assert.Empty(t, elems) @@ -65,6 +69,10 @@ func TestEmptyChecker(t *testing.T) { assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty" assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" + assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty" + assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" + assert.NotEmpty(t, elems) // want "empty: use assert\\.NotEmpty" + assert.NotEmptyf(t, elems, "msg with args %d %s", 42, "42") // want "empty: use assert\\.NotEmptyf" // Valid. assert.NotEmpty(t, elems) diff --git a/internal/checkers/empty.go b/internal/checkers/empty.go index ca7ff41d..79eb432e 100644 --- a/internal/checkers/empty.go +++ b/internal/checkers/empty.go @@ -21,11 +21,15 @@ import ( // assert.Greater(t, 0, len(arr)) // assert.Less(t, len(arr), 1) // assert.Greater(t, 1, len(arr)) +// assert.Zero(t, len(arr)) +// assert.Empty(t, len(arr)) // // assert.NotEqual(t, 0, len(arr)) // assert.NotEqualValues(t, 0, len(arr)) // assert.Less(t, 0, len(arr)) // assert.Greater(t, len(arr), 0) +// assert.NotZero(t, len(arr)) +// assert.NotEmpty(t, len(arr)) // // and requires // @@ -56,10 +60,23 @@ func (checker Empty) checkEmpty(pass *analysis.Pass, call *CallMeta) *analysis.D ) } + if len(call.Args) == 0 { + return nil + } + + a := call.Args[0] + switch call.Fn.NameFTrimmed { + case "Zero", "Empty": + lenArg, ok := isBuiltinLenCall(pass, a) + if ok { + return newUseEmptyDiagnostic(a.Pos(), a.End(), lenArg) + } + } + if len(call.Args) < 2 { return nil } - a, b := call.Args[0], call.Args[1] + b := call.Args[1] switch call.Fn.NameFTrimmed { case "Len": @@ -110,10 +127,23 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi ) } + if len(call.Args) == 0 { + return nil + } + + a := call.Args[0] + switch call.Fn.NameFTrimmed { + case "NotZero", "NotEmpty": + lenArg, ok := isBuiltinLenCall(pass, a) + if ok { + return newUseNotEmptyDiagnostic(a.Pos(), a.End(), lenArg) + } + } + if len(call.Args) < 2 { return nil } - a, b := call.Args[0], call.Args[1] + b := call.Args[1] switch call.Fn.NameFTrimmed { case "NotEqual", "NotEqualValues": diff --git a/internal/testgen/gen_empty.go b/internal/testgen/gen_empty.go index 823f20af..3088a5f8 100644 --- a/internal/testgen/gen_empty.go +++ b/internal/testgen/gen_empty.go @@ -59,6 +59,8 @@ func (g EmptyTestsGenerator) TemplateData() any { {Fn: "GreaterOrEqual", Argsf: "0, len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"}, {Fn: "Less", Argsf: "len(elems), 1", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"}, {Fn: "Greater", Argsf: "1, len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"}, + {Fn: "Zero", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"}, + {Fn: "Empty", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "Empty", ProposedArgsf: "elems"}, // Bullshit, but supported by the checker: // n < 0, n <= 0 @@ -84,6 +86,8 @@ func (g EmptyTestsGenerator) TemplateData() any { {Fn: "NotEqualValues", Argsf: "0, len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"}, {Fn: "Greater", Argsf: "len(elems), 0", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"}, {Fn: "Less", Argsf: "0, len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"}, + {Fn: "NotZero", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"}, + {Fn: "NotEmpty", Argsf: "len(elems)", ReportMsgf: report, ProposedFn: "NotEmpty", ProposedArgsf: "elems"}, }, ValidAssertions: []Assertion{ {Fn: "NotEmpty", Argsf: "elems"}, @@ -161,7 +165,7 @@ func {{ .CheckerName.AsTestName }}(t *testing.T) { {{- range $ai, $assrn := $test.InvalidAssertions }} {{ NewAssertionExpander.Expand $assrn "assert" "t" nil }} {{- end }} - + // Valid. {{- range $ai, $assrn := $test.ValidAssertions }} {{ NewAssertionExpander.Expand $assrn "assert" "t" nil }}