Skip to content

Commit

Permalink
Adding more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
grantnelson-wf committed Oct 3, 2024
1 parent 67ec590 commit f6d7860
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 99 deletions.
12 changes: 10 additions & 2 deletions compiler/internal/analysis/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,17 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
fi.literalFuncCallees[f] = append(fi.literalFuncCallees[f], fi.visitorStack.copy())
return nil // No need to walk under this CallExpr, we already did it manually.
case *ast.IndexExpr:
// Collect info about the instantiated type or function.
// Collect info about the instantiated type or function, or index expression.
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
// This is a type conversion to an instance of a generic type,
// not a call. Type assertion itself is not blocking, but we will
// visit the input expression.
} else {

fmt.Printf(">> %[1]T %#[1]v\n", n) // TODO(gn): Finish implementing!
fmt.Printf(">> %[1]T %#[1]v\n", f) // TODO(gn): Finish implementing!
fmt.Printf(" >> %[1]T %#[1]v\n", f.Index) // TODO(gn): Finish implementing!

}

default:
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
Expand Down
246 changes: 149 additions & 97 deletions compiler/internal/analysis/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,95 +10,119 @@ import (

// See: https://github.com/gopherjs/gopherjs/issues/955.
func TestBlockingFunctionLiteral(t *testing.T) {
src := `
package test
blockingTest(t, blockingTestArgs{
src: `package test
func blocking() {
c := make(chan bool)
<-c
}
func blocking() {
c := make(chan bool)
<-c
}
func indirectlyBlocking() {
func() { blocking() }()
}
func directlyBlocking() {
func() {
c := make(chan bool)
<-c
}()
}
func indirectlyBlocking() {
func() { blocking() }()
func notBlocking() {
func() { println() } ()
}`,
blocking: []string{`blocking`, `indirectlyBlocking`, `directlyBlocking`},
notBlocking: []string{`notBlocking`},
})
}

func directlyBlocking() {
func() {
c := make(chan bool)
<-c
}()
func TestBlockingLinkedFunction(t *testing.T) {
blockingTest(t, blockingTestArgs{
src: `package test
// linked to some other function
func blocking()
func indirectlyBlocking() {
blocking()
}`,
blocking: []string{`blocking`, `indirectlyBlocking`},
})
}

func notBlocking() {
func() { println() } ()
func TestBlockingInstanceWithSingleTypeArgument(t *testing.T) {
blockingTest(t, blockingTestArgs{
src: `package test
func blocking[T any]() {
c := make(chan T)
<-c
}
func notBlocking[T any]() {
var v T
println(v)
}
func bInt() {
blocking[int]()
}
func nbUint() {
notBlocking[uint]()
}`,
blocking: []string{`blocking`, `bInt`},
notBlocking: []string{`notBlocking`, `nbUint`},
})
}
`
f := srctesting.New(t)
file := f.Parse("test.go", src)
typesInfo, typesPkg := f.Check("pkg/test", file)

pkgInfo := AnalyzePkg([]*ast.File{file}, f.FileSet, typesInfo, typesPkg, func(f *types.Func) bool {
panic("isBlocking() should be never called for imported functions in this test.")
func TestBlockingInstanceWithMultipleTypeArguments(t *testing.T) {
blockingTest(t, blockingTestArgs{
src: `package test
func blocking[K comparable, V any, M ~map[K]V]() {
c := make(chan M)
<-c
}
func notBlocking[K comparable, V any, M ~map[K]V]() {
var m M
println(m)
}
func bInt() {
blocking[string, int, map[string]int]()
}
func nbUint() {
notBlocking[string, uint, map[string]uint]()
}`,
blocking: []string{`blocking`, `bInt`},
notBlocking: []string{`notBlocking`, `nbUint`},
})
}

assertBlocking(t, file, pkgInfo, "blocking")
assertBlocking(t, file, pkgInfo, "indirectlyBlocking")
assertBlocking(t, file, pkgInfo, "directlyBlocking")
assertNotBlocking(t, file, pkgInfo, "notBlocking")
func TestBlockingIndexedFromFunctionSlice(t *testing.T) {
// This calls notBlocking but since the function pointers
// are in the slice they will both be considered as blocking.
// This is just checking that the analysis can tell between
// indexing and instantiation of a generic.
blockingTest(t, blockingTestArgs{
src: `package test
func blocking() {
c := make(chan int)
<-c
}
func notBlocking() {
println()
}
var funcs = []func() { blocking, notBlocking }
func indexer(i int) {
funcs[i]()
}`,
blocking: []string{`blocking`, `indexer`},
notBlocking: []string{`notBlocking`},
})
}

func TestInstanceBlocking(t *testing.T) {
tests := []struct {
name string
src string
blocking []string
notBlocking []string
}{
{
name: `blocking instance`,
src: `package test
func blocking[T any]() {
c := make(chan T)
<-c
}
func notBlocking[T any]() {
println()
}
func bInt() {
blocking[int]()
}
func nbUint() {
notBlocking[uint]()
}`,
blocking: []string{`blocking`, `bInt`},
notBlocking: []string{`notBlocking`, `nbUint`},
},
{
name: `differentiate indexing`,
// Below calls notBlocking but since the function pointers
// are in the slice they will both be considered as blocking.
// This is just checking that the analysis can tell between
// indexing and instantiation of a generic.
src: `package test
func blocking() {
c := make(chan int)
<-c
}
func notBlocking() {
println()
}
var funcs = []func() { blocking, notBlocking }
func indexer() {
funcs[1]()
}`,
blocking: []string{`blocking`, `indexer`},
notBlocking: []string{`notBlocking`},
},
{
name: `differentiate casting`,
// Below checks that casting to an instance type is treated as a
// cast an not accidentally treated as a function call.
src: `package test
func TestBlockingCastingToAnInterfaceInstance(t *testing.T) {
// This checks that casting to an instance type is treated as a
// cast an not accidentally treated as a function call.
blockingTest(t, blockingTestArgs{
src: `package test
type Foo[T any] interface {
Baz() T
}
Expand All @@ -108,30 +132,58 @@ func TestInstanceBlocking(t *testing.T) {
func (b Bar) Baz() string {
return b.name
}
func caster() {
a := Bar{"foo"}
b := Foo[string](a)
println(b.Baz())
func caster() Foo[string] {
b := Bar{"foo"}
return Foo[string](b)
}`,
notBlocking: []string{`caster`},
},
}
notBlocking: []string{`caster`},
})
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
f := srctesting.New(t)
file := f.Parse(`test.go`, test.src)
typesInfo, typesPkg := f.Check(`pkg/test`, file)
pkgInfo := AnalyzePkg([]*ast.File{file}, f.FileSet, typesInfo, typesPkg, func(f *types.Func) bool {
panic(`isBlocking() should be never called for imported functions in this test.`)
})
for _, funcName := range test.blocking {
assertBlocking(t, file, pkgInfo, funcName)
func TestBlockingCastingToAnInterface(t *testing.T) {
// This checks of non-generic casting of type is treated as a
// cast an not accidentally treated as a function call.
blockingTest(t, blockingTestArgs{
src: `package test
type Foo interface {
Baz() string
}
type Bar struct {
name string
}
for _, funcName := range test.notBlocking {
assertNotBlocking(t, file, pkgInfo, funcName)
func (b Bar) Baz() string {
return b.name
}
})
func caster() Foo {
b := Bar{"foo"}
return Foo(b)
}`,
notBlocking: []string{`caster`},
})
}

type blockingTestArgs struct {
src string
blocking []string
notBlocking []string
}

func blockingTest(t *testing.T, test blockingTestArgs) {
f := srctesting.New(t)

file := f.Parse(`test.go`, test.src)
typesInfo, typesPkg := f.Check(`pkg/test`, file)

pkgInfo := AnalyzePkg([]*ast.File{file}, f.FileSet, typesInfo, typesPkg, func(f *types.Func) bool {
panic(`isBlocking() should be never called for imported functions in this test.`)
})

for _, funcName := range test.blocking {
assertBlocking(t, file, pkgInfo, funcName)
}

for _, funcName := range test.notBlocking {
assertNotBlocking(t, file, pkgInfo, funcName)
}
}

Expand Down

0 comments on commit f6d7860

Please sign in to comment.