summaryrefslogtreecommitdiff
path: root/src/pkg/go/ast
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go/ast')
-rw-r--r--src/pkg/go/ast/Makefile1
-rw-r--r--src/pkg/go/ast/ast.go58
-rw-r--r--src/pkg/go/ast/filter.go6
-rw-r--r--src/pkg/go/ast/print.go94
-rw-r--r--src/pkg/go/ast/print_test.go80
-rw-r--r--src/pkg/go/ast/resolve.go188
-rw-r--r--src/pkg/go/ast/scope.go268
-rw-r--r--src/pkg/go/ast/walk.go8
8 files changed, 443 insertions, 260 deletions
diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile
index e9b885c70..40be10208 100644
--- a/src/pkg/go/ast/Makefile
+++ b/src/pkg/go/ast/Makefile
@@ -9,6 +9,7 @@ GOFILES=\
ast.go\
filter.go\
print.go\
+ resolve.go\
scope.go\
walk.go\
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index abafb5663..ed3e2cdd9 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -66,7 +66,7 @@ type Decl interface {
// A Comment node represents a single //-style or /*-style comment.
type Comment struct {
Slash token.Pos // position of "/" starting the comment
- Text []byte // comment text (excluding '\n' for //-style comments)
+ Text string // comment text (excluding '\n' for //-style comments)
}
@@ -199,7 +199,7 @@ type (
BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
- Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
+ Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
// A FuncLit node represents a function literal.
@@ -602,12 +602,12 @@ type (
Else Stmt // else branch; or nil
}
- // A CaseClause represents a case of an expression switch statement.
+ // A CaseClause represents a case of an expression or type switch statement.
CaseClause struct {
- Case token.Pos // position of "case" or "default" keyword
- Values []Expr // nil means default case
- Colon token.Pos // position of ":"
- Body []Stmt // statement list; or nil
+ Case token.Pos // position of "case" or "default" keyword
+ List []Expr // list of expressions or types; nil means default case
+ Colon token.Pos // position of ":"
+ Body []Stmt // statement list; or nil
}
// A SwitchStmt node represents an expression switch statement.
@@ -618,20 +618,12 @@ type (
Body *BlockStmt // CaseClauses only
}
- // A TypeCaseClause represents a case of a type switch statement.
- TypeCaseClause struct {
- Case token.Pos // position of "case" or "default" keyword
- Types []Expr // nil means default case
- Colon token.Pos // position of ":"
- Body []Stmt // statement list; or nil
- }
-
// An TypeSwitchStmt node represents a type switch statement.
TypeSwitchStmt struct {
Switch token.Pos // position of "switch" keyword
Init Stmt // initalization statement; or nil
- Assign Stmt // x := y.(type)
- Body *BlockStmt // TypeCaseClauses only
+ Assign Stmt // x := y.(type) or y.(type)
+ Body *BlockStmt // CaseClauses only
}
// A CommClause node represents a case of a select statement.
@@ -687,7 +679,6 @@ func (s *BlockStmt) Pos() token.Pos { return s.Lbrace }
func (s *IfStmt) Pos() token.Pos { return s.If }
func (s *CaseClause) Pos() token.Pos { return s.Case }
func (s *SwitchStmt) Pos() token.Pos { return s.Switch }
-func (s *TypeCaseClause) Pos() token.Pos { return s.Case }
func (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch }
func (s *CommClause) Pos() token.Pos { return s.Case }
func (s *SelectStmt) Pos() token.Pos { return s.Select }
@@ -734,13 +725,7 @@ func (s *CaseClause) End() token.Pos {
}
return s.Colon + 1
}
-func (s *SwitchStmt) End() token.Pos { return s.Body.End() }
-func (s *TypeCaseClause) End() token.Pos {
- if n := len(s.Body); n > 0 {
- return s.Body[n-1].End()
- }
- return s.Colon + 1
-}
+func (s *SwitchStmt) End() token.Pos { return s.Body.End() }
func (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() }
func (s *CommClause) End() token.Pos {
if n := len(s.Body); n > 0 {
@@ -772,7 +757,6 @@ func (s *BlockStmt) stmtNode() {}
func (s *IfStmt) stmtNode() {}
func (s *CaseClause) stmtNode() {}
func (s *SwitchStmt) stmtNode() {}
-func (s *TypeCaseClause) stmtNode() {}
func (s *TypeSwitchStmt) stmtNode() {}
func (s *CommClause) stmtNode() {}
func (s *SelectStmt) stmtNode() {}
@@ -797,7 +781,7 @@ type (
ImportSpec struct {
Doc *CommentGroup // associated documentation; or nil
Name *Ident // local package name (including "."); or nil
- Path *BasicLit // package path
+ Path *BasicLit // import path
Comment *CommentGroup // line comments; or nil
}
@@ -937,11 +921,14 @@ func (d *FuncDecl) declNode() {}
// via Doc and Comment fields.
//
type File struct {
- Doc *CommentGroup // associated documentation; or nil
- Package token.Pos // position of "package" keyword
- Name *Ident // package name
- Decls []Decl // top-level declarations; or nil
- Comments []*CommentGroup // list of all comments in the source file
+ Doc *CommentGroup // associated documentation; or nil
+ Package token.Pos // position of "package" keyword
+ Name *Ident // package name
+ Decls []Decl // top-level declarations; or nil
+ Scope *Scope // package scope (this file only)
+ Imports []*ImportSpec // imports in this file
+ Unresolved []*Ident // unresolved identifiers in this file
+ Comments []*CommentGroup // list of all comments in the source file
}
@@ -958,9 +945,10 @@ func (f *File) End() token.Pos {
// collectively building a Go package.
//
type Package struct {
- Name string // package name
- Scope *Scope // package scope; or nil
- Files map[string]*File // Go source files by filename
+ Name string // package name
+ Scope *Scope // package scope
+ Imports map[string]*Scope // map of import path -> package scope across all files
+ Files map[string]*File // Go source files by filename
}
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 0c3cef4b2..090d08d34 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -304,7 +304,7 @@ const (
// separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group
//
-var separator = &Comment{noPos, []byte("//")}
+var separator = &Comment{noPos, "//"}
// MergePackageFiles creates a file AST by merging the ASTs of the
@@ -425,5 +425,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
}
}
- return &File{doc, pos, NewIdent(pkg.Name), decls, comments}
+ // TODO(gri) need to compute pkgScope and unresolved identifiers!
+ // TODO(gri) need to compute imports!
+ return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
}
diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go
index d71490d4a..e6d4e838d 100644
--- a/src/pkg/go/ast/print.go
+++ b/src/pkg/go/ast/print.go
@@ -21,24 +21,29 @@ type FieldFilter func(name string, value reflect.Value) bool
// NotNilFilter returns true for field values that are not nil;
// it returns false otherwise.
-func NotNilFilter(_ string, value reflect.Value) bool {
- v, ok := value.(interface {
- IsNil() bool
- })
- return !ok || !v.IsNil()
+func NotNilFilter(_ string, v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return !v.IsNil()
+ }
+ return true
}
// Fprint prints the (sub-)tree starting at AST node x to w.
+// If fset != nil, position information is interpreted relative
+// to that file set. Otherwise positions are printed as integer
+// values (file set specific offsets).
//
// A non-nil FieldFilter f may be provided to control the output:
// struct fields for which f(fieldname, fieldvalue) is true are
// are printed; all others are filtered from the output.
//
-func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) {
+func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (n int, err os.Error) {
// setup printer
p := printer{
output: w,
+ fset: fset,
filter: f,
ptrmap: make(map[interface{}]int),
last: '\n', // force printing of line number on first line
@@ -65,16 +70,17 @@ func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) {
// Print prints x to standard output, skipping nil fields.
-// Print(x) is the same as Fprint(os.Stdout, x, NotNilFilter).
-func Print(x interface{}) (int, os.Error) {
- return Fprint(os.Stdout, x, NotNilFilter)
+// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
+func Print(fset *token.FileSet, x interface{}) (int, os.Error) {
+ return Fprint(os.Stdout, fset, x, NotNilFilter)
}
type printer struct {
output io.Writer
+ fset *token.FileSet
filter FieldFilter
- ptrmap map[interface{}]int // *reflect.PtrValue -> line number
+ ptrmap map[interface{}]int // *T -> line number
written int // number of bytes written to output
indent int // current indentation level
last byte // the last byte processed by Write
@@ -135,73 +141,69 @@ func (p *printer) printf(format string, args ...interface{}) {
// Implementation note: Print is written for AST nodes but could be
// used to print arbitrary data structures; such a version should
// probably be in a different package.
+//
+// Note: This code detects (some) cycles created via pointers but
+// not cycles that are created via slices or maps containing the
+// same slice or map. Code for general data structures probably
+// should catch those as well.
func (p *printer) print(x reflect.Value) {
- // Note: This test is only needed because AST nodes
- // embed a token.Position, and thus all of them
- // understand the String() method (but it only
- // applies to the Position field).
- // TODO: Should reconsider this AST design decision.
- if pos, ok := x.Interface().(token.Position); ok {
- p.printf("%s", pos)
- return
- }
-
if !NotNilFilter("", x) {
p.printf("nil")
return
}
- switch v := x.(type) {
- case *reflect.InterfaceValue:
- p.print(v.Elem())
+ switch x.Kind() {
+ case reflect.Interface:
+ p.print(x.Elem())
- case *reflect.MapValue:
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ case reflect.Map:
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for _, key := range v.Keys() {
+ for _, key := range x.MapKeys() {
p.print(key)
p.printf(": ")
- p.print(v.Elem(key))
+ p.print(x.MapIndex(key))
+ p.printf("\n")
}
p.indent--
p.printf("}")
- case *reflect.PtrValue:
+ case reflect.Ptr:
p.printf("*")
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
// already and print the respective line number instead
- ptr := v.Interface()
+ ptr := x.Interface()
if line, exists := p.ptrmap[ptr]; exists {
p.printf("(obj @ %d)", line)
} else {
p.ptrmap[ptr] = p.line
- p.print(v.Elem())
+ p.print(x.Elem())
}
- case *reflect.SliceValue:
- if s, ok := v.Interface().([]byte); ok {
+ case reflect.Slice:
+ if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
- p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
+ p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
p.indent++
- for i, n := 0, v.Len(); i < n; i++ {
+ for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
- p.print(v.Elem(i))
+ p.print(x.Index(i))
p.printf("\n")
}
p.indent--
p.printf("}")
- case *reflect.StructValue:
+ case reflect.Struct:
p.printf("%s {\n", x.Type().String())
p.indent++
- t := v.Type().(*reflect.StructType)
+ t := x.Type()
for i, n := 0, t.NumField(); i < n; i++ {
name := t.Field(i).Name
- value := v.Field(i)
+ value := x.Field(i)
if p.filter == nil || p.filter(name, value) {
p.printf("%s: ", name)
p.print(value)
@@ -212,6 +214,20 @@ func (p *printer) print(x reflect.Value) {
p.printf("}")
default:
- p.printf("%v", x.Interface())
+ v := x.Interface()
+ switch v := v.(type) {
+ case string:
+ // print strings in quotes
+ p.printf("%q", v)
+ return
+ case token.Pos:
+ // position values can be printed nicely if we have a file set
+ if p.fset != nil {
+ p.printf("%s", p.fset.Position(v))
+ return
+ }
+ }
+ // default
+ p.printf("%v", v)
}
}
diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go
new file mode 100644
index 000000000..0820dcfce
--- /dev/null
+++ b/src/pkg/go/ast/print_test.go
@@ -0,0 +1,80 @@
+// Copyright 2011 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 ast
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+
+var tests = []struct {
+ x interface{} // x is printed as s
+ s string
+}{
+ // basic types
+ {nil, "0 nil"},
+ {true, "0 true"},
+ {42, "0 42"},
+ {3.14, "0 3.14"},
+ {1 + 2.718i, "0 (1+2.718i)"},
+ {"foobar", "0 \"foobar\""},
+
+ // maps
+ {map[string]int{"a": 1, "b": 2},
+ `0 map[string] int (len = 2) {
+ 1 . "a": 1
+ 2 . "b": 2
+ 3 }`},
+
+ // pointers
+ {new(int), "0 *0"},
+
+ // slices
+ {[]int{1, 2, 3},
+ `0 []int (len = 3) {
+ 1 . 0: 1
+ 2 . 1: 2
+ 3 . 2: 3
+ 4 }`},
+
+ // structs
+ {struct{ x, y int }{42, 991},
+ `0 struct { x int; y int } {
+ 1 . x: 42
+ 2 . y: 991
+ 3 }`},
+}
+
+
+// Split s into lines, trim whitespace from all lines, and return
+// the concatenated non-empty lines.
+func trim(s string) string {
+ lines := strings.Split(s, "\n", -1)
+ i := 0
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line != "" {
+ lines[i] = line
+ i++
+ }
+ }
+ return strings.Join(lines[0:i], "\n")
+}
+
+
+func TestPrint(t *testing.T) {
+ var buf bytes.Buffer
+ for _, test := range tests {
+ buf.Reset()
+ if _, err := Fprint(&buf, nil, test.x, nil); err != nil {
+ t.Errorf("Fprint failed: %s", err)
+ }
+ if s, ts := trim(buf.String()), trim(test.s); s != ts {
+ t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts)
+ }
+ }
+}
diff --git a/src/pkg/go/ast/resolve.go b/src/pkg/go/ast/resolve.go
new file mode 100644
index 000000000..fddc3baab
--- /dev/null
+++ b/src/pkg/go/ast/resolve.go
@@ -0,0 +1,188 @@
+// Copyright 2011 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 NewPackage.
+
+package ast
+
+import (
+ "fmt"
+ "go/scanner"
+ "go/token"
+ "os"
+)
+
+
+type pkgBuilder struct {
+ scanner.ErrorVector
+ fset *token.FileSet
+}
+
+
+func (p *pkgBuilder) error(pos token.Pos, msg string) {
+ p.Error(p.fset.Position(pos), msg)
+}
+
+
+func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
+ p.error(pos, fmt.Sprintf(format, args...))
+}
+
+
+func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
+ alt := scope.Insert(obj)
+ if alt == nil && altScope != nil {
+ // see if there is a conflicting declaration in altScope
+ alt = altScope.Lookup(obj.Name)
+ }
+ if alt != nil {
+ prevDecl := ""
+ if pos := alt.Pos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
+ }
+ p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
+ }
+}
+
+
+func resolve(scope *Scope, ident *Ident) bool {
+ for ; scope != nil; scope = scope.Outer {
+ if obj := scope.Lookup(ident.Name); obj != nil {
+ ident.Obj = obj
+ return true
+ }
+ }
+ return false
+}
+
+
+// NewPackage uses an Importer to resolve imports. Given an importPath,
+// an importer returns the imported package's name, its scope of exported
+// objects, and an error, if any.
+//
+type Importer func(path string) (name string, scope *Scope, err os.Error)
+
+
+// NewPackage creates a new Package node from a set of File nodes. It resolves
+// unresolved identifiers across files and updates each file's Unresolved list
+// accordingly. If a non-nil importer and universe scope are provided, they are
+// used to resolve identifiers not declared in any of the package files. Any
+// remaining unresolved identifiers are reported as undeclared. If the files
+// belong to different packages, one package name is selected and files with
+// different package name are reported and then ignored.
+// The result is a package node and a scanner.ErrorList if there were errors.
+//
+func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, os.Error) {
+ var p pkgBuilder
+ p.fset = fset
+
+ // complete package scope
+ pkgName := ""
+ pkgScope := NewScope(universe)
+ for _, file := range files {
+ // package names must match
+ switch name := file.Name.Name; {
+ case pkgName == "":
+ pkgName = name
+ case name != pkgName:
+ p.errorf(file.Package, "package %s; expected %s", name, pkgName)
+ continue // ignore this file
+ }
+
+ // collect top-level file objects in package scope
+ for _, obj := range file.Scope.Objects {
+ p.declare(pkgScope, nil, obj)
+ }
+ }
+
+ // imports maps import paths to package names and scopes
+ // TODO(gri): Eventually we like to get to the import scope from
+ // a package object. Then we can have a map path -> Obj.
+ type importedPkg struct {
+ name string
+ scope *Scope
+ }
+ imports := make(map[string]*importedPkg)
+
+ // complete file scopes with imports and resolve identifiers
+ for _, file := range files {
+ // ignore file if it belongs to a different package
+ // (error has already been reported)
+ if file.Name.Name != pkgName {
+ continue
+ }
+
+ // build file scope by processing all imports
+ importErrors := false
+ fileScope := NewScope(pkgScope)
+ for _, spec := range file.Imports {
+ // add import to global map of imports
+ path := string(spec.Path.Value)
+ path = path[1 : len(path)-1] // strip ""'s
+ pkg := imports[path]
+ if pkg == nil {
+ if importer == nil {
+ importErrors = true
+ continue
+ }
+ name, scope, err := importer(path)
+ if err != nil {
+ p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
+ importErrors = true
+ continue
+ }
+ pkg = &importedPkg{name, scope}
+ imports[path] = pkg
+ // TODO(gri) If a local package name != "." is provided,
+ // global identifier resolution could proceed even if the
+ // import failed. Consider adjusting the logic here a bit.
+ }
+ // local name overrides imported package name
+ name := pkg.name
+ if spec.Name != nil {
+ name = spec.Name.Name
+ }
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range pkg.scope.Objects {
+ p.declare(fileScope, pkgScope, obj)
+ }
+ } else {
+ // declare imported package object in file scope
+ obj := NewObj(Pkg, name)
+ obj.Decl = spec
+ p.declare(fileScope, pkgScope, obj)
+ }
+ }
+
+ // resolve identifiers
+ if importErrors {
+ // don't use the universe scope without correct imports
+ // (objects in the universe may be shadowed by imports;
+ // with missing imports identifiers might get resolved
+ // wrongly)
+ pkgScope.Outer = nil
+ }
+ i := 0
+ for _, ident := range file.Unresolved {
+ if !resolve(fileScope, ident) {
+ p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+ file.Unresolved[i] = ident
+ i++
+ }
+
+ }
+ file.Unresolved = file.Unresolved[0:i]
+ pkgScope.Outer = universe // reset universe scope
+ }
+
+ // collect all import paths and respective package scopes
+ importedScopes := make(map[string]*Scope)
+ for path, pkg := range imports {
+ importedScopes[path] = pkg.scope
+ }
+
+ return &Package{pkgName, pkgScope, importedScopes, files}, p.GetError(scanner.Sorted)
+}
diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go
index 956a208ae..830d88aef 100644
--- a/src/pkg/go/ast/scope.go
+++ b/src/pkg/go/ast/scope.go
@@ -2,31 +2,31 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements scopes, the objects they contain,
-// and object types.
+// This file implements scopes and the objects they contain.
package ast
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+)
+
+
// A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer)
// scope.
//
type Scope struct {
Outer *Scope
- Objects []*Object // in declaration order
- // Implementation note: In some cases (struct fields,
- // function parameters) we need the source order of
- // variables. Thus for now, we store scope entries
- // in a linear list. If scopes become very large
- // (say, for packages), we may need to change this
- // to avoid slow lookups.
+ Objects map[string]*Object
}
// NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope {
- const n = 4 // initial scope capacity, must be > 0
- return &Scope{outer, make([]*Object, 0, n)}
+ const n = 4 // initial scope capacity
+ return &Scope{outer, make(map[string]*Object, n)}
}
@@ -34,73 +34,111 @@ func NewScope(outer *Scope) *Scope {
// found in scope s, otherwise it returns nil. Outer scopes
// are ignored.
//
-// Lookup always returns nil if name is "_", even if the scope
-// contains objects with that name.
-//
func (s *Scope) Lookup(name string) *Object {
- if name != "_" {
- for _, obj := range s.Objects {
- if obj.Name == name {
- return obj
- }
- }
- }
- return nil
+ return s.Objects[name]
}
-// Insert attempts to insert a named object into the scope s.
-// If the scope does not contain an object with that name yet
-// or if the object is named "_", Insert inserts the object
-// and returns it. Otherwise, Insert leaves the scope unchanged
-// and returns the object found in the scope instead.
+// Insert attempts to insert a named object obj into the scope s.
+// If the scope already contains an object alt with the same name,
+// Insert leaves the scope unchanged and returns alt. Otherwise
+// it inserts obj and returns nil."
//
-func (s *Scope) Insert(obj *Object) *Object {
- alt := s.Lookup(obj.Name)
- if alt == nil {
- s.append(obj)
- alt = obj
+func (s *Scope) Insert(obj *Object) (alt *Object) {
+ if alt = s.Objects[obj.Name]; alt == nil {
+ s.Objects[obj.Name] = obj
}
- return alt
+ return
}
-func (s *Scope) append(obj *Object) {
- s.Objects = append(s.Objects, obj)
+// Debugging support
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "scope %p {", s)
+ if s != nil && len(s.Objects) > 0 {
+ fmt.Fprintln(&buf)
+ for _, obj := range s.Objects {
+ fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
+ }
+ }
+ fmt.Fprintf(&buf, "}\n")
+ return buf.String()
}
+
// ----------------------------------------------------------------------------
// Objects
-// An Object describes a language entity such as a package,
-// constant, type, variable, or function (incl. methods).
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
//
type Object struct {
- Kind Kind
- Name string // declared name
- Type *Type
- Decl interface{} // corresponding Field, XxxSpec or FuncDecl
- N int // value of iota for this declaration
+ Kind ObjKind
+ Name string // declared name
+ Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
+ Type interface{} // place holder for type information; may be nil
}
// NewObj creates a new object of a given kind and name.
-func NewObj(kind Kind, name string) *Object {
+func NewObj(kind ObjKind, name string) *Object {
return &Object{Kind: kind, Name: name}
}
-// Kind describes what an object represents.
-type Kind int
+// Pos computes the source position of the declaration of an object name.
+// The result may be an invalid position if it cannot be computed
+// (obj.Decl may be nil or not correct).
+func (obj *Object) Pos() token.Pos {
+ name := obj.Name
+ switch d := obj.Decl.(type) {
+ case *Field:
+ for _, n := range d.Names {
+ if n.Name == name {
+ return n.Pos()
+ }
+ }
+ case *ImportSpec:
+ if d.Name != nil && d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ return d.Path.Pos()
+ case *ValueSpec:
+ for _, n := range d.Names {
+ if n.Name == name {
+ return n.Pos()
+ }
+ }
+ case *TypeSpec:
+ if d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ case *FuncDecl:
+ if d.Name.Name == name {
+ return d.Name.Pos()
+ }
+ case *LabeledStmt:
+ if d.Label.Name == name {
+ return d.Label.Pos()
+ }
+ }
+ return token.NoPos
+}
+
+
+// ObKind describes what an object represents.
+type ObjKind int
// The list of possible Object kinds.
const (
- Bad Kind = iota // for error handling
- Pkg // package
- Con // constant
- Typ // type
- Var // variable
- Fun // function or method
+ Bad ObjKind = iota // for error handling
+ Pkg // package
+ Con // constant
+ Typ // type
+ Var // variable
+ Fun // function or method
+ Lbl // label
)
@@ -111,132 +149,8 @@ var objKindStrings = [...]string{
Typ: "type",
Var: "var",
Fun: "func",
+ Lbl: "label",
}
-func (kind Kind) String() string { return objKindStrings[kind] }
-
-
-// IsExported returns whether obj is exported.
-func (obj *Object) IsExported() bool { return IsExported(obj.Name) }
-
-
-// ----------------------------------------------------------------------------
-// Types
-
-// A Type represents a Go type.
-type Type struct {
- Form Form
- Obj *Object // corresponding type name, or nil
- Scope *Scope // fields and methods, always present
- N uint // basic type id, array length, number of function results, or channel direction
- Key, Elt *Type // map key and array, pointer, slice, map or channel element
- Params *Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
- Expr Expr // corresponding AST expression
-}
-
-
-// NewType creates a new type of a given form.
-func NewType(form Form) *Type {
- return &Type{Form: form, Scope: NewScope(nil)}
-}
-
-
-// Form describes the form of a type.
-type Form int
-
-// The list of possible type forms.
-const (
- BadType Form = iota // for error handling
- Unresolved // type not fully setup
- Basic
- Array
- Struct
- Pointer
- Function
- Method
- Interface
- Slice
- Map
- Channel
- Tuple
-)
-
-
-var formStrings = [...]string{
- BadType: "badType",
- Unresolved: "unresolved",
- Basic: "basic",
- Array: "array",
- Struct: "struct",
- Pointer: "pointer",
- Function: "function",
- Method: "method",
- Interface: "interface",
- Slice: "slice",
- Map: "map",
- Channel: "channel",
- Tuple: "tuple",
-}
-
-
-func (form Form) String() string { return formStrings[form] }
-
-
-// The list of basic type id's.
-const (
- Bool = iota
- Byte
- Uint
- Int
- Float
- Complex
- Uintptr
- String
-
- Uint8
- Uint16
- Uint32
- Uint64
-
- Int8
- Int16
- Int32
- Int64
-
- Float32
- Float64
-
- Complex64
- Complex128
-
- // TODO(gri) ideal types are missing
-)
-
-
-var BasicTypes = map[uint]string{
- Bool: "bool",
- Byte: "byte",
- Uint: "uint",
- Int: "int",
- Float: "float",
- Complex: "complex",
- Uintptr: "uintptr",
- String: "string",
-
- Uint8: "uint8",
- Uint16: "uint16",
- Uint32: "uint32",
- Uint64: "uint64",
-
- Int8: "int8",
- Int16: "int16",
- Int32: "int32",
- Int64: "int64",
-
- Float32: "float32",
- Float64: "float64",
-
- Complex64: "complex64",
- Complex128: "complex128",
-}
+func (kind ObjKind) String() string { return objKindStrings[kind] }
diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go
index 20c337c3b..95c4b3a35 100644
--- a/src/pkg/go/ast/walk.go
+++ b/src/pkg/go/ast/walk.go
@@ -234,7 +234,7 @@ func Walk(v Visitor, node Node) {
}
case *CaseClause:
- walkExprList(v, n.Values)
+ walkExprList(v, n.List)
walkStmtList(v, n.Body)
case *SwitchStmt:
@@ -246,12 +246,6 @@ func Walk(v Visitor, node Node) {
}
Walk(v, n.Body)
- case *TypeCaseClause:
- for _, x := range n.Types {
- Walk(v, x)
- }
- walkStmtList(v, n.Body)
-
case *TypeSwitchStmt:
if n.Init != nil {
Walk(v, n.Init)