From 2f67c38cc2e7a48a96ab83770d68ca4b3a693b22 Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sat, 16 Nov 2024 15:06:38 +0300 Subject: [PATCH 1/8] expected-actual: support len --- README.md | 4 +++ .../expected-actual/expected_actual_test.go | 35 ++++++++++++++----- .../expected_actual_test.go.golden | 35 ++++++++++++++----- internal/checkers/expected_actual.go | 7 ++++ internal/testgen/gen_expected_actual.go | 12 +++++++ 5 files changed, 75 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 94483a4e..3631ae97 100644 --- a/README.md +++ b/README.md @@ -370,6 +370,8 @@ assert.Error(t, err) ```go ❌ assert.Equal(t, result, expected) +assert.Equal(t, result, len(expected)) +assert.Equal(t, len(resultFields), len(expectedFields)) assert.EqualExportedValues(t, resultObj, User{Name: "Rob"}) assert.EqualValues(t, result, 42) assert.Exactly(t, result, int64(42)) @@ -389,6 +391,8 @@ assert.YAMLEq(t, result, "version: '3'") ✅ assert.Equal(t, expected, result) +assert.Equal(t, len(expected), result) +assert.Equal(t, len(expectedFields), len(resultFields)) assert.EqualExportedValues(t, User{Name: "Rob"}, resultObj) assert.EqualValues(t, 42, result) // And so on... diff --git a/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go b/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go index 1a7aff00..160891d7 100644 --- a/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go +++ b/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go @@ -30,6 +30,7 @@ func TestExpectedActualChecker(t *testing.T) { expectedVal := func() any { return nil } var expectedObj struct{ Val int } var expectedObjPtr = &expectedObj + var expectedFields []string var result any @@ -81,6 +82,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.Equalf(t, result, *(tt.expPtr()), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, result, ttPtr.expected) // want "expected-actual: need to reverse actual and expected values" assert.Equalf(t, result, ttPtr.expected, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" + assert.Equal(t, result, len(expectedFields)) // want "expected-actual: need to reverse actual and expected values" + assert.Equalf(t, result, len(expectedFields), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, result, 42) // want "expected-actual: need to reverse actual and expected values" assert.Equalf(t, result, 42, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, result, 3.14) // want "expected-actual: need to reverse actual and expected values" @@ -140,6 +143,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.NotEqualf(t, result, *(tt.expPtr()), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, result, ttPtr.expected) // want "expected-actual: need to reverse actual and expected values" assert.NotEqualf(t, result, ttPtr.expected, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" + assert.NotEqual(t, result, len(expectedFields)) // want "expected-actual: need to reverse actual and expected values" + assert.NotEqualf(t, result, len(expectedFields), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, result, 42) // want "expected-actual: need to reverse actual and expected values" assert.NotEqualf(t, result, 42, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, result, 3.14) // want "expected-actual: need to reverse actual and expected values" @@ -202,6 +207,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.Equalf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") assert.Equal(t, ttPtr.expected, result) assert.Equalf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") + assert.Equal(t, len(expectedFields), result) + assert.Equalf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") assert.Equal(t, 42, result) assert.Equalf(t, 42, result, "msg with args %d %s", 42, "42") assert.Equal(t, 3.14, result) @@ -261,6 +268,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.NotEqualf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") assert.NotEqual(t, ttPtr.expected, result) assert.NotEqualf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") + assert.NotEqual(t, len(expectedFields), result) + assert.NotEqualf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") assert.NotEqual(t, 42, result) assert.NotEqualf(t, 42, result, "msg with args %d %s", 42, "42") assert.NotEqual(t, 3.14, result) @@ -278,15 +287,18 @@ func TestExpectedActualChecker(t *testing.T) { func TestExpectedActualChecker_Other(t *testing.T) { var ( - result, expected any - resultPtr, expectedPtr *int - resultObj, expectedObj user - resultTime, expectedTime time.Time - value int + result, expected any + resultPtr, expectedPtr *int + resultObj, expectedObj user + resultTime, expectedTime time.Time + value int + actualFields, expectedFields []string ) // Invalid. { + assert.Equal(t, len(actualFields), len(expectedFields)) // want "expected-actual: need to reverse actual and expected values" + assert.Equalf(t, len(actualFields), len(expectedFields), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValues(t, resultObj, expectedObj) // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValuesf(t, resultObj, expectedObj, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValues(t, resultObj, user{Name: "Rob"}) // want "expected-actual: need to reverse actual and expected values" @@ -1150,10 +1162,11 @@ func TestExpectedActualChecker_CannotDetectVariablesLookedLikeConsts(t *testing. func TestExpectedActualChecker_Ignored(t *testing.T) { var ( - result, expected any - resultPtr, expectedPtr *int - value int - expectedTime time.Time + result, expected any + resultPtr, expectedPtr *int + value int + expectedTime time.Time + expectedFields, interfaces, directions []string ) assert.Equal(t, nil, nil) @@ -1162,6 +1175,10 @@ func TestExpectedActualChecker_Ignored(t *testing.T) { assert.Equalf(t, "value", "value", "msg with args %d %s", 42, "42") assert.Equal(t, expected, expected) assert.Equalf(t, expected, expected, "msg with args %d %s", 42, "42") + assert.Equal(t, len(expectedFields), len(expectedFields)) + assert.Equalf(t, len(expectedFields), len(expectedFields), "msg with args %d %s", 42, "42") + assert.Equal(t, len(interfaces), len(directions)) + assert.Equalf(t, len(interfaces), len(directions), "msg with args %d %s", 42, "42") assert.Equal(t, value, &resultPtr) assert.Equalf(t, value, &resultPtr, "msg with args %d %s", 42, "42") assert.Equal(t, []int{1, 2}, map[int]int{1: 2}) diff --git a/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go.golden b/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go.golden index b0a37bd7..b059f493 100644 --- a/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go.golden +++ b/analyzer/testdata/src/checkers-default/expected-actual/expected_actual_test.go.golden @@ -30,6 +30,7 @@ func TestExpectedActualChecker(t *testing.T) { expectedVal := func() any { return nil } var expectedObj struct{ Val int } var expectedObjPtr = &expectedObj + var expectedFields []string var result any @@ -81,6 +82,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.Equalf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, ttPtr.expected, result) // want "expected-actual: need to reverse actual and expected values" assert.Equalf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" + assert.Equal(t, len(expectedFields), result) // want "expected-actual: need to reverse actual and expected values" + assert.Equalf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, 42, result) // want "expected-actual: need to reverse actual and expected values" assert.Equalf(t, 42, result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.Equal(t, 3.14, result) // want "expected-actual: need to reverse actual and expected values" @@ -140,6 +143,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.NotEqualf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, ttPtr.expected, result) // want "expected-actual: need to reverse actual and expected values" assert.NotEqualf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" + assert.NotEqual(t, len(expectedFields), result) // want "expected-actual: need to reverse actual and expected values" + assert.NotEqualf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, 42, result) // want "expected-actual: need to reverse actual and expected values" assert.NotEqualf(t, 42, result, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.NotEqual(t, 3.14, result) // want "expected-actual: need to reverse actual and expected values" @@ -202,6 +207,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.Equalf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") assert.Equal(t, ttPtr.expected, result) assert.Equalf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") + assert.Equal(t, len(expectedFields), result) + assert.Equalf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") assert.Equal(t, 42, result) assert.Equalf(t, 42, result, "msg with args %d %s", 42, "42") assert.Equal(t, 3.14, result) @@ -261,6 +268,8 @@ func TestExpectedActualChecker(t *testing.T) { assert.NotEqualf(t, *(tt.expPtr()), result, "msg with args %d %s", 42, "42") assert.NotEqual(t, ttPtr.expected, result) assert.NotEqualf(t, ttPtr.expected, result, "msg with args %d %s", 42, "42") + assert.NotEqual(t, len(expectedFields), result) + assert.NotEqualf(t, len(expectedFields), result, "msg with args %d %s", 42, "42") assert.NotEqual(t, 42, result) assert.NotEqualf(t, 42, result, "msg with args %d %s", 42, "42") assert.NotEqual(t, 3.14, result) @@ -278,15 +287,18 @@ func TestExpectedActualChecker(t *testing.T) { func TestExpectedActualChecker_Other(t *testing.T) { var ( - result, expected any - resultPtr, expectedPtr *int - resultObj, expectedObj user - resultTime, expectedTime time.Time - value int + result, expected any + resultPtr, expectedPtr *int + resultObj, expectedObj user + resultTime, expectedTime time.Time + value int + actualFields, expectedFields []string ) // Invalid. { + assert.Equal(t, len(expectedFields), len(actualFields)) // want "expected-actual: need to reverse actual and expected values" + assert.Equalf(t, len(expectedFields), len(actualFields), "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValues(t, expectedObj, resultObj) // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValuesf(t, expectedObj, resultObj, "msg with args %d %s", 42, "42") // want "expected-actual: need to reverse actual and expected values" assert.EqualExportedValues(t, user{Name: "Rob"}, resultObj) // want "expected-actual: need to reverse actual and expected values" @@ -1150,10 +1162,11 @@ func TestExpectedActualChecker_CannotDetectVariablesLookedLikeConsts(t *testing. func TestExpectedActualChecker_Ignored(t *testing.T) { var ( - result, expected any - resultPtr, expectedPtr *int - value int - expectedTime time.Time + result, expected any + resultPtr, expectedPtr *int + value int + expectedTime time.Time + expectedFields, interfaces, directions []string ) assert.Equal(t, nil, nil) @@ -1162,6 +1175,10 @@ func TestExpectedActualChecker_Ignored(t *testing.T) { assert.Equalf(t, "value", "value", "msg with args %d %s", 42, "42") assert.Equal(t, expected, expected) assert.Equalf(t, expected, expected, "msg with args %d %s", 42, "42") + assert.Equal(t, len(expectedFields), len(expectedFields)) + assert.Equalf(t, len(expectedFields), len(expectedFields), "msg with args %d %s", 42, "42") + assert.Equal(t, len(interfaces), len(directions)) + assert.Equalf(t, len(interfaces), len(directions), "msg with args %d %s", 42, "42") assert.Equal(t, value, &resultPtr) assert.Equalf(t, value, &resultPtr, "msg with args %d %s", 42, "42") assert.Equal(t, []int{1, 2}, map[int]int{1: 2}) diff --git a/internal/checkers/expected_actual.go b/internal/checkers/expected_actual.go index 351d675c..31ea3ff4 100644 --- a/internal/checkers/expected_actual.go +++ b/internal/checkers/expected_actual.go @@ -17,6 +17,8 @@ var DefaultExpectedVarPattern = regexp.MustCompile( // ExpectedActual detects situations like // // assert.Equal(t, result, expected) +// assert.Equal(t, result, len(expected)) +// assert.Equal(t, len(resultFields), len(expectedFields)) // assert.EqualExportedValues(t, resultObj, User{Name: "Anton"}) // assert.EqualValues(t, result, 42) // assert.Exactly(t, result, int64(42)) @@ -37,6 +39,8 @@ var DefaultExpectedVarPattern = regexp.MustCompile( // and requires // // assert.Equal(t, expected, result) +// assert.Equal(t, len(expected), result) +// assert.Equal(t, len(expectedFields), len(resultFields)) // assert.EqualExportedValues(t, User{Name: "Anton"}, resultObj) // assert.EqualValues(t, 42, result) // ... @@ -122,6 +126,9 @@ func (checker ExpectedActual) isExpectedValueCandidate(pass *analysis.Pass, expr return true case *ast.CallExpr: + if lv, ok := isBuiltinLenCall(pass, expr); ok { + return isIdentNamedAfterPattern(checker.expVarPattern, lv) + } return isParenExpr(v) || isCastedBasicLitOrExpectedValue(v, checker.expVarPattern) || isExpectedValueFactory(pass, v, checker.expVarPattern) diff --git a/internal/testgen/gen_expected_actual.go b/internal/testgen/gen_expected_actual.go index a969d9c3..708d5a2a 100644 --- a/internal/testgen/gen_expected_actual.go +++ b/internal/testgen/gen_expected_actual.go @@ -106,6 +106,8 @@ func (g ExpectedActualTestsGenerator) TemplateData() any { "*(tt.expPtr())", "ttPtr.expected", + "len(expectedFields)", + // NOTE(a.telyshev): Unsupported rare cases: // "(*expectedObjPtr).Val", // "(*ttPtr).expected", @@ -122,6 +124,11 @@ func (g ExpectedActualTestsGenerator) TemplateData() any { }, OtherExpActFunctions: test{ InvalidAssertions: []Assertion{ + { + Fn: "Equal", Argsf: "len(actualFields), len(expectedFields)", + ReportMsgf: report, ProposedArgsf: "len(expectedFields), len(actualFields)", + }, + { Fn: "EqualExportedValues", Argsf: "resultObj, expectedObj", ReportMsgf: report, ProposedArgsf: "expectedObj, resultObj", @@ -261,6 +268,8 @@ func (g ExpectedActualTestsGenerator) TemplateData() any { {Fn: "Equal", Argsf: "nil, nil"}, {Fn: "Equal", Argsf: `"value", "value"`}, {Fn: "Equal", Argsf: "expected, expected"}, + {Fn: "Equal", Argsf: "len(expectedFields), len(expectedFields)"}, + {Fn: "Equal", Argsf: "len(interfaces), len(directions)"}, {Fn: "Equal", Argsf: "value, &resultPtr"}, {Fn: "Equal", Argsf: "[]int{1, 2}, map[int]int{1: 2}"}, {Fn: "NotEqual", Argsf: "result, result"}, @@ -348,6 +357,7 @@ func {{ .CheckerName.AsTestName }}(t *testing.T) { expectedVal := func() any { return nil } var expectedObj struct { Val int } var expectedObjPtr = &expectedObj + var expectedFields []string var result any @@ -369,6 +379,7 @@ func {{ .CheckerName.AsTestName }}_Other(t *testing.T) { resultObj, expectedObj user resultTime, expectedTime time.Time value int + actualFields, expectedFields []string ) // Invalid. @@ -503,6 +514,7 @@ func {{ .CheckerName.AsTestName }}_Ignored(t *testing.T) { resultPtr, expectedPtr *int value int expectedTime time.Time + expectedFields, interfaces, directions []string ) {{ range $ai, $assrn := $.IgnoredAssertions }} From b1201e3961dc1a5c84c57b6395c067f1a56ce3e3 Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sat, 16 Nov 2024 21:56:17 +0300 Subject: [PATCH 2/8] len: support len-len and value-len cases --- README.md | 26 ++++- analyzer/checkers_factory_test.go | 4 +- .../src/checkers-default/len/len_test.go | 78 ++++++++------- .../checkers-default/len/len_test.go.golden | 78 ++++++++------- .../checkers_priority_test.go | 4 +- .../checkers_priority_test.go.golden | 6 +- internal/checkers/checkers_registry.go | 2 +- internal/checkers/checkers_registry_test.go | 4 +- internal/checkers/helpers_len.go | 26 ----- internal/checkers/len.go | 98 +++++++++++++------ internal/testgen/gen_len.go | 27 +++-- 11 files changed, 209 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 3631ae97..a3144b1d 100644 --- a/README.md +++ b/README.md @@ -622,13 +622,29 @@ P.S. Look at [testify's issue](https://github.com/stretchr/testify/issues/772), ```go ❌ -assert.Equal(t, 3, len(arr)) -assert.EqualValues(t, 3, len(arr)) -assert.Exactly(t, 3, len(arr)) -assert.True(t, len(arr) == 3) +assert.Equal(t, 42, len(arr)) +assert.Equal(t, len(arr), 42) +assert.EqualValues(t, 42, len(arr)) +assert.EqualValues(t, len(arr), 42) +assert.Exactly(t, 42, len(arr)) +assert.Exactly(t, len(arr), 42) +assert.True(t, 42 == len(arr)) +assert.True(t, len(arr) == 42) + +assert.Equal(t, value, len(arr)) +assert.EqualValues(t, value, len(arr)) +assert.Exactly(t, value, len(arr)) +assert.True(t, len(arr) == value) + +assert.Equal(t, len(expArr), len(arr)) +assert.EqualValues(t, len(expArr), len(arr)) +assert.Exactly(t, len(expArr), len(arr)) +assert.True(t, len(arr) == len(expArr)) ✅ -assert.Len(t, arr, 3) +assert.Len(t, arr, 42) +assert.Len(t, arr, value) +assert.Len(t, arr, len(expArr)) ``` **Autofix**: true.
diff --git a/analyzer/checkers_factory_test.go b/analyzer/checkers_factory_test.go index 28bfb1dd..ad25d664 100644 --- a/analyzer/checkers_factory_test.go +++ b/analyzer/checkers_factory_test.go @@ -18,7 +18,6 @@ func Test_newCheckers(t *testing.T) { checkers.NewFloatCompare(), checkers.NewBoolCompare(), checkers.NewEmpty(), - checkers.NewLen(), checkers.NewNegativePositive(), checkers.NewCompares(), checkers.NewContains(), @@ -27,6 +26,7 @@ func Test_newCheckers(t *testing.T) { checkers.NewErrorIsAs(), checkers.NewEncodedCompare(), checkers.NewExpectedActual(), + checkers.NewLen(), checkers.NewRegexp(), checkers.NewSuiteExtraAssertCall(), checkers.NewSuiteDontUsePkg(), @@ -37,7 +37,6 @@ func Test_newCheckers(t *testing.T) { checkers.NewFloatCompare(), checkers.NewBoolCompare(), checkers.NewEmpty(), - checkers.NewLen(), checkers.NewNegativePositive(), checkers.NewCompares(), checkers.NewContains(), @@ -46,6 +45,7 @@ func Test_newCheckers(t *testing.T) { checkers.NewErrorIsAs(), checkers.NewEncodedCompare(), checkers.NewExpectedActual(), + checkers.NewLen(), checkers.NewRegexp(), checkers.NewSuiteExtraAssertCall(), checkers.NewSuiteDontUsePkg(), diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go b/analyzer/testdata/src/checkers-default/len/len_test.go index 8f1b3ecb..4a42a859 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go +++ b/analyzer/testdata/src/checkers-default/len/len_test.go @@ -8,62 +8,74 @@ import ( "github.com/stretchr/testify/assert" ) +const constNum = 10 + func TestLenChecker(t *testing.T) { - var arr [3]int + var arr, expArr [3]int var value int // Invalid. { - assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" - assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" - assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" + assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == value) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == len(expArr)) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == constNum) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" } // Valid. { assert.Len(t, arr, 42) assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") + assert.Len(t, arr, value) + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") } // Ignored. { - assert.Equal(t, len(arr), len(arr)) - assert.Equalf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.EqualValues(t, len(arr), len(arr)) - assert.EqualValuesf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.Exactly(t, len(arr), len(arr)) - assert.Exactlyf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) assert.Exactlyf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.True(t, len(arr) == len(arr)) - assert.Truef(t, len(arr) == len(arr), "msg with args %d %s", 42, "42") - assert.True(t, len(arr) == value) - assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") assert.True(t, value == len(arr)) assert.Truef(t, value == len(arr), "msg with args %d %s", 42, "42") assert.NotEqual(t, 42, len(arr)) diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go.golden b/analyzer/testdata/src/checkers-default/len/len_test.go.golden index 9a0928e1..111395c7 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go.golden +++ b/analyzer/testdata/src/checkers-default/len/len_test.go.golden @@ -8,62 +8,74 @@ import ( "github.com/stretchr/testify/assert" ) +const constNum = 10 + func TestLenChecker(t *testing.T) { - var arr [3]int + var arr, expArr [3]int var value int // Invalid. { - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, value) // want "len: use assert\\.Len" - assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, value) // want "len: use assert\\.Len" - assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, value) // want "len: use assert\\.Len" - assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, arr, 42) // want "len: use assert\\.Len" - assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, value) // want "len: use assert\\.Len" + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, len(expArr)) // want "len: use assert\\.Len" + assert.Lenf(t, arr, len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, value) // want "len: use assert\\.Len" + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, len(expArr)) // want "len: use assert\\.Len" + assert.Lenf(t, arr, len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, value) // want "len: use assert\\.Len" + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, len(expArr)) // want "len: use assert\\.Len" + assert.Lenf(t, arr, len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, 42) // want "len: use assert\\.Len" + assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, value) // want "len: use assert\\.Len" + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, len(expArr)) // want "len: use assert\\.Len" + assert.Lenf(t, arr, len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, constNum) // want "len: use assert\\.Len" + assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, constNum) // want "len: use assert\\.Len" + assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, constNum) // want "len: use assert\\.Len" + assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, arr, constNum) // want "len: use assert\\.Len" + assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" } // Valid. { assert.Len(t, arr, 42) assert.Lenf(t, arr, 42, "msg with args %d %s", 42, "42") + assert.Len(t, arr, value) + assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") } // Ignored. { - assert.Equal(t, len(arr), len(arr)) - assert.Equalf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.EqualValues(t, len(arr), len(arr)) - assert.EqualValuesf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.Exactly(t, len(arr), len(arr)) - assert.Exactlyf(t, len(arr), len(arr), "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) assert.Exactlyf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.True(t, len(arr) == len(arr)) - assert.Truef(t, len(arr) == len(arr), "msg with args %d %s", 42, "42") - assert.True(t, len(arr) == value) - assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") assert.True(t, value == len(arr)) assert.Truef(t, value == len(arr), "msg with args %d %s", 42, "42") assert.NotEqual(t, 42, len(arr)) diff --git a/analyzer/testdata/src/checkers-priority/checkers_priority_test.go b/analyzer/testdata/src/checkers-priority/checkers_priority_test.go index 1a6bb110..a2fa7fac 100644 --- a/analyzer/testdata/src/checkers-priority/checkers_priority_test.go +++ b/analyzer/testdata/src/checkers-priority/checkers_priority_test.go @@ -11,9 +11,9 @@ func TestCheckersPriority(t *testing.T) { var f float64 var b bool - // `empty` > `len` > `expected-actual` + // `empty` > `expected-actual` > `len` assert.Equal(t, len([]int{}), 0) // want "empty: use assert\\.Empty" - assert.Equal(t, len([]int{}), 3) // want "len: use assert\\.Len" + assert.Equal(t, len([]int{}), 3) // want "expected-actual: need to reverse actual and expected values" // `float-compare` > `bool-compare` > `compares` > `expected-actual` require.True(t, 42.42 == f) // want "float-compare: use require\\.InEpsilon \\(or InDelta\\)" diff --git a/analyzer/testdata/src/checkers-priority/checkers_priority_test.go.golden b/analyzer/testdata/src/checkers-priority/checkers_priority_test.go.golden index 3f2d8df7..983bb828 100644 --- a/analyzer/testdata/src/checkers-priority/checkers_priority_test.go.golden +++ b/analyzer/testdata/src/checkers-priority/checkers_priority_test.go.golden @@ -11,9 +11,9 @@ func TestCheckersPriority(t *testing.T) { var f float64 var b bool - // `empty` > `len` > `expected-actual` - assert.Empty(t, []int{}) // want "empty: use assert\\.Empty" - assert.Len(t, []int{}, 3) // want "len: use assert\\.Len" + // `empty` > `expected-actual` > `len` + assert.Empty(t, []int{}) // want "empty: use assert\\.Empty" + assert.Equal(t, 3, len([]int{})) // want "expected-actual: need to reverse actual and expected values" // `float-compare` > `bool-compare` > `compares` > `expected-actual` require.True(t, 42.42 == f) // want "float-compare: use require\\.InEpsilon \\(or InDelta\\)" diff --git a/internal/checkers/checkers_registry.go b/internal/checkers/checkers_registry.go index f881be4f..bb4aa4d0 100644 --- a/internal/checkers/checkers_registry.go +++ b/internal/checkers/checkers_registry.go @@ -10,7 +10,6 @@ var registry = checkersRegistry{ {factory: asCheckerFactory(NewFloatCompare), enabledByDefault: true}, {factory: asCheckerFactory(NewBoolCompare), enabledByDefault: true}, {factory: asCheckerFactory(NewEmpty), enabledByDefault: true}, - {factory: asCheckerFactory(NewLen), enabledByDefault: true}, {factory: asCheckerFactory(NewNegativePositive), enabledByDefault: true}, {factory: asCheckerFactory(NewCompares), enabledByDefault: true}, {factory: asCheckerFactory(NewContains), enabledByDefault: true}, @@ -19,6 +18,7 @@ var registry = checkersRegistry{ {factory: asCheckerFactory(NewErrorIsAs), enabledByDefault: true}, {factory: asCheckerFactory(NewEncodedCompare), enabledByDefault: true}, {factory: asCheckerFactory(NewExpectedActual), enabledByDefault: true}, + {factory: asCheckerFactory(NewLen), enabledByDefault: true}, {factory: asCheckerFactory(NewRegexp), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteExtraAssertCall), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteDontUsePkg), enabledByDefault: true}, diff --git a/internal/checkers/checkers_registry_test.go b/internal/checkers/checkers_registry_test.go index 08c3a397..eff55712 100644 --- a/internal/checkers/checkers_registry_test.go +++ b/internal/checkers/checkers_registry_test.go @@ -38,7 +38,6 @@ func TestAll(t *testing.T) { "float-compare", "bool-compare", "empty", - "len", "negative-positive", "compares", "contains", @@ -47,6 +46,7 @@ func TestAll(t *testing.T) { "error-is-as", "encoded-compare", "expected-actual", + "len", "regexp", "suite-extra-assert-call", "suite-dont-use-pkg", @@ -75,7 +75,6 @@ func TestEnabledByDefault(t *testing.T) { "float-compare", "bool-compare", "empty", - "len", "negative-positive", "compares", "contains", @@ -84,6 +83,7 @@ func TestEnabledByDefault(t *testing.T) { "error-is-as", "encoded-compare", "expected-actual", + "len", "regexp", "suite-extra-assert-call", "suite-dont-use-pkg", diff --git a/internal/checkers/helpers_len.go b/internal/checkers/helpers_len.go index 904950ff..6afa5849 100644 --- a/internal/checkers/helpers_len.go +++ b/internal/checkers/helpers_len.go @@ -2,7 +2,6 @@ package checkers import ( "go/ast" - "go/token" "go/types" "golang.org/x/tools/go/analysis" @@ -12,31 +11,6 @@ import ( var lenObj = types.Universe.Lookup("len") -func isLenEquality(pass *analysis.Pass, e ast.Expr) (ast.Expr, ast.Expr, bool) { - be, ok := e.(*ast.BinaryExpr) - if !ok { - return nil, nil, false - } - - if be.Op != token.EQL { - return nil, nil, false - } - return xorLenCall(pass, be.X, be.Y) -} - -func xorLenCall(pass *analysis.Pass, a, b ast.Expr) (lenArg ast.Expr, expectedLen ast.Expr, ok bool) { - arg1, ok1 := isBuiltinLenCall(pass, a) - arg2, ok2 := isBuiltinLenCall(pass, b) - - if xor(ok1, ok2) { - if ok1 { - return arg1, b, true - } - return arg2, a, true - } - return nil, nil, false -} - func isLenCallAndZero(pass *analysis.Pass, a, b ast.Expr) (ast.Expr, bool) { lenArg, ok := isBuiltinLenCall(pass, a) return lenArg, ok && isZero(b) diff --git a/internal/checkers/len.go b/internal/checkers/len.go index c240a617..a03aca55 100644 --- a/internal/checkers/len.go +++ b/internal/checkers/len.go @@ -1,19 +1,38 @@ package checkers import ( + "go/ast" + "go/token" + "golang.org/x/tools/go/analysis" ) // Len detects situations like // -// assert.Equal(t, 3, len(arr)) -// assert.EqualValues(t, 3, len(arr)) -// assert.Exactly(t, 3, len(arr)) -// assert.True(t, len(arr) == 3) +// assert.Equal(t, 42, len(arr)) +// assert.Equal(t, len(arr), 42) +// assert.EqualValues(t, 42, len(arr)) +// assert.EqualValues(t, len(arr), 42) +// assert.Exactly(t, 42, len(arr)) +// assert.Exactly(t, len(arr), 42) +// assert.True(t, 42 == len(arr)) +// assert.True(t, len(arr) == 42) +// +// assert.Equal(t, value, len(arr)) +// assert.EqualValues(t, value, len(arr)) +// assert.Exactly(t, value, len(arr)) +// assert.True(t, len(arr) == value) + +// assert.Equal(t, len(expArr), len(arr)) +// assert.EqualValues(t, len(expArr), len(arr)) +// assert.Exactly(t, len(expArr), len(arr)) +// assert.True(t, len(arr) == len(expArr)) // // and requires // -// assert.Len(t, arr, 3) +// assert.Len(t, arr, 42) +// assert.Len(t, arr, value) +// assert.Len(t, arr, len(expArr)) type Len struct{} // NewLen constructs Len checker. @@ -21,45 +40,62 @@ func NewLen() Len { return Len{} } func (Len) Name() string { return "len" } func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { - const proposedFn = "Len" - switch call.Fn.NameFTrimmed { case "Equal", "EqualValues", "Exactly": if len(call.Args) < 2 { return nil } - a, b := call.Args[0], call.Args[1] - if lenArg, expectedLen, ok := xorLenCall(pass, a, b); ok { - if _, ok := isIntBasicLit(expectedLen); (expectedLen == b) && !ok { - // https://github.com/Antonboom/testifylint/issues/9 - return nil - } - return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - analysis.TextEdit{ - Pos: a.Pos(), - End: b.End(), - NewText: formatAsCallArgs(pass, lenArg, expectedLen), - }) - } + a, b := call.Args[0], call.Args[1] + return checker.checkArgs(call, pass, a, b, false) case "True": if len(call.Args) < 1 { return nil } - expr := call.Args[0] - if lenArg, expectedLen, ok := isLenEquality(pass, expr); ok { - if _, ok := isIntBasicLit(expectedLen); !ok { - return nil - } - return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - analysis.TextEdit{ - Pos: expr.Pos(), - End: expr.End(), - NewText: formatAsCallArgs(pass, lenArg, expectedLen), - }) + be, ok := call.Args[0].(*ast.BinaryExpr) + if !ok { + return nil + } + if be.Op != token.EQL { + return nil + } + return checker.checkArgs(call, pass, be.Y, be.X, true) // In True, the actual value is usually first. + } + return nil +} + +func (checker Len) checkArgs(call *CallMeta, pass *analysis.Pass, a, b ast.Expr, inverted bool) *analysis.Diagnostic { + newUseLenDiagnostic := func(lenArg, expectedLen ast.Expr) *analysis.Diagnostic { + const proposedFn = "Len" + start, end := a.Pos(), b.End() + if inverted { + start, end = b.Pos(), a.End() } + return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, + analysis.TextEdit{ + Pos: start, + End: end, + NewText: formatAsCallArgs(pass, lenArg, expectedLen), + }) } + + arg1, firstIsLen := isBuiltinLenCall(pass, a) + arg2, secondIsLen := isBuiltinLenCall(pass, b) + + switch { + case firstIsLen && secondIsLen: + return newUseLenDiagnostic(arg2, a) + + case firstIsLen: + if _, secondIsNum := isIntBasicLit(b); secondIsNum { + return newUseLenDiagnostic(arg1, b) + } + + case secondIsLen: + return newUseLenDiagnostic(arg2, a) + } + return nil } diff --git a/internal/testgen/gen_len.go b/internal/testgen/gen_len.go index 4b3140da..ade360a8 100644 --- a/internal/testgen/gen_len.go +++ b/internal/testgen/gen_len.go @@ -31,28 +31,41 @@ func (g LenTestsGenerator) TemplateData() any { {Fn: "Equal", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "Equal", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "Equal", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "Equal", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + {Fn: "EqualValues", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "EqualValues", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "EqualValues", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + { + Fn: "EqualValues", Argsf: "len(expArr), len(arr)", ReportMsgf: report, + ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", + }, + {Fn: "Exactly", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "Exactly", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "Exactly", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "Exactly", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + {Fn: "True", Argsf: "len(arr) == 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, {Fn: "True", Argsf: "42 == len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "True", Argsf: "len(arr) == value", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "True", Argsf: "len(arr) == len(expArr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + + // Constant case. + {Fn: "Equal", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "EqualValues", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "Exactly", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "True", Argsf: "len(arr) == constNum", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, }, ValidAssertions: []Assertion{ {Fn: "Len", Argsf: "arr, 42"}, + {Fn: "Len", Argsf: "arr, value"}, {Fn: "Len", Argsf: "arr, len(arr)"}, }, IgnoredAssertions: []Assertion{ - {Fn: "Equal", Argsf: "len(arr), len(arr)"}, {Fn: "Equal", Argsf: "len(arr), value"}, - {Fn: "EqualValues", Argsf: "len(arr), len(arr)"}, {Fn: "EqualValues", Argsf: "len(arr), value"}, - {Fn: "Exactly", Argsf: "len(arr), len(arr)"}, {Fn: "Exactly", Argsf: "len(arr), value"}, - {Fn: "True", Argsf: "len(arr) == len(arr)"}, - {Fn: "True", Argsf: "len(arr) == value"}, {Fn: "True", Argsf: "value == len(arr)"}, {Fn: "NotEqual", Argsf: "42, len(arr)"}, @@ -117,8 +130,10 @@ import ( "github.com/stretchr/testify/assert" ) +const constNum = 10 + func {{ .CheckerName.AsTestName }}(t *testing.T) { - var arr [3]int + var arr, expArr [3]int var value int // Invalid. From 1d666af3dc3c74c17999df6bbdee40a38fba2064 Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sat, 16 Nov 2024 22:03:56 +0300 Subject: [PATCH 3/8] len: add disclaimer about not-len-first assetions --- README.md | 6 ++++++ internal/checkers/len.go | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index a3144b1d..7b4a22eb 100644 --- a/README.md +++ b/README.md @@ -651,6 +651,12 @@ assert.Len(t, arr, len(expArr)) **Enabled by default**: true.
**Reason**: More appropriate `testify` API with clearer failure message. +> [!CAUTION] +> The checker ignores assertions in which length checking is not a priority, e.g. +> ```go +> assert.Equal(t, len(arr), value) +> ``` + --- ### negative-positive diff --git a/internal/checkers/len.go b/internal/checkers/len.go index a03aca55..9bdd8ff9 100644 --- a/internal/checkers/len.go +++ b/internal/checkers/len.go @@ -33,6 +33,10 @@ import ( // assert.Len(t, arr, 42) // assert.Len(t, arr, value) // assert.Len(t, arr, len(expArr)) +// +// The checker ignores assertions in which length checking is not a priority, e.g +// +// assert.Equal(t, len(arr), value) type Len struct{} // NewLen constructs Len checker. From 03ade38d5e5f5cfdb3df10bf986556eb28583c79 Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sat, 16 Nov 2024 22:19:22 +0300 Subject: [PATCH 4/8] len: add TestLenInterface as a proof --- .../testdata/src/debug/len_interface_test.go | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 analyzer/testdata/src/debug/len_interface_test.go diff --git a/analyzer/testdata/src/debug/len_interface_test.go b/analyzer/testdata/src/debug/len_interface_test.go new file mode 100644 index 00000000..1c37f95a --- /dev/null +++ b/analyzer/testdata/src/debug/len_interface_test.go @@ -0,0 +1,26 @@ +package debug + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLenInterface(t *testing.T) { + const n = 10 + a := newArr(n) + assert.Equal(t, n, a.Len()) + assert.Len(t, a, n) // Error: "{[ ]}" could not be applied builtin len() +} + +type arr struct { + v []string +} + +func newArr(n int) arr { + return arr{v: make([]string, n)} +} + +func (a arr) Len() int { + return len(a.v) +} From e2e1092c2fbba49ab6633e55bb1f11957a5bf21a Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sun, 17 Nov 2024 10:20:38 +0300 Subject: [PATCH 5/8] len: support []byte/string/raw.Message type conversions --- README.md | 9 ++ .../src/checkers-default/len/len_test.go | 109 ++++++++----- .../checkers-default/len/len_test.go.golden | 29 ++++ .../src/debug/len_type_conversions_test.go | 20 +++ internal/checkers/encoded_compare.go | 7 + internal/checkers/helpers_diagnostic.go | 22 +++ internal/checkers/len.go | 38 +++++ internal/testgen/gen_len.go | 148 +++++++++++++++--- 8 files changed, 321 insertions(+), 61 deletions(-) create mode 100644 analyzer/testdata/src/debug/len_type_conversions_test.go diff --git a/README.md b/README.md index 7b4a22eb..4c603d54 100644 --- a/README.md +++ b/README.md @@ -657,6 +657,15 @@ assert.Len(t, arr, len(expArr)) > assert.Equal(t, len(arr), value) > ``` +Also, `len` repeats expected [gosimple](https://golangci-lint.run/usage/linters/#gosimple) (from staticcheck) +[behaviour](https://github.com/dominikh/go-tools/issues/760) and detects (and removes) unnecessary +`string/[]byte/json.RawMessage` conversions like + +```go +❌ assert.Len(t, string(headContents), 40) +✅ assert.Len(t, headContents, 40) +``` + --- ### negative-positive diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go b/analyzer/testdata/src/checkers-default/len/len_test.go index 4a42a859..08bcfa8a 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go +++ b/analyzer/testdata/src/checkers-default/len/len_test.go @@ -3,6 +3,8 @@ package len import ( + "encoding/json" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -13,49 +15,68 @@ const constNum = 10 func TestLenChecker(t *testing.T) { var arr, expArr [3]int var value int + var resp []byte // Invalid. { - assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" - assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" - assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == value) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == len(expArr)) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == constNum) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" + assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == value) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == len(expArr)) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == constNum) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(string(resp))) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(string(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len([]byte(resp))) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len([]byte(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(json.RawMessage(resp))) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(json.RawMessage(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(string([]byte(json.RawMessage(resp))))) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(string([]byte(json.RawMessage(resp)))), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(string(resp)) == 42) // want "len: use assert\\.Len" + assert.Truef(t, len(string(resp)) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, string(resp), 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, string(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, []byte(resp), 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, []byte(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, json.RawMessage(resp), 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, json.RawMessage(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, string([]byte(json.RawMessage(resp))), 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, string([]byte(json.RawMessage(resp))), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" } // Valid. @@ -66,12 +87,16 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") + assert.Len(t, String(10), 10) + assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") } // Ignored. { assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") + assert.Len(t, String(10), 10) + assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) @@ -148,3 +173,7 @@ func TestLenChecker(t *testing.T) { assert.Falsef(t, len(arr) <= 42, "msg with args %d %s", 42, "42") } } + +func String(n int) string { + return strings.Repeat("*", n) +} diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go.golden b/analyzer/testdata/src/checkers-default/len/len_test.go.golden index 111395c7..5831d680 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go.golden +++ b/analyzer/testdata/src/checkers-default/len/len_test.go.golden @@ -3,6 +3,8 @@ package len import ( + "encoding/json" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -13,6 +15,7 @@ const constNum = 10 func TestLenChecker(t *testing.T) { var arr, expArr [3]int var value int + var resp []byte // Invalid. { @@ -56,6 +59,24 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" assert.Len(t, arr, constNum) // want "len: use assert\\.Len" assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: use assert\\.Len" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: use assert\\.Len" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: use assert\\.Len" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: use assert\\.Len" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: use assert\\.Len" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" + assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" } // Valid. @@ -66,12 +87,16 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") + assert.Len(t, String(10), 10) + assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") } // Ignored. { assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") + assert.Len(t, String(10), 10) + assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) @@ -148,3 +173,7 @@ func TestLenChecker(t *testing.T) { assert.Falsef(t, len(arr) <= 42, "msg with args %d %s", 42, "42") } } + +func String(n int) string { + return strings.Repeat("*", n) +} diff --git a/analyzer/testdata/src/debug/len_type_conversions_test.go b/analyzer/testdata/src/debug/len_type_conversions_test.go new file mode 100644 index 00000000..e1cea799 --- /dev/null +++ b/analyzer/testdata/src/debug/len_type_conversions_test.go @@ -0,0 +1,20 @@ +package debug + +import ( + "encoding/json" + "testing" + + "github.com/ghetzel/testify/assert" +) + +func TestLenTypeConversions(t *testing.T) { + const resp = "Mutlti-языковая string 你好世界 🙂" + const respLen = len(resp) // 48 + + assert.Equal(t, respLen, len(resp)) + assert.Equal(t, respLen, len([]byte(resp))) + assert.Equal(t, respLen, len(json.RawMessage(resp))) + assert.Len(t, resp, respLen) + assert.Len(t, []byte(resp), respLen) + assert.Len(t, json.RawMessage(resp), respLen) +} diff --git a/internal/checkers/encoded_compare.go b/internal/checkers/encoded_compare.go index 1464fd64..ab40627a 100644 --- a/internal/checkers/encoded_compare.go +++ b/internal/checkers/encoded_compare.go @@ -23,6 +23,13 @@ import ( // // assert.JSONEq(t, `{"foo": "bar"}`, body) // assert.YAMLEq(t, expectedYML, conf) +// +// EncodedCompare detects JSON-style string constants (usable in fmt.Sprintf also) and JSON-style/YAML-style named +// variables. If variable is converted to json.RawMessage, then it is considered JSON unconditionally. +// +// When fixing, EncodedCompare removes unnecessary conversions to []byte, string, json.RawMessage and calls of +// strings.Replace, strings.ReplaceAll, strings.Trim, strings.TrimSpace, and adds a conversion to string when +// needed. type EncodedCompare struct{} // NewEncodedCompare constructs EncodedCompare checker. diff --git a/internal/checkers/helpers_diagnostic.go b/internal/checkers/helpers_diagnostic.go index f12d87aa..231fa5a1 100644 --- a/internal/checkers/helpers_diagnostic.go +++ b/internal/checkers/helpers_diagnostic.go @@ -5,6 +5,8 @@ import ( "go/ast" "golang.org/x/tools/go/analysis" + + "github.com/Antonboom/testifylint/internal/analysisutil" ) func newRemoveFnAndUseDiagnostic( @@ -86,6 +88,26 @@ func newRemoveFnDiagnostic( newSuggestedFuncRemoving(pass, fnName, fnPos, fnArgs...)) } +func newRemoveTypeConversionDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + convPos analysis.Range, + replaceWith ast.Expr, +) *analysis.Diagnostic { + return newDiagnostic(checker, call, "remove unnecessary type conversion", + analysis.SuggestedFix{ + Message: "Remove type conversions", + TextEdits: []analysis.TextEdit{ + { + Pos: convPos.Pos(), + End: convPos.End(), + NewText: analysisutil.NodeBytes(pass.Fset, replaceWith), + }, + }, + }) +} + func newDiagnostic( checker string, rng analysis.Range, diff --git a/internal/checkers/len.go b/internal/checkers/len.go index 9bdd8ff9..d1e9f052 100644 --- a/internal/checkers/len.go +++ b/internal/checkers/len.go @@ -28,11 +28,16 @@ import ( // assert.Exactly(t, len(expArr), len(arr)) // assert.True(t, len(arr) == len(expArr)) // +// assert.Len(t, string(headContents), 40) +// assert.Len(t, []byte(headContents), 40) +// assert.Len(t, json.RawMessage(headContents), 40) +// // and requires // // assert.Len(t, arr, 42) // assert.Len(t, arr, value) // assert.Len(t, arr, len(expArr)) +// assert.Len(t, headContents, 40) // // The checker ignores assertions in which length checking is not a priority, e.g // @@ -66,6 +71,16 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost return nil } return checker.checkArgs(call, pass, be.Y, be.X, true) // In True, the actual value is usually first. + + case "Len": + if len(call.Args) < 2 { + return nil + } + a := call.Args[0] + + if cleaned, ok := checker.unwrap(pass, a); ok { + return newRemoveTypeConversionDiagnostic(pass, checker.Name(), call, a, cleaned) + } } return nil } @@ -73,10 +88,13 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost func (checker Len) checkArgs(call *CallMeta, pass *analysis.Pass, a, b ast.Expr, inverted bool) *analysis.Diagnostic { newUseLenDiagnostic := func(lenArg, expectedLen ast.Expr) *analysis.Diagnostic { const proposedFn = "Len" + start, end := a.Pos(), b.End() if inverted { start, end = b.Pos(), a.End() } + + lenArg, _ = checker.unwrap(pass, lenArg) return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, analysis.TextEdit{ Pos: start, @@ -103,3 +121,23 @@ func (checker Len) checkArgs(call *CallMeta, pass *analysis.Pass, a, b ast.Expr, return nil } + +// unwrap unwraps expression from string, []byte, and json.RawMessage type conversions. +// Return false if no conversion found. +func (checker Len) unwrap(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) { + ce, ok := e.(*ast.CallExpr) + if !ok { + return e, false + } + if len(ce.Args) == 0 { + return e, false + } + + if isIdentWithName("string", ce.Fun) || + isByteArray(ce.Fun) || + isJSONRawMessageCast(pass, ce) { + v, _ := checker.unwrap(pass, ce.Args[0]) + return v, true + } + return e, false +} diff --git a/internal/testgen/gen_len.go b/internal/testgen/gen_len.go index ade360a8..70fe331d 100644 --- a/internal/testgen/gen_len.go +++ b/internal/testgen/gen_len.go @@ -16,8 +16,10 @@ func (LenTestsGenerator) Checker() checkers.Checker { func (g LenTestsGenerator) TemplateData() any { var ( checker = g.Checker().Name() - report = checker + ": use %s.%s" proposedFn = "Len" + + reportUse = checker + ": use %s.%s" + reportRemoveTypeConv = checker + ": remove unnecessary type conversion" ) return struct { @@ -28,42 +30,139 @@ func (g LenTestsGenerator) TemplateData() any { }{ CheckerName: CheckerName(checker), InvalidAssertions: []Assertion{ - {Fn: "Equal", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "Equal", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "Equal", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, - {Fn: "Equal", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + { + Fn: "Equal", Argsf: "len(arr), 42", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "Equal", Argsf: "42, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "Equal", Argsf: "value, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, value", + }, + { + Fn: "Equal", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", + }, - {Fn: "EqualValues", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "EqualValues", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "EqualValues", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, { - Fn: "EqualValues", Argsf: "len(expArr), len(arr)", ReportMsgf: report, + Fn: "EqualValues", Argsf: "len(arr), 42", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "EqualValues", Argsf: "42, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "EqualValues", Argsf: "value, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, value", + }, + { + Fn: "EqualValues", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", }, - {Fn: "Exactly", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "Exactly", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "Exactly", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, - {Fn: "Exactly", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + { + Fn: "Exactly", Argsf: "len(arr), 42", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "Exactly", Argsf: "42, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "Exactly", Argsf: "value, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, value", + }, + { + Fn: "Exactly", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", + }, - {Fn: "True", Argsf: "len(arr) == 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "True", Argsf: "42 == len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, - {Fn: "True", Argsf: "len(arr) == value", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, - {Fn: "True", Argsf: "len(arr) == len(expArr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + { + Fn: "True", Argsf: "len(arr) == 42", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "True", Argsf: "42 == len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, 42", + }, + { + Fn: "True", Argsf: "len(arr) == value", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, value", + }, + { + Fn: "True", Argsf: "len(arr) == len(expArr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", + }, // Constant case. - {Fn: "Equal", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, - {Fn: "EqualValues", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, - {Fn: "Exactly", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, - {Fn: "True", Argsf: "len(arr) == constNum", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + { + Fn: "Equal", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", + }, + { + Fn: "EqualValues", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", + }, + { + Fn: "Exactly", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", + }, + { + Fn: "True", Argsf: "len(arr) == constNum", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", + }, + + // Type conversions cases. + { + Fn: "Equal", Argsf: "42, len(string(resp))", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "resp, 42", + }, + { + Fn: "Equal", Argsf: "42, len([]byte(resp))", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "resp, 42", + }, + { + Fn: "Equal", Argsf: "42, len(json.RawMessage(resp))", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "resp, 42", + }, + { + Fn: "Equal", Argsf: "42, len(string([]byte(json.RawMessage(resp))))", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "resp, 42", + }, + { + Fn: "True", Argsf: "len(string(resp)) == 42", ReportMsgf: reportUse, + ProposedFn: proposedFn, ProposedArgsf: "resp, 42", + }, + { + Fn: "Len", Argsf: "string(resp), 42", ReportMsgf: reportRemoveTypeConv, + ProposedArgsf: "resp, 42", + }, + { + Fn: "Len", Argsf: "[]byte(resp), 42", ReportMsgf: reportRemoveTypeConv, + ProposedArgsf: "resp, 42", + }, + { + Fn: "Len", Argsf: "json.RawMessage(resp), 42", ReportMsgf: reportRemoveTypeConv, + ProposedArgsf: "resp, 42", + }, + { + Fn: "Len", Argsf: "string([]byte(json.RawMessage(resp))), 42", + ReportMsgf: reportRemoveTypeConv, ProposedArgsf: "resp, 42", + }, }, ValidAssertions: []Assertion{ {Fn: "Len", Argsf: "arr, 42"}, {Fn: "Len", Argsf: "arr, value"}, {Fn: "Len", Argsf: "arr, len(arr)"}, + {Fn: "Len", Argsf: "String(10), 10"}, }, IgnoredAssertions: []Assertion{ {Fn: "Equal", Argsf: "len(arr), value"}, + {Fn: "Len", Argsf: "String(10), 10"}, {Fn: "EqualValues", Argsf: "len(arr), value"}, {Fn: "Exactly", Argsf: "len(arr), value"}, {Fn: "True", Argsf: "value == len(arr)"}, @@ -125,6 +224,8 @@ const lenTestTmpl = header + ` package {{ .CheckerName.AsPkgName }} import ( + "encoding/json" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -135,6 +236,7 @@ const constNum = 10 func {{ .CheckerName.AsTestName }}(t *testing.T) { var arr, expArr [3]int var value int + var resp []byte // Invalid. { @@ -157,4 +259,8 @@ func {{ .CheckerName.AsTestName }}(t *testing.T) { {{- end }} } } + +func String(n int) string { + return strings.Repeat("*", n) +} ` From 1bf6ee1a6901ba3db216f2528c012d25c2881eba Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sun, 17 Nov 2024 14:31:31 +0300 Subject: [PATCH 6/8] len: empty: add TestBytesReadability --- .../src/debug/len_type_conversions_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/analyzer/testdata/src/debug/len_type_conversions_test.go b/analyzer/testdata/src/debug/len_type_conversions_test.go index e1cea799..676e0888 100644 --- a/analyzer/testdata/src/debug/len_type_conversions_test.go +++ b/analyzer/testdata/src/debug/len_type_conversions_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/ghetzel/testify/assert" + "github.com/stretchr/testify/assert" ) func TestLenTypeConversions(t *testing.T) { @@ -18,3 +18,15 @@ func TestLenTypeConversions(t *testing.T) { assert.Len(t, []byte(resp), respLen) assert.Len(t, json.RawMessage(resp), respLen) } + +func TestBytesReadability(t *testing.T) { + resp := []byte(`{"status": 200}`) + assert.Len(t, resp, 3) // "[123 34 115 116 97 116 117 115 34 58 32 50 48 48 125]" should have 3 item(s), but has 15 + assert.Len(t, string(resp), 3) // "{"status": 200}" should have 3 item(s), but has 15 + + var noResp []byte + assert.Empty(t, resp) // Should be empty, but was [123 34 115 116 97 116 117 115 34 58 32 50 48 48 125] + assert.Empty(t, string(resp)) // Should be empty, but was {"status": 200} + assert.NotEmpty(t, noResp) // Should NOT be empty, but was [] + assert.NotEmpty(t, string(noResp)) // Should NOT be empty, but was +} From 286987268a12bf28ef24952e682484030103eaf2 Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sun, 17 Nov 2024 14:34:58 +0300 Subject: [PATCH 7/8] Revert "len: support []byte/string/raw.Message type conversions" This reverts commit e2e1092c2fbba49ab6633e55bb1f11957a5bf21a. --- README.md | 9 -- .../src/checkers-default/len/len_test.go | 109 +++++-------- .../checkers-default/len/len_test.go.golden | 29 ---- internal/checkers/encoded_compare.go | 7 - internal/checkers/helpers_diagnostic.go | 22 --- internal/checkers/len.go | 38 ----- internal/testgen/gen_len.go | 148 +++--------------- 7 files changed, 61 insertions(+), 301 deletions(-) diff --git a/README.md b/README.md index 4c603d54..7b4a22eb 100644 --- a/README.md +++ b/README.md @@ -657,15 +657,6 @@ assert.Len(t, arr, len(expArr)) > assert.Equal(t, len(arr), value) > ``` -Also, `len` repeats expected [gosimple](https://golangci-lint.run/usage/linters/#gosimple) (from staticcheck) -[behaviour](https://github.com/dominikh/go-tools/issues/760) and detects (and removes) unnecessary -`string/[]byte/json.RawMessage` conversions like - -```go -❌ assert.Len(t, string(headContents), 40) -✅ assert.Len(t, headContents, 40) -``` - --- ### negative-positive diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go b/analyzer/testdata/src/checkers-default/len/len_test.go index 08bcfa8a..4a42a859 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go +++ b/analyzer/testdata/src/checkers-default/len/len_test.go @@ -3,8 +3,6 @@ package len import ( - "encoding/json" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -15,68 +13,49 @@ const constNum = 10 func TestLenChecker(t *testing.T) { var arr, expArr [3]int var value int - var resp []byte // Invalid. { - assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" - assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" - assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, len(expArr), len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" - assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == value) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == len(expArr)) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.Equalf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.EqualValues(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.EqualValuesf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Exactly(t, constNum, len(arr)) // want "len: use assert\\.Len" - assert.Exactlyf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(arr) == constNum) // want "len: use assert\\.Len" - assert.Truef(t, len(arr) == constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(string(resp))) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(string(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len([]byte(resp))) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len([]byte(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(json.RawMessage(resp))) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(json.RawMessage(resp)), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Equal(t, 42, len(string([]byte(json.RawMessage(resp))))) // want "len: use assert\\.Len" - assert.Equalf(t, 42, len(string([]byte(json.RawMessage(resp)))), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.True(t, len(string(resp)) == 42) // want "len: use assert\\.Len" - assert.Truef(t, len(string(resp)) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, string(resp), 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, string(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, []byte(resp), 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, []byte(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, json.RawMessage(resp), 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, json.RawMessage(resp), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, string([]byte(json.RawMessage(resp))), 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, string([]byte(json.RawMessage(resp))), 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" + assert.Equal(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Equalf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(arr), 42) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, value, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(arr), 42) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(arr), 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, 42, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, 42, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, value, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, value, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, len(expArr), len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, len(expArr), len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == 42) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, 42 == len(arr)) // want "len: use assert\\.Len" + assert.Truef(t, 42 == len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == value) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == value, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == len(expArr)) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == len(expArr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Equal(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Equalf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.EqualValues(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.EqualValuesf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.Exactly(t, constNum, len(arr)) // want "len: use assert\\.Len" + assert.Exactlyf(t, constNum, len(arr), "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" + assert.True(t, len(arr) == constNum) // want "len: use assert\\.Len" + assert.Truef(t, len(arr) == constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" } // Valid. @@ -87,16 +66,12 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") - assert.Len(t, String(10), 10) - assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") } // Ignored. { assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.Len(t, String(10), 10) - assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) @@ -173,7 +148,3 @@ func TestLenChecker(t *testing.T) { assert.Falsef(t, len(arr) <= 42, "msg with args %d %s", 42, "42") } } - -func String(n int) string { - return strings.Repeat("*", n) -} diff --git a/analyzer/testdata/src/checkers-default/len/len_test.go.golden b/analyzer/testdata/src/checkers-default/len/len_test.go.golden index 5831d680..111395c7 100644 --- a/analyzer/testdata/src/checkers-default/len/len_test.go.golden +++ b/analyzer/testdata/src/checkers-default/len/len_test.go.golden @@ -3,8 +3,6 @@ package len import ( - "encoding/json" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -15,7 +13,6 @@ const constNum = 10 func TestLenChecker(t *testing.T) { var arr, expArr [3]int var value int - var resp []byte // Invalid. { @@ -59,24 +56,6 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" assert.Len(t, arr, constNum) // want "len: use assert\\.Len" assert.Lenf(t, arr, constNum, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: use assert\\.Len" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: use assert\\.Len" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: use assert\\.Len" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: use assert\\.Len" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: use assert\\.Len" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: use assert\\.Lenf" - assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" - assert.Len(t, resp, 42) // want "len: remove unnecessary type conversion" - assert.Lenf(t, resp, 42, "msg with args %d %s", 42, "42") // want "len: remove unnecessary type conversion" } // Valid. @@ -87,16 +66,12 @@ func TestLenChecker(t *testing.T) { assert.Lenf(t, arr, value, "msg with args %d %s", 42, "42") assert.Len(t, arr, len(arr)) assert.Lenf(t, arr, len(arr), "msg with args %d %s", 42, "42") - assert.Len(t, String(10), 10) - assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") } // Ignored. { assert.Equal(t, len(arr), value) assert.Equalf(t, len(arr), value, "msg with args %d %s", 42, "42") - assert.Len(t, String(10), 10) - assert.Lenf(t, String(10), 10, "msg with args %d %s", 42, "42") assert.EqualValues(t, len(arr), value) assert.EqualValuesf(t, len(arr), value, "msg with args %d %s", 42, "42") assert.Exactly(t, len(arr), value) @@ -173,7 +148,3 @@ func TestLenChecker(t *testing.T) { assert.Falsef(t, len(arr) <= 42, "msg with args %d %s", 42, "42") } } - -func String(n int) string { - return strings.Repeat("*", n) -} diff --git a/internal/checkers/encoded_compare.go b/internal/checkers/encoded_compare.go index ab40627a..1464fd64 100644 --- a/internal/checkers/encoded_compare.go +++ b/internal/checkers/encoded_compare.go @@ -23,13 +23,6 @@ import ( // // assert.JSONEq(t, `{"foo": "bar"}`, body) // assert.YAMLEq(t, expectedYML, conf) -// -// EncodedCompare detects JSON-style string constants (usable in fmt.Sprintf also) and JSON-style/YAML-style named -// variables. If variable is converted to json.RawMessage, then it is considered JSON unconditionally. -// -// When fixing, EncodedCompare removes unnecessary conversions to []byte, string, json.RawMessage and calls of -// strings.Replace, strings.ReplaceAll, strings.Trim, strings.TrimSpace, and adds a conversion to string when -// needed. type EncodedCompare struct{} // NewEncodedCompare constructs EncodedCompare checker. diff --git a/internal/checkers/helpers_diagnostic.go b/internal/checkers/helpers_diagnostic.go index 231fa5a1..f12d87aa 100644 --- a/internal/checkers/helpers_diagnostic.go +++ b/internal/checkers/helpers_diagnostic.go @@ -5,8 +5,6 @@ import ( "go/ast" "golang.org/x/tools/go/analysis" - - "github.com/Antonboom/testifylint/internal/analysisutil" ) func newRemoveFnAndUseDiagnostic( @@ -88,26 +86,6 @@ func newRemoveFnDiagnostic( newSuggestedFuncRemoving(pass, fnName, fnPos, fnArgs...)) } -func newRemoveTypeConversionDiagnostic( - pass *analysis.Pass, - checker string, - call *CallMeta, - convPos analysis.Range, - replaceWith ast.Expr, -) *analysis.Diagnostic { - return newDiagnostic(checker, call, "remove unnecessary type conversion", - analysis.SuggestedFix{ - Message: "Remove type conversions", - TextEdits: []analysis.TextEdit{ - { - Pos: convPos.Pos(), - End: convPos.End(), - NewText: analysisutil.NodeBytes(pass.Fset, replaceWith), - }, - }, - }) -} - func newDiagnostic( checker string, rng analysis.Range, diff --git a/internal/checkers/len.go b/internal/checkers/len.go index d1e9f052..9bdd8ff9 100644 --- a/internal/checkers/len.go +++ b/internal/checkers/len.go @@ -28,16 +28,11 @@ import ( // assert.Exactly(t, len(expArr), len(arr)) // assert.True(t, len(arr) == len(expArr)) // -// assert.Len(t, string(headContents), 40) -// assert.Len(t, []byte(headContents), 40) -// assert.Len(t, json.RawMessage(headContents), 40) -// // and requires // // assert.Len(t, arr, 42) // assert.Len(t, arr, value) // assert.Len(t, arr, len(expArr)) -// assert.Len(t, headContents, 40) // // The checker ignores assertions in which length checking is not a priority, e.g // @@ -71,16 +66,6 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost return nil } return checker.checkArgs(call, pass, be.Y, be.X, true) // In True, the actual value is usually first. - - case "Len": - if len(call.Args) < 2 { - return nil - } - a := call.Args[0] - - if cleaned, ok := checker.unwrap(pass, a); ok { - return newRemoveTypeConversionDiagnostic(pass, checker.Name(), call, a, cleaned) - } } return nil } @@ -88,13 +73,10 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost func (checker Len) checkArgs(call *CallMeta, pass *analysis.Pass, a, b ast.Expr, inverted bool) *analysis.Diagnostic { newUseLenDiagnostic := func(lenArg, expectedLen ast.Expr) *analysis.Diagnostic { const proposedFn = "Len" - start, end := a.Pos(), b.End() if inverted { start, end = b.Pos(), a.End() } - - lenArg, _ = checker.unwrap(pass, lenArg) return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, analysis.TextEdit{ Pos: start, @@ -121,23 +103,3 @@ func (checker Len) checkArgs(call *CallMeta, pass *analysis.Pass, a, b ast.Expr, return nil } - -// unwrap unwraps expression from string, []byte, and json.RawMessage type conversions. -// Return false if no conversion found. -func (checker Len) unwrap(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) { - ce, ok := e.(*ast.CallExpr) - if !ok { - return e, false - } - if len(ce.Args) == 0 { - return e, false - } - - if isIdentWithName("string", ce.Fun) || - isByteArray(ce.Fun) || - isJSONRawMessageCast(pass, ce) { - v, _ := checker.unwrap(pass, ce.Args[0]) - return v, true - } - return e, false -} diff --git a/internal/testgen/gen_len.go b/internal/testgen/gen_len.go index 70fe331d..ade360a8 100644 --- a/internal/testgen/gen_len.go +++ b/internal/testgen/gen_len.go @@ -16,10 +16,8 @@ func (LenTestsGenerator) Checker() checkers.Checker { func (g LenTestsGenerator) TemplateData() any { var ( checker = g.Checker().Name() + report = checker + ": use %s.%s" proposedFn = "Len" - - reportUse = checker + ": use %s.%s" - reportRemoveTypeConv = checker + ": remove unnecessary type conversion" ) return struct { @@ -30,139 +28,42 @@ func (g LenTestsGenerator) TemplateData() any { }{ CheckerName: CheckerName(checker), InvalidAssertions: []Assertion{ - { - Fn: "Equal", Argsf: "len(arr), 42", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "Equal", Argsf: "42, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "Equal", Argsf: "value, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, value", - }, - { - Fn: "Equal", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", - }, + {Fn: "Equal", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "Equal", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "Equal", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "Equal", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, + {Fn: "EqualValues", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "EqualValues", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "EqualValues", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, { - Fn: "EqualValues", Argsf: "len(arr), 42", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "EqualValues", Argsf: "42, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "EqualValues", Argsf: "value, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, value", - }, - { - Fn: "EqualValues", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, + Fn: "EqualValues", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", }, - { - Fn: "Exactly", Argsf: "len(arr), 42", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "Exactly", Argsf: "42, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "Exactly", Argsf: "value, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, value", - }, - { - Fn: "Exactly", Argsf: "len(expArr), len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", - }, + {Fn: "Exactly", Argsf: "len(arr), 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "Exactly", Argsf: "42, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "Exactly", Argsf: "value, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "Exactly", Argsf: "len(expArr), len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, - { - Fn: "True", Argsf: "len(arr) == 42", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "True", Argsf: "42 == len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, 42", - }, - { - Fn: "True", Argsf: "len(arr) == value", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, value", - }, - { - Fn: "True", Argsf: "len(arr) == len(expArr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)", - }, + {Fn: "True", Argsf: "len(arr) == 42", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "True", Argsf: "42 == len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, 42"}, + {Fn: "True", Argsf: "len(arr) == value", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, value"}, + {Fn: "True", Argsf: "len(arr) == len(expArr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, len(expArr)"}, // Constant case. - { - Fn: "Equal", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", - }, - { - Fn: "EqualValues", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", - }, - { - Fn: "Exactly", Argsf: "constNum, len(arr)", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", - }, - { - Fn: "True", Argsf: "len(arr) == constNum", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "arr, constNum", - }, - - // Type conversions cases. - { - Fn: "Equal", Argsf: "42, len(string(resp))", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "resp, 42", - }, - { - Fn: "Equal", Argsf: "42, len([]byte(resp))", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "resp, 42", - }, - { - Fn: "Equal", Argsf: "42, len(json.RawMessage(resp))", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "resp, 42", - }, - { - Fn: "Equal", Argsf: "42, len(string([]byte(json.RawMessage(resp))))", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "resp, 42", - }, - { - Fn: "True", Argsf: "len(string(resp)) == 42", ReportMsgf: reportUse, - ProposedFn: proposedFn, ProposedArgsf: "resp, 42", - }, - { - Fn: "Len", Argsf: "string(resp), 42", ReportMsgf: reportRemoveTypeConv, - ProposedArgsf: "resp, 42", - }, - { - Fn: "Len", Argsf: "[]byte(resp), 42", ReportMsgf: reportRemoveTypeConv, - ProposedArgsf: "resp, 42", - }, - { - Fn: "Len", Argsf: "json.RawMessage(resp), 42", ReportMsgf: reportRemoveTypeConv, - ProposedArgsf: "resp, 42", - }, - { - Fn: "Len", Argsf: "string([]byte(json.RawMessage(resp))), 42", - ReportMsgf: reportRemoveTypeConv, ProposedArgsf: "resp, 42", - }, + {Fn: "Equal", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "EqualValues", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "Exactly", Argsf: "constNum, len(arr)", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, + {Fn: "True", Argsf: "len(arr) == constNum", ReportMsgf: report, ProposedFn: proposedFn, ProposedArgsf: "arr, constNum"}, }, ValidAssertions: []Assertion{ {Fn: "Len", Argsf: "arr, 42"}, {Fn: "Len", Argsf: "arr, value"}, {Fn: "Len", Argsf: "arr, len(arr)"}, - {Fn: "Len", Argsf: "String(10), 10"}, }, IgnoredAssertions: []Assertion{ {Fn: "Equal", Argsf: "len(arr), value"}, - {Fn: "Len", Argsf: "String(10), 10"}, {Fn: "EqualValues", Argsf: "len(arr), value"}, {Fn: "Exactly", Argsf: "len(arr), value"}, {Fn: "True", Argsf: "value == len(arr)"}, @@ -224,8 +125,6 @@ const lenTestTmpl = header + ` package {{ .CheckerName.AsPkgName }} import ( - "encoding/json" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -236,7 +135,6 @@ const constNum = 10 func {{ .CheckerName.AsTestName }}(t *testing.T) { var arr, expArr [3]int var value int - var resp []byte // Invalid. { @@ -259,8 +157,4 @@ func {{ .CheckerName.AsTestName }}(t *testing.T) { {{- end }} } } - -func String(n int) string { - return strings.Repeat("*", n) -} ` From bd361afeb0ad1be9067c619b9e467739ea1858ed Mon Sep 17 00:00:00 2001 From: Anton Telyshev Date: Sun, 17 Nov 2024 14:48:22 +0300 Subject: [PATCH 8/8] debug: fix typo --- analyzer/testdata/src/debug/len_type_conversions_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analyzer/testdata/src/debug/len_type_conversions_test.go b/analyzer/testdata/src/debug/len_type_conversions_test.go index 676e0888..21040f46 100644 --- a/analyzer/testdata/src/debug/len_type_conversions_test.go +++ b/analyzer/testdata/src/debug/len_type_conversions_test.go @@ -8,8 +8,8 @@ import ( ) func TestLenTypeConversions(t *testing.T) { - const resp = "Mutlti-языковая string 你好世界 🙂" - const respLen = len(resp) // 48 + const resp = "Multi-языковая string 你好世界 🙂" + const respLen = len(resp) // 47 assert.Equal(t, respLen, len(resp)) assert.Equal(t, respLen, len([]byte(resp)))