From 5a0b597017aaa294d61ab744d4372bda20667c41 Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Thu, 20 Jan 2022 21:25:19 +0300 Subject: [PATCH] ruleguard: implement Type.IdenticalTo method --- analyzer/testdata/src/filtertest/f1.go | 41 +++++++++++++++++++ analyzer/testdata/src/filtertest/rules.go | 4 ++ go.mod | 2 +- go.sum | 2 + ruleguard/filters.go | 11 +++++ ruleguard/ir/filter_op.gen.go | 50 +++++++++++++---------- ruleguard/ir/gen_filter_op.go | 1 + ruleguard/ir_loader.go | 5 +++ ruleguard/irconv/irconv.go | 12 ++++++ 9 files changed, 105 insertions(+), 23 deletions(-) diff --git a/analyzer/testdata/src/filtertest/f1.go b/analyzer/testdata/src/filtertest/f1.go index a0d45dc..a3b304e 100644 --- a/analyzer/testdata/src/filtertest/f1.go +++ b/analyzer/testdata/src/filtertest/f1.go @@ -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 diff --git a/analyzer/testdata/src/filtertest/rules.go b/analyzer/testdata/src/filtertest/rules.go index 472e112..9903a36 100644 --- a/analyzer/testdata/src/filtertest/rules.go +++ b/analyzer/testdata/src/filtertest/rules.go @@ -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`) } diff --git a/go.mod b/go.mod index 995632e..5a69621 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 9cd6ca3..fb667d0 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/ruleguard/filters.go b/ruleguard/filters.go index f2656d1..7d93bcb 100644 --- a/ruleguard/filters.go +++ b/ruleguard/filters.go @@ -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 { diff --git a/ruleguard/ir/filter_op.gen.go b/ruleguard/ir/filter_op.gen.go index 766d8e8..c3b022d 100644 --- a/ruleguard/ir/filter_op.gen.go +++ b/ruleguard/ir/filter_op.gen.go @@ -84,91 +84,95 @@ const ( // $Value type: string FilterVarTypeIsOp FilterOp = 22 + // m[$Value].Type.IdenticalTo($Args[0]) + // $Value type: string + FilterVarTypeIdenticalToOp FilterOp = 23 + // m[$Value].Type.Underlying().Is($Args[0]) // $Value type: string - FilterVarTypeUnderlyingIsOp FilterOp = 23 + FilterVarTypeUnderlyingIsOp FilterOp = 24 // m[$Value].Type.OfKind($Args[0]) // $Value type: string - FilterVarTypeOfKindOp FilterOp = 24 + FilterVarTypeOfKindOp FilterOp = 25 // m[$Value].Type.Underlying().OfKind($Args[0]) // $Value type: string - FilterVarTypeUnderlyingOfKindOp FilterOp = 25 + FilterVarTypeUnderlyingOfKindOp FilterOp = 26 // m[$Value].Type.ConvertibleTo($Args[0]) // $Value type: string - FilterVarTypeConvertibleToOp FilterOp = 26 + FilterVarTypeConvertibleToOp FilterOp = 27 // m[$Value].Type.AssignableTo($Args[0]) // $Value type: string - FilterVarTypeAssignableToOp FilterOp = 27 + FilterVarTypeAssignableToOp FilterOp = 28 // m[$Value].Type.Implements($Args[0]) // $Value type: string - FilterVarTypeImplementsOp FilterOp = 28 + FilterVarTypeImplementsOp FilterOp = 29 // m[$Value].Type.HasMethod($Args[0]) // $Value type: string - FilterVarTypeHasMethodOp FilterOp = 29 + FilterVarTypeHasMethodOp FilterOp = 30 // m[$Value].Text.Matches($Args[0]) // $Value type: string - FilterVarTextMatchesOp FilterOp = 30 + FilterVarTextMatchesOp FilterOp = 31 // m[$Value].Contains($Args[0]) // $Value type: string - FilterVarContainsOp FilterOp = 31 + FilterVarContainsOp FilterOp = 32 // m.Deadcode() - FilterDeadcodeOp FilterOp = 32 + FilterDeadcodeOp FilterOp = 33 // m.GoVersion().Eq($Value) // $Value type: string - FilterGoVersionEqOp FilterOp = 33 + FilterGoVersionEqOp FilterOp = 34 // m.GoVersion().LessThan($Value) // $Value type: string - FilterGoVersionLessThanOp FilterOp = 34 + FilterGoVersionLessThanOp FilterOp = 35 // m.GoVersion().GreaterThan($Value) // $Value type: string - FilterGoVersionGreaterThanOp FilterOp = 35 + FilterGoVersionGreaterThanOp FilterOp = 36 // m.GoVersion().LessEqThan($Value) // $Value type: string - FilterGoVersionLessEqThanOp FilterOp = 36 + FilterGoVersionLessEqThanOp FilterOp = 37 // m.GoVersion().GreaterEqThan($Value) // $Value type: string - FilterGoVersionGreaterEqThanOp FilterOp = 37 + FilterGoVersionGreaterEqThanOp FilterOp = 38 // m.File.Imports($Value) // $Value type: string - FilterFileImportsOp FilterOp = 38 + FilterFileImportsOp FilterOp = 39 // m.File.PkgPath.Matches($Value) // $Value type: string - FilterFilePkgPathMatchesOp FilterOp = 39 + FilterFilePkgPathMatchesOp FilterOp = 40 // m.File.Name.Matches($Value) // $Value type: string - FilterFileNameMatchesOp FilterOp = 40 + FilterFileNameMatchesOp FilterOp = 41 // $Value holds a function name // $Value type: string - FilterFilterFuncRefOp FilterOp = 41 + FilterFilterFuncRefOp FilterOp = 42 // $Value holds a string constant // $Value type: string - FilterStringOp FilterOp = 42 + FilterStringOp FilterOp = 43 // $Value holds an int64 constant // $Value type: int64 - FilterIntOp FilterOp = 43 + FilterIntOp FilterOp = 44 // m[`$$`].Node.Parent().Is($Args[0]) - FilterRootNodeParentIsOp FilterOp = 44 + FilterRootNodeParentIsOp FilterOp = 45 ) var filterOpNames = map[FilterOp]string{ @@ -195,6 +199,7 @@ var filterOpNames = map[FilterOp]string{ FilterVarNodeIsOp: `VarNodeIs`, FilterVarObjectIsOp: `VarObjectIs`, FilterVarTypeIsOp: `VarTypeIs`, + FilterVarTypeIdenticalToOp: `VarTypeIdenticalTo`, FilterVarTypeUnderlyingIsOp: `VarTypeUnderlyingIs`, FilterVarTypeOfKindOp: `VarTypeOfKind`, FilterVarTypeUnderlyingOfKindOp: `VarTypeUnderlyingOfKind`, @@ -240,6 +245,7 @@ var filterOpFlags = map[FilterOp]uint64{ FilterVarNodeIsOp: flagHasVar, FilterVarObjectIsOp: flagHasVar, FilterVarTypeIsOp: flagHasVar, + FilterVarTypeIdenticalToOp: flagHasVar, FilterVarTypeUnderlyingIsOp: flagHasVar, FilterVarTypeOfKindOp: flagHasVar, FilterVarTypeUnderlyingOfKindOp: flagHasVar, diff --git a/ruleguard/ir/gen_filter_op.go b/ruleguard/ir/gen_filter_op.go index ea3bab9..727a805 100644 --- a/ruleguard/ir/gen_filter_op.go +++ b/ruleguard/ir/gen_filter_op.go @@ -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}, diff --git a/ruleguard/ir_loader.go b/ruleguard/ir_loader.go index e50c843..01f59bf 100644 --- a/ruleguard/ir_loader.go +++ b/ruleguard/ir_loader.go @@ -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 == "" { diff --git a/ruleguard/irconv/irconv.go b/ruleguard/irconv/irconv.go index f8efd53..e30339e 100644 --- a/ruleguard/irconv/irconv.go +++ b/ruleguard/irconv/irconv.go @@ -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 {