github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/source.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  package ir
     6  
     7  // This file defines utilities for working with source positions
     8  // or source-level named entities ("objects").
     9  
    10  // TODO(adonovan): test that {Value,Instruction}.Pos() positions match
    11  // the originating syntax, as specified.
    12  
    13  import (
    14  	"go/ast"
    15  	"go/token"
    16  	"go/types"
    17  )
    18  
    19  // EnclosingFunction returns the function that contains the syntax
    20  // node denoted by path.
    21  //
    22  // Syntax associated with package-level variable specifications is
    23  // enclosed by the package's init() function.
    24  //
    25  // Returns nil if not found; reasons might include:
    26  //   - the node is not enclosed by any function.
    27  //   - the node is within an anonymous function (FuncLit) and
    28  //     its IR function has not been created yet
    29  //     (pkg.Build() has not yet been called).
    30  func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
    31  	// Start with package-level function...
    32  	fn := findEnclosingPackageLevelFunction(pkg, path)
    33  	if fn == nil {
    34  		return nil // not in any function
    35  	}
    36  
    37  	// ...then walk down the nested anonymous functions.
    38  	n := len(path)
    39  outer:
    40  	for i := range path {
    41  		if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
    42  			for _, anon := range fn.AnonFuncs {
    43  				if anon.Pos() == lit.Type.Func {
    44  					fn = anon
    45  					continue outer
    46  				}
    47  			}
    48  			// IR function not found:
    49  			// - package not yet built, or maybe
    50  			// - builder skipped FuncLit in dead block
    51  			//   (in principle; but currently the Builder
    52  			//   generates even dead FuncLits).
    53  			return nil
    54  		}
    55  	}
    56  	return fn
    57  }
    58  
    59  // HasEnclosingFunction returns true if the AST node denoted by path
    60  // is contained within the declaration of some function or
    61  // package-level variable.
    62  //
    63  // Unlike EnclosingFunction, the behaviour of this function does not
    64  // depend on whether IR code for pkg has been built, so it can be
    65  // used to quickly reject check inputs that will cause
    66  // EnclosingFunction to fail, prior to IR building.
    67  func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
    68  	return findEnclosingPackageLevelFunction(pkg, path) != nil
    69  }
    70  
    71  // findEnclosingPackageLevelFunction returns the Function
    72  // corresponding to the package-level function enclosing path.
    73  func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
    74  	if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
    75  		switch decl := path[n-2].(type) {
    76  		case *ast.GenDecl:
    77  			if decl.Tok == token.VAR && n >= 3 {
    78  				// Package-level 'var' initializer.
    79  				return pkg.init
    80  			}
    81  
    82  		case *ast.FuncDecl:
    83  			// Declared function/method.
    84  			fn := findNamedFunc(pkg, decl.Pos())
    85  			if fn == nil && decl.Recv == nil && decl.Name.Name == "init" {
    86  				// Hack: return non-nil when IR is not yet
    87  				// built so that HasEnclosingFunction works.
    88  				return pkg.init
    89  			}
    90  			return fn
    91  		}
    92  	}
    93  	return nil // not in any function
    94  }
    95  
    96  // findNamedFunc returns the named function whose FuncDecl.Ident is at
    97  // position pos.
    98  func findNamedFunc(pkg *Package, pos token.Pos) *Function {
    99  	for _, fn := range pkg.Functions {
   100  		if fn.Pos() == pos {
   101  			return fn
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // ValueForExpr returns the IR Value that corresponds to non-constant
   108  // expression e.
   109  //
   110  // It returns nil if no value was found, e.g.
   111  //   - the expression is not lexically contained within f;
   112  //   - f was not built with debug information; or
   113  //   - e is a constant expression.  (For efficiency, no debug
   114  //     information is stored for constants. Use
   115  //     go/types.Info.Types[e].Value instead.)
   116  //   - e is a reference to nil or a built-in function.
   117  //   - the value was optimised away.
   118  //
   119  // If e is an addressable expression used in an lvalue context,
   120  // value is the address denoted by e, and isAddr is true.
   121  //
   122  // The types of e (or &e, if isAddr) and the result are equal
   123  // (modulo "untyped" bools resulting from comparisons).
   124  //
   125  // (Tip: to find the ir.Value given a source position, use
   126  // astutil.PathEnclosingInterval to locate the ast.Node, then
   127  // EnclosingFunction to locate the Function, then ValueForExpr to find
   128  // the ir.Value.)
   129  func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
   130  	if f.debugInfo() { // (opt)
   131  		e = unparen(e)
   132  		for _, b := range f.Blocks {
   133  			for _, instr := range b.Instrs {
   134  				if ref, ok := instr.(*DebugRef); ok {
   135  					if ref.Expr == e {
   136  						return ref.X, ref.IsAddr
   137  					}
   138  				}
   139  			}
   140  		}
   141  	}
   142  	return
   143  }
   144  
   145  // --- Lookup functions for source-level named entities (types.Objects) ---
   146  
   147  // Package returns the IR Package corresponding to the specified
   148  // type-checker package object.
   149  // It returns nil if no such IR package has been created.
   150  func (prog *Program) Package(obj *types.Package) *Package {
   151  	return prog.packages[obj]
   152  }
   153  
   154  // packageLevelValue returns the package-level value corresponding to
   155  // the specified named object, which may be a package-level const
   156  // (*Const), var (*Global) or func (*Function) of some package in
   157  // prog.  It returns nil if the object is not found.
   158  func (prog *Program) packageLevelValue(obj types.Object) Value {
   159  	if pkg, ok := prog.packages[obj.Pkg()]; ok {
   160  		return pkg.values[obj]
   161  	}
   162  	return nil
   163  }
   164  
   165  // FuncValue returns the concrete Function denoted by the source-level
   166  // named function obj, or nil if obj denotes an interface method.
   167  //
   168  // TODO(adonovan): check the invariant that obj.Type() matches the
   169  // result's Signature, both in the params/results and in the receiver.
   170  func (prog *Program) FuncValue(obj *types.Func) *Function {
   171  	obj = obj.Origin()
   172  	fn, _ := prog.packageLevelValue(obj).(*Function)
   173  	return fn
   174  }
   175  
   176  // ConstValue returns the IR Value denoted by the source-level named
   177  // constant obj.
   178  func (prog *Program) ConstValue(obj *types.Const) *Const {
   179  	// TODO(adonovan): opt: share (don't reallocate)
   180  	// Consts for const objects and constant ast.Exprs.
   181  
   182  	// Universal constant? {true,false,nil}
   183  	if obj.Parent() == types.Universe {
   184  		return NewConst(obj.Val(), obj.Type(), nil)
   185  	}
   186  	// Package-level named constant?
   187  	if v := prog.packageLevelValue(obj); v != nil {
   188  		return v.(*Const)
   189  	}
   190  	return NewConst(obj.Val(), obj.Type(), nil)
   191  }
   192  
   193  // VarValue returns the IR Value that corresponds to a specific
   194  // identifier denoting the source-level named variable obj.
   195  //
   196  // VarValue returns nil if a local variable was not found, perhaps
   197  // because its package was not built, the debug information was not
   198  // requested during IR construction, or the value was optimized away.
   199  //
   200  // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
   201  // and that ident must resolve to obj.
   202  //
   203  // pkg is the package enclosing the reference.  (A reference to a var
   204  // always occurs within a function, so we need to know where to find it.)
   205  //
   206  // If the identifier is a field selector and its base expression is
   207  // non-addressable, then VarValue returns the value of that field.
   208  // For example:
   209  //
   210  //	func f() struct {x int}
   211  //	f().x  // VarValue(x) returns a *Field instruction of type int
   212  //
   213  // All other identifiers denote addressable locations (variables).
   214  // For them, VarValue may return either the variable's address or its
   215  // value, even when the expression is evaluated only for its value; the
   216  // situation is reported by isAddr, the second component of the result.
   217  //
   218  // If !isAddr, the returned value is the one associated with the
   219  // specific identifier.  For example,
   220  //
   221  //	var x int    // VarValue(x) returns Const 0 here
   222  //	x = 1        // VarValue(x) returns Const 1 here
   223  //
   224  // It is not specified whether the value or the address is returned in
   225  // any particular case, as it may depend upon optimizations performed
   226  // during IR code generation, such as registerization, constant
   227  // folding, avoidance of materialization of subexpressions, etc.
   228  func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
   229  	// All references to a var are local to some function, possibly init.
   230  	fn := EnclosingFunction(pkg, ref)
   231  	if fn == nil {
   232  		return // e.g. def of struct field; IR not built?
   233  	}
   234  
   235  	id := ref[0].(*ast.Ident)
   236  
   237  	// Defining ident of a parameter?
   238  	if id.Pos() == obj.Pos() {
   239  		for _, param := range fn.Params {
   240  			if param.Object() == obj {
   241  				return param, false
   242  			}
   243  		}
   244  	}
   245  
   246  	// Other ident?
   247  	for _, b := range fn.Blocks {
   248  		for _, instr := range b.Instrs {
   249  			if dr, ok := instr.(*DebugRef); ok {
   250  				if dr.Pos() == id.Pos() {
   251  					return dr.X, dr.IsAddr
   252  				}
   253  			}
   254  		}
   255  	}
   256  
   257  	// Defining ident of package-level var?
   258  	if v := prog.packageLevelValue(obj); v != nil {
   259  		return v.(*Global), true
   260  	}
   261  
   262  	return // e.g. debug info not requested, or var optimized away
   263  }