From 50480789be61cd5b76424b536f64e45ec6bfb34b Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Sat, 1 Jan 2022 04:08:25 +0300 Subject: [PATCH] ruleguard: implement Type.OfKind filter --- analyzer/testdata/src/filtertest/f1.go | 97 ++++++++++++ analyzer/testdata/src/filtertest/rules.go | 28 ++++ go.mod | 2 +- go.sum | 2 + ruleguard/filters.go | 47 ++++++ ruleguard/ir/filter_op.gen.go | 180 ++++++++++++---------- ruleguard/ir/gen_filter_op.go | 2 + ruleguard/ir_loader.go | 40 +++++ ruleguard/irconv/irconv.go | 4 + ruleguard/ruleguard_error_test.go | 5 + 10 files changed, 322 insertions(+), 85 deletions(-) diff --git a/analyzer/testdata/src/filtertest/f1.go b/analyzer/testdata/src/filtertest/f1.go index bfed4339..60caa114 100644 --- a/analyzer/testdata/src/filtertest/f1.go +++ b/analyzer/testdata/src/filtertest/f1.go @@ -309,6 +309,103 @@ func detectType() { typeTest(1, "no", 3, "variadic underlying int") // want `false` typeTest(1, 2, "no", "variadic underlying int") // want `false` } + + { + type myInt int + typeTest(1, "is numeric") // want `true` + typeTest(myInt(1), "is numeric") + typeTest("not numeric", "is numeric") + + typeTest(1, "underlying is numeric") // want `true` + typeTest(1.63, "underlying is numeric") // want `true` + typeTest(myInt(1), "underlying is numeric") // want `true` + typeTest("not", "underlying is numeric") + typeTest([]int{1}, "underlying is numeric") + + typeTest(uintptr(5), "is unsigned") // want `true` + typeTest(uint(5), "is unsigned") // want `true` + typeTest(uint8(5), "is unsigned") // want `true` + typeTest(uint16(5), "is unsigned") // want `true` + typeTest(uint32(5), "is unsigned") // want `true` + typeTest(uint64(5), "is unsigned") // want `true` + typeTest(5, "is unsigned") + typeTest(int32(5), "is unsigned") + typeTest(int(5), "is unsigned") + typeTest(rune(5), "is unsigned") + typeTest("934", "is unsigned") + typeTest(4.6, "is unsigned") + + typeTest(uint(5), "is signed") + typeTest(uint8(5), "is signed") + typeTest(uint16(5), "is signed") + typeTest(uint32(5), "is signed") + typeTest(uint64(5), "is signed") + typeTest(uintptr(5), "is signed") + typeTest(5, "is signed") // want `true` + typeTest(int8(5), "is signed") // want `true` + typeTest(int16(5), "is signed") // want `true` + typeTest(int32(5), "is signed") // want `true` + typeTest(int64(5), "is signed") // want `true` + typeTest(int(5), "is signed") // want `true` + typeTest(rune(5), "is signed") // want `true` + typeTest("934", "is signed") + typeTest(4.6, "is signed") + typeTest([]int{225}, "is signed") + + typeTest(uint(5), "is float") + typeTest(uint8(5), "is float") + typeTest(uint16(5), "is float") + typeTest(uint32(5), "is float") + typeTest(uint64(5), "is float") + typeTest(uintptr(5), "is float") + typeTest(5, "is float") + typeTest(int8(5), "is float") + typeTest(int16(5), "is float") + typeTest(int32(5), "is float") + typeTest(int64(5), "is float") + typeTest(int(5), "is float") + typeTest("934", "is float") + typeTest(4.6, "is float") // want `true` + typeTest(float32(4.6), "is float") // want `true` + typeTest(float64(4.6), "is float") // want `true` + typeTest([]int{225}, "is float") + + typeTest(5, "is int") // want `true` + typeTest(int8(1), "is int") // want `true` + typeTest(int16(1), "is int") // want `true` + typeTest(int32(1), "is int") // want `true` + typeTest(int64(1), "is int") // want `true` + typeTest(rune(1), "is int") // want `true` + typeTest(byte(1), "is int") + typeTest(uint(1), "is int") + typeTest(uint8(1), "is int") + typeTest(uint16(1), "is int") + typeTest(uint32(1), "is int") + typeTest(uint64(1), "is int") + typeTest([]int{3}, "is int") + typeTest("ds", "is int") + typeTest(54.2, "is int") + typeTest(float64(5.3), "is int") + typeTest(float32(5.3), "is int") + + typeTest(5, "is uint") + typeTest(int8(1), "is uint") + typeTest(int16(1), "is uint") + typeTest(int32(1), "is uint") + typeTest(int64(1), "is uint") + typeTest(rune(1), "is uint") + typeTest(byte(1), "is uint") // want `true` + typeTest(uint(1), "is uint") // want `true` + typeTest(uint8(1), "is uint") // want `true` + typeTest(uint16(1), "is uint") // want `true` + typeTest(uint32(1), "is uint") // want `true` + typeTest(uint64(1), "is uint") // want `true` + typeTest([]int{3}, "is uint") + typeTest("ds", "is uint") + typeTest(54.2, "is uint") + typeTest(float64(5.3), "is uint") + typeTest(float32(5.3), "is uint") + } } func detectAddressable(x int, xs []int) { diff --git a/analyzer/testdata/src/filtertest/rules.go b/analyzer/testdata/src/filtertest/rules.go index cabefad5..298e6cee 100644 --- a/analyzer/testdata/src/filtertest/rules.go +++ b/analyzer/testdata/src/filtertest/rules.go @@ -202,4 +202,32 @@ func testRules(m dsl.Matcher) { m.Match(`textTest("", "root text test")`). Where(m["$$"].Text == `textTest("", "root text test")`). Report(`true`) + + m.Match(`typeTest($x, "is numeric")`). + Where(m["x"].Type.OfKind("numeric")). + Report(`true`) + + m.Match(`typeTest($x, "underlying is numeric")`). + Where(m["x"].Type.Underlying().OfKind("numeric")). + Report(`true`) + + m.Match(`typeTest($x, "is unsigned")`). + Where(m["x"].Type.Underlying().OfKind("unsigned")). + Report(`true`) + + m.Match(`typeTest($x, "is signed")`). + Where(m["x"].Type.Underlying().OfKind("signed")). + Report(`true`) + + m.Match(`typeTest($x, "is float")`). + Where(m["x"].Type.Underlying().OfKind("float")). + Report(`true`) + + m.Match(`typeTest($x, "is int")`). + Where(m["x"].Type.Underlying().OfKind("int")). + Report(`true`) + + m.Match(`typeTest($x, "is uint")`). + Where(m["x"].Type.Underlying().OfKind("uint")). + Report(`true`) } diff --git a/go.mod b/go.mod index 9e6d86cc..d7a8f452 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.15 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.10 + github.com/quasilyte/go-ruleguard/dsl v0.3.11 github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 github.com/quasilyte/gogrep v0.0.0-20211226113550-e12a97c7d96d golang.org/x/tools v0.0.0-20201230224404-63754364767c diff --git a/go.sum b/go.sum index 007307dc..a96c0ab3 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1 github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/dsl v0.3.10 h1:4tVlVVcBT+nNWoF+t/zrAMO13sHAqYotX1K12Gc8f8A= github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.11 h1:TOyCsB86VE915JMP8ErXf12k+OvIYNkBN6D/waKQih4= +github.com/quasilyte/go-ruleguard/dsl v0.3.11/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 471e9364..d1cb327d 100644 --- a/ruleguard/filters.go +++ b/ruleguard/filters.go @@ -189,6 +189,53 @@ func makeTypeImplementsFilter(src, varname string, iface *types.Interface) filte } } +func makeTypeIsIntUintFilter(src, varname string, underlying bool, kind types.BasicKind) filterFunc { + return func(params *filterParams) matchFilterResult { + typ := params.typeofNode(params.subExpr(varname)) + if underlying { + typ = typ.Underlying() + } + if basicType, ok := typ.(*types.Basic); ok { + first := kind + last := kind + 4 + if basicType.Kind() >= first && basicType.Kind() <= last { + return filterSuccess + } + } + return filterFailure(src) + } +} + +func makeTypeIsSignedFilter(src, varname string, underlying bool) filterFunc { + return func(params *filterParams) matchFilterResult { + typ := params.typeofNode(params.subExpr(varname)) + if underlying { + typ = typ.Underlying() + } + if basicType, ok := typ.(*types.Basic); ok { + if basicType.Info()&types.IsInteger != 0 && basicType.Info()&types.IsUnsigned == 0 { + return filterSuccess + } + } + return filterFailure(src) + } +} + +func makeTypeOfKindFilter(src, varname string, underlying bool, kind types.BasicInfo) filterFunc { + return func(params *filterParams) matchFilterResult { + typ := params.typeofNode(params.subExpr(varname)) + if underlying { + typ = typ.Underlying() + } + if basicType, ok := typ.(*types.Basic); ok { + if basicType.Info()&kind != 0 { + 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 46feaf0b..f3e0da8e 100644 --- a/ruleguard/ir/filter_op.gen.go +++ b/ruleguard/ir/filter_op.gen.go @@ -84,141 +84,153 @@ const ( // $Value type: string FilterVarTypeUnderlyingIsOp FilterOp = 22 + // m[$Value].Type.OfKind($Args[0]) + // $Value type: string + FilterVarTypeOfKindOp FilterOp = 23 + + // m[$Value].Type.Underlying().OfKind($Args[0]) + // $Value type: string + FilterVarTypeUnderlyingOfKindOp FilterOp = 24 + // m[$Value].Type.ConvertibleTo($Args[0]) // $Value type: string - FilterVarTypeConvertibleToOp FilterOp = 23 + FilterVarTypeConvertibleToOp FilterOp = 25 // m[$Value].Type.AssignableTo($Args[0]) // $Value type: string - FilterVarTypeAssignableToOp FilterOp = 24 + FilterVarTypeAssignableToOp FilterOp = 26 // m[$Value].Type.Implements($Args[0]) // $Value type: string - FilterVarTypeImplementsOp FilterOp = 25 + FilterVarTypeImplementsOp FilterOp = 27 // m[$Value].Text.Matches($Args[0]) // $Value type: string - FilterVarTextMatchesOp FilterOp = 26 + FilterVarTextMatchesOp FilterOp = 28 // m.Deadcode() - FilterDeadcodeOp FilterOp = 27 + FilterDeadcodeOp FilterOp = 29 // m.GoVersion().Eq($Value) // $Value type: string - FilterGoVersionEqOp FilterOp = 28 + FilterGoVersionEqOp FilterOp = 30 // m.GoVersion().LessThan($Value) // $Value type: string - FilterGoVersionLessThanOp FilterOp = 29 + FilterGoVersionLessThanOp FilterOp = 31 // m.GoVersion().GreaterThan($Value) // $Value type: string - FilterGoVersionGreaterThanOp FilterOp = 30 + FilterGoVersionGreaterThanOp FilterOp = 32 // m.GoVersion().LessEqThan($Value) // $Value type: string - FilterGoVersionLessEqThanOp FilterOp = 31 + FilterGoVersionLessEqThanOp FilterOp = 33 // m.GoVersion().GreaterEqThan($Value) // $Value type: string - FilterGoVersionGreaterEqThanOp FilterOp = 32 + FilterGoVersionGreaterEqThanOp FilterOp = 34 // m.File.Imports($Value) // $Value type: string - FilterFileImportsOp FilterOp = 33 + FilterFileImportsOp FilterOp = 35 // m.File.PkgPath.Matches($Value) // $Value type: string - FilterFilePkgPathMatchesOp FilterOp = 34 + FilterFilePkgPathMatchesOp FilterOp = 36 // m.File.Name.Matches($Value) // $Value type: string - FilterFileNameMatchesOp FilterOp = 35 + FilterFileNameMatchesOp FilterOp = 37 // $Value holds a function name // $Value type: string - FilterFilterFuncRefOp FilterOp = 36 + FilterFilterFuncRefOp FilterOp = 38 // $Value holds a string constant // $Value type: string - FilterStringOp FilterOp = 37 + FilterStringOp FilterOp = 39 // $Value holds an int64 constant // $Value type: int64 - FilterIntOp FilterOp = 38 + FilterIntOp FilterOp = 40 // m[`$$`].Node.Parent().Is($Args[0]) - FilterRootNodeParentIsOp FilterOp = 39 + FilterRootNodeParentIsOp FilterOp = 41 ) var filterOpNames = map[FilterOp]string{ - FilterInvalidOp: `Invalid`, - FilterNotOp: `Not`, - FilterAndOp: `And`, - FilterOrOp: `Or`, - FilterEqOp: `Eq`, - FilterNeqOp: `Neq`, - FilterGtOp: `Gt`, - FilterLtOp: `Lt`, - FilterGtEqOp: `GtEq`, - FilterLtEqOp: `LtEq`, - FilterVarAddressableOp: `VarAddressable`, - FilterVarPureOp: `VarPure`, - FilterVarConstOp: `VarConst`, - FilterVarConstSliceOp: `VarConstSlice`, - FilterVarTextOp: `VarText`, - FilterVarLineOp: `VarLine`, - FilterVarValueIntOp: `VarValueInt`, - FilterVarTypeSizeOp: `VarTypeSize`, - FilterVarFilterOp: `VarFilter`, - FilterVarNodeIsOp: `VarNodeIs`, - FilterVarObjectIsOp: `VarObjectIs`, - FilterVarTypeIsOp: `VarTypeIs`, - FilterVarTypeUnderlyingIsOp: `VarTypeUnderlyingIs`, - FilterVarTypeConvertibleToOp: `VarTypeConvertibleTo`, - FilterVarTypeAssignableToOp: `VarTypeAssignableTo`, - FilterVarTypeImplementsOp: `VarTypeImplements`, - FilterVarTextMatchesOp: `VarTextMatches`, - FilterDeadcodeOp: `Deadcode`, - FilterGoVersionEqOp: `GoVersionEq`, - FilterGoVersionLessThanOp: `GoVersionLessThan`, - FilterGoVersionGreaterThanOp: `GoVersionGreaterThan`, - FilterGoVersionLessEqThanOp: `GoVersionLessEqThan`, - FilterGoVersionGreaterEqThanOp: `GoVersionGreaterEqThan`, - FilterFileImportsOp: `FileImports`, - FilterFilePkgPathMatchesOp: `FilePkgPathMatches`, - FilterFileNameMatchesOp: `FileNameMatches`, - FilterFilterFuncRefOp: `FilterFuncRef`, - FilterStringOp: `String`, - FilterIntOp: `Int`, - FilterRootNodeParentIsOp: `RootNodeParentIs`, + FilterInvalidOp: `Invalid`, + FilterNotOp: `Not`, + FilterAndOp: `And`, + FilterOrOp: `Or`, + FilterEqOp: `Eq`, + FilterNeqOp: `Neq`, + FilterGtOp: `Gt`, + FilterLtOp: `Lt`, + FilterGtEqOp: `GtEq`, + FilterLtEqOp: `LtEq`, + FilterVarAddressableOp: `VarAddressable`, + FilterVarPureOp: `VarPure`, + FilterVarConstOp: `VarConst`, + FilterVarConstSliceOp: `VarConstSlice`, + FilterVarTextOp: `VarText`, + FilterVarLineOp: `VarLine`, + FilterVarValueIntOp: `VarValueInt`, + FilterVarTypeSizeOp: `VarTypeSize`, + FilterVarFilterOp: `VarFilter`, + FilterVarNodeIsOp: `VarNodeIs`, + FilterVarObjectIsOp: `VarObjectIs`, + FilterVarTypeIsOp: `VarTypeIs`, + FilterVarTypeUnderlyingIsOp: `VarTypeUnderlyingIs`, + FilterVarTypeOfKindOp: `VarTypeOfKind`, + FilterVarTypeUnderlyingOfKindOp: `VarTypeUnderlyingOfKind`, + FilterVarTypeConvertibleToOp: `VarTypeConvertibleTo`, + FilterVarTypeAssignableToOp: `VarTypeAssignableTo`, + FilterVarTypeImplementsOp: `VarTypeImplements`, + FilterVarTextMatchesOp: `VarTextMatches`, + FilterDeadcodeOp: `Deadcode`, + FilterGoVersionEqOp: `GoVersionEq`, + FilterGoVersionLessThanOp: `GoVersionLessThan`, + FilterGoVersionGreaterThanOp: `GoVersionGreaterThan`, + FilterGoVersionLessEqThanOp: `GoVersionLessEqThan`, + FilterGoVersionGreaterEqThanOp: `GoVersionGreaterEqThan`, + FilterFileImportsOp: `FileImports`, + FilterFilePkgPathMatchesOp: `FilePkgPathMatches`, + FilterFileNameMatchesOp: `FileNameMatches`, + FilterFilterFuncRefOp: `FilterFuncRef`, + FilterStringOp: `String`, + FilterIntOp: `Int`, + FilterRootNodeParentIsOp: `RootNodeParentIs`, } var filterOpFlags = map[FilterOp]uint64{ - FilterAndOp: flagIsBinaryExpr, - FilterOrOp: flagIsBinaryExpr, - FilterEqOp: flagIsBinaryExpr, - FilterNeqOp: flagIsBinaryExpr, - FilterGtOp: flagIsBinaryExpr, - FilterLtOp: flagIsBinaryExpr, - FilterGtEqOp: flagIsBinaryExpr, - FilterLtEqOp: flagIsBinaryExpr, - FilterVarAddressableOp: flagHasVar, - FilterVarPureOp: flagHasVar, - FilterVarConstOp: flagHasVar, - FilterVarConstSliceOp: flagHasVar, - FilterVarTextOp: flagHasVar, - FilterVarLineOp: flagHasVar, - FilterVarValueIntOp: flagHasVar, - FilterVarTypeSizeOp: flagHasVar, - FilterVarFilterOp: flagHasVar, - FilterVarNodeIsOp: flagHasVar, - FilterVarObjectIsOp: flagHasVar, - FilterVarTypeIsOp: flagHasVar, - FilterVarTypeUnderlyingIsOp: flagHasVar, - FilterVarTypeConvertibleToOp: flagHasVar, - FilterVarTypeAssignableToOp: flagHasVar, - FilterVarTypeImplementsOp: flagHasVar, - FilterVarTextMatchesOp: flagHasVar, - FilterStringOp: flagIsBasicLit, - FilterIntOp: flagIsBasicLit, + FilterAndOp: flagIsBinaryExpr, + FilterOrOp: flagIsBinaryExpr, + FilterEqOp: flagIsBinaryExpr, + FilterNeqOp: flagIsBinaryExpr, + FilterGtOp: flagIsBinaryExpr, + FilterLtOp: flagIsBinaryExpr, + FilterGtEqOp: flagIsBinaryExpr, + FilterLtEqOp: flagIsBinaryExpr, + FilterVarAddressableOp: flagHasVar, + FilterVarPureOp: flagHasVar, + FilterVarConstOp: flagHasVar, + FilterVarConstSliceOp: flagHasVar, + FilterVarTextOp: flagHasVar, + FilterVarLineOp: flagHasVar, + FilterVarValueIntOp: flagHasVar, + FilterVarTypeSizeOp: flagHasVar, + FilterVarFilterOp: flagHasVar, + FilterVarNodeIsOp: flagHasVar, + FilterVarObjectIsOp: flagHasVar, + FilterVarTypeIsOp: flagHasVar, + FilterVarTypeUnderlyingIsOp: flagHasVar, + FilterVarTypeOfKindOp: flagHasVar, + FilterVarTypeUnderlyingOfKindOp: flagHasVar, + FilterVarTypeConvertibleToOp: flagHasVar, + FilterVarTypeAssignableToOp: flagHasVar, + FilterVarTypeImplementsOp: flagHasVar, + FilterVarTextMatchesOp: flagHasVar, + FilterStringOp: flagIsBasicLit, + FilterIntOp: flagIsBasicLit, } diff --git a/ruleguard/ir/gen_filter_op.go b/ruleguard/ir/gen_filter_op.go index a5b7b07e..ffa80351 100644 --- a/ruleguard/ir/gen_filter_op.go +++ b/ruleguard/ir/gen_filter_op.go @@ -53,6 +53,8 @@ func main() { {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: "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}, {name: "VarTypeConvertibleTo", comment: "m[$Value].Type.ConvertibleTo($Args[0])", valueType: "string", flags: flagHasVar}, {name: "VarTypeAssignableTo", comment: "m[$Value].Type.AssignableTo($Args[0])", valueType: "string", flags: flagHasVar}, {name: "VarTypeImplements", comment: "m[$Value].Type.Implements($Args[0])", valueType: "string", flags: flagHasVar}, diff --git a/ruleguard/ir_loader.go b/ruleguard/ir_loader.go index 1aaaf0ee..e711386e 100644 --- a/ruleguard/ir_loader.go +++ b/ruleguard/ir_loader.go @@ -460,6 +460,25 @@ func (l *irLoader) unwrapStringExpr(filter ir.FilterExpr) string { return "" } +func (l *irLoader) stringToBasicKind(s string) types.BasicInfo { + switch s { + case "integer": + return types.IsInteger + case "unsigned": + return types.IsUnsigned + case "float": + return types.IsFloat + case "complex": + return types.IsComplex + case "untyped": + return types.IsUnsigned + case "numeric": + return types.IsNumeric + default: + return 0 + } +} + func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilter, error) { if filter.HasVar() { info.Vars[filter.Value.(string)] = struct{}{} @@ -513,6 +532,27 @@ func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilte } result.fn = makeNodeIsFilter(result.src, filter.Value.(string), tag) + case ir.FilterVarTypeOfKindOp, ir.FilterVarTypeUnderlyingOfKindOp: + kindString := l.unwrapStringExpr(filter.Args[0]) + if kindString == "" { + return result, l.errorf(filter.Line, nil, "expected a non-empty string argument") + } + underlying := filter.Op == ir.FilterVarTypeUnderlyingOfKindOp + switch kindString { + case "signed": + result.fn = makeTypeIsSignedFilter(result.src, filter.Value.(string), underlying) + case "int": + result.fn = makeTypeIsIntUintFilter(result.src, filter.Value.(string), underlying, types.Int) + case "uint": + result.fn = makeTypeIsIntUintFilter(result.src, filter.Value.(string), underlying, types.Uint) + default: + kind := l.stringToBasicKind(kindString) + if kind == 0 { + return result, l.errorf(filter.Line, nil, "unknown kind %s", kindString) + } + result.fn = makeTypeOfKindFilter(result.src, filter.Value.(string), underlying, kind) + } + 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 386e2d80..3d294dcc 100644 --- a/ruleguard/irconv/irconv.go +++ b/ruleguard/irconv/irconv.go @@ -696,6 +696,10 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr { return ir.FilterExpr{Op: ir.FilterVarTypeIsOp, Value: op.varName, Args: args} case "Type.Underlying.Is": return ir.FilterExpr{Op: ir.FilterVarTypeUnderlyingIsOp, Value: op.varName, Args: args} + case "Type.OfKind": + return ir.FilterExpr{Op: ir.FilterVarTypeOfKindOp, Value: op.varName, Args: args} + case "Type.Underlying.OfKind": + return ir.FilterExpr{Op: ir.FilterVarTypeUnderlyingOfKindOp, Value: op.varName, Args: args} case "Type.ConvertibleTo": return ir.FilterExpr{Op: ir.FilterVarTypeConvertibleToOp, Value: op.varName, Args: args} case "Type.AssignableTo": diff --git a/ruleguard/ruleguard_error_test.go b/ruleguard/ruleguard_error_test.go index 247cbe74..422fc5d0 100644 --- a/ruleguard/ruleguard_error_test.go +++ b/ruleguard/ruleguard_error_test.go @@ -376,6 +376,11 @@ func TestParseFilterError(t *testing.T) { `Replacer3 is not found in strings`, }, + { + `m["x"].Type.OfKind("badkind")`, + `unknown kind badkind`, + }, + { `m["x"].Node.Is("abc")`, `abc is not a valid go/ast type name`,