golang.org/x/tools@v0.21.0/internal/refactor/inline/escape.go (about)

     1  // Copyright 2023 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 inline
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"go/types"
    12  )
    13  
    14  // escape implements a simple "address-taken" escape analysis. It
    15  // calls f for each local variable that appears on the left side of an
    16  // assignment (escapes=false) or has its address taken (escapes=true).
    17  // The initialization of a variable by its declaration does not count
    18  // as an assignment.
    19  func escape(info *types.Info, root ast.Node, f func(v *types.Var, escapes bool)) {
    20  
    21  	// lvalue is called for each address-taken expression or LHS of assignment.
    22  	// Supported forms are: x, (x), x[i], x.f, *x, T{}.
    23  	var lvalue func(e ast.Expr, escapes bool)
    24  	lvalue = func(e ast.Expr, escapes bool) {
    25  		switch e := e.(type) {
    26  		case *ast.Ident:
    27  			if v, ok := info.Uses[e].(*types.Var); ok {
    28  				if !isPkgLevel(v) {
    29  					f(v, escapes)
    30  				}
    31  			}
    32  		case *ast.ParenExpr:
    33  			lvalue(e.X, escapes)
    34  		case *ast.IndexExpr:
    35  			// TODO(adonovan): support generics without assuming e.X has a core type.
    36  			// Consider:
    37  			//
    38  			// func Index[T interface{ [3]int | []int }](t T, i int) *int {
    39  			//     return &t[i]
    40  			// }
    41  			//
    42  			// We must traverse the normal terms and check
    43  			// whether any of them is an array.
    44  			if _, ok := info.TypeOf(e.X).Underlying().(*types.Array); ok {
    45  				lvalue(e.X, escapes) // &a[i] on array
    46  			}
    47  		case *ast.SelectorExpr:
    48  			if _, ok := info.TypeOf(e.X).Underlying().(*types.Struct); ok {
    49  				lvalue(e.X, escapes) // &s.f on struct
    50  			}
    51  		case *ast.StarExpr:
    52  			// *ptr indirects an existing pointer
    53  		case *ast.CompositeLit:
    54  			// &T{...} creates a new variable
    55  		default:
    56  			panic(fmt.Sprintf("&x on %T", e)) // unreachable in well-typed code
    57  		}
    58  	}
    59  
    60  	// Search function body for operations &x, x.f(), x++, and x = y
    61  	// where x is a parameter. Each of these treats x as an address.
    62  	ast.Inspect(root, func(n ast.Node) bool {
    63  		switch n := n.(type) {
    64  		case *ast.UnaryExpr:
    65  			if n.Op == token.AND {
    66  				lvalue(n.X, true) // &x
    67  			}
    68  
    69  		case *ast.CallExpr:
    70  			// implicit &x in method call x.f(),
    71  			// where x has type T and method is (*T).f
    72  			if sel, ok := n.Fun.(*ast.SelectorExpr); ok {
    73  				if seln, ok := info.Selections[sel]; ok &&
    74  					seln.Kind() == types.MethodVal &&
    75  					isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type()) {
    76  					tArg, indirect := effectiveReceiver(seln)
    77  					if !indirect && !isPointer(tArg) {
    78  						lvalue(sel.X, true) // &x.f
    79  					}
    80  				}
    81  			}
    82  
    83  		case *ast.AssignStmt:
    84  			for _, lhs := range n.Lhs {
    85  				if id, ok := lhs.(*ast.Ident); ok &&
    86  					info.Defs[id] != nil &&
    87  					n.Tok == token.DEFINE {
    88  					// declaration: doesn't count
    89  				} else {
    90  					lvalue(lhs, false)
    91  				}
    92  			}
    93  
    94  		case *ast.IncDecStmt:
    95  			lvalue(n.X, false)
    96  		}
    97  		return true
    98  	})
    99  }