From cb19258d2ade88dbf466420bb4585dc747bcec57 Mon Sep 17 00:00:00 2001 From: Denis Limarev Date: Wed, 2 Feb 2022 17:27:29 +0300 Subject: [PATCH] ruleguard: implement Var.Object.IsGlobal() predicate (#370) --- analyzer/testdata/src/filtertest/f1.go | 20 +++++++++ analyzer/testdata/src/filtertest/rules.go | 7 +++ go.mod | 2 +- go.sum | 4 +- ruleguard/filters.go | 17 ++++++- ruleguard/ir/filter_op.gen.go | 54 +++++++++++++---------- ruleguard/ir/gen_filter_op.go | 2 + ruleguard/ir_loader.go | 7 ++- ruleguard/irconv/irconv.go | 5 ++- 9 files changed, 86 insertions(+), 32 deletions(-) diff --git a/analyzer/testdata/src/filtertest/f1.go b/analyzer/testdata/src/filtertest/f1.go index a3b304e..924e530 100644 --- a/analyzer/testdata/src/filtertest/f1.go +++ b/analyzer/testdata/src/filtertest/f1.go @@ -872,3 +872,23 @@ func detectNode() { nodeTest(rows[0][5], "IndexExpr") // want `true` nodeTest("42", "IndexExpr") } + +var globalVar string +var globalVar2 string = time.Now().String() // want `\Qglobal var` +var globalVar3 = time.Now().String() // want `\Qglobal var` +var ( + globalVar4 string +) + +func detectGlobal() { + globalVar = time.Now().String() // want `\Qglobal var` + globalVar4 = time.Now().String() // want `\Qglobal var` + { + globalVar := time.Now().String() // shadowed global var + print(globalVar) + } + { + var globalVar = time.Now().String() // shadowed global var + print(globalVar) + } +} diff --git a/analyzer/testdata/src/filtertest/rules.go b/analyzer/testdata/src/filtertest/rules.go index 9903a36..e0210a7 100644 --- a/analyzer/testdata/src/filtertest/rules.go +++ b/analyzer/testdata/src/filtertest/rules.go @@ -263,4 +263,11 @@ func testRules(m dsl.Matcher) { m.Match(`typeTest($x, $y, "identical types")`). Where(m["x"].Type.IdenticalTo(m["y"])). Report(`true`) + + m.Match(`$x = time.Now().String()`, + `var $x = time.Now().String()`, + `var $x $_ = time.Now().String()`, + `$x := time.Now().String()`). + Where(m["x"].Object.IsGlobal()). + Report(`global var`) } diff --git a/go.mod b/go.mod index 41cffc3..f0f3b10 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.15 + github.com/quasilyte/go-ruleguard/dsl v0.3.16 github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 diff --git a/go.sum b/go.sum index 84d076f..cbd6b9e 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= github.com/quasilyte/go-ruleguard/dsl v0.3.0/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/dsl v0.3.16 h1:yJtIpd4oyNS+/c/gKqxNwoGO9+lPOsy1A4BzKjJRcrI= +github.com/quasilyte/go-ruleguard/dsl v0.3.16/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 cd0b135..52bfa64 100644 --- a/ruleguard/filters.go +++ b/ruleguard/filters.go @@ -7,12 +7,13 @@ import ( "go/types" "path/filepath" + "github.com/quasilyte/gogrep" + "github.com/quasilyte/gogrep/nodetag" + "github.com/quasilyte/go-ruleguard/internal/xtypes" "github.com/quasilyte/go-ruleguard/ruleguard/quasigo" "github.com/quasilyte/go-ruleguard/ruleguard/textmatch" "github.com/quasilyte/go-ruleguard/ruleguard/typematch" - "github.com/quasilyte/gogrep" - "github.com/quasilyte/gogrep/nodetag" ) const filterSuccess = matchFilterResult("") @@ -363,6 +364,18 @@ func makeLineFilter(src, varname string, op token.Token, rhsVarname string) filt } } +func makeObjectIsGlobalFilter(src, varname string) filterFunc { + return func(params *filterParams) matchFilterResult { + obj := params.ctx.Types.ObjectOf(identOf(params.subExpr(varname))) + globalScope := params.ctx.Pkg.Scope() + if obj.Parent() == globalScope { + return filterSuccess + } + + return filterFailure(src) + } +} + func makeGoVersionFilter(src string, op token.Token, version GoVersion) filterFunc { return func(params *filterParams) matchFilterResult { if params.ctx.GoVersion.IsAny() { diff --git a/ruleguard/ir/filter_op.gen.go b/ruleguard/ir/filter_op.gen.go index c3b022d..639724b 100644 --- a/ruleguard/ir/filter_op.gen.go +++ b/ruleguard/ir/filter_op.gen.go @@ -80,99 +80,103 @@ const ( // $Value type: string FilterVarObjectIsOp FilterOp = 21 + // m[$Value].Object.IsGlobal() + // $Value type: string + FilterVarObjectIsGlobalOp FilterOp = 22 + // m[$Value].Type.Is($Args[0]) // $Value type: string - FilterVarTypeIsOp FilterOp = 22 + FilterVarTypeIsOp FilterOp = 23 // m[$Value].Type.IdenticalTo($Args[0]) // $Value type: string - FilterVarTypeIdenticalToOp FilterOp = 23 + FilterVarTypeIdenticalToOp FilterOp = 24 // m[$Value].Type.Underlying().Is($Args[0]) // $Value type: string - FilterVarTypeUnderlyingIsOp FilterOp = 24 + FilterVarTypeUnderlyingIsOp FilterOp = 25 // m[$Value].Type.OfKind($Args[0]) // $Value type: string - FilterVarTypeOfKindOp FilterOp = 25 + FilterVarTypeOfKindOp FilterOp = 26 // m[$Value].Type.Underlying().OfKind($Args[0]) // $Value type: string - FilterVarTypeUnderlyingOfKindOp FilterOp = 26 + FilterVarTypeUnderlyingOfKindOp FilterOp = 27 // m[$Value].Type.ConvertibleTo($Args[0]) // $Value type: string - FilterVarTypeConvertibleToOp FilterOp = 27 + FilterVarTypeConvertibleToOp FilterOp = 28 // m[$Value].Type.AssignableTo($Args[0]) // $Value type: string - FilterVarTypeAssignableToOp FilterOp = 28 + FilterVarTypeAssignableToOp FilterOp = 29 // m[$Value].Type.Implements($Args[0]) // $Value type: string - FilterVarTypeImplementsOp FilterOp = 29 + FilterVarTypeImplementsOp FilterOp = 30 // m[$Value].Type.HasMethod($Args[0]) // $Value type: string - FilterVarTypeHasMethodOp FilterOp = 30 + FilterVarTypeHasMethodOp FilterOp = 31 // m[$Value].Text.Matches($Args[0]) // $Value type: string - FilterVarTextMatchesOp FilterOp = 31 + FilterVarTextMatchesOp FilterOp = 32 // m[$Value].Contains($Args[0]) // $Value type: string - FilterVarContainsOp FilterOp = 32 + FilterVarContainsOp FilterOp = 33 // m.Deadcode() - FilterDeadcodeOp FilterOp = 33 + FilterDeadcodeOp FilterOp = 34 // m.GoVersion().Eq($Value) // $Value type: string - FilterGoVersionEqOp FilterOp = 34 + FilterGoVersionEqOp FilterOp = 35 // m.GoVersion().LessThan($Value) // $Value type: string - FilterGoVersionLessThanOp FilterOp = 35 + FilterGoVersionLessThanOp FilterOp = 36 // m.GoVersion().GreaterThan($Value) // $Value type: string - FilterGoVersionGreaterThanOp FilterOp = 36 + FilterGoVersionGreaterThanOp FilterOp = 37 // m.GoVersion().LessEqThan($Value) // $Value type: string - FilterGoVersionLessEqThanOp FilterOp = 37 + FilterGoVersionLessEqThanOp FilterOp = 38 // m.GoVersion().GreaterEqThan($Value) // $Value type: string - FilterGoVersionGreaterEqThanOp FilterOp = 38 + FilterGoVersionGreaterEqThanOp FilterOp = 39 // m.File.Imports($Value) // $Value type: string - FilterFileImportsOp FilterOp = 39 + FilterFileImportsOp FilterOp = 40 // m.File.PkgPath.Matches($Value) // $Value type: string - FilterFilePkgPathMatchesOp FilterOp = 40 + FilterFilePkgPathMatchesOp FilterOp = 41 // m.File.Name.Matches($Value) // $Value type: string - FilterFileNameMatchesOp FilterOp = 41 + FilterFileNameMatchesOp FilterOp = 42 // $Value holds a function name // $Value type: string - FilterFilterFuncRefOp FilterOp = 42 + FilterFilterFuncRefOp FilterOp = 43 // $Value holds a string constant // $Value type: string - FilterStringOp FilterOp = 43 + FilterStringOp FilterOp = 44 // $Value holds an int64 constant // $Value type: int64 - FilterIntOp FilterOp = 44 + FilterIntOp FilterOp = 45 // m[`$$`].Node.Parent().Is($Args[0]) - FilterRootNodeParentIsOp FilterOp = 45 + FilterRootNodeParentIsOp FilterOp = 46 ) var filterOpNames = map[FilterOp]string{ @@ -198,6 +202,7 @@ var filterOpNames = map[FilterOp]string{ FilterVarFilterOp: `VarFilter`, FilterVarNodeIsOp: `VarNodeIs`, FilterVarObjectIsOp: `VarObjectIs`, + FilterVarObjectIsGlobalOp: `VarObjectIsGlobal`, FilterVarTypeIsOp: `VarTypeIs`, FilterVarTypeIdenticalToOp: `VarTypeIdenticalTo`, FilterVarTypeUnderlyingIsOp: `VarTypeUnderlyingIs`, @@ -244,6 +249,7 @@ var filterOpFlags = map[FilterOp]uint64{ FilterVarFilterOp: flagHasVar, FilterVarNodeIsOp: flagHasVar, FilterVarObjectIsOp: flagHasVar, + FilterVarObjectIsGlobalOp: flagHasVar, FilterVarTypeIsOp: flagHasVar, FilterVarTypeIdenticalToOp: flagHasVar, FilterVarTypeUnderlyingIsOp: flagHasVar, diff --git a/ruleguard/ir/gen_filter_op.go b/ruleguard/ir/gen_filter_op.go index 727a805..3e9806d 100644 --- a/ruleguard/ir/gen_filter_op.go +++ b/ruleguard/ir/gen_filter_op.go @@ -1,3 +1,4 @@ +//go:build generate // +build generate package main @@ -52,6 +53,7 @@ func main() { {name: "VarFilter", comment: "m[$Value].Filter($Args[0])", valueType: "string", flags: flagHasVar}, {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: "VarObjectIsGlobal", comment: "m[$Value].Object.IsGlobal()", 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}, diff --git a/ruleguard/ir_loader.go b/ruleguard/ir_loader.go index 01f59bf..ea3402f 100644 --- a/ruleguard/ir_loader.go +++ b/ruleguard/ir_loader.go @@ -11,13 +11,14 @@ import ( "io/ioutil" "regexp" + "github.com/quasilyte/gogrep" + "github.com/quasilyte/gogrep/nodetag" + "github.com/quasilyte/go-ruleguard/ruleguard/goutil" "github.com/quasilyte/go-ruleguard/ruleguard/ir" "github.com/quasilyte/go-ruleguard/ruleguard/quasigo" "github.com/quasilyte/go-ruleguard/ruleguard/textmatch" "github.com/quasilyte/go-ruleguard/ruleguard/typematch" - "github.com/quasilyte/gogrep" - "github.com/quasilyte/gogrep/nodetag" ) type irLoaderConfig struct { @@ -675,6 +676,8 @@ func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilte result.fn = makePureFilter(result.src, filter.Value.(string)) case ir.FilterVarConstOp: result.fn = makeConstFilter(result.src, filter.Value.(string)) + case ir.FilterVarObjectIsGlobalOp: + result.fn = makeObjectIsGlobalFilter(result.src, filter.Value.(string)) case ir.FilterVarConstSliceOp: result.fn = makeConstSliceFilter(result.src, filter.Value.(string)) case ir.FilterVarAddressableOp: diff --git a/ruleguard/irconv/irconv.go b/ruleguard/irconv/irconv.go index e30339e..7ebd1fe 100644 --- a/ruleguard/irconv/irconv.go +++ b/ruleguard/irconv/irconv.go @@ -11,9 +11,10 @@ import ( "strings" "github.com/go-toolsmith/astcopy" + "golang.org/x/tools/go/ast/astutil" + "github.com/quasilyte/go-ruleguard/ruleguard/goutil" "github.com/quasilyte/go-ruleguard/ruleguard/ir" - "golang.org/x/tools/go/ast/astutil" ) type Context struct { @@ -714,6 +715,8 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr { return ir.FilterExpr{Op: ir.FilterRootNodeParentIsOp, Args: args} case "Object.Is": return ir.FilterExpr{Op: ir.FilterVarObjectIsOp, Value: op.varName, Args: args} + case "Object.IsGlobal": + return ir.FilterExpr{Op: ir.FilterVarObjectIsGlobalOp, Value: op.varName} case "Type.HasPointers": return ir.FilterExpr{Op: ir.FilterVarTypeHasPointersOp, Value: op.varName} case "Type.Is":