github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/pointsto14.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/ast/astutil"
    16  	"golang.org/x/tools/go/loader"
    17  	"golang.org/x/tools/go/pointer"
    18  	"golang.org/x/tools/go/ssa"
    19  	"golang.org/x/tools/go/ssa/ssautil"
    20  	"golang.org/x/tools/go/types"
    21  	"golang.org/x/tools/oracle/serial"
    22  )
    23  
    24  // pointsto runs the pointer analysis on the selected expression,
    25  // and reports its points-to set (for a pointer-like expression)
    26  // or its dynamic types (for an interface, reflect.Value, or
    27  // reflect.Type expression) and their points-to sets.
    28  //
    29  // All printed sets are sorted to ensure determinism.
    30  //
    31  func pointsto(q *Query) error {
    32  	lconf := loader.Config{Build: q.Build}
    33  
    34  	if err := setPTAScope(&lconf, q.Scope); err != nil {
    35  		return err
    36  	}
    37  
    38  	// Load/parse/type-check the program.
    39  	lprog, err := lconf.Load()
    40  	if err != nil {
    41  		return err
    42  	}
    43  	q.Fset = lprog.Fset
    44  
    45  	qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
    51  
    52  	ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	path, action := findInterestingNode(qpos.info, qpos.path)
    58  	if action != actionExpr {
    59  		return fmt.Errorf("pointer analysis wants an expression; got %s",
    60  			astutil.NodeDescription(qpos.path[0]))
    61  	}
    62  
    63  	var expr ast.Expr
    64  	var obj types.Object
    65  	switch n := path[0].(type) {
    66  	case *ast.ValueSpec:
    67  		// ambiguous ValueSpec containing multiple names
    68  		return fmt.Errorf("multiple value specification")
    69  	case *ast.Ident:
    70  		obj = qpos.info.ObjectOf(n)
    71  		expr = n
    72  	case ast.Expr:
    73  		expr = n
    74  	default:
    75  		// TODO(adonovan): is this reachable?
    76  		return fmt.Errorf("unexpected AST for expr: %T", n)
    77  	}
    78  
    79  	// Reject non-pointerlike types (includes all constants---except nil).
    80  	// TODO(adonovan): reject nil too.
    81  	typ := qpos.info.TypeOf(expr)
    82  	if !pointer.CanPoint(typ) {
    83  		return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ)
    84  	}
    85  
    86  	// Determine the ssa.Value for the expression.
    87  	var value ssa.Value
    88  	var isAddr bool
    89  	if obj != nil {
    90  		// def/ref of func/var object
    91  		value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path)
    92  	} else {
    93  		value, isAddr, err = ssaValueForExpr(prog, qpos.info, path)
    94  	}
    95  	if err != nil {
    96  		return err // e.g. trivially dead code
    97  	}
    98  
    99  	// Defer SSA construction till after errors are reported.
   100  	prog.Build()
   101  
   102  	// Run the pointer analysis.
   103  	ptrs, err := runPTA(ptaConfig, value, isAddr)
   104  	if err != nil {
   105  		return err // e.g. analytically unreachable
   106  	}
   107  
   108  	q.result = &pointstoResult{
   109  		qpos: qpos,
   110  		typ:  typ,
   111  		ptrs: ptrs,
   112  	}
   113  	return nil
   114  }
   115  
   116  // ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
   117  // to the root of the AST is path.  isAddr reports whether the
   118  // ssa.Value is the address denoted by the ast.Ident, not its value.
   119  //
   120  func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
   121  	switch obj := obj.(type) {
   122  	case *types.Var:
   123  		pkg := prog.Package(qinfo.Pkg)
   124  		pkg.Build()
   125  		if v, addr := prog.VarValue(obj, pkg, path); v != nil {
   126  			return v, addr, nil
   127  		}
   128  		return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
   129  
   130  	case *types.Func:
   131  		fn := prog.FuncValue(obj)
   132  		if fn == nil {
   133  			return nil, false, fmt.Errorf("%s is an interface method", obj)
   134  		}
   135  		// TODO(adonovan): there's no point running PTA on a *Func ident.
   136  		// Eliminate this feature.
   137  		return fn, false, nil
   138  	}
   139  	panic(obj)
   140  }
   141  
   142  // ssaValueForExpr returns the ssa.Value of the non-ast.Ident
   143  // expression whose path to the root of the AST is path.
   144  //
   145  func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
   146  	pkg := prog.Package(qinfo.Pkg)
   147  	pkg.SetDebugMode(true)
   148  	pkg.Build()
   149  
   150  	fn := ssa.EnclosingFunction(pkg, path)
   151  	if fn == nil {
   152  		return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
   153  	}
   154  
   155  	if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
   156  		return v, addr, nil
   157  	}
   158  
   159  	return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
   160  }
   161  
   162  // runPTA runs the pointer analysis of the selected SSA value or address.
   163  func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
   164  	T := v.Type()
   165  	if isAddr {
   166  		conf.AddIndirectQuery(v)
   167  		T = deref(T)
   168  	} else {
   169  		conf.AddQuery(v)
   170  	}
   171  	ptares := ptrAnalysis(conf)
   172  
   173  	var ptr pointer.Pointer
   174  	if isAddr {
   175  		ptr = ptares.IndirectQueries[v]
   176  	} else {
   177  		ptr = ptares.Queries[v]
   178  	}
   179  	if ptr == (pointer.Pointer{}) {
   180  		return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
   181  	}
   182  	pts := ptr.PointsTo()
   183  
   184  	if pointer.CanHaveDynamicTypes(T) {
   185  		// Show concrete types for interface/reflect.Value expression.
   186  		if concs := pts.DynamicTypes(); concs.Len() > 0 {
   187  			concs.Iterate(func(conc types.Type, pta interface{}) {
   188  				labels := pta.(pointer.PointsToSet).Labels()
   189  				sort.Sort(byPosAndString(labels)) // to ensure determinism
   190  				ptrs = append(ptrs, pointerResult{conc, labels})
   191  			})
   192  		}
   193  	} else {
   194  		// Show labels for other expressions.
   195  		labels := pts.Labels()
   196  		sort.Sort(byPosAndString(labels)) // to ensure determinism
   197  		ptrs = append(ptrs, pointerResult{T, labels})
   198  	}
   199  	sort.Sort(byTypeString(ptrs)) // to ensure determinism
   200  	return ptrs, nil
   201  }
   202  
   203  type pointerResult struct {
   204  	typ    types.Type       // type of the pointer (always concrete)
   205  	labels []*pointer.Label // set of labels
   206  }
   207  
   208  type pointstoResult struct {
   209  	qpos *queryPos
   210  	typ  types.Type      // type of expression
   211  	ptrs []pointerResult // pointer info (typ is concrete => len==1)
   212  }
   213  
   214  func (r *pointstoResult) display(printf printfFunc) {
   215  	if pointer.CanHaveDynamicTypes(r.typ) {
   216  		// Show concrete types for interface, reflect.Type or
   217  		// reflect.Value expression.
   218  
   219  		if len(r.ptrs) > 0 {
   220  			printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ))
   221  			for _, ptr := range r.ptrs {
   222  				var obj types.Object
   223  				if nt, ok := deref(ptr.typ).(*types.Named); ok {
   224  					obj = nt.Obj()
   225  				}
   226  				if len(ptr.labels) > 0 {
   227  					printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ))
   228  					printLabels(printf, ptr.labels, "\t\t")
   229  				} else {
   230  					printf(obj, "\t%s", r.qpos.typeString(ptr.typ))
   231  				}
   232  			}
   233  		} else {
   234  			printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
   235  		}
   236  	} else {
   237  		// Show labels for other expressions.
   238  		if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
   239  			printf(r.qpos, "this %s may point to these objects:",
   240  				r.qpos.typeString(r.typ))
   241  			printLabels(printf, ptr.labels, "\t")
   242  		} else {
   243  			printf(r.qpos, "this %s may not point to anything.",
   244  				r.qpos.typeString(r.typ))
   245  		}
   246  	}
   247  }
   248  
   249  func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) {
   250  	var pts []serial.PointsTo
   251  	for _, ptr := range r.ptrs {
   252  		var namePos string
   253  		if nt, ok := deref(ptr.typ).(*types.Named); ok {
   254  			namePos = fset.Position(nt.Obj().Pos()).String()
   255  		}
   256  		var labels []serial.PointsToLabel
   257  		for _, l := range ptr.labels {
   258  			labels = append(labels, serial.PointsToLabel{
   259  				Pos:  fset.Position(l.Pos()).String(),
   260  				Desc: l.String(),
   261  			})
   262  		}
   263  		pts = append(pts, serial.PointsTo{
   264  			Type:    r.qpos.typeString(ptr.typ),
   265  			NamePos: namePos,
   266  			Labels:  labels,
   267  		})
   268  	}
   269  	res.PointsTo = pts
   270  }
   271  
   272  type byTypeString []pointerResult
   273  
   274  func (a byTypeString) Len() int           { return len(a) }
   275  func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
   276  func (a byTypeString) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   277  
   278  type byPosAndString []*pointer.Label
   279  
   280  func (a byPosAndString) Len() int { return len(a) }
   281  func (a byPosAndString) Less(i, j int) bool {
   282  	cmp := a[i].Pos() - a[j].Pos()
   283  	return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
   284  }
   285  func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   286  
   287  func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
   288  	// TODO(adonovan): due to context-sensitivity, many of these
   289  	// labels may differ only by context, which isn't apparent.
   290  	for _, label := range labels {
   291  		printf(label, "%s%s", prefix, label)
   292  	}
   293  }