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