...

Source file src/golang.org/x/tools/go/ssa/func.go

Documentation: golang.org/x/tools/go/ssa

     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 ssa
     6  
     7  // This file implements the Function type.
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/types"
    14  	"io"
    15  	"os"
    16  	"strings"
    17  
    18  	"golang.org/x/tools/internal/typeparams"
    19  )
    20  
    21  // Like ObjectOf, but panics instead of returning nil.
    22  // Only valid during f's create and build phases.
    23  func (f *Function) objectOf(id *ast.Ident) types.Object {
    24  	if o := f.info.ObjectOf(id); o != nil {
    25  		return o
    26  	}
    27  	panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
    28  		id.Name, f.Prog.Fset.Position(id.Pos())))
    29  }
    30  
    31  // Like TypeOf, but panics instead of returning nil.
    32  // Only valid during f's create and build phases.
    33  func (f *Function) typeOf(e ast.Expr) types.Type {
    34  	if T := f.info.TypeOf(e); T != nil {
    35  		return f.typ(T)
    36  	}
    37  	panic(fmt.Sprintf("no type for %T @ %s", e, f.Prog.Fset.Position(e.Pos())))
    38  }
    39  
    40  // typ is the locally instantiated type of T.
    41  // If f is not an instantiation, then f.typ(T)==T.
    42  func (f *Function) typ(T types.Type) types.Type {
    43  	return f.subst.typ(T)
    44  }
    45  
    46  // If id is an Instance, returns info.Instances[id].Type.
    47  // Otherwise returns f.typeOf(id).
    48  func (f *Function) instanceType(id *ast.Ident) types.Type {
    49  	if t, ok := f.info.Instances[id]; ok {
    50  		return t.Type
    51  	}
    52  	return f.typeOf(id)
    53  }
    54  
    55  // selection returns a *selection corresponding to f.info.Selections[selector]
    56  // with potential updates for type substitution.
    57  func (f *Function) selection(selector *ast.SelectorExpr) *selection {
    58  	sel := f.info.Selections[selector]
    59  	if sel == nil {
    60  		return nil
    61  	}
    62  
    63  	switch sel.Kind() {
    64  	case types.MethodExpr, types.MethodVal:
    65  		if recv := f.typ(sel.Recv()); recv != sel.Recv() {
    66  			// recv changed during type substitution.
    67  			pkg := f.declaredPackage().Pkg
    68  			obj, index, indirect := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
    69  
    70  			// sig replaces sel.Type(). See (types.Selection).Typ() for details.
    71  			sig := obj.Type().(*types.Signature)
    72  			sig = changeRecv(sig, newVar(sig.Recv().Name(), recv))
    73  			if sel.Kind() == types.MethodExpr {
    74  				sig = recvAsFirstArg(sig)
    75  			}
    76  			return &selection{
    77  				kind:     sel.Kind(),
    78  				recv:     recv,
    79  				typ:      sig,
    80  				obj:      obj,
    81  				index:    index,
    82  				indirect: indirect,
    83  			}
    84  		}
    85  	}
    86  	return toSelection(sel)
    87  }
    88  
    89  // Destinations associated with unlabelled for/switch/select stmts.
    90  // We push/pop one of these as we enter/leave each construct and for
    91  // each BranchStmt we scan for the innermost target of the right type.
    92  type targets struct {
    93  	tail         *targets // rest of stack
    94  	_break       *BasicBlock
    95  	_continue    *BasicBlock
    96  	_fallthrough *BasicBlock
    97  }
    98  
    99  // Destinations associated with a labelled block.
   100  // We populate these as labels are encountered in forward gotos or
   101  // labelled statements.
   102  type lblock struct {
   103  	_goto     *BasicBlock
   104  	_break    *BasicBlock
   105  	_continue *BasicBlock
   106  }
   107  
   108  // labelledBlock returns the branch target associated with the
   109  // specified label, creating it if needed.
   110  // label should be a non-blank identifier (label.Name != "_").
   111  func (f *Function) labelledBlock(label *ast.Ident) *lblock {
   112  	obj := f.objectOf(label).(*types.Label)
   113  	lb := f.lblocks[obj]
   114  	if lb == nil {
   115  		lb = &lblock{_goto: f.newBasicBlock(label.Name)}
   116  		if f.lblocks == nil {
   117  			f.lblocks = make(map[*types.Label]*lblock)
   118  		}
   119  		f.lblocks[obj] = lb
   120  	}
   121  	return lb
   122  }
   123  
   124  // addParamVar adds a parameter to f.Params.
   125  func (f *Function) addParamVar(v *types.Var) *Parameter {
   126  	name := v.Name()
   127  	if name == "" {
   128  		name = fmt.Sprintf("arg%d", len(f.Params))
   129  	}
   130  	param := &Parameter{
   131  		name:   name,
   132  		object: v,
   133  		typ:    f.typ(v.Type()),
   134  		parent: f,
   135  	}
   136  	f.Params = append(f.Params, param)
   137  	return param
   138  }
   139  
   140  // addSpilledParam declares a parameter that is pre-spilled to the
   141  // stack; the function body will load/store the spilled location.
   142  // Subsequent lifting will eliminate spills where possible.
   143  func (f *Function) addSpilledParam(obj *types.Var) {
   144  	param := f.addParamVar(obj)
   145  	spill := emitLocalVar(f, obj)
   146  	f.emit(&Store{Addr: spill, Val: param})
   147  }
   148  
   149  // startBody initializes the function prior to generating SSA code for its body.
   150  // Precondition: f.Type() already set.
   151  func (f *Function) startBody() {
   152  	f.currentBlock = f.newBasicBlock("entry")
   153  	f.vars = make(map[*types.Var]Value) // needed for some synthetics, e.g. init
   154  }
   155  
   156  // createSyntacticParams populates f.Params and generates code (spills
   157  // and named result locals) for all the parameters declared in the
   158  // syntax.  In addition it populates the f.objects mapping.
   159  //
   160  // Preconditions:
   161  // f.startBody() was called. f.info != nil.
   162  // Postcondition:
   163  // len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
   164  func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
   165  	// Receiver (at most one inner iteration).
   166  	if recv != nil {
   167  		for _, field := range recv.List {
   168  			for _, n := range field.Names {
   169  				f.addSpilledParam(identVar(f, n))
   170  			}
   171  			// Anonymous receiver?  No need to spill.
   172  			if field.Names == nil {
   173  				f.addParamVar(f.Signature.Recv())
   174  			}
   175  		}
   176  	}
   177  
   178  	// Parameters.
   179  	if functype.Params != nil {
   180  		n := len(f.Params) // 1 if has recv, 0 otherwise
   181  		for _, field := range functype.Params.List {
   182  			for _, n := range field.Names {
   183  				f.addSpilledParam(identVar(f, n))
   184  			}
   185  			// Anonymous parameter?  No need to spill.
   186  			if field.Names == nil {
   187  				f.addParamVar(f.Signature.Params().At(len(f.Params) - n))
   188  			}
   189  		}
   190  	}
   191  
   192  	// Named results.
   193  	if functype.Results != nil {
   194  		for _, field := range functype.Results.List {
   195  			// Implicit "var" decl of locals for named results.
   196  			for _, n := range field.Names {
   197  				namedResult := emitLocalVar(f, identVar(f, n))
   198  				f.namedResults = append(f.namedResults, namedResult)
   199  			}
   200  		}
   201  	}
   202  }
   203  
   204  type setNumable interface {
   205  	setNum(int)
   206  }
   207  
   208  // numberRegisters assigns numbers to all SSA registers
   209  // (value-defining Instructions) in f, to aid debugging.
   210  // (Non-Instruction Values are named at construction.)
   211  func numberRegisters(f *Function) {
   212  	v := 0
   213  	for _, b := range f.Blocks {
   214  		for _, instr := range b.Instrs {
   215  			switch instr.(type) {
   216  			case Value:
   217  				instr.(setNumable).setNum(v)
   218  				v++
   219  			}
   220  		}
   221  	}
   222  }
   223  
   224  // buildReferrers populates the def/use information in all non-nil
   225  // Value.Referrers slice.
   226  // Precondition: all such slices are initially empty.
   227  func buildReferrers(f *Function) {
   228  	var rands []*Value
   229  	for _, b := range f.Blocks {
   230  		for _, instr := range b.Instrs {
   231  			rands = instr.Operands(rands[:0]) // recycle storage
   232  			for _, rand := range rands {
   233  				if r := *rand; r != nil {
   234  					if ref := r.Referrers(); ref != nil {
   235  						*ref = append(*ref, instr)
   236  					}
   237  				}
   238  			}
   239  		}
   240  	}
   241  }
   242  
   243  // finishBody() finalizes the contents of the function after SSA code generation of its body.
   244  //
   245  // The function is not done being built until done() is called.
   246  func (f *Function) finishBody() {
   247  	f.vars = nil
   248  	f.currentBlock = nil
   249  	f.lblocks = nil
   250  
   251  	// Remove from f.Locals any Allocs that escape to the heap.
   252  	j := 0
   253  	for _, l := range f.Locals {
   254  		if !l.Heap {
   255  			f.Locals[j] = l
   256  			j++
   257  		}
   258  	}
   259  	// Nil out f.Locals[j:] to aid GC.
   260  	for i := j; i < len(f.Locals); i++ {
   261  		f.Locals[i] = nil
   262  	}
   263  	f.Locals = f.Locals[:j]
   264  
   265  	optimizeBlocks(f)
   266  
   267  	buildReferrers(f)
   268  
   269  	buildDomTree(f)
   270  
   271  	if f.Prog.mode&NaiveForm == 0 {
   272  		// For debugging pre-state of lifting pass:
   273  		// numberRegisters(f)
   274  		// f.WriteTo(os.Stderr)
   275  		lift(f)
   276  	}
   277  
   278  	// clear remaining builder state
   279  	f.namedResults = nil // (used by lifting)
   280  	f.subst = nil
   281  
   282  	numberRegisters(f) // uses f.namedRegisters
   283  }
   284  
   285  // done marks the building of f's SSA body complete,
   286  // along with any nested functions, and optionally prints them.
   287  func (f *Function) done() {
   288  	assert(f.parent == nil, "done called on an anonymous function")
   289  
   290  	var visit func(*Function)
   291  	visit = func(f *Function) {
   292  		for _, anon := range f.AnonFuncs {
   293  			visit(anon) // anon is done building before f.
   294  		}
   295  
   296  		f.build = nil // function is built
   297  
   298  		if f.Prog.mode&PrintFunctions != 0 {
   299  			printMu.Lock()
   300  			f.WriteTo(os.Stdout)
   301  			printMu.Unlock()
   302  		}
   303  
   304  		if f.Prog.mode&SanityCheckFunctions != 0 {
   305  			mustSanityCheck(f, nil)
   306  		}
   307  	}
   308  	visit(f)
   309  }
   310  
   311  // removeNilBlocks eliminates nils from f.Blocks and updates each
   312  // BasicBlock.Index.  Use this after any pass that may delete blocks.
   313  func (f *Function) removeNilBlocks() {
   314  	j := 0
   315  	for _, b := range f.Blocks {
   316  		if b != nil {
   317  			b.Index = j
   318  			f.Blocks[j] = b
   319  			j++
   320  		}
   321  	}
   322  	// Nil out f.Blocks[j:] to aid GC.
   323  	for i := j; i < len(f.Blocks); i++ {
   324  		f.Blocks[i] = nil
   325  	}
   326  	f.Blocks = f.Blocks[:j]
   327  }
   328  
   329  // SetDebugMode sets the debug mode for package pkg.  If true, all its
   330  // functions will include full debug info.  This greatly increases the
   331  // size of the instruction stream, and causes Functions to depend upon
   332  // the ASTs, potentially keeping them live in memory for longer.
   333  func (pkg *Package) SetDebugMode(debug bool) {
   334  	pkg.debug = debug
   335  }
   336  
   337  // debugInfo reports whether debug info is wanted for this function.
   338  func (f *Function) debugInfo() bool {
   339  	// debug info for instantiations follows the debug info of their origin.
   340  	p := f.declaredPackage()
   341  	return p != nil && p.debug
   342  }
   343  
   344  // lookup returns the address of the named variable identified by obj
   345  // that is local to function f or one of its enclosing functions.
   346  // If escaping, the reference comes from a potentially escaping pointer
   347  // expression and the referent must be heap-allocated.
   348  // We assume the referent is a *Alloc or *Phi.
   349  // (The only Phis at this stage are those created directly by go1.22 "for" loops.)
   350  func (f *Function) lookup(obj *types.Var, escaping bool) Value {
   351  	if v, ok := f.vars[obj]; ok {
   352  		if escaping {
   353  			switch v := v.(type) {
   354  			case *Alloc:
   355  				v.Heap = true
   356  			case *Phi:
   357  				for _, edge := range v.Edges {
   358  					if alloc, ok := edge.(*Alloc); ok {
   359  						alloc.Heap = true
   360  					}
   361  				}
   362  			}
   363  		}
   364  		return v // function-local var (address)
   365  	}
   366  
   367  	// Definition must be in an enclosing function;
   368  	// plumb it through intervening closures.
   369  	if f.parent == nil {
   370  		panic("no ssa.Value for " + obj.String())
   371  	}
   372  	outer := f.parent.lookup(obj, true) // escaping
   373  	v := &FreeVar{
   374  		name:   obj.Name(),
   375  		typ:    outer.Type(),
   376  		pos:    outer.Pos(),
   377  		outer:  outer,
   378  		parent: f,
   379  	}
   380  	f.vars[obj] = v
   381  	f.FreeVars = append(f.FreeVars, v)
   382  	return v
   383  }
   384  
   385  // emit emits the specified instruction to function f.
   386  func (f *Function) emit(instr Instruction) Value {
   387  	return f.currentBlock.emit(instr)
   388  }
   389  
   390  // RelString returns the full name of this function, qualified by
   391  // package name, receiver type, etc.
   392  //
   393  // The specific formatting rules are not guaranteed and may change.
   394  //
   395  // Examples:
   396  //
   397  //	"math.IsNaN"                  // a package-level function
   398  //	"(*bytes.Buffer).Bytes"       // a declared method or a wrapper
   399  //	"(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
   400  //	"(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
   401  //	"main.main$1"                 // an anonymous function in main
   402  //	"main.init#1"                 // a declared init function
   403  //	"main.init"                   // the synthesized package initializer
   404  //
   405  // When these functions are referred to from within the same package
   406  // (i.e. from == f.Pkg.Object), they are rendered without the package path.
   407  // For example: "IsNaN", "(*Buffer).Bytes", etc.
   408  //
   409  // All non-synthetic functions have distinct package-qualified names.
   410  // (But two methods may have the same name "(T).f" if one is a synthetic
   411  // wrapper promoting a non-exported method "f" from another package; in
   412  // that case, the strings are equal but the identifiers "f" are distinct.)
   413  func (f *Function) RelString(from *types.Package) string {
   414  	// Anonymous?
   415  	if f.parent != nil {
   416  		// An anonymous function's Name() looks like "parentName$1",
   417  		// but its String() should include the type/package/etc.
   418  		parent := f.parent.RelString(from)
   419  		for i, anon := range f.parent.AnonFuncs {
   420  			if anon == f {
   421  				return fmt.Sprintf("%s$%d", parent, 1+i)
   422  			}
   423  		}
   424  
   425  		return f.name // should never happen
   426  	}
   427  
   428  	// Method (declared or wrapper)?
   429  	if recv := f.Signature.Recv(); recv != nil {
   430  		return f.relMethod(from, recv.Type())
   431  	}
   432  
   433  	// Thunk?
   434  	if f.method != nil {
   435  		return f.relMethod(from, f.method.recv)
   436  	}
   437  
   438  	// Bound?
   439  	if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
   440  		return f.relMethod(from, f.FreeVars[0].Type())
   441  	}
   442  
   443  	// Package-level function?
   444  	// Prefix with package name for cross-package references only.
   445  	if p := f.relPkg(); p != nil && p != from {
   446  		return fmt.Sprintf("%s.%s", p.Path(), f.name)
   447  	}
   448  
   449  	// Unknown.
   450  	return f.name
   451  }
   452  
   453  func (f *Function) relMethod(from *types.Package, recv types.Type) string {
   454  	return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
   455  }
   456  
   457  // writeSignature writes to buf the signature sig in declaration syntax.
   458  func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature) {
   459  	buf.WriteString("func ")
   460  	if recv := sig.Recv(); recv != nil {
   461  		buf.WriteString("(")
   462  		if name := recv.Name(); name != "" {
   463  			buf.WriteString(name)
   464  			buf.WriteString(" ")
   465  		}
   466  		types.WriteType(buf, recv.Type(), types.RelativeTo(from))
   467  		buf.WriteString(") ")
   468  	}
   469  	buf.WriteString(name)
   470  	types.WriteSignature(buf, sig, types.RelativeTo(from))
   471  }
   472  
   473  // declaredPackage returns the package fn is declared in or nil if the
   474  // function is not declared in a package.
   475  func (fn *Function) declaredPackage() *Package {
   476  	switch {
   477  	case fn.Pkg != nil:
   478  		return fn.Pkg // non-generic function  (does that follow??)
   479  	case fn.topLevelOrigin != nil:
   480  		return fn.topLevelOrigin.Pkg // instance of a named generic function
   481  	case fn.parent != nil:
   482  		return fn.parent.declaredPackage() // instance of an anonymous [generic] function
   483  	default:
   484  		return nil // function is not declared in a package, e.g. a wrapper.
   485  	}
   486  }
   487  
   488  // relPkg returns types.Package fn is printed in relationship to.
   489  func (fn *Function) relPkg() *types.Package {
   490  	if p := fn.declaredPackage(); p != nil {
   491  		return p.Pkg
   492  	}
   493  	return nil
   494  }
   495  
   496  var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
   497  
   498  func (f *Function) WriteTo(w io.Writer) (int64, error) {
   499  	var buf bytes.Buffer
   500  	WriteFunction(&buf, f)
   501  	n, err := w.Write(buf.Bytes())
   502  	return int64(n), err
   503  }
   504  
   505  // WriteFunction writes to buf a human-readable "disassembly" of f.
   506  func WriteFunction(buf *bytes.Buffer, f *Function) {
   507  	fmt.Fprintf(buf, "# Name: %s\n", f.String())
   508  	if f.Pkg != nil {
   509  		fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
   510  	}
   511  	if syn := f.Synthetic; syn != "" {
   512  		fmt.Fprintln(buf, "# Synthetic:", syn)
   513  	}
   514  	if pos := f.Pos(); pos.IsValid() {
   515  		fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
   516  	}
   517  
   518  	if f.parent != nil {
   519  		fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
   520  	}
   521  
   522  	if f.Recover != nil {
   523  		fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
   524  	}
   525  
   526  	from := f.relPkg()
   527  
   528  	if f.FreeVars != nil {
   529  		buf.WriteString("# Free variables:\n")
   530  		for i, fv := range f.FreeVars {
   531  			fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
   532  		}
   533  	}
   534  
   535  	if len(f.Locals) > 0 {
   536  		buf.WriteString("# Locals:\n")
   537  		for i, l := range f.Locals {
   538  			fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(typeparams.MustDeref(l.Type()), from))
   539  		}
   540  	}
   541  	writeSignature(buf, from, f.Name(), f.Signature)
   542  	buf.WriteString(":\n")
   543  
   544  	if f.Blocks == nil {
   545  		buf.WriteString("\t(external)\n")
   546  	}
   547  
   548  	// NB. column calculations are confused by non-ASCII
   549  	// characters and assume 8-space tabs.
   550  	const punchcard = 80 // for old time's sake.
   551  	const tabwidth = 8
   552  	for _, b := range f.Blocks {
   553  		if b == nil {
   554  			// Corrupt CFG.
   555  			fmt.Fprintf(buf, ".nil:\n")
   556  			continue
   557  		}
   558  		n, _ := fmt.Fprintf(buf, "%d:", b.Index)
   559  		bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
   560  		fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
   561  
   562  		if false { // CFG debugging
   563  			fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
   564  		}
   565  		for _, instr := range b.Instrs {
   566  			buf.WriteString("\t")
   567  			switch v := instr.(type) {
   568  			case Value:
   569  				l := punchcard - tabwidth
   570  				// Left-align the instruction.
   571  				if name := v.Name(); name != "" {
   572  					n, _ := fmt.Fprintf(buf, "%s = ", name)
   573  					l -= n
   574  				}
   575  				n, _ := buf.WriteString(instr.String())
   576  				l -= n
   577  				// Right-align the type if there's space.
   578  				if t := v.Type(); t != nil {
   579  					buf.WriteByte(' ')
   580  					ts := relType(t, from)
   581  					l -= len(ts) + len("  ") // (spaces before and after type)
   582  					if l > 0 {
   583  						fmt.Fprintf(buf, "%*s", l, "")
   584  					}
   585  					buf.WriteString(ts)
   586  				}
   587  			case nil:
   588  				// Be robust against bad transforms.
   589  				buf.WriteString("<deleted>")
   590  			default:
   591  				buf.WriteString(instr.String())
   592  			}
   593  			// -mode=S: show line numbers
   594  			if f.Prog.mode&LogSource != 0 {
   595  				if pos := instr.Pos(); pos.IsValid() {
   596  					fmt.Fprintf(buf, " L%d", f.Prog.Fset.Position(pos).Line)
   597  				}
   598  			}
   599  			buf.WriteString("\n")
   600  		}
   601  	}
   602  	fmt.Fprintf(buf, "\n")
   603  }
   604  
   605  // newBasicBlock adds to f a new basic block and returns it.  It does
   606  // not automatically become the current block for subsequent calls to emit.
   607  // comment is an optional string for more readable debugging output.
   608  func (f *Function) newBasicBlock(comment string) *BasicBlock {
   609  	b := &BasicBlock{
   610  		Index:   len(f.Blocks),
   611  		Comment: comment,
   612  		parent:  f,
   613  	}
   614  	b.Succs = b.succs2[:0]
   615  	f.Blocks = append(f.Blocks, b)
   616  	return b
   617  }
   618  
   619  // NewFunction returns a new synthetic Function instance belonging to
   620  // prog, with its name and signature fields set as specified.
   621  //
   622  // The caller is responsible for initializing the remaining fields of
   623  // the function object, e.g. Pkg, Params, Blocks.
   624  //
   625  // It is practically impossible for clients to construct well-formed
   626  // SSA functions/packages/programs directly, so we assume this is the
   627  // job of the Builder alone.  NewFunction exists to provide clients a
   628  // little flexibility.  For example, analysis tools may wish to
   629  // construct fake Functions for the root of the callgraph, a fake
   630  // "reflect" package, etc.
   631  //
   632  // TODO(adonovan): think harder about the API here.
   633  func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
   634  	return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
   635  }
   636  
   637  // Syntax returns the function's syntax (*ast.Func{Decl,Lit)
   638  // if it was produced from syntax.
   639  func (f *Function) Syntax() ast.Node { return f.syntax }
   640  
   641  // identVar returns the variable defined by id.
   642  func identVar(fn *Function, id *ast.Ident) *types.Var {
   643  	return fn.info.Defs[id].(*types.Var)
   644  }
   645  

View as plain text