Skip to content

Commit ac9da7d

Browse files
authored
compares: support Same/NotSame (#96)
* compares: support Same/NotSame * compares: simplify the code
1 parent eb12578 commit ac9da7d

File tree

7 files changed

+120
-34
lines changed

7 files changed

+120
-34
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ To turn off this behavior use the `--bool-compare.ignore-custom-types` flag.
191191
**Enabled by default**: true. <br>
192192
**Reason**: More appropriate `testify` API with clearer failure message.
193193

194+
If `a` and `b` are pointers then `assert.Same`/`NotSame` is required instead,
195+
due to the inappropriate recursive nature of `assert.Equal` (based on
196+
[reflect.DeepEqual](https://pkg.go.dev/reflect#DeepEqual)).
197+
194198
---
195199

196200
### empty

analyzer/testdata/src/checkers-default/compares/compares_test.go

+37-24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

analyzer/testdata/src/checkers-default/compares/compares_test.go.golden

+13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
func TestComparesChecker(t *testing.T) {
1212
var a, b int
1313
var c, d bool
14+
var ptrA, ptrB *int
1415

1516
// Invalid.
1617
{
@@ -38,6 +39,14 @@ func TestComparesChecker(t *testing.T) {
3839
assert.GreaterOrEqualf(t, a, b, "msg with args %d %s", 42, "42") // want "compares: use assert\\.GreaterOrEqualf"
3940
assert.Greater(t, a, b) // want "compares: use assert\\.Greater"
4041
assert.Greaterf(t, a, b, "msg with args %d %s", 42, "42") // want "compares: use assert\\.Greaterf"
42+
assert.Same(t, ptrA, ptrB) // want "compares: use assert\\.Same"
43+
assert.Samef(t, ptrA, ptrB, "msg with args %d %s", 42, "42") // want "compares: use assert\\.Samef"
44+
assert.NotSame(t, ptrA, ptrB) // want "compares: use assert\\.NotSame"
45+
assert.NotSamef(t, ptrA, ptrB, "msg with args %d %s", 42, "42") // want "compares: use assert\\.NotSamef"
46+
assert.NotSame(t, ptrA, ptrB) // want "compares: use assert\\.NotSame"
47+
assert.NotSamef(t, ptrA, ptrB, "msg with args %d %s", 42, "42") // want "compares: use assert\\.NotSamef"
48+
assert.Same(t, ptrA, ptrB) // want "compares: use assert\\.Same"
49+
assert.Samef(t, ptrA, ptrB, "msg with args %d %s", 42, "42") // want "compares: use assert\\.Samef"
4150
}
4251

4352
// Valid.
@@ -54,6 +63,10 @@ func TestComparesChecker(t *testing.T) {
5463
assert.Lessf(t, a, b, "msg with args %d %s", 42, "42")
5564
assert.LessOrEqual(t, a, b)
5665
assert.LessOrEqualf(t, a, b, "msg with args %d %s", 42, "42")
66+
assert.Same(t, ptrA, ptrB)
67+
assert.Samef(t, ptrA, ptrB, "msg with args %d %s", 42, "42")
68+
assert.NotSame(t, ptrA, ptrB)
69+
assert.NotSamef(t, ptrA, ptrB, "msg with args %d %s", 42, "42")
5770
}
5871

5972
// Ignored.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package debug
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestSameNotSameReplacements(t *testing.T) {
11+
var tm tMock
12+
13+
a := new(os.PathError)
14+
b := new(os.PathError)
15+
c := &os.PathError{Path: "/tmp"}
16+
17+
// Invalid.
18+
assert.Equal(t, assert.True(tm, a == b), assert.Equal(tm, a, b))
19+
assert.Equal(t, assert.True(tm, a != b), assert.NotEqual(tm, a, b))
20+
assert.Equal(t, assert.True(tm, a == c), assert.Equal(tm, a, c))
21+
assert.Equal(t, assert.True(tm, a != c), assert.NotEqual(tm, a, c))
22+
23+
// Valid.
24+
assert.Equal(t, assert.True(tm, a == b), assert.Same(tm, a, b))
25+
assert.Equal(t, assert.True(tm, a != b), assert.NotSame(tm, a, b))
26+
assert.Equal(t, assert.True(tm, a == c), assert.Same(tm, a, c))
27+
assert.Equal(t, assert.True(tm, a != c), assert.NotSame(tm, a, c))
28+
}

internal/checkers/compares.go

+24-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import (
2626
// assert.GreaterOrEqual(t, a, b)
2727
// assert.Less(t, a, b)
2828
// assert.LessOrEqual(t, a, b)
29+
//
30+
// If `a` and `b` are pointers then `assert.Same`/`NotSame` is required instead,
31+
// due to the inappropriate recursive nature of `assert.Equal` (based on `reflect.DeepEqual`).
2932
type Compares struct{}
3033

3134
// NewCompares constructs Compares checker.
@@ -53,17 +56,28 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
5356
return nil
5457
}
5558

56-
if proposedFn, ok := tokenToProposedFn[be.Op]; ok {
57-
a, b := be.X, be.Y
58-
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
59-
newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
60-
Pos: be.X.Pos(),
61-
End: be.Y.End(),
62-
NewText: formatAsCallArgs(pass, a, b),
63-
}),
64-
)
59+
proposedFn, ok := tokenToProposedFn[be.Op]
60+
if !ok {
61+
return nil
62+
}
63+
64+
if isPointer(pass, be.X) && isPointer(pass, be.Y) {
65+
switch proposedFn {
66+
case "Equal":
67+
proposedFn = "Same"
68+
case "NotEqual":
69+
proposedFn = "NotSame"
70+
}
6571
}
66-
return nil
72+
73+
a, b := be.X, be.Y
74+
return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
75+
newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
76+
Pos: be.X.Pos(),
77+
End: be.Y.End(),
78+
NewText: formatAsCallArgs(pass, a, b),
79+
}),
80+
)
6781
}
6882

6983
var tokenToProposedFnInsteadOfTrue = map[token.Token]string{

internal/checkers/helpers_basic_type.go

+5
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ func isFloat(pass *analysis.Pass, expr ast.Expr) bool {
5454
bt, ok := t.Underlying().(*types.Basic)
5555
return ok && (bt.Info()&types.IsFloat > 0)
5656
}
57+
58+
func isPointer(pass *analysis.Pass, expr ast.Expr) bool {
59+
_, ok := pass.TypesInfo.TypeOf(expr).(*types.Pointer)
60+
return ok
61+
}

internal/testgen/gen_compares.go

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ func (g ComparesTestsGenerator) TemplateData() any {
5151
{Fn: "False", Argsf: "a >= b", ReportMsgf: report, ProposedFn: "Less", ProposedArgsf: "a, b"},
5252
{Fn: "False", Argsf: "a < b", ReportMsgf: report, ProposedFn: "GreaterOrEqual", ProposedArgsf: "a, b"},
5353
{Fn: "False", Argsf: "a <= b", ReportMsgf: report, ProposedFn: "Greater", ProposedArgsf: "a, b"},
54+
55+
{Fn: "True", Argsf: "ptrA == ptrB", ReportMsgf: report, ProposedFn: "Same", ProposedArgsf: "ptrA, ptrB"},
56+
{Fn: "True", Argsf: "ptrA != ptrB", ReportMsgf: report, ProposedFn: "NotSame", ProposedArgsf: "ptrA, ptrB"},
57+
{Fn: "False", Argsf: "ptrA == ptrB", ReportMsgf: report, ProposedFn: "NotSame", ProposedArgsf: "ptrA, ptrB"},
58+
{Fn: "False", Argsf: "ptrA != ptrB", ReportMsgf: report, ProposedFn: "Same", ProposedArgsf: "ptrA, ptrB"},
5459
},
5560
ValidAssertions: []Assertion{
5661
{Fn: "Equal", Argsf: "a, b"},
@@ -59,6 +64,9 @@ func (g ComparesTestsGenerator) TemplateData() any {
5964
{Fn: "GreaterOrEqual", Argsf: "a, b"},
6065
{Fn: "Less", Argsf: "a, b"},
6166
{Fn: "LessOrEqual", Argsf: "a, b"},
67+
68+
{Fn: "Same", Argsf: "ptrA, ptrB"},
69+
{Fn: "NotSame", Argsf: "ptrA, ptrB"},
6270
},
6371
IgnoredAssertions: ignored,
6472
}
@@ -89,6 +97,7 @@ import (
8997
func {{ .CheckerName.AsTestName }}(t *testing.T) {
9098
var a, b int
9199
var c, d bool
100+
var ptrA, ptrB *int
92101
93102
// Invalid.
94103
{

0 commit comments

Comments
 (0)