Skip to content

Commit b6d904e

Browse files
authored
ruleguard: implement Var.Comparable predicate (#382)
1 parent c887ed9 commit b6d904e

File tree

10 files changed

+133
-37
lines changed

10 files changed

+133
-37
lines changed

analyzer/testdata/src/filtertest/f1.go

+63
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,69 @@ func detectValue() {
133133
valueTest(0, 5, "variadic value 5") // want `false`
134134
}
135135

136+
func detectComparable() {
137+
typeTest("", "comparable") // want `true`
138+
typeTest(0, "comparable") // want `true`
139+
140+
type good1 struct {
141+
x, y int
142+
}
143+
type good2 struct {
144+
nested good1
145+
x [2]byte
146+
s string
147+
}
148+
type good3 struct {
149+
x *int
150+
}
151+
type good4 struct {
152+
*good3
153+
good2
154+
}
155+
156+
typeTest(good1{}, "comparable") // want `true`
157+
typeTest(good2{}, "comparable") // want `true`
158+
typeTest(good3{}, "comparable") // want `true`
159+
typeTest(good4{}, "comparable") // want `true`
160+
typeTest(&good1{}, "comparable") // want `true`
161+
typeTest(&good2{}, "comparable") // want `true`
162+
typeTest(&good3{}, "comparable") // want `true`
163+
typeTest(&good4{}, "comparable") // want `true`
164+
165+
var (
166+
g1 good1
167+
g2 good2
168+
g3 good3
169+
g4 good4
170+
)
171+
_ = g1 == good1{}
172+
_ = g2 == good2{}
173+
_ = g3 == good3{}
174+
_ = g4 == good4{}
175+
_ = g1 != good1{}
176+
_ = g2 != good2{}
177+
_ = g3 != good3{}
178+
_ = g4 != good4{}
179+
180+
type bad1 struct {
181+
_ [1]func()
182+
}
183+
type bad2 struct {
184+
slice []int
185+
}
186+
type bad3 struct {
187+
bad2
188+
}
189+
190+
typeTest(bad1{}, "comparable")
191+
typeTest(bad2{}, "comparable")
192+
typeTest(bad3{}, "comparable")
193+
194+
typeTest(&bad1{}, "comparable") // want `true`
195+
typeTest(&bad2{}, "comparable") // want `true`
196+
typeTest(&bad3{}, "comparable") // want `true`
197+
}
198+
136199
func detectType() {
137200
{
138201
var s fmt.Stringer

analyzer/testdata/src/filtertest/rules.go

+4
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ func testRules(m dsl.Matcher) {
264264
Where(m["x"].Type.IdenticalTo(m["y"])).
265265
Report(`true`)
266266

267+
m.Match(`typeTest($x, "comparable")`).
268+
Where(m["x"].Comparable).
269+
Report(`true`)
270+
267271
m.Match(`$x = time.Now().String()`,
268272
`var $x = time.Now().String()`,
269273
`var $x $_ = time.Now().String()`,

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.17
55
require (
66
github.com/go-toolsmith/astcopy v1.0.0
77
github.com/google/go-cmp v0.5.6
8-
github.com/quasilyte/go-ruleguard/dsl v0.3.17
8+
github.com/quasilyte/go-ruleguard/dsl v0.3.18
99
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71
1010
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5
1111
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1
1212
github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
1313
github.com/quasilyte/go-ruleguard/dsl v0.3.17 h1:L5xf3nifnRIdYe9vyMuY2sDnZHIgQol/fDq74FQz7ZY=
1414
github.com/quasilyte/go-ruleguard/dsl v0.3.17/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
15+
github.com/quasilyte/go-ruleguard/dsl v0.3.18 h1:gzHcFxmTwhn+ZKZd6nGw7JyjoDcYuwcA+TY5MNn9oMk=
16+
github.com/quasilyte/go-ruleguard/dsl v0.3.18/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
1517
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
1618
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI=
1719
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=

ruleguard/filters.go

+15
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ func makeAddressableFilter(src, varname string) filterFunc {
160160
}
161161
}
162162

163+
func makeComparableFilter(src, varname string) filterFunc {
164+
return func(params *filterParams) matchFilterResult {
165+
if list, ok := params.subNode(varname).(gogrep.ExprSlice); ok {
166+
return exprListFilterApply(src, list, func(x ast.Expr) bool {
167+
return types.Comparable(params.typeofNode(x))
168+
})
169+
}
170+
171+
if types.Comparable(params.typeofNode(params.subNode(varname))) {
172+
return filterSuccess
173+
}
174+
return filterFailure(src)
175+
}
176+
}
177+
163178
func makeVarContainsFilter(src, varname string, pat *gogrep.Pattern) filterFunc {
164179
return func(params *filterParams) matchFilterResult {
165180
params.gogrepSubState.CapturePreset = params.match.CaptureList()

ruleguard/ir/filter_op.gen.go

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

ruleguard/ir/gen_filter_op.go

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func main() {
4141
{name: "LtEq", comment: "$Args[0] <= $Args[1]", flags: flagIsBinaryExpr},
4242

4343
{name: "VarAddressable", comment: "m[$Value].Addressable", valueType: "string", flags: flagHasVar},
44+
{name: "VarComparable", comment: "m[$Value].Comparable", valueType: "string", flags: flagHasVar},
4445
{name: "VarPure", comment: "m[$Value].Pure", valueType: "string", flags: flagHasVar},
4546
{name: "VarConst", comment: "m[$Value].Const", valueType: "string", flags: flagHasVar},
4647
{name: "VarConstSlice", comment: "m[$Value].ConstSlice", valueType: "string", flags: flagHasVar},

ruleguard/ir_loader.go

+2
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilte
691691
result.fn = makeConstSliceFilter(result.src, filter.Value.(string))
692692
case ir.FilterVarAddressableOp:
693693
result.fn = makeAddressableFilter(result.src, filter.Value.(string))
694+
case ir.FilterVarComparableOp:
695+
result.fn = makeComparableFilter(result.src, filter.Value.(string))
694696

695697
case ir.FilterFileImportsOp:
696698
result.fn = makeFileImportsFilter(result.src, filter.Value.(string))

ruleguard/irconv/irconv.go

+2
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,8 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr {
663663
return ir.FilterExpr{Op: ir.FilterVarConstSliceOp, Value: op.varName}
664664
case "Addressable":
665665
return ir.FilterExpr{Op: ir.FilterVarAddressableOp, Value: op.varName}
666+
case "Comparable":
667+
return ir.FilterExpr{Op: ir.FilterVarComparableOp, Value: op.varName}
666668
case "Type.Size":
667669
return ir.FilterExpr{Op: ir.FilterVarTypeSizeOp, Value: op.varName}
668670
}

ruleguard/irconv/irconv_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func TestConvFilterExpr(t *testing.T) {
1919
{`m["x"].Pure`, `(VarPure ["x"])`},
2020
{`m["x"].Const`, `(VarConst ["x"])`},
2121
{`m["x"].Addressable`, `(VarAddressable ["x"])`},
22+
{`m["x"].Comparable`, `(VarComparable ["x"])`},
2223

2324
// Parens should not break the conversion.
2425
{`(m["x"].Pure)`, `(VarPure ["x"])`},

0 commit comments

Comments
 (0)