-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscope.go
230 lines (202 loc) · 7.63 KB
/
scope.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
// Source: ../../cmd/compile/internal/types2/scope.go
// Copyright 2013 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.
// This file implements Scopes.
package types
import (
"fmt"
"go/token"
"io"
"slices"
"strings"
"sync"
)
// A Scope maintains a set of objects and links to its containing
// (parent) and contained (children) scopes. Objects may be inserted
// and looked up by name. The zero value for Scope is a ready-to-use
// empty scope.
type Scope struct {
parent *Scope
children []*Scope
number int // parent.children[number-1] is this scope; 0 if there is no parent
elems map[string]Object // lazily allocated
pos, end token.Pos // scope extent; may be invalid
comment string // for debugging only
isFunc bool // set if this is a function scope (internal use only)
}
// NewScope returns a new, empty scope contained in the given parent
// scope, if any. The comment is for debugging only.
func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope {
s := &Scope{parent, nil, 0, nil, pos, end, comment, false}
// don't add children to Universe scope!
if parent != nil && parent != Universe {
parent.children = append(parent.children, s)
s.number = len(parent.children)
}
return s
}
// Parent returns the scope's containing (parent) scope.
func (s *Scope) Parent() *Scope { return s.parent }
// Len returns the number of scope elements.
func (s *Scope) Len() int { return len(s.elems) }
// Names returns the scope's element names in sorted order.
func (s *Scope) Names() []string {
names := make([]string, len(s.elems))
i := 0
for name := range s.elems {
names[i] = name
i++
}
slices.Sort(names)
return names
}
// NumChildren returns the number of scopes nested in s.
func (s *Scope) NumChildren() int { return len(s.children) }
// Child returns the i'th child scope for 0 <= i < NumChildren().
func (s *Scope) Child(i int) *Scope { return s.children[i] }
// Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil.
func (s *Scope) Lookup(name string) Object {
obj := resolve(name, s.elems[name])
// Hijack Lookup for "any": with gotypesalias=1, we want the Universe to
// return an Alias for "any", and with gotypesalias=0 we want to return
// the legacy representation of aliases.
//
// This is rather tricky, but works out after auditing of the usage of
// s.elems. The only external API to access scope elements is Lookup.
//
// TODO: remove this once gotypesalias=0 is no longer supported.
if obj == universeAnyAlias && !aliasAny() {
return universeAnyNoAlias
}
return obj
}
// lookupIgnoringCase returns the objects in scope s whose names match
// the given name ignoring case. If exported is set, only exported names
// are returned.
func (s *Scope) lookupIgnoringCase(name string, exported bool) []Object {
var matches []Object
for _, n := range s.Names() {
if (!exported || isExported(n)) && strings.EqualFold(n, name) {
matches = append(matches, s.Lookup(n))
}
}
return matches
}
// Insert attempts to insert an object obj into scope s.
// If s already contains an alternative object alt with
// the same name, Insert leaves s unchanged and returns alt.
// Otherwise it inserts obj, sets the object's parent scope
// if not already set, and returns nil.
func (s *Scope) Insert(obj Object) Object {
name := obj.Name()
if alt := s.Lookup(name); alt != nil {
return alt
}
s.insert(name, obj)
// TODO(gri) Can we always set the parent to s (or is there
// a need to keep the original parent or some race condition)?
// If we can, than we may not need environment.lookupScope
// which is only there so that we get the correct scope for
// marking "used" dot-imported packages.
if obj.Parent() == nil {
obj.setParent(s)
}
return nil
}
// InsertLazy is like Insert, but allows deferring construction of the
// inserted object until it's accessed with Lookup. The Object
// returned by resolve must have the same name as given to InsertLazy.
// If s already contains an alternative object with the same name,
// InsertLazy leaves s unchanged and returns false. Otherwise it
// records the binding and returns true. The object's parent scope
// will be set to s after resolve is called.
func (s *Scope) _InsertLazy(name string, resolve func() Object) bool {
if s.elems[name] != nil {
return false
}
s.insert(name, &lazyObject{parent: s, resolve: resolve})
return true
}
func (s *Scope) insert(name string, obj Object) {
if s.elems == nil {
s.elems = make(map[string]Object)
}
s.elems[name] = obj
}
// WriteTo writes a string representation of the scope to w,
// with the scope elements sorted by name.
// The level of indentation is controlled by n >= 0, with
// n == 0 for no indentation.
// If recurse is set, it also writes nested (children) scopes.
func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
const ind = ". "
indn := strings.Repeat(ind, n)
fmt.Fprintf(w, "%s%s scope %p {\n", indn, s.comment, s)
indn1 := indn + ind
for _, name := range s.Names() {
fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name))
}
if recurse {
for _, s := range s.children {
s.WriteTo(w, n+1, recurse)
}
}
fmt.Fprintf(w, "%s}\n", indn)
}
// String returns a string representation of the scope, for debugging.
func (s *Scope) String() string {
var buf strings.Builder
s.WriteTo(&buf, 0, false)
return buf.String()
}
// A lazyObject represents an imported Object that has not been fully
// resolved yet by its importer.
type lazyObject struct {
parent *Scope
resolve func() Object
obj Object
once sync.Once
}
// resolve returns the Object represented by obj, resolving lazy
// objects as appropriate.
func resolve(name string, obj Object) Object {
if lazy, ok := obj.(*lazyObject); ok {
lazy.once.Do(func() {
obj := lazy.resolve()
if _, ok := obj.(*lazyObject); ok {
panic("recursive lazy object")
}
if obj.Name() != name {
panic("lazy object has unexpected name")
}
if obj.Parent() == nil {
obj.setParent(lazy.parent)
}
lazy.obj = obj
})
obj = lazy.obj
}
return obj
}
// stub implementations so *lazyObject implements Object and we can
// store them directly into Scope.elems.
func (*lazyObject) Parent() *Scope { panic("unreachable") }
func (*lazyObject) Pos() token.Pos { panic("unreachable") }
func (*lazyObject) Pkg() *Package { panic("unreachable") }
func (*lazyObject) Name() string { panic("unreachable") }
func (*lazyObject) Type() Type { panic("unreachable") }
func (*lazyObject) Exported() bool { panic("unreachable") }
func (*lazyObject) Id() string { panic("unreachable") }
func (*lazyObject) String() string { panic("unreachable") }
func (*lazyObject) order() uint32 { panic("unreachable") }
func (*lazyObject) color() color { panic("unreachable") }
func (*lazyObject) setType(Type) { panic("unreachable") }
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
func (*lazyObject) setColor(color color) { panic("unreachable") }
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
func (*lazyObject) setScopePos(token.Pos) { panic("unreachable") }