* tree-ssa-reassoc.c (reassociate_bb): Clarify code slighly.
[official-gcc.git] / libgo / go / go / types / scope.go
blobb5d34d6e655af9ab06a96b48790119088830aa44
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.
5 // This file implements Scopes.
7 package types
9 import (
10 "bytes"
11 "fmt"
12 "go/token"
13 "io"
14 "sort"
15 "strings"
18 // TODO(gri) Provide scopes with a name or other mechanism so that
19 // objects can use that information for better printing.
21 // A Scope maintains a set of objects and links to its containing
22 // (parent) and contained (children) scopes. Objects may be inserted
23 // and looked up by name. The zero value for Scope is a ready-to-use
24 // empty scope.
25 type Scope struct {
26 parent *Scope
27 children []*Scope
28 elems map[string]Object // lazily allocated
29 pos, end token.Pos // scope extent; may be invalid
30 comment string // for debugging only
33 // NewScope returns a new, empty scope contained in the given parent
34 // scope, if any. The comment is for debugging only.
35 func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope {
36 s := &Scope{parent, nil, nil, pos, end, comment}
37 // don't add children to Universe scope!
38 if parent != nil && parent != Universe {
39 parent.children = append(parent.children, s)
41 return s
44 // Parent returns the scope's containing (parent) scope.
45 func (s *Scope) Parent() *Scope { return s.parent }
47 // Len() returns the number of scope elements.
48 func (s *Scope) Len() int { return len(s.elems) }
50 // Names returns the scope's element names in sorted order.
51 func (s *Scope) Names() []string {
52 names := make([]string, len(s.elems))
53 i := 0
54 for name := range s.elems {
55 names[i] = name
56 i++
58 sort.Strings(names)
59 return names
62 // NumChildren() returns the number of scopes nested in s.
63 func (s *Scope) NumChildren() int { return len(s.children) }
65 // Child returns the i'th child scope for 0 <= i < NumChildren().
66 func (s *Scope) Child(i int) *Scope { return s.children[i] }
68 // Lookup returns the object in scope s with the given name if such an
69 // object exists; otherwise the result is nil.
70 func (s *Scope) Lookup(name string) Object {
71 return s.elems[name]
74 // LookupParent follows the parent chain of scopes starting with s until
75 // it finds a scope where Lookup(name) returns a non-nil object, and then
76 // returns that scope and object. If a valid position pos is provided,
77 // only objects that were declared at or before pos are considered.
78 // If no such scope and object exists, the result is (nil, nil).
80 // Note that obj.Parent() may be different from the returned scope if the
81 // object was inserted into the scope and already had a parent at that
82 // time (see Insert, below). This can only happen for dot-imported objects
83 // whose scope is the scope of the package that exported them.
84 func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
85 for ; s != nil; s = s.parent {
86 if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
87 return s, obj
90 return nil, nil
93 // Insert attempts to insert an object obj into scope s.
94 // If s already contains an alternative object alt with
95 // the same name, Insert leaves s unchanged and returns alt.
96 // Otherwise it inserts obj, sets the object's parent scope
97 // if not already set, and returns nil.
98 func (s *Scope) Insert(obj Object) Object {
99 name := obj.Name()
100 if alt := s.elems[name]; alt != nil {
101 return alt
103 if s.elems == nil {
104 s.elems = make(map[string]Object)
106 s.elems[name] = obj
107 if obj.Parent() == nil {
108 obj.setParent(s)
110 return nil
113 // Pos and End describe the scope's source code extent [pos, end).
114 // The results are guaranteed to be valid only if the type-checked
115 // AST has complete position information. The extent is undefined
116 // for Universe and package scopes.
117 func (s *Scope) Pos() token.Pos { return s.pos }
118 func (s *Scope) End() token.Pos { return s.end }
120 // Contains returns true if pos is within the scope's extent.
121 // The result is guaranteed to be valid only if the type-checked
122 // AST has complete position information.
123 func (s *Scope) Contains(pos token.Pos) bool {
124 return s.pos <= pos && pos < s.end
127 // Innermost returns the innermost (child) scope containing
128 // pos. If pos is not within any scope, the result is nil.
129 // The result is also nil for the Universe scope.
130 // The result is guaranteed to be valid only if the type-checked
131 // AST has complete position information.
132 func (s *Scope) Innermost(pos token.Pos) *Scope {
133 // Package scopes do not have extents since they may be
134 // discontiguous, so iterate over the package's files.
135 if s.parent == Universe {
136 for _, s := range s.children {
137 if inner := s.Innermost(pos); inner != nil {
138 return inner
143 if s.Contains(pos) {
144 for _, s := range s.children {
145 if s.Contains(pos) {
146 return s.Innermost(pos)
149 return s
151 return nil
154 // WriteTo writes a string representation of the scope to w,
155 // with the scope elements sorted by name.
156 // The level of indentation is controlled by n >= 0, with
157 // n == 0 for no indentation.
158 // If recurse is set, it also writes nested (children) scopes.
159 func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
160 const ind = ". "
161 indn := strings.Repeat(ind, n)
163 fmt.Fprintf(w, "%s%s scope %p {", indn, s.comment, s)
164 if len(s.elems) == 0 {
165 fmt.Fprintf(w, "}\n")
166 return
169 fmt.Fprintln(w)
170 indn1 := indn + ind
171 for _, name := range s.Names() {
172 fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
175 if recurse {
176 for _, s := range s.children {
177 fmt.Fprintln(w)
178 s.WriteTo(w, n+1, recurse)
182 fmt.Fprintf(w, "%s}", indn)
185 // String returns a string representation of the scope, for debugging.
186 func (s *Scope) String() string {
187 var buf bytes.Buffer
188 s.WriteTo(&buf, 0, false)
189 return buf.String()