Skip to content

Commit 3db0900

Browse files
authored
ruleguard: implement ExprType.HasPointers() predicate (#337)
1 parent 969a394 commit 3db0900

File tree

9 files changed

+162
-25
lines changed

9 files changed

+162
-25
lines changed

analyzer/testdata/src/filtertest/f1.go

+75
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package filtertest
33
import (
44
"errors"
55
"fmt"
6+
"io"
67
"os"
78
"time"
89
"unsafe"
@@ -406,6 +407,80 @@ func detectType() {
406407
typeTest(float64(5.3), "is uint")
407408
typeTest(float32(5.3), "is uint")
408409
}
410+
411+
{
412+
const untypedStr = "123"
413+
type myString string
414+
415+
type scalarObject struct {
416+
x int
417+
y int
418+
}
419+
420+
type withPointers struct {
421+
x int
422+
y *int
423+
}
424+
425+
var intarr [4]int
426+
var intptrarr [4]*int
427+
var r io.Reader
428+
var ch chan int
429+
430+
typeTest(1, "pointer-free") // want `true`
431+
typeTest(1.6, "pointer-free") // want `true`
432+
typeTest(true, "pointer-free") // want `true`
433+
typeTest(scalarObject{1, 2}, "pointer-free") // want `true`
434+
typeTest(intarr, "pointer-free") // want `true`
435+
typeTest([2]scalarObject{}, "pointer-free") // want `true`
436+
437+
typeTest(withPointers{}, "pointer-free")
438+
typeTest(&withPointers{}, "pointer-free")
439+
typeTest(ch, "pointer-free")
440+
typeTest(r, "pointer-free")
441+
typeTest(&r, "pointer-free")
442+
typeTest(&intarr, "pointer-free")
443+
typeTest(intptrarr, "pointer-free")
444+
typeTest(&intptrarr, "pointer-free")
445+
typeTest(&scalarObject{1, 2}, "pointer-free")
446+
typeTest("str", "pointer-free")
447+
typeTest(untypedStr, "pointer-free")
448+
typeTest(myString("123"), "pointer-free")
449+
typeTest(unsafe.Pointer(nil), "pointer-free")
450+
typeTest(nil, "pointer-free")
451+
typeTest([]int{1}, "pointer-free")
452+
typeTest([]string{""}, "pointer-free")
453+
typeTest(map[string]string{}, "pointer-free")
454+
typeTest(new(int), "pointer-free")
455+
typeTest(new(string), "pointer-free")
456+
457+
typeTest(1, "has pointers")
458+
typeTest(1.6, "has pointers")
459+
typeTest(true, "has pointers")
460+
typeTest(scalarObject{1, 2}, "has pointers")
461+
typeTest(intarr, "has pointers")
462+
typeTest([2]scalarObject{}, "has pointers")
463+
464+
typeTest(withPointers{}, "has pointers") // want `true`
465+
typeTest(&withPointers{}, "has pointers") // want `true`
466+
typeTest(ch, "has pointers") // want `true`
467+
typeTest(r, "has pointers") // want `true`
468+
typeTest(&r, "has pointers") // want `true`
469+
typeTest(&intarr, "has pointers") // want `true`
470+
typeTest(intptrarr, "has pointers") // want `true`
471+
typeTest(&intptrarr, "has pointers") // want `true`
472+
typeTest(&scalarObject{1, 2}, "has pointers") // want `true`
473+
typeTest("str", "has pointers") // want `true`
474+
typeTest(untypedStr, "has pointers") // want `true`
475+
typeTest(myString("123"), "has pointers") // want `true`
476+
typeTest(unsafe.Pointer(nil), "has pointers") // want `true`
477+
typeTest(nil, "has pointers") // want `true`
478+
typeTest([]int{1}, "has pointers") // want `true`
479+
typeTest([]string{""}, "has pointers") // want `true`
480+
typeTest(map[string]string{}, "has pointers") // want `true`
481+
typeTest(new(int), "has pointers") // want `true`
482+
typeTest(new(string), "has pointers") // want `true`
483+
}
409484
}
410485

411486
func detectAddressable(x int, xs []int) {

analyzer/testdata/src/filtertest/rules.go

+8
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,12 @@ func testRules(m dsl.Matcher) {
230230
m.Match(`typeTest($x, "is uint")`).
231231
Where(m["x"].Type.Underlying().OfKind("uint")).
232232
Report(`true`)
233+
234+
m.Match(`typeTest($x, "pointer-free")`).
235+
Where(!m["x"].Type.HasPointers()).
236+
Report(`true`)
237+
238+
m.Match(`typeTest($x, "has pointers")`).
239+
Where(m["x"].Type.HasPointers()).
240+
Report(`true`)
233241
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.15
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.11
8+
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451
99
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71
1010
github.com/quasilyte/gogrep v0.0.0-20211226113550-e12a97c7d96d
1111
golang.org/x/tools v0.0.0-20201230224404-63754364767c

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.10 h1:4tVlVVcBT+nNWoF+t/zrAMO13sHAqYo
1414
github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
1515
github.com/quasilyte/go-ruleguard/dsl v0.3.11 h1:TOyCsB86VE915JMP8ErXf12k+OvIYNkBN6D/waKQih4=
1616
github.com/quasilyte/go-ruleguard/dsl v0.3.11/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
17+
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101143450-184fad673365 h1:4yuPNY0w97V+2V8dJyiw07sPMpDvtaOEXEmZUOdUHcQ=
18+
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101143450-184fad673365/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
19+
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451 h1:hNInk1SGCCKYcNoVib0KFjwiZZEA/gO75zUxSuLtVJ0=
20+
github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
1721
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
1822
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI=
1923
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=

ruleguard/filters.go

+38
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ func makeTypeImplementsFilter(src, varname string, iface *types.Interface) filte
189189
}
190190
}
191191

192+
func makeTypeHasPointersFilter(src, varname string) filterFunc {
193+
return func(params *filterParams) matchFilterResult {
194+
typ := params.typeofNode(params.subExpr(varname))
195+
if typeHasPointers(typ) {
196+
return filterSuccess
197+
}
198+
return filterFailure(src)
199+
}
200+
}
201+
192202
func makeTypeIsIntUintFilter(src, varname string, underlying bool, kind types.BasicKind) filterFunc {
193203
return func(params *filterParams) matchFilterResult {
194204
typ := params.typeofNode(params.subExpr(varname))
@@ -531,3 +541,31 @@ func nodeIs(n ast.Node, tag nodetag.Value) bool {
531541
}
532542
return matched
533543
}
544+
545+
func typeHasPointers(typ types.Type) bool {
546+
switch typ := typ.(type) {
547+
case *types.Basic:
548+
switch typ.Kind() {
549+
case types.UnsafePointer, types.String, types.UntypedNil, types.UntypedString:
550+
return true
551+
}
552+
return false
553+
554+
case *types.Named:
555+
return typeHasPointers(typ.Underlying())
556+
557+
case *types.Struct:
558+
for i := 0; i < typ.NumFields(); i++ {
559+
if typeHasPointers(typ.Field(i).Type()) {
560+
return true
561+
}
562+
}
563+
return false
564+
565+
case *types.Array:
566+
return typeHasPointers(typ.Elem())
567+
568+
default:
569+
return true
570+
}
571+
}

ruleguard/ir/filter_op.gen.go

+30-24
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
@@ -47,6 +47,7 @@ func main() {
4747
{name: "VarLine", comment: "m[$Value].Line", valueType: "string", flags: flagHasVar},
4848
{name: "VarValueInt", comment: "m[$Value].Value.Int()", valueType: "string", flags: flagHasVar},
4949
{name: "VarTypeSize", comment: "m[$Value].Type.Size", valueType: "string", flags: flagHasVar},
50+
{name: "VarTypeHasPointers", comment: "m[$Value].Type.HasPointers()", valueType: "string", flags: flagHasVar},
5051

5152
{name: "VarFilter", comment: "m[$Value].Filter($Args[0])", valueType: "string", flags: flagHasVar},
5253
{name: "VarNodeIs", comment: "m[$Value].Node.Is($Args[0])", valueType: "string", flags: flagHasVar},

ruleguard/ir_loader.go

+3
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ func (l *irLoader) newFilter(filter ir.FilterExpr, info *filterInfo) (matchFilte
532532
}
533533
result.fn = makeNodeIsFilter(result.src, filter.Value.(string), tag)
534534

535+
case ir.FilterVarTypeHasPointersOp:
536+
result.fn = makeTypeHasPointersFilter(result.src, filter.Value.(string))
537+
535538
case ir.FilterVarTypeOfKindOp, ir.FilterVarTypeUnderlyingOfKindOp:
536539
kindString := l.unwrapStringExpr(filter.Args[0])
537540
if kindString == "" {

ruleguard/irconv/irconv.go

+2
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,8 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr {
692692
return ir.FilterExpr{Op: ir.FilterRootNodeParentIsOp, Args: args}
693693
case "Object.Is":
694694
return ir.FilterExpr{Op: ir.FilterVarObjectIsOp, Value: op.varName, Args: args}
695+
case "Type.HasPointers":
696+
return ir.FilterExpr{Op: ir.FilterVarTypeHasPointersOp, Value: op.varName}
695697
case "Type.Is":
696698
return ir.FilterExpr{Op: ir.FilterVarTypeIsOp, Value: op.varName, Args: args}
697699
case "Type.Underlying.Is":

0 commit comments

Comments
 (0)