github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/peers14.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build !go1.5
     6  
     7  package oracle
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"sort"
    14  
    15  	"golang.org/x/tools/go/loader"
    16  	"golang.org/x/tools/go/ssa"
    17  	"golang.org/x/tools/go/ssa/ssautil"
    18  	"golang.org/x/tools/go/types"
    19  	"golang.org/x/tools/oracle/serial"
    20  )
    21  
    22  // peers enumerates, for a given channel send (or receive) operation,
    23  // the set of possible receives (or sends) that correspond to it.
    24  //
    25  // TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
    26  // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
    27  // or the implicit receive in "for v := range ch".
    28  func peers(q *Query) error {
    29  	lconf := loader.Config{Build: q.Build}
    30  
    31  	if err := setPTAScope(&lconf, q.Scope); err != nil {
    32  		return err
    33  	}
    34  
    35  	// Load/parse/type-check the program.
    36  	lprog, err := lconf.Load()
    37  	if err != nil {
    38  		return err
    39  	}
    40  	q.Fset = lprog.Fset
    41  
    42  	qpos, err := parseQueryPos(lprog, q.Pos, false)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
    48  
    49  	ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	opPos := findOp(qpos)
    55  	if opPos == token.NoPos {
    56  		return fmt.Errorf("there is no channel operation here")
    57  	}
    58  
    59  	// Defer SSA construction till after errors are reported.
    60  	prog.Build()
    61  
    62  	var queryOp chanOp // the originating send or receive operation
    63  	var ops []chanOp   // all sends/receives of opposite direction
    64  
    65  	// Look at all channel operations in the whole ssa.Program.
    66  	// Build a list of those of same type as the query.
    67  	allFuncs := ssautil.AllFunctions(prog)
    68  	for fn := range allFuncs {
    69  		for _, b := range fn.Blocks {
    70  			for _, instr := range b.Instrs {
    71  				for _, op := range chanOps(instr) {
    72  					ops = append(ops, op)
    73  					if op.pos == opPos {
    74  						queryOp = op // we found the query op
    75  					}
    76  				}
    77  			}
    78  		}
    79  	}
    80  	if queryOp.ch == nil {
    81  		return fmt.Errorf("ssa.Instruction for send/receive not found")
    82  	}
    83  
    84  	// Discard operations of wrong channel element type.
    85  	// Build set of channel ssa.Values as query to pointer analysis.
    86  	// We compare channels by element types, not channel types, to
    87  	// ignore both directionality and type names.
    88  	queryType := queryOp.ch.Type()
    89  	queryElemType := queryType.Underlying().(*types.Chan).Elem()
    90  	ptaConfig.AddQuery(queryOp.ch)
    91  	i := 0
    92  	for _, op := range ops {
    93  		if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
    94  			ptaConfig.AddQuery(op.ch)
    95  			ops[i] = op
    96  			i++
    97  		}
    98  	}
    99  	ops = ops[:i]
   100  
   101  	// Run the pointer analysis.
   102  	ptares := ptrAnalysis(ptaConfig)
   103  
   104  	// Find the points-to set.
   105  	queryChanPtr := ptares.Queries[queryOp.ch]
   106  
   107  	// Ascertain which make(chan) labels the query's channel can alias.
   108  	var makes []token.Pos
   109  	for _, label := range queryChanPtr.PointsTo().Labels() {
   110  		makes = append(makes, label.Pos())
   111  	}
   112  	sort.Sort(byPos(makes))
   113  
   114  	// Ascertain which channel operations can alias the same make(chan) labels.
   115  	var sends, receives, closes []token.Pos
   116  	for _, op := range ops {
   117  		if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
   118  			switch op.dir {
   119  			case types.SendOnly:
   120  				sends = append(sends, op.pos)
   121  			case types.RecvOnly:
   122  				receives = append(receives, op.pos)
   123  			case types.SendRecv:
   124  				closes = append(closes, op.pos)
   125  			}
   126  		}
   127  	}
   128  	sort.Sort(byPos(sends))
   129  	sort.Sort(byPos(receives))
   130  	sort.Sort(byPos(closes))
   131  
   132  	q.result = &peersResult{
   133  		queryPos:  opPos,
   134  		queryType: queryType,
   135  		makes:     makes,
   136  		sends:     sends,
   137  		receives:  receives,
   138  		closes:    closes,
   139  	}
   140  	return nil
   141  }
   142  
   143  // findOp returns the position of the enclosing send/receive/close op.
   144  // For send and receive operations, this is the position of the <- token;
   145  // for close operations, it's the Lparen of the function call.
   146  //
   147  // TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
   148  func findOp(qpos *queryPos) token.Pos {
   149  	for _, n := range qpos.path {
   150  		switch n := n.(type) {
   151  		case *ast.UnaryExpr:
   152  			if n.Op == token.ARROW {
   153  				return n.OpPos
   154  			}
   155  		case *ast.SendStmt:
   156  			return n.Arrow
   157  		case *ast.CallExpr:
   158  			// close function call can only exist as a direct identifier
   159  			if close, ok := unparen(n.Fun).(*ast.Ident); ok {
   160  				if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
   161  					return n.Lparen
   162  				}
   163  			}
   164  		}
   165  	}
   166  	return token.NoPos
   167  }
   168  
   169  // chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
   170  type chanOp struct {
   171  	ch  ssa.Value
   172  	dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
   173  	pos token.Pos
   174  }
   175  
   176  // chanOps returns a slice of all the channel operations in the instruction.
   177  func chanOps(instr ssa.Instruction) []chanOp {
   178  	// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
   179  	var ops []chanOp
   180  	switch instr := instr.(type) {
   181  	case *ssa.UnOp:
   182  		if instr.Op == token.ARROW {
   183  			ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
   184  		}
   185  	case *ssa.Send:
   186  		ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
   187  	case *ssa.Select:
   188  		for _, st := range instr.States {
   189  			ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
   190  		}
   191  	case ssa.CallInstruction:
   192  		cc := instr.Common()
   193  		if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
   194  			ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
   195  		}
   196  	}
   197  	return ops
   198  }
   199  
   200  type peersResult struct {
   201  	queryPos                       token.Pos   // of queried channel op
   202  	queryType                      types.Type  // type of queried channel
   203  	makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
   204  }
   205  
   206  func (r *peersResult) display(printf printfFunc) {
   207  	if len(r.makes) == 0 {
   208  		printf(r.queryPos, "This channel can't point to anything.")
   209  		return
   210  	}
   211  	printf(r.queryPos, "This channel of type %s may be:", r.queryType)
   212  	for _, alloc := range r.makes {
   213  		printf(alloc, "\tallocated here")
   214  	}
   215  	for _, send := range r.sends {
   216  		printf(send, "\tsent to, here")
   217  	}
   218  	for _, receive := range r.receives {
   219  		printf(receive, "\treceived from, here")
   220  	}
   221  	for _, clos := range r.closes {
   222  		printf(clos, "\tclosed, here")
   223  	}
   224  }
   225  
   226  func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
   227  	peers := &serial.Peers{
   228  		Pos:  fset.Position(r.queryPos).String(),
   229  		Type: r.queryType.String(),
   230  	}
   231  	for _, alloc := range r.makes {
   232  		peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
   233  	}
   234  	for _, send := range r.sends {
   235  		peers.Sends = append(peers.Sends, fset.Position(send).String())
   236  	}
   237  	for _, receive := range r.receives {
   238  		peers.Receives = append(peers.Receives, fset.Position(receive).String())
   239  	}
   240  	for _, clos := range r.closes {
   241  		peers.Closes = append(peers.Closes, fset.Position(clos).String())
   242  	}
   243  	res.Peers = peers
   244  }
   245  
   246  // -------- utils --------
   247  
   248  // NB: byPos is not deterministic across packages since it depends on load order.
   249  // Use lessPos if the tests need it.
   250  type byPos []token.Pos
   251  
   252  func (p byPos) Len() int           { return len(p) }
   253  func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
   254  func (p byPos) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }