cuelang.org/go@v0.10.1/internal/golangorgx/tools/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 }