gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/staticcheck/functions/pure.go (about)

     1  package functions
     2  
     3  import (
     4  	"go/token"
     5  	"go/types"
     6  
     7  	"github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa"
     8  	"github.com/360EntSecGroup-Skylar/goreporter/linters/staticcheck/callgraph"
     9  )
    10  
    11  func (d *Descriptions) IsPure(fn *ssa.Function) bool {
    12  	if fn.Signature.Results().Len() == 0 {
    13  		// A function with no return values is empty or is doing some
    14  		// work we cannot see (for example because of build tags);
    15  		// don't consider it pure.
    16  		return false
    17  	}
    18  
    19  	for _, param := range fn.Params {
    20  		if _, ok := param.Type().Underlying().(*types.Basic); !ok {
    21  			return false
    22  		}
    23  	}
    24  
    25  	if fn.Blocks == nil {
    26  		return false
    27  	}
    28  	checkCall := func(common *ssa.CallCommon) bool {
    29  		if common.IsInvoke() {
    30  			return false
    31  		}
    32  		builtin, ok := common.Value.(*ssa.Builtin)
    33  		if !ok {
    34  			if common.StaticCallee() != fn {
    35  				if common.StaticCallee() == nil {
    36  					return false
    37  				}
    38  				// TODO(dh): ideally, IsPure wouldn't be responsible
    39  				// for avoiding infinite recursion, but
    40  				// FunctionDescriptions would be.
    41  				node := d.CallGraph.CreateNode(common.StaticCallee())
    42  				if callgraph.PathSearch(node, func(other *callgraph.Node) bool {
    43  					return other.Func == fn
    44  				}) != nil {
    45  					return false
    46  				}
    47  				if !d.Get(common.StaticCallee()).Pure {
    48  					return false
    49  				}
    50  			}
    51  		} else {
    52  			switch builtin.Name() {
    53  			case "len", "cap", "make", "new":
    54  			default:
    55  				return false
    56  			}
    57  		}
    58  		return true
    59  	}
    60  	for _, b := range fn.Blocks {
    61  		for _, ins := range b.Instrs {
    62  			switch ins := ins.(type) {
    63  			case *ssa.Call:
    64  				if !checkCall(ins.Common()) {
    65  					return false
    66  				}
    67  			case *ssa.Defer:
    68  				if !checkCall(&ins.Call) {
    69  					return false
    70  				}
    71  			case *ssa.Select:
    72  				return false
    73  			case *ssa.Send:
    74  				return false
    75  			case *ssa.Go:
    76  				return false
    77  			case *ssa.Panic:
    78  				return false
    79  			case *ssa.Store:
    80  				return false
    81  			case *ssa.FieldAddr:
    82  				return false
    83  			case *ssa.UnOp:
    84  				if ins.Op == token.MUL || ins.Op == token.AND {
    85  					return false
    86  				}
    87  			}
    88  		}
    89  	}
    90  	return true
    91  }