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  }