Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
merged #1351
  • Loading branch information
grantnelson-wf committed Nov 21, 2024
commit ea5dd714a2c7b9e100e00941a619f3258b84f780
196 changes: 135 additions & 61 deletions compiler/internal/analysis/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,15 @@ func TestBlocking_Defers_WithReturns_WithNamedFuncs(t *testing.T) {
bt.assertNotBlocking(`nonBlockingPrint`)

bt.assertBlocking(`blockingBody`)
bt.assertBlockingReturn(13)
bt.assertBlockingReturn(13, ``)

bt.assertBlocking(`blockingArg`)
// The defer is non-blocking so the return is not blocking
// even though the function is blocking.
bt.assertNotBlockingReturn(18)
bt.assertNotBlockingReturn(18, ``)

bt.assertNotBlocking(`notBlocking`)
bt.assertNotBlockingReturn(23)
bt.assertNotBlockingReturn(23, ``)
}

func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
Expand Down Expand Up @@ -365,13 +365,13 @@ func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
bt.assertBlocking(`foo`)
bt.assertNotBlockingLit(4, ``)
// Early escape from function without blocking defers is not blocking.
bt.assertNotBlockingReturn(11)
bt.assertNotBlockingReturn(11, ``)
bt.assertNotBlockingLit(14, ``)
// Function has had blocking by this point but no blocking defers yet.
bt.assertNotBlockingReturn(20)
bt.assertNotBlockingReturn(20, ``)
bt.assertBlockingLit(24, ``)
// The return is blocking because of a blocking defer.
bt.assertBlockingReturn(28)
bt.assertBlockingReturn(28, ``)
// Technically the return on line 31 is not blocking since the defer that
// is blocking can only exit through the return on line 28, but it would be
// difficult to determine which defers would only affect certain returns
Expand All @@ -384,7 +384,7 @@ func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
//
// For now we simply build up the list of defers as we go making
// the return on line 31 also blocking.
bt.assertBlockingReturn(31)
bt.assertBlockingReturn(31, ``)
}

func TestBlocking_Defers_WithReturnsAndDefaultBlocking(t *testing.T) {
Expand Down Expand Up @@ -453,12 +453,12 @@ func TestBlocking_Defers_WithReturnsAndDefaultBlocking(t *testing.T) {
bt.assertBlocking(`deferMappedFuncCall`)

// All of these returns are blocking because they have blocking defers.
bt.assertBlockingReturn(17)
bt.assertBlockingReturn(22)
bt.assertBlockingReturn(28)
bt.assertBlockingReturn(34)
bt.assertBlockingReturn(40)
bt.assertBlockingReturn(49)
bt.assertBlockingReturn(17, ``)
bt.assertBlockingReturn(22, ``)
bt.assertBlockingReturn(28, ``)
bt.assertBlockingReturn(34, ``)
bt.assertBlockingReturn(40, ``)
bt.assertBlockingReturn(49, ``)
}

func TestBlocking_Defers_WithReturnsAndDeferBuiltin(t *testing.T) {
Expand All @@ -477,7 +477,7 @@ func TestBlocking_Defers_WithReturnsAndDeferBuiltin(t *testing.T) {

bt.assertFuncInstCount(1)
bt.assertNotBlocking(`deferBuiltinCall`)
bt.assertNotBlockingReturn(10)
bt.assertNotBlockingReturn(10, ``)
}

func TestBlocking_Defers_WithReturnsInLoops(t *testing.T) {
Expand Down Expand Up @@ -575,14 +575,14 @@ func TestBlocking_Defers_WithReturnsInLoops(t *testing.T) {
// When the following 2 returns are defined there are no defers, however,
// because of the loop, the blocking defers defined after the return will
// block the returns.
bt.assertBlockingReturn(12)
bt.assertBlockingReturn(22)
bt.assertBlockingReturn(31)
bt.assertBlockingReturn(44)
bt.assertBlockingReturn(52)
bt.assertBlockingReturn(66)
bt.assertBlockingReturn(73)
bt.assertBlockingReturn(77)
bt.assertBlockingReturn(12, ``)
bt.assertBlockingReturn(22, ``)
bt.assertBlockingReturn(31, ``)
bt.assertBlockingReturn(44, ``)
bt.assertBlockingReturn(52, ``)
bt.assertBlockingReturn(66, ``)
bt.assertBlockingReturn(73, ``)
bt.assertBlockingReturn(77, ``)
}

func TestBlocking_Defers_WithReturnsInLoopsInLoops(t *testing.T) {
Expand Down Expand Up @@ -652,19 +652,19 @@ func TestBlocking_Defers_WithReturnsInLoopsInLoops(t *testing.T) {
bt.assertFuncInstCount(4)
bt.assertBlocking(`blocking`)
bt.assertBlocking(`forLoopTheLoop`)
bt.assertNotBlockingReturn(9)
bt.assertBlockingReturn(13)
bt.assertBlockingReturn(17)
bt.assertBlockingReturn(21)
bt.assertBlockingReturn(25)
bt.assertBlockingReturn(28)
bt.assertNotBlockingReturn(9, ``)
bt.assertBlockingReturn(13, ``)
bt.assertBlockingReturn(17, ``)
bt.assertBlockingReturn(21, ``)
bt.assertBlockingReturn(25, ``)
bt.assertBlockingReturn(28, ``)
bt.assertBlocking(`rangeLoopTheLoop`)
bt.assertBlockingReturn(36)
bt.assertBlockingReturn(41)
bt.assertBlockingReturn(36, ``)
bt.assertBlockingReturn(41, ``)
bt.assertBlocking(`noopThenLoop`)
bt.assertNotBlockingReturn(48)
bt.assertBlockingReturn(54)
bt.assertBlockingReturn(58)
bt.assertNotBlockingReturn(48, ``)
bt.assertBlockingReturn(54, ``)
bt.assertBlockingReturn(58, ``)
}

func TestBlocking_Returns_WithoutDefers(t *testing.T) {
Expand Down Expand Up @@ -693,19 +693,67 @@ func TestBlocking_Returns_WithoutDefers(t *testing.T) {
return true // line 22
}`)
bt.assertBlocking(`blocking`)
bt.assertBlockingReturn(4)
bt.assertBlockingReturn(4, ``)

bt.assertBlocking(`blockingBeforeReturn`)
bt.assertNotBlockingReturn(9)
bt.assertNotBlockingReturn(9, ``)

bt.assertBlocking(`indirectlyBlocking`)
bt.assertBlockingReturn(13)
bt.assertBlockingReturn(13, ``)

bt.assertBlocking(`indirectlyBlockingBeforeReturn`)
bt.assertNotBlockingReturn(18)
bt.assertNotBlockingReturn(18, ``)

bt.assertNotBlocking(`notBlocking`)
bt.assertNotBlockingReturn(22)
bt.assertNotBlockingReturn(22, ``)
}

func TestBlocking_Defers_WithReturnsInInstances(t *testing.T) {
// This is an example of a deferred function literal inside of
// an instance of a generic function affecting the return
// differently based on the type arguments of the instance.
bt := newBlockingTest(t,
`package test

type BazBlocker struct {
c chan bool
}
func (bb BazBlocker) Baz() {
println(<-bb.c)
}

type BazNotBlocker struct {}
func (bnb BazNotBlocker) Baz() {
println("hi")
}

type Foo interface { Baz() }
func FooBaz[T Foo]() bool {
defer func() { // line 17
var foo T
foo.Baz()
}()
return true // line 21
}

func main() {
FooBaz[BazBlocker]()
FooBaz[BazNotBlocker]()
}`)

bt.assertFuncInstCount(5)
bt.assertBlocking(`BazBlocker.Baz`)
bt.assertNotBlocking(`BazNotBlocker.Baz`)
bt.assertBlockingInst(`pkg/test.FooBaz<pkg/test.BazBlocker>`)
bt.assertNotBlockingInst(`pkg/test.FooBaz<pkg/test.BazNotBlocker>`)
bt.assertBlocking(`main`)

bt.assertFuncLitCount(2)
bt.assertBlockingLit(17, `pkg/test.BazBlocker`)
bt.assertNotBlockingLit(17, `pkg/test.BazNotBlocker`)

bt.assertBlockingReturn(21, `pkg/test.BazBlocker`)
bt.assertNotBlockingReturn(21, `pkg/test.BazNotBlocker`)
}

func TestBlocking_Defers_WithReturnsAndOtherPackages(t *testing.T) {
Expand Down Expand Up @@ -737,10 +785,10 @@ func TestBlocking_Defers_WithReturnsAndOtherPackages(t *testing.T) {
bt := newBlockingTestWithOtherPackage(t, testSrc, otherSrc)

bt.assertBlocking(`deferOtherBlocking`)
bt.assertBlockingReturn(7)
bt.assertBlockingReturn(7, ``)

bt.assertNotBlocking(`deferOtherNotBlocking`)
bt.assertNotBlockingReturn(12)
bt.assertNotBlockingReturn(12, ``)
}

func TestBlocking_FunctionLiteral(t *testing.T) {
Expand Down Expand Up @@ -1652,15 +1700,23 @@ func (bt *blockingTest) assertFuncInstCount(expCount int) {
}

func (bt *blockingTest) assertFuncLitCount(expCount int) {
if got := len(bt.pkgInfo.funcLitInfos); got != expCount {
got := 0
for _, fis := range bt.pkgInfo.funcLitInfos {
got += len(fis)
}
if got != expCount {
bt.f.T.Errorf(`Got %d function literal infos but expected %d.`, got, expCount)
pos := make([]string, 0, len(bt.pkgInfo.funcLitInfos))
for fl := range bt.pkgInfo.funcLitInfos {
pos = append(pos, bt.f.FileSet.Position(fl.Pos()).String())

lits := make([]string, 0, len(bt.pkgInfo.funcLitInfos))
for fl, fis := range bt.pkgInfo.funcLitInfos {
pos := bt.f.FileSet.Position(fl.Pos()).String()
for _, fi := range fis {
lits = append(lits, pos+`<`+fi.typeArgs.String()+`>`)
}
}
sort.Strings(pos)
for i := range pos {
bt.f.T.Logf(` %d. %q`, i+1, pos)
sort.Strings(lits)
for i := range lits {
bt.f.T.Logf(` %d. %q`, i+1, lits[i])
}
}
}
Expand Down Expand Up @@ -1716,13 +1772,13 @@ func (bt *blockingTest) isTypesFuncBlocking(funcName string) bool {

func (bt *blockingTest) assertBlockingLit(lineNo int, typeArgsStr string) {
if !bt.isFuncLitBlocking(lineNo, typeArgsStr) {
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
}
}

func (bt *blockingTest) assertNotBlockingLit(lineNo int, typeArgsStr string) {
if bt.isFuncLitBlocking(lineNo, typeArgsStr) {
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
}
}

Expand All @@ -1745,7 +1801,7 @@ func (bt *blockingTest) isFuncLitBlocking(lineNo int, typeArgsStr string) bool {

bt.f.T.Logf("FuncList instances:")
for i, fi := range fis {
bt.f.T.Logf("\t%d. %q\n", i, fi.typeArgs.String())
bt.f.T.Logf("\t%d. %q\n", i+1, fi.typeArgs.String())
}
bt.f.T.Fatalf(`No FuncInfo found for FuncLit at line %d with type args %q.`, lineNo, typeArgsStr)
return false
Expand All @@ -1772,34 +1828,52 @@ func (bt *blockingTest) isFuncInstBlocking(instanceStr string) bool {
}
bt.f.T.Logf(`Function instances found in package info:`)
for i, inst := range instances {
bt.f.T.Logf(` %d. %s`, i+1, inst.String())
bt.f.T.Logf("\t%d. %s", i+1, inst.String())
}
bt.f.T.Fatalf(`No function instance found for %q in package info.`, instanceStr)
return false
}

func (bt *blockingTest) assertBlockingReturn(lineNo int) {
if !bt.isReturnBlocking(lineNo) {
bt.f.T.Errorf(`Got return at line %d as not blocking but expected it to be blocking.`, lineNo)
func (bt *blockingTest) assertBlockingReturn(lineNo int, typeArgsStr string) {
if !bt.isReturnBlocking(lineNo, typeArgsStr) {
bt.f.T.Errorf(`Got return at line %d (%q) as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
}
}

func (bt *blockingTest) assertNotBlockingReturn(lineNo int) {
if bt.isReturnBlocking(lineNo) {
bt.f.T.Errorf(`Got return at line %d as blocking but expected it to be not blocking.`, lineNo)
func (bt *blockingTest) assertNotBlockingReturn(lineNo int, typeArgsStr string) {
if bt.isReturnBlocking(lineNo, typeArgsStr) {
bt.f.T.Errorf(`Got return at line %d (%q) as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
}
}

func (bt *blockingTest) isReturnBlocking(lineNo int) bool {
func (bt *blockingTest) isReturnBlocking(lineNo int, typeArgsStr string) bool {
ret := srctesting.GetNodeAtLineNo[*ast.ReturnStmt](bt.file, bt.f.FileSet, lineNo)
if ret == nil {
bt.f.T.Fatalf(`ReturnStmt on line %d not found in the AST.`, lineNo)
}

foundInfo := []*FuncInfo{}
for _, info := range bt.pkgInfo.allInfos {
if blocking, found := info.Blocking[ret]; found {
return blocking
for _, rs := range info.returnStmts {
if rs.analyzeStack[len(rs.analyzeStack)-1] == ret {
if info.typeArgs.String() == typeArgsStr {
// Found info that matches the type args and
// has the return statement so return the blocking value.
return info.Blocking[ret]
}

// Wrong instance, record for error message in the case
// that the correct one instance is not found.
foundInfo = append(foundInfo, info)
break
}
}
}
// If not found in any info.Blocking, then it is not blocking.

bt.f.T.Logf("FuncInfo instances with ReturnStmt at line %d:", lineNo)
for i, info := range foundInfo {
bt.f.T.Logf("\t%d. %q\n", i+1, info.typeArgs.String())
}
bt.f.T.Fatalf(`No FuncInfo found for ReturnStmt at line %d with type args %q.`, lineNo, typeArgsStr)
return false
}
1 change: 1 addition & 0 deletions compiler/typesutil/typelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func (tl TypeList) String() string {
return buf.String()
}

// Equal returns true if both lists of type arguments are identical.
func (tl TypeList) Equal(other TypeList) bool {
if len(tl) != len(other) {
return false
Expand Down
Loading