gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/go/pointer/query.go (about)

     1  package pointer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"go/types"
    10  	"strconv"
    11  )
    12  
    13  // An extendedQuery represents a sequence of destructuring operations
    14  // applied to an ssa.Value (denoted by "x").
    15  type extendedQuery struct {
    16  	ops []interface{}
    17  	ptr *Pointer
    18  }
    19  
    20  // indexValue returns the value of an integer literal used as an
    21  // index.
    22  func indexValue(expr ast.Expr) (int, error) {
    23  	lit, ok := expr.(*ast.BasicLit)
    24  	if !ok {
    25  		return 0, fmt.Errorf("non-integer index (%T)", expr)
    26  	}
    27  	if lit.Kind != token.INT {
    28  		return 0, fmt.Errorf("non-integer index %s", lit.Value)
    29  	}
    30  	return strconv.Atoi(lit.Value)
    31  }
    32  
    33  // parseExtendedQuery parses and validates a destructuring Go
    34  // expression and returns the sequence of destructuring operations.
    35  // See parseDestructuringExpr for details.
    36  func parseExtendedQuery(typ types.Type, query string) ([]interface{}, types.Type, error) {
    37  	expr, err := parser.ParseExpr(query)
    38  	if err != nil {
    39  		return nil, nil, err
    40  	}
    41  	ops, typ, err := destructuringOps(typ, expr)
    42  	if err != nil {
    43  		return nil, nil, err
    44  	}
    45  	if len(ops) == 0 {
    46  		return nil, nil, errors.New("invalid query: must not be empty")
    47  	}
    48  	if ops[0] != "x" {
    49  		return nil, nil, fmt.Errorf("invalid query: query operand must be named x")
    50  	}
    51  	if !CanPoint(typ) {
    52  		return nil, nil, fmt.Errorf("query does not describe a pointer-like value: %s", typ)
    53  	}
    54  	return ops, typ, nil
    55  }
    56  
    57  // destructuringOps parses a Go expression consisting only of an
    58  // identifier "x", field selections, indexing, channel receives, load
    59  // operations and parens---for example: "<-(*x[i])[key]"--- and
    60  // returns the sequence of destructuring operations on x.
    61  func destructuringOps(typ types.Type, expr ast.Expr) ([]interface{}, types.Type, error) {
    62  	switch expr := expr.(type) {
    63  	case *ast.SelectorExpr:
    64  		out, typ, err := destructuringOps(typ, expr.X)
    65  		if err != nil {
    66  			return nil, nil, err
    67  		}
    68  
    69  		var structT *types.Struct
    70  		switch typ := typ.(type) {
    71  		case *types.Pointer:
    72  			var ok bool
    73  			structT, ok = typ.Elem().Underlying().(*types.Struct)
    74  			if !ok {
    75  				return nil, nil, fmt.Errorf("cannot access field %s of pointer to type %s", expr.Sel.Name, typ.Elem())
    76  			}
    77  
    78  			out = append(out, "load")
    79  		case *types.Struct:
    80  			structT = typ
    81  		default:
    82  			return nil, nil, fmt.Errorf("cannot access field %s of type %s", expr.Sel.Name, typ)
    83  		}
    84  
    85  		for i := 0; i < structT.NumFields(); i++ {
    86  			field := structT.Field(i)
    87  			if field.Name() == expr.Sel.Name {
    88  				out = append(out, "field", i)
    89  				return out, field.Type().Underlying(), nil
    90  			}
    91  		}
    92  		// TODO(dh): supporting embedding would need something like
    93  		// types.LookupFieldOrMethod, but without taking package
    94  		// boundaries into account, because we may want to access
    95  		// unexported fields. If we were only interested in one level
    96  		// of unexported name, we could determine the appropriate
    97  		// package and run LookupFieldOrMethod with that. However, a
    98  		// single query may want to cross multiple package boundaries,
    99  		// and at this point it's not really worth the complexity.
   100  		return nil, nil, fmt.Errorf("no field %s in %s (embedded fields must be resolved manually)", expr.Sel.Name, structT)
   101  	case *ast.Ident:
   102  		return []interface{}{expr.Name}, typ, nil
   103  	case *ast.BasicLit:
   104  		return []interface{}{expr.Value}, nil, nil
   105  	case *ast.IndexExpr:
   106  		out, typ, err := destructuringOps(typ, expr.X)
   107  		if err != nil {
   108  			return nil, nil, err
   109  		}
   110  		switch typ := typ.(type) {
   111  		case *types.Array:
   112  			out = append(out, "arrayelem")
   113  			return out, typ.Elem().Underlying(), nil
   114  		case *types.Slice:
   115  			out = append(out, "sliceelem")
   116  			return out, typ.Elem().Underlying(), nil
   117  		case *types.Map:
   118  			out = append(out, "mapelem")
   119  			return out, typ.Elem().Underlying(), nil
   120  		case *types.Tuple:
   121  			out = append(out, "index")
   122  			idx, err := indexValue(expr.Index)
   123  			if err != nil {
   124  				return nil, nil, err
   125  			}
   126  			out = append(out, idx)
   127  			if idx >= typ.Len() || idx < 0 {
   128  				return nil, nil, fmt.Errorf("tuple index %d out of bounds", idx)
   129  			}
   130  			return out, typ.At(idx).Type().Underlying(), nil
   131  		default:
   132  			return nil, nil, fmt.Errorf("cannot index type %s", typ)
   133  		}
   134  
   135  	case *ast.UnaryExpr:
   136  		if expr.Op != token.ARROW {
   137  			return nil, nil, fmt.Errorf("unsupported unary operator %s", expr.Op)
   138  		}
   139  		out, typ, err := destructuringOps(typ, expr.X)
   140  		if err != nil {
   141  			return nil, nil, err
   142  		}
   143  		ch, ok := typ.(*types.Chan)
   144  		if !ok {
   145  			return nil, nil, fmt.Errorf("cannot receive from value of type %s", typ)
   146  		}
   147  		out = append(out, "recv")
   148  		return out, ch.Elem().Underlying(), err
   149  	case *ast.ParenExpr:
   150  		return destructuringOps(typ, expr.X)
   151  	case *ast.StarExpr:
   152  		out, typ, err := destructuringOps(typ, expr.X)
   153  		if err != nil {
   154  			return nil, nil, err
   155  		}
   156  		ptr, ok := typ.(*types.Pointer)
   157  		if !ok {
   158  			return nil, nil, fmt.Errorf("cannot dereference type %s", typ)
   159  		}
   160  		out = append(out, "load")
   161  		return out, ptr.Elem().Underlying(), err
   162  	default:
   163  		return nil, nil, fmt.Errorf("unsupported expression %T", expr)
   164  	}
   165  }
   166  
   167  func (a *analysis) evalExtendedQuery(t types.Type, id nodeid, ops []interface{}) (types.Type, nodeid) {
   168  	pid := id
   169  	// TODO(dh): we're allocating intermediary nodes each time
   170  	// evalExtendedQuery is called. We should probably only generate
   171  	// them once per (v, ops) pair.
   172  	for i := 1; i < len(ops); i++ {
   173  		var nid nodeid
   174  		switch ops[i] {
   175  		case "recv":
   176  			t = t.(*types.Chan).Elem().Underlying()
   177  			nid = a.addNodes(t, "query.extended")
   178  			a.load(nid, pid, 0, a.sizeof(t))
   179  		case "field":
   180  			i++ // fetch field index
   181  			tt := t.(*types.Struct)
   182  			idx := ops[i].(int)
   183  			offset := a.offsetOf(t, idx)
   184  			t = tt.Field(idx).Type().Underlying()
   185  			nid = a.addNodes(t, "query.extended")
   186  			a.copy(nid, pid+nodeid(offset), a.sizeof(t))
   187  		case "arrayelem":
   188  			t = t.(*types.Array).Elem().Underlying()
   189  			nid = a.addNodes(t, "query.extended")
   190  			a.copy(nid, 1+pid, a.sizeof(t))
   191  		case "sliceelem":
   192  			t = t.(*types.Slice).Elem().Underlying()
   193  			nid = a.addNodes(t, "query.extended")
   194  			a.load(nid, pid, 1, a.sizeof(t))
   195  		case "mapelem":
   196  			tt := t.(*types.Map)
   197  			t = tt.Elem()
   198  			ksize := a.sizeof(tt.Key())
   199  			vsize := a.sizeof(tt.Elem())
   200  			nid = a.addNodes(t, "query.extended")
   201  			a.load(nid, pid, ksize, vsize)
   202  		case "index":
   203  			i++ // fetch index
   204  			tt := t.(*types.Tuple)
   205  			idx := ops[i].(int)
   206  			t = tt.At(idx).Type().Underlying()
   207  			nid = a.addNodes(t, "query.extended")
   208  			a.copy(nid, pid+nodeid(idx), a.sizeof(t))
   209  		case "load":
   210  			t = t.(*types.Pointer).Elem().Underlying()
   211  			nid = a.addNodes(t, "query.extended")
   212  			a.load(nid, pid, 0, a.sizeof(t))
   213  		default:
   214  			// shouldn't happen
   215  			panic(fmt.Sprintf("unknown op %q", ops[i]))
   216  		}
   217  		pid = nid
   218  	}
   219  
   220  	return t, pid
   221  }