Skip to content

Commit

Permalink
ruleguard: implement Type.IdenticalTo method (#365)
Browse files Browse the repository at this point in the history
  • Loading branch information
quasilyte committed Jan 20, 2022
1 parent fdceb55 commit c57998e
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 23 deletions.
41 changes: 41 additions & 0 deletions analyzer/testdata/src/filtertest/f1.go
Expand Up @@ -674,6 +674,47 @@ func detectConvertibleTo(x int, xs []int) {
typeTest("", xs[:], "variadic convertible to string") // want `false`
}

func detectIdenticalTypes() {
{
typeTest(1, 1, "identical types") // want `true`
typeTest("a", "b", "identical types") // want `true`
typeTest([]int{}, []int{1}, "identical types") // want `true`
typeTest([]int32{}, []int32{1}, "identical types") // want `true`
typeTest([]int32{}, []rune{}, "identical types") // want `true`
typeTest([4]int{}, [4]int{}, "identical types") // want `true`

typeTest(1, 1.5, "identical types")
typeTest("ok", 1.5, "identical types")
typeTest([]int{}, []int32{1}, "identical types")
typeTest([4]int{}, [3]int{}, "identical types")

type point struct {
x, y float64
}
type myString string
var s string
var myStr myString
var pt point
ppt := &point{}
var eface interface{}
var i int
typeTest([]point{}, []point{}, "identical types") // want `true`
typeTest(s, s, "identical types") // want `true`
typeTest(myStr, myStr, "identical types") // want `true`
typeTest(pt, pt, "identical types") // want `true`
typeTest(&pt, ppt, "identical types") // want `true`
typeTest(eface, eface, "identical types") // want `true`
typeTest([]point{}, [1]point{}, "identical types")
typeTest(myStr, s, "identical types")
typeTest(s, myStr, "identical types")
typeTest(ppt, pt, "identical types")
typeTest(eface, pt, "identical types")
typeTest(eface, ppt, "identical types")
typeTest(i, eface, "identical types")
typeTest(eface, i, "identical types")
}
}

func detectAssignableTo(x int, xs []int) {
type newString string
type stringAlias = string
Expand Down
4 changes: 4 additions & 0 deletions analyzer/testdata/src/filtertest/rules.go
Expand Up @@ -259,4 +259,8 @@ func testRules(m dsl.Matcher) {
m.Match(`typeTest($x, "is func")`).
Where(m["x"].Type.Is(`func ($*_) $*_`)).
Report(`true`)

m.Match(`typeTest($x, $y, "identical types")`).
Where(m["x"].Type.IdenticalTo(m["y"])).
Report(`true`)
}
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -5,7 +5,7 @@ go 1.17
require (
github.com/go-toolsmith/astcopy v1.0.0
github.com/google/go-cmp v0.5.6
github.com/quasilyte/go-ruleguard/dsl v0.3.14
github.com/quasilyte/go-ruleguard/dsl v0.3.15
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5
golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -14,6 +14,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.13 h1:WmtzUkp28TMarzfBCogPf7plyI/2gsN
github.com/quasilyte/go-ruleguard/dsl v0.3.13/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.14 h1:diesHrFHZ6rxuFltuwiW7NRQaqUIypuSSmUulRCNuqM=
github.com/quasilyte/go-ruleguard/dsl v0.3.14/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.15 h1:rClYn6lk8wUV5kXnQG4JVsRQjZhSetaNtwml5wkFp5g=
github.com/quasilyte/go-ruleguard/dsl v0.3.15/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
Expand Down
11 changes: 11 additions & 0 deletions ruleguard/filters.go
Expand Up @@ -278,6 +278,17 @@ func makeTypeOfKindFilter(src, varname string, underlying bool, kind types.Basic
}
}

func makeTypesIdenticalFilter(src, lhsVarname, rhsVarname string) filterFunc {
return func(params *filterParams) matchFilterResult {
lhsType := params.typeofNode(params.subNode(lhsVarname))
rhsType := params.typeofNode(params.subNode(rhsVarname))
if xtypes.Identical(lhsType, rhsType) {
return filterSuccess
}
return filterFailure(src)
}
}

func makeTypeIsFilter(src, varname string, underlying bool, pat *typematch.Pattern) filterFunc {
if underlying {
return func(params *filterParams) matchFilterResult {
Expand Down
50 changes: 28 additions & 22 deletions ruleguard/ir/filter_op.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ruleguard/ir/gen_filter_op.go
Expand Up @@ -53,6 +53,7 @@ func main() {
{name: "VarNodeIs", comment: "m[$Value].Node.Is($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarObjectIs", comment: "m[$Value].Object.Is($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarTypeIs", comment: "m[$Value].Type.Is($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarTypeIdenticalTo", comment: "m[$Value].Type.IdenticalTo($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarTypeUnderlyingIs", comment: "m[$Value].Type.Underlying().Is($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarTypeOfKind", comment: "m[$Value].Type.OfKind($Args[0])", valueType: "string", flags: flagHasVar},
{name: "VarTypeUnderlyingOfKind", comment: "m[$Value].Type.Underlying().OfKind($Args[0])", valueType: "string", flags: flagHasVar},
Expand Down
5 changes: 5 additions & 0 deletions ruleguard/ir_loader.go
Expand Up @@ -622,6 +622,11 @@ func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilte
result.fn = makeTypeOfKindFilter(result.src, filter.Value.(string), underlying, kind)
}

case ir.FilterVarTypeIdenticalToOp:
lhsVarname := filter.Value.(string)
rhsVarname := filter.Args[0].Value.(string)
result.fn = makeTypesIdenticalFilter(result.src, lhsVarname, rhsVarname)

case ir.FilterVarTypeIsOp, ir.FilterVarTypeUnderlyingIsOp:
typeString := l.unwrapStringExpr(filter.Args[0])
if typeString == "" {
Expand Down
12 changes: 12 additions & 0 deletions ruleguard/irconv/irconv.go
Expand Up @@ -671,6 +671,18 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr {
},
}

case "Type.IdenticalTo":
// TODO: reuse the code with parsing At() args?
index, ok := e.Args[0].(*ast.IndexExpr)
if !ok {
panic(conv.errorf(e.Args[0], "expected %s[`varname`] expression", conv.group.MatcherName))
}
rhsVarname := conv.parseStringArg(index.Index)
args := []ir.FilterExpr{
{Op: ir.FilterStringOp, Value: rhsVarname},
}
return ir.FilterExpr{Op: ir.FilterVarTypeIdenticalToOp, Value: op.varName, Args: args}

case "Filter":
funcName, ok := e.Args[0].(*ast.Ident)
if !ok {
Expand Down

0 comments on commit c57998e

Please sign in to comment.