github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/irutil/terminates.go (about) 1 package irutil 2 3 import ( 4 "go/types" 5 6 "github.com/amarpal/go-tools/go/ir" 7 ) 8 9 // Terminates reports whether fn is supposed to return, that is if it 10 // has at least one theoretic path that returns from the function. 11 // Explicit panics do not count as terminating. 12 func Terminates(fn *ir.Function) bool { 13 if fn.Blocks == nil { 14 // assuming that a function terminates is the conservative 15 // choice 16 return true 17 } 18 19 for _, block := range fn.Blocks { 20 if _, ok := block.Control().(*ir.Return); ok { 21 if len(block.Preds) == 0 { 22 return true 23 } 24 for _, pred := range block.Preds { 25 switch ctrl := pred.Control().(type) { 26 case *ir.Panic: 27 // explicit panics do not count as terminating 28 case *ir.If: 29 // Check if we got here by receiving from a closed 30 // time.Tick channel – this cannot happen at 31 // runtime and thus doesn't constitute termination 32 iff := ctrl 33 if !ok { 34 return true 35 } 36 ex, ok := iff.Cond.(*ir.Extract) 37 if !ok { 38 return true 39 } 40 if ex.Index != 1 { 41 return true 42 } 43 recv, ok := ex.Tuple.(*ir.Recv) 44 if !ok { 45 return true 46 } 47 call, ok := recv.Chan.(*ir.Call) 48 if !ok { 49 return true 50 } 51 fn, ok := call.Common().Value.(*ir.Function) 52 if !ok { 53 return true 54 } 55 fn2, ok := fn.Object().(*types.Func) 56 if !ok { 57 return true 58 } 59 if fn2.FullName() != "time.Tick" { 60 return true 61 } 62 default: 63 // we've reached the exit block 64 return true 65 } 66 } 67 } 68 } 69 return false 70 }