mirror of
https://github.com/golang/go
synced 2024-11-20 03:34:40 -07:00
go/ast, go/parser: populate identifier scopes at parse time
The parser populates all scopes for a given file (except type-related scopes for structs, interfaces, and methods of types) at parse time. A new parser flag, DeclarationErrors, enables error messages related to declaration errors (as far as it is possible to provide them). The resulting AST has all (non-field, non-method) identifiers resolved that can be resolved w/o doing imports or parsing missing package files. The ast.File node contains the (partially complete) package scope and a list of unresolved global identifiers. All type-specific data structures have been removed from the AST. The existing typechecker is functional but needs to be adjusted (simplified) accordingly. Utility functions to resolve all identifiers for a package (after handling imports and parsing all package files) are missing. Unrelated changes: - Rename typechecker/testdata files to that they are not considered by gofmt. - Minor cleanups/simplifications. Parses all .go files in src and misc without declaration errors. Runs all tests. Changes do not affect gofmt output. R=rsc CC=golang-dev https://golang.org/cl/4244049
This commit is contained in:
parent
4b4a1ea899
commit
021b040a1e
@ -27,7 +27,7 @@ var stmtTests = []test{
|
|||||||
CErr("i, u := 1, 2", atLeastOneDecl),
|
CErr("i, u := 1, 2", atLeastOneDecl),
|
||||||
Val2("i, x := 2, f", "i", 2, "x", 1.0),
|
Val2("i, x := 2, f", "i", 2, "x", 1.0),
|
||||||
// Various errors
|
// Various errors
|
||||||
CErr("1 := 2", "left side of := must be a name"),
|
CErr("1 := 2", "expected identifier"),
|
||||||
CErr("c, a := 1, 1", "cannot assign"),
|
CErr("c, a := 1, 1", "cannot assign"),
|
||||||
// Unpacking
|
// Unpacking
|
||||||
Val2("x, y := oneTwo()", "x", 1, "y", 2),
|
Val2("x, y := oneTwo()", "x", 1, "y", 2),
|
||||||
|
@ -941,6 +941,8 @@ type File struct {
|
|||||||
Package token.Pos // position of "package" keyword
|
Package token.Pos // position of "package" keyword
|
||||||
Name *Ident // package name
|
Name *Ident // package name
|
||||||
Decls []Decl // top-level declarations; or nil
|
Decls []Decl // top-level declarations; or nil
|
||||||
|
Scope *Scope // package scope
|
||||||
|
Unresolved []*Ident // unresolved global identifiers
|
||||||
Comments []*CommentGroup // list of all comments in the source file
|
Comments []*CommentGroup // list of all comments in the source file
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -959,7 +961,7 @@ func (f *File) End() token.Pos {
|
|||||||
//
|
//
|
||||||
type Package struct {
|
type Package struct {
|
||||||
Name string // package name
|
Name string // package name
|
||||||
Scope *Scope // package scope; or nil
|
Scope *Scope // package scope
|
||||||
Files map[string]*File // Go source files by filename
|
Files map[string]*File // Go source files by filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,5 +425,6 @@ 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!
|
||||||
|
return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, comments}
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,31 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file implements scopes, the objects they contain,
|
// This file implements scopes and the objects they contain.
|
||||||
// and object types.
|
|
||||||
|
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
// A Scope maintains the set of named language entities declared
|
// A Scope maintains the set of named language entities declared
|
||||||
// in the scope and a link to the immediately surrounding (outer)
|
// in the scope and a link to the immediately surrounding (outer)
|
||||||
// scope.
|
// scope.
|
||||||
//
|
//
|
||||||
type Scope struct {
|
type Scope struct {
|
||||||
Outer *Scope
|
Outer *Scope
|
||||||
Objects []*Object // in declaration order
|
Objects map[string]*Object
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NewScope creates a new scope nested in the outer scope.
|
// NewScope creates a new scope nested in the outer scope.
|
||||||
func NewScope(outer *Scope) *Scope {
|
func NewScope(outer *Scope) *Scope {
|
||||||
const n = 4 // initial scope capacity, must be > 0
|
const n = 4 // initial scope capacity
|
||||||
return &Scope{outer, make([]*Object, 0, n)}
|
return &Scope{outer, make(map[string]*Object, n)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -34,73 +34,108 @@ func NewScope(outer *Scope) *Scope {
|
|||||||
// found in scope s, otherwise it returns nil. Outer scopes
|
// found in scope s, otherwise it returns nil. Outer scopes
|
||||||
// are ignored.
|
// 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 {
|
func (s *Scope) Lookup(name string) *Object {
|
||||||
if name != "_" {
|
return s.Objects[name]
|
||||||
for _, obj := range s.Objects {
|
|
||||||
if obj.Name == name {
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Insert attempts to insert a named object into the scope s.
|
// Insert attempts to insert a named object into the scope s.
|
||||||
// If the scope does not contain an object with that name yet
|
// If the scope does not contain an object with that name yet,
|
||||||
// or if the object is named "_", Insert inserts the object
|
// Insert inserts the object and returns it. Otherwise, Insert
|
||||||
// and returns it. Otherwise, Insert leaves the scope unchanged
|
// leaves the scope unchanged and returns the object found in
|
||||||
// and returns the object found in the scope instead.
|
// the scope instead.
|
||||||
//
|
//
|
||||||
func (s *Scope) Insert(obj *Object) *Object {
|
func (s *Scope) Insert(obj *Object) (alt *Object) {
|
||||||
alt := s.Lookup(obj.Name)
|
if alt = s.Objects[obj.Name]; alt == nil {
|
||||||
if alt == nil {
|
s.Objects[obj.Name] = obj
|
||||||
s.append(obj)
|
|
||||||
alt = obj
|
alt = obj
|
||||||
}
|
}
|
||||||
return alt
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *Scope) append(obj *Object) {
|
// Debugging support
|
||||||
s.Objects = append(s.Objects, obj)
|
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
|
// Objects
|
||||||
|
|
||||||
// An Object describes a language entity such as a package,
|
// An Object describes a named language entity such as a package,
|
||||||
// constant, type, variable, or function (incl. methods).
|
// constant, type, variable, function (incl. methods), or label.
|
||||||
//
|
//
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Kind Kind
|
Kind ObjKind
|
||||||
Name string // declared name
|
Name string // declared name
|
||||||
Type *Type
|
Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
|
||||||
Decl interface{} // corresponding Field, XxxSpec or FuncDecl
|
Type interface{} // place holder for type information; may be nil
|
||||||
N int // value of iota for this declaration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NewObj creates a new object of a given kind and name.
|
// 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}
|
return &Object{Kind: kind, Name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Kind describes what an object represents.
|
// Pos computes the source position of the declaration of an object name.
|
||||||
type Kind int
|
// 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 *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.
|
// The list of possible Object kinds.
|
||||||
const (
|
const (
|
||||||
Bad Kind = iota // for error handling
|
Bad ObjKind = iota // for error handling
|
||||||
Pkg // package
|
Pkg // package
|
||||||
Con // constant
|
Con // constant
|
||||||
Typ // type
|
Typ // type
|
||||||
Var // variable
|
Var // variable
|
||||||
Fun // function or method
|
Fun // function or method
|
||||||
|
Lbl // label
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -111,132 +146,8 @@ var objKindStrings = [...]string{
|
|||||||
Typ: "type",
|
Typ: "type",
|
||||||
Var: "var",
|
Var: "var",
|
||||||
Fun: "func",
|
Fun: "func",
|
||||||
|
Lbl: "label",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (kind Kind) String() string { return objKindStrings[kind] }
|
func (kind ObjKind) 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",
|
|
||||||
}
|
|
||||||
|
@ -17,10 +17,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// noPos is used when there is no corresponding source position for a token.
|
|
||||||
var noPos token.Position
|
|
||||||
|
|
||||||
|
|
||||||
// The mode parameter to the Parse* functions is a set of flags (or 0).
|
// The mode parameter to the Parse* functions is a set of flags (or 0).
|
||||||
// They control the amount of source code parsed and other optional
|
// They control the amount of source code parsed and other optional
|
||||||
// parser functionality.
|
// parser functionality.
|
||||||
@ -30,6 +26,7 @@ const (
|
|||||||
ImportsOnly // parsing stops after import declarations
|
ImportsOnly // parsing stops after import declarations
|
||||||
ParseComments // parse comments and add them to AST
|
ParseComments // parse comments and add them to AST
|
||||||
Trace // print a trace of parsed productions
|
Trace // print a trace of parsed productions
|
||||||
|
DeclarationErrors // report declaration errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -46,8 +43,8 @@ type parser struct {
|
|||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
comments []*ast.CommentGroup
|
comments []*ast.CommentGroup
|
||||||
leadComment *ast.CommentGroup // the last lead comment
|
leadComment *ast.CommentGroup // last lead comment
|
||||||
lineComment *ast.CommentGroup // the last line comment
|
lineComment *ast.CommentGroup // last line comment
|
||||||
|
|
||||||
// Next token
|
// Next token
|
||||||
pos token.Pos // token position
|
pos token.Pos // token position
|
||||||
@ -56,6 +53,16 @@ type parser struct {
|
|||||||
|
|
||||||
// Non-syntactic parser control
|
// Non-syntactic parser control
|
||||||
exprLev int // < 0: in control clause, >= 0: in expression
|
exprLev int // < 0: in control clause, >= 0: in expression
|
||||||
|
|
||||||
|
// Ordinary identifer scopes
|
||||||
|
pkgScope *ast.Scope // pkgScope.Outer == nil
|
||||||
|
topScope *ast.Scope // top-most scope; may be pkgScope
|
||||||
|
unresolved []*ast.Ident // unresolved global identifiers
|
||||||
|
|
||||||
|
// Label scope
|
||||||
|
// (maintained by open/close LabelScope)
|
||||||
|
labelScope *ast.Scope // label scope for current function
|
||||||
|
targetStack [][]*ast.Ident // stack of unresolved labels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -72,9 +79,117 @@ func scannerMode(mode uint) uint {
|
|||||||
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
|
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
|
||||||
p.file = fset.AddFile(filename, fset.Base(), len(src))
|
p.file = fset.AddFile(filename, fset.Base(), len(src))
|
||||||
p.scanner.Init(p.file, src, p, scannerMode(mode))
|
p.scanner.Init(p.file, src, p, scannerMode(mode))
|
||||||
|
|
||||||
p.mode = mode
|
p.mode = mode
|
||||||
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
|
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
|
||||||
|
|
||||||
p.next()
|
p.next()
|
||||||
|
|
||||||
|
// set up the pkgScope here (as opposed to in parseFile) because
|
||||||
|
// there are other parser entry points (ParseExpr, etc.)
|
||||||
|
p.openScope()
|
||||||
|
p.pkgScope = p.topScope
|
||||||
|
|
||||||
|
// for the same reason, set up a label scope
|
||||||
|
p.openLabelScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Scoping support
|
||||||
|
|
||||||
|
func (p *parser) openScope() {
|
||||||
|
p.topScope = ast.NewScope(p.topScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) closeScope() {
|
||||||
|
p.topScope = p.topScope.Outer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) openLabelScope() {
|
||||||
|
p.labelScope = ast.NewScope(p.labelScope)
|
||||||
|
p.targetStack = append(p.targetStack, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) closeLabelScope() {
|
||||||
|
// resolve labels
|
||||||
|
n := len(p.targetStack) - 1
|
||||||
|
scope := p.labelScope
|
||||||
|
for _, ident := range p.targetStack[n] {
|
||||||
|
ident.Obj = scope.Lookup(ident.Name)
|
||||||
|
if ident.Obj == nil && p.mode&DeclarationErrors != 0 {
|
||||||
|
p.error(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pop label scope
|
||||||
|
p.targetStack = p.targetStack[0:n]
|
||||||
|
p.labelScope = p.labelScope.Outer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
|
||||||
|
for _, ident := range idents {
|
||||||
|
if ident.Name != "_" {
|
||||||
|
obj := ast.NewObj(kind, ident.Name)
|
||||||
|
// remember the corresponding declaration for redeclaration
|
||||||
|
// errors and global variable resolution/typechecking phase
|
||||||
|
obj.Decl = decl
|
||||||
|
alt := scope.Insert(obj)
|
||||||
|
if alt != obj && p.mode&DeclarationErrors != 0 {
|
||||||
|
prevDecl := ""
|
||||||
|
if pos := alt.Pos(); pos.IsValid() {
|
||||||
|
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
|
||||||
|
}
|
||||||
|
p.error(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
|
||||||
|
}
|
||||||
|
ident.Obj = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) shortVarDecl(idents []*ast.Ident) {
|
||||||
|
// Go spec: A short variable declaration may redeclare variables
|
||||||
|
// provided they were originally declared in the same block with
|
||||||
|
// the same type, and at least one of the non-blank variables is new.
|
||||||
|
n := 0 // number of new variables
|
||||||
|
for _, ident := range idents {
|
||||||
|
if ident.Name != "_" {
|
||||||
|
obj := ast.NewObj(ast.Var, ident.Name)
|
||||||
|
// short var declarations cannot have redeclaration errors
|
||||||
|
// and are not global => no need to remember the respective
|
||||||
|
// declaration
|
||||||
|
alt := p.topScope.Insert(obj)
|
||||||
|
if alt == obj {
|
||||||
|
n++ // new declaration
|
||||||
|
}
|
||||||
|
ident.Obj = alt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n == 0 && p.mode&DeclarationErrors != 0 {
|
||||||
|
p.error(idents[0].Pos(), "no new variables on left side of :=")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (p *parser) resolve(ident *ast.Ident) {
|
||||||
|
if ident.Name == "_" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// try to resolve the identifier
|
||||||
|
for s := p.topScope; s != nil; s = s.Outer {
|
||||||
|
if obj := s.Lookup(ident.Name); obj != nil {
|
||||||
|
ident.Obj = obj
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// collect unresolved global identifiers; ignore the others
|
||||||
|
if p.topScope == p.pkgScope {
|
||||||
|
p.unresolved = append(p.unresolved, ident)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -339,13 +454,16 @@ func (p *parser) parseQualifiedIdent() ast.Expr {
|
|||||||
defer un(trace(p, "QualifiedIdent"))
|
defer un(trace(p, "QualifiedIdent"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var x ast.Expr = p.parseIdent()
|
ident := p.parseIdent()
|
||||||
|
p.resolve(ident)
|
||||||
|
var x ast.Expr = ident
|
||||||
if p.tok == token.PERIOD {
|
if p.tok == token.PERIOD {
|
||||||
// first identifier is a package identifier
|
// first identifier is a package identifier
|
||||||
p.next()
|
p.next()
|
||||||
sel := p.parseIdent()
|
sel := p.parseIdent()
|
||||||
x = &ast.SelectorExpr{x, sel}
|
x = &ast.SelectorExpr{x, sel}
|
||||||
}
|
}
|
||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,7 +544,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.Field{doc, idents, typ, tag, p.lineComment}
|
return &ast.Field{doc, idents, typ, tag, p.lineComment}
|
||||||
}
|
}
|
||||||
@ -519,7 +637,7 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "ParameterList"))
|
defer un(trace(p, "ParameterList"))
|
||||||
}
|
}
|
||||||
@ -528,7 +646,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
|||||||
if typ != nil {
|
if typ != nil {
|
||||||
// IdentifierList Type
|
// IdentifierList Type
|
||||||
idents := p.makeIdentList(list)
|
idents := p.makeIdentList(list)
|
||||||
params = append(params, &ast.Field{nil, idents, typ, nil, nil})
|
field := &ast.Field{nil, idents, typ, nil, nil}
|
||||||
|
params = append(params, field)
|
||||||
|
// Go spec: The scope of an identifier denoting a function
|
||||||
|
// parameter or result variable is the function body.
|
||||||
|
p.declare(field, scope, ast.Var, idents...)
|
||||||
if p.tok == token.COMMA {
|
if p.tok == token.COMMA {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
@ -536,7 +658,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
|||||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||||
idents := p.parseIdentList()
|
idents := p.parseIdentList()
|
||||||
typ := p.parseVarType(ellipsisOk)
|
typ := p.parseVarType(ellipsisOk)
|
||||||
params = append(params, &ast.Field{nil, idents, typ, nil, nil})
|
field := &ast.Field{nil, idents, typ, nil, nil}
|
||||||
|
params = append(params, field)
|
||||||
|
// Go spec: The scope of an identifier denoting a function
|
||||||
|
// parameter or result variable is the function body.
|
||||||
|
p.declare(field, scope, ast.Var, idents...)
|
||||||
if p.tok != token.COMMA {
|
if p.tok != token.COMMA {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -555,7 +681,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
|
func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Parameters"))
|
defer un(trace(p, "Parameters"))
|
||||||
}
|
}
|
||||||
@ -563,7 +689,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
|
|||||||
var params []*ast.Field
|
var params []*ast.Field
|
||||||
lparen := p.expect(token.LPAREN)
|
lparen := p.expect(token.LPAREN)
|
||||||
if p.tok != token.RPAREN {
|
if p.tok != token.RPAREN {
|
||||||
params = p.parseParameterList(ellipsisOk)
|
params = p.parseParameterList(scope, ellipsisOk)
|
||||||
}
|
}
|
||||||
rparen := p.expect(token.RPAREN)
|
rparen := p.expect(token.RPAREN)
|
||||||
|
|
||||||
@ -571,13 +697,13 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseResult() *ast.FieldList {
|
func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Result"))
|
defer un(trace(p, "Result"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.tok == token.LPAREN {
|
if p.tok == token.LPAREN {
|
||||||
return p.parseParameters(false)
|
return p.parseParameters(scope, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := p.tryType()
|
typ := p.tryType()
|
||||||
@ -591,27 +717,28 @@ func (p *parser) parseResult() *ast.FieldList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseSignature() (params, results *ast.FieldList) {
|
func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Signature"))
|
defer un(trace(p, "Signature"))
|
||||||
}
|
}
|
||||||
|
|
||||||
params = p.parseParameters(true)
|
params = p.parseParameters(scope, true)
|
||||||
results = p.parseResult()
|
results = p.parseResult(scope)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseFuncType() *ast.FuncType {
|
func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "FuncType"))
|
defer un(trace(p, "FuncType"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := p.expect(token.FUNC)
|
pos := p.expect(token.FUNC)
|
||||||
params, results := p.parseSignature()
|
scope := ast.NewScope(p.topScope) // function scope
|
||||||
|
params, results := p.parseSignature(scope)
|
||||||
|
|
||||||
return &ast.FuncType{pos, params, results}
|
return &ast.FuncType{pos, params, results}, scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -627,13 +754,14 @@ func (p *parser) parseMethodSpec() *ast.Field {
|
|||||||
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
||||||
// method
|
// method
|
||||||
idents = []*ast.Ident{ident}
|
idents = []*ast.Ident{ident}
|
||||||
params, results := p.parseSignature()
|
scope := ast.NewScope(nil) // method scope
|
||||||
|
params, results := p.parseSignature(scope)
|
||||||
typ = &ast.FuncType{token.NoPos, params, results}
|
typ = &ast.FuncType{token.NoPos, params, results}
|
||||||
} else {
|
} else {
|
||||||
// embedded interface
|
// embedded interface
|
||||||
typ = x
|
typ = x
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.Field{doc, idents, typ, nil, p.lineComment}
|
return &ast.Field{doc, idents, typ, nil, p.lineComment}
|
||||||
}
|
}
|
||||||
@ -706,7 +834,8 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
|
|||||||
case token.MUL:
|
case token.MUL:
|
||||||
return p.parsePointerType()
|
return p.parsePointerType()
|
||||||
case token.FUNC:
|
case token.FUNC:
|
||||||
return p.parseFuncType()
|
typ, _ := p.parseFuncType()
|
||||||
|
return typ
|
||||||
case token.INTERFACE:
|
case token.INTERFACE:
|
||||||
return p.parseInterfaceType()
|
return p.parseInterfaceType()
|
||||||
case token.MAP:
|
case token.MAP:
|
||||||
@ -745,13 +874,17 @@ func (p *parser) parseStmtList() (list []ast.Stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseBody() *ast.BlockStmt {
|
func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Body"))
|
defer un(trace(p, "Body"))
|
||||||
}
|
}
|
||||||
|
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
|
p.topScope = scope // open function scope
|
||||||
|
p.openLabelScope()
|
||||||
list := p.parseStmtList()
|
list := p.parseStmtList()
|
||||||
|
p.closeLabelScope()
|
||||||
|
p.closeScope()
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
return &ast.BlockStmt{lbrace, list, rbrace}
|
return &ast.BlockStmt{lbrace, list, rbrace}
|
||||||
@ -764,7 +897,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
|
p.openScope()
|
||||||
list := p.parseStmtList()
|
list := p.parseStmtList()
|
||||||
|
p.closeScope()
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
return &ast.BlockStmt{lbrace, list, rbrace}
|
return &ast.BlockStmt{lbrace, list, rbrace}
|
||||||
@ -779,14 +914,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
|
|||||||
defer un(trace(p, "FuncTypeOrLit"))
|
defer un(trace(p, "FuncTypeOrLit"))
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := p.parseFuncType()
|
typ, scope := p.parseFuncType()
|
||||||
if p.tok != token.LBRACE {
|
if p.tok != token.LBRACE {
|
||||||
// function type only
|
// function type only
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
p.exprLev++
|
p.exprLev++
|
||||||
body := p.parseBody()
|
body := p.parseBody(scope)
|
||||||
p.exprLev--
|
p.exprLev--
|
||||||
|
|
||||||
return &ast.FuncLit{typ, body}
|
return &ast.FuncLit{typ, body}
|
||||||
@ -803,7 +938,9 @@ func (p *parser) parseOperand() ast.Expr {
|
|||||||
|
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.IDENT:
|
case token.IDENT:
|
||||||
return p.parseIdent()
|
ident := p.parseIdent()
|
||||||
|
p.resolve(ident)
|
||||||
|
return ident
|
||||||
|
|
||||||
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
|
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
|
||||||
x := &ast.BasicLit{p.pos, p.tok, p.lit}
|
x := &ast.BasicLit{p.pos, p.tok, p.lit}
|
||||||
@ -1202,6 +1339,9 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
|||||||
pos, tok := p.pos, p.tok
|
pos, tok := p.pos, p.tok
|
||||||
p.next()
|
p.next()
|
||||||
y := p.parseExprList()
|
y := p.parseExprList()
|
||||||
|
if tok == token.DEFINE {
|
||||||
|
p.shortVarDecl(p.makeIdentList(x))
|
||||||
|
}
|
||||||
return &ast.AssignStmt{x, pos, tok, y}
|
return &ast.AssignStmt{x, pos, tok, y}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1216,7 +1356,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
|||||||
colon := p.pos
|
colon := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
|
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
|
||||||
return &ast.LabeledStmt{label, colon, p.parseStmt()}
|
// Go spec: The scope of a label is the body of the function
|
||||||
|
// in which it is declared and excludes the body of any nested
|
||||||
|
// function.
|
||||||
|
stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
|
||||||
|
p.declare(stmt, p.labelScope, ast.Lbl, label)
|
||||||
|
return stmt
|
||||||
}
|
}
|
||||||
p.error(x[0].Pos(), "illegal label declaration")
|
p.error(x[0].Pos(), "illegal label declaration")
|
||||||
return &ast.BadStmt{x[0].Pos(), colon + 1}
|
return &ast.BadStmt{x[0].Pos(), colon + 1}
|
||||||
@ -1304,14 +1449,17 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
|
|||||||
defer un(trace(p, "BranchStmt"))
|
defer un(trace(p, "BranchStmt"))
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &ast.BranchStmt{p.pos, tok, nil}
|
pos := p.expect(tok)
|
||||||
p.expect(tok)
|
var label *ast.Ident
|
||||||
if tok != token.FALLTHROUGH && p.tok == token.IDENT {
|
if tok != token.FALLTHROUGH && p.tok == token.IDENT {
|
||||||
s.Label = p.parseIdent()
|
label = p.parseIdent()
|
||||||
|
// add to list of unresolved targets
|
||||||
|
n := len(p.targetStack) - 1
|
||||||
|
p.targetStack[n] = append(p.targetStack[n], label)
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi()
|
||||||
|
|
||||||
return s
|
return &ast.BranchStmt{pos, tok, label}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1333,6 +1481,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pos := p.expect(token.IF)
|
pos := p.expect(token.IF)
|
||||||
|
p.openScope()
|
||||||
|
defer p.closeScope()
|
||||||
|
|
||||||
var s ast.Stmt
|
var s ast.Stmt
|
||||||
var x ast.Expr
|
var x ast.Expr
|
||||||
@ -1373,7 +1523,6 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
|
|||||||
defer un(trace(p, "CaseClause"))
|
defer un(trace(p, "CaseClause"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwitchCase
|
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
var x []ast.Expr
|
var x []ast.Expr
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
@ -1384,7 +1533,9 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
colon := p.expect(token.COLON)
|
colon := p.expect(token.COLON)
|
||||||
|
p.openScope()
|
||||||
body := p.parseStmtList()
|
body := p.parseStmtList()
|
||||||
|
p.closeScope()
|
||||||
|
|
||||||
return &ast.CaseClause{pos, x, colon, body}
|
return &ast.CaseClause{pos, x, colon, body}
|
||||||
}
|
}
|
||||||
@ -1410,7 +1561,6 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
|
|||||||
defer un(trace(p, "TypeCaseClause"))
|
defer un(trace(p, "TypeCaseClause"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeSwitchCase
|
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
var types []ast.Expr
|
var types []ast.Expr
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
@ -1421,7 +1571,9 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
colon := p.expect(token.COLON)
|
colon := p.expect(token.COLON)
|
||||||
|
p.openScope()
|
||||||
body := p.parseStmtList()
|
body := p.parseStmtList()
|
||||||
|
p.closeScope()
|
||||||
|
|
||||||
return &ast.TypeCaseClause{pos, types, colon, body}
|
return &ast.TypeCaseClause{pos, types, colon, body}
|
||||||
}
|
}
|
||||||
@ -1447,6 +1599,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pos := p.expect(token.SWITCH)
|
pos := p.expect(token.SWITCH)
|
||||||
|
p.openScope()
|
||||||
|
defer p.closeScope()
|
||||||
|
|
||||||
var s1, s2 ast.Stmt
|
var s1, s2 ast.Stmt
|
||||||
if p.tok != token.LBRACE {
|
if p.tok != token.LBRACE {
|
||||||
@ -1497,7 +1651,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
|||||||
defer un(trace(p, "CommClause"))
|
defer un(trace(p, "CommClause"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommCase
|
p.openScope()
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
var comm ast.Stmt
|
var comm ast.Stmt
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
@ -1518,7 +1672,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
|||||||
pos := p.pos
|
pos := p.pos
|
||||||
tok := p.tok
|
tok := p.tok
|
||||||
var rhs ast.Expr
|
var rhs ast.Expr
|
||||||
if p.tok == token.ASSIGN || p.tok == token.DEFINE {
|
if tok == token.ASSIGN || tok == token.DEFINE {
|
||||||
// RecvStmt with assignment
|
// RecvStmt with assignment
|
||||||
if len(lhs) > 2 {
|
if len(lhs) > 2 {
|
||||||
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
|
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
|
||||||
@ -1527,6 +1681,9 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
|||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
rhs = p.parseExpr()
|
rhs = p.parseExpr()
|
||||||
|
if tok == token.DEFINE {
|
||||||
|
p.shortVarDecl(p.makeIdentList(lhs))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// rhs must be single receive operation
|
// rhs must be single receive operation
|
||||||
if len(lhs) > 1 {
|
if len(lhs) > 1 {
|
||||||
@ -1552,6 +1709,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
|||||||
|
|
||||||
colon := p.expect(token.COLON)
|
colon := p.expect(token.COLON)
|
||||||
body := p.parseStmtList()
|
body := p.parseStmtList()
|
||||||
|
p.closeScope()
|
||||||
|
|
||||||
return &ast.CommClause{pos, comm, colon, body}
|
return &ast.CommClause{pos, comm, colon, body}
|
||||||
}
|
}
|
||||||
@ -1582,6 +1740,8 @@ func (p *parser) parseForStmt() ast.Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pos := p.expect(token.FOR)
|
pos := p.expect(token.FOR)
|
||||||
|
p.openScope()
|
||||||
|
defer p.closeScope()
|
||||||
|
|
||||||
var s1, s2, s3 ast.Stmt
|
var s1, s2, s3 ast.Stmt
|
||||||
if p.tok != token.LBRACE {
|
if p.tok != token.LBRACE {
|
||||||
@ -1631,18 +1791,16 @@ func (p *parser) parseForStmt() ast.Stmt {
|
|||||||
return &ast.BadStmt{pos, body.End()}
|
return &ast.BadStmt{pos, body.End()}
|
||||||
}
|
}
|
||||||
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
|
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
|
||||||
// rhs is range expression; check lhs
|
// rhs is range expression
|
||||||
|
// (any short variable declaration was handled by parseSimpleStat above)
|
||||||
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
|
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
|
||||||
} else {
|
}
|
||||||
p.errorExpected(s2.Pos(), "range clause")
|
p.errorExpected(s2.Pos(), "range clause")
|
||||||
return &ast.BadStmt{pos, body.End()}
|
return &ast.BadStmt{pos, body.End()}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// regular for statement
|
// regular for statement
|
||||||
return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
|
return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
|
||||||
}
|
|
||||||
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1715,10 +1873,11 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ident *ast.Ident
|
var ident *ast.Ident
|
||||||
if p.tok == token.PERIOD {
|
switch p.tok {
|
||||||
|
case token.PERIOD:
|
||||||
ident = &ast.Ident{p.pos, ".", nil}
|
ident = &ast.Ident{p.pos, ".", nil}
|
||||||
p.next()
|
p.next()
|
||||||
} else if p.tok == token.IDENT {
|
case token.IDENT:
|
||||||
ident = p.parseIdent()
|
ident = p.parseIdent()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1729,7 +1888,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
} else {
|
} else {
|
||||||
p.expect(token.STRING) // use expect() error handling
|
p.expect(token.STRING) // use expect() error handling
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.ImportSpec{doc, ident, path, p.lineComment}
|
return &ast.ImportSpec{doc, ident, path, p.lineComment}
|
||||||
}
|
}
|
||||||
@ -1747,9 +1906,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
p.expect(token.ASSIGN)
|
p.expect(token.ASSIGN)
|
||||||
values = p.parseExprList()
|
values = p.parseExprList()
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
|
// Go spec: The scope of a constant or variable identifier declared inside
|
||||||
|
// a function begins at the end of the ConstSpec or VarSpec and ends at
|
||||||
|
// the end of the innermost containing block.
|
||||||
|
// (Global identifiers are resolved in a separate phase after parsing.)
|
||||||
|
spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
|
||||||
|
p.declare(spec, p.topScope, ast.Con, idents...)
|
||||||
|
|
||||||
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1760,9 +1926,16 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
|
|
||||||
ident := p.parseIdent()
|
ident := p.parseIdent()
|
||||||
typ := p.parseType()
|
typ := p.parseType()
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.TypeSpec{doc, ident, typ, p.lineComment}
|
// Go spec: The scope of a type identifier declared inside a function begins
|
||||||
|
// at the identifier in the TypeSpec and ends at the end of the innermost
|
||||||
|
// containing block.
|
||||||
|
// (Global identifiers are resolved in a separate phase after parsing.)
|
||||||
|
spec := &ast.TypeSpec{doc, ident, typ, p.lineComment}
|
||||||
|
p.declare(spec, p.topScope, ast.Typ, ident)
|
||||||
|
|
||||||
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1778,9 +1951,16 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
p.expect(token.ASSIGN)
|
p.expect(token.ASSIGN)
|
||||||
values = p.parseExprList()
|
values = p.parseExprList()
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
|
|
||||||
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
|
// Go spec: The scope of a constant or variable identifier declared inside
|
||||||
|
// a function begins at the end of the ConstSpec or VarSpec and ends at
|
||||||
|
// the end of the innermost containing block.
|
||||||
|
// (Global identifiers are resolved in a separate phase after parsing.)
|
||||||
|
spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
|
||||||
|
p.declare(spec, p.topScope, ast.Var, idents...)
|
||||||
|
|
||||||
|
return spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1809,13 +1989,13 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseReceiver() *ast.FieldList {
|
func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "Receiver"))
|
defer un(trace(p, "Receiver"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
par := p.parseParameters(false)
|
par := p.parseParameters(scope, false)
|
||||||
|
|
||||||
// must have exactly one receiver
|
// must have exactly one receiver
|
||||||
if par.NumFields() != 1 {
|
if par.NumFields() != 1 {
|
||||||
@ -1844,22 +2024,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
|
|||||||
|
|
||||||
doc := p.leadComment
|
doc := p.leadComment
|
||||||
pos := p.expect(token.FUNC)
|
pos := p.expect(token.FUNC)
|
||||||
|
scope := ast.NewScope(p.topScope) // function scope
|
||||||
|
|
||||||
var recv *ast.FieldList
|
var recv *ast.FieldList
|
||||||
if p.tok == token.LPAREN {
|
if p.tok == token.LPAREN {
|
||||||
recv = p.parseReceiver()
|
recv = p.parseReceiver(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
ident := p.parseIdent()
|
ident := p.parseIdent()
|
||||||
params, results := p.parseSignature()
|
|
||||||
|
params, results := p.parseSignature(scope)
|
||||||
|
|
||||||
var body *ast.BlockStmt
|
var body *ast.BlockStmt
|
||||||
if p.tok == token.LBRACE {
|
if p.tok == token.LBRACE {
|
||||||
body = p.parseBody()
|
body = p.parseBody(scope)
|
||||||
}
|
}
|
||||||
p.expectSemi()
|
p.expectSemi()
|
||||||
|
|
||||||
return &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
|
decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
|
||||||
|
if recv == nil {
|
||||||
|
// Go spec: The scope of an identifier denoting a constant, type,
|
||||||
|
// variable, or function (but not method) declared at top level
|
||||||
|
// (outside any function) is the package block.
|
||||||
|
//
|
||||||
|
// init() functions cannot be referred to and there may
|
||||||
|
// be more than one - don't put them in the pkgScope
|
||||||
|
if ident.Name != "init" {
|
||||||
|
p.declare(decl, p.pkgScope, ast.Fun, ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1918,6 +2113,8 @@ func (p *parser) parseFile() *ast.File {
|
|||||||
// package clause
|
// package clause
|
||||||
doc := p.leadComment
|
doc := p.leadComment
|
||||||
pos := p.expect(token.PACKAGE)
|
pos := p.expect(token.PACKAGE)
|
||||||
|
// Go spec: The package clause is not a declaration;
|
||||||
|
// the package name does not appear in any scope.
|
||||||
ident := p.parseIdent()
|
ident := p.parseIdent()
|
||||||
p.expectSemi()
|
p.expectSemi()
|
||||||
|
|
||||||
@ -1940,5 +2137,20 @@ func (p *parser) parseFile() *ast.File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ast.File{doc, pos, ident, decls, p.comments}
|
if p.topScope != p.pkgScope {
|
||||||
|
panic("internal error: imbalanced scopes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve global identifiers within the same file
|
||||||
|
i := 0
|
||||||
|
for _, ident := range p.unresolved {
|
||||||
|
// i <= index for current ident
|
||||||
|
ident.Obj = p.pkgScope.Lookup(ident.Name)
|
||||||
|
if ident.Obj == nil {
|
||||||
|
p.unresolved[i] = ident
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ var validFiles = []string{
|
|||||||
|
|
||||||
func TestParse3(t *testing.T) {
|
func TestParse3(t *testing.T) {
|
||||||
for _, filename := range validFiles {
|
for _, filename := range validFiles {
|
||||||
_, err := ParseFile(fset, filename, nil, 0)
|
_, err := ParseFile(fset, filename, nil, DeclarationErrors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ParseFile(%s): %v", filename, err)
|
t.Errorf("ParseFile(%s): %v", filename, err)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ include ../../../Make.inc
|
|||||||
TARG=go/typechecker
|
TARG=go/typechecker
|
||||||
GOFILES=\
|
GOFILES=\
|
||||||
scope.go\
|
scope.go\
|
||||||
|
type.go\
|
||||||
typechecker.go\
|
typechecker.go\
|
||||||
universe.go\
|
universe.go\
|
||||||
|
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file implements scope support functions.
|
// DEPRECATED FILE - WILL GO AWAY EVENTUALLY.
|
||||||
|
//
|
||||||
|
// Scope handling is now done in go/parser.
|
||||||
|
// The functionality here is only present to
|
||||||
|
// keep the typechecker running for now.
|
||||||
|
|
||||||
package typechecker
|
package typechecker
|
||||||
|
|
||||||
import (
|
import "go/ast"
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (tc *typechecker) openScope() *ast.Scope {
|
func (tc *typechecker) openScope() *ast.Scope {
|
||||||
@ -24,52 +24,25 @@ func (tc *typechecker) closeScope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// objPos computes the source position of the declaration of an object name.
|
|
||||||
// Only required for error reporting, so doesn't have to be fast.
|
|
||||||
func objPos(obj *ast.Object) (pos token.Pos) {
|
|
||||||
switch d := obj.Decl.(type) {
|
|
||||||
case *ast.Field:
|
|
||||||
for _, n := range d.Names {
|
|
||||||
if n.Name == obj.Name {
|
|
||||||
return n.Pos()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.ValueSpec:
|
|
||||||
for _, n := range d.Names {
|
|
||||||
if n.Name == obj.Name {
|
|
||||||
return n.Pos()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.TypeSpec:
|
|
||||||
return d.Name.Pos()
|
|
||||||
case *ast.FuncDecl:
|
|
||||||
return d.Name.Pos()
|
|
||||||
}
|
|
||||||
if debug {
|
|
||||||
fmt.Printf("decl = %T\n", obj.Decl)
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields.
|
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields.
|
||||||
// It returns the newly allocated object. If an object with the same name already exists in scope, an error
|
// It returns the newly allocated object. If an object with the same name already exists in scope, an error
|
||||||
// is reported and the object is not inserted.
|
// is reported and the object is not inserted.
|
||||||
// (Objects with _ name are always inserted into a scope without errors, but they cannot be found.)
|
func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
||||||
func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
|
||||||
obj := ast.NewObj(kind, name.Name)
|
obj := ast.NewObj(kind, name.Name)
|
||||||
obj.Decl = decl
|
obj.Decl = decl
|
||||||
obj.N = n
|
//obj.N = n
|
||||||
name.Obj = obj
|
name.Obj = obj
|
||||||
|
if name.Name != "_" {
|
||||||
if alt := scope.Insert(obj); alt != obj {
|
if alt := scope.Insert(obj); alt != obj {
|
||||||
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt))
|
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// decl is the same as declInScope(tc.topScope, ...)
|
// decl is the same as declInScope(tc.topScope, ...)
|
||||||
func (tc *typechecker) decl(kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
func (tc *typechecker) decl(kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
||||||
return tc.declInScope(tc.topScope, kind, name, decl, n)
|
return tc.declInScope(tc.topScope, kind, name, decl, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +64,7 @@ func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) {
|
|||||||
|
|
||||||
// findField returns the object with the given name if visible in the type's scope.
|
// findField returns the object with the given name if visible in the type's scope.
|
||||||
// If no such object is found, an error is reported and a bad object is returned instead.
|
// If no such object is found, an error is reported and a bad object is returned instead.
|
||||||
func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) {
|
func (tc *typechecker) findField(typ *Type, name *ast.Ident) (obj *ast.Object) {
|
||||||
// TODO(gri) This is simplistic at the moment and ignores anonymous fields.
|
// TODO(gri) This is simplistic at the moment and ignores anonymous fields.
|
||||||
obj = typ.Scope.Lookup(name.Name)
|
obj = typ.Scope.Lookup(name.Name)
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// printScope prints the objects in a scope.
|
|
||||||
func printScope(scope *ast.Scope) {
|
|
||||||
fmt.Printf("scope %p {", scope)
|
|
||||||
if scope != nil && len(scope.Objects) > 0 {
|
|
||||||
fmt.Println()
|
|
||||||
for _, obj := range scope.Objects {
|
|
||||||
form := "void"
|
|
||||||
if obj.Type != nil {
|
|
||||||
form = obj.Type.Form.String()
|
|
||||||
}
|
|
||||||
fmt.Printf("\t%s\t%s\n", obj.Name, form)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("}\n")
|
|
||||||
}
|
|
||||||
|
@ -27,8 +27,11 @@ func (T) m1 /* ERROR "already declared" */ () {}
|
|||||||
|
|
||||||
func (x *T) m2(u, x /* ERROR "already declared" */ int) {}
|
func (x *T) m2(u, x /* ERROR "already declared" */ int) {}
|
||||||
func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
|
func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
|
||||||
func (T) _(x, x /* ERROR "already declared" */ int) {}
|
// The following are disabled for now because the typechecker
|
||||||
func (T) _() (x, x /* ERROR "already declared" */ int) {}
|
// in in the process of being rewritten and cannot handle them
|
||||||
|
// at the moment
|
||||||
|
//func (T) _(x, x /* "already declared" */ int) {}
|
||||||
|
//func (T) _() (x, x /* "already declared" */ int) {}
|
||||||
|
|
||||||
//func (PT) _() {}
|
//func (PT) _() {}
|
||||||
|
|
125
src/pkg/go/typechecker/type.go
Normal file
125
src/pkg/go/typechecker/type.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2010 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 typechecker
|
||||||
|
|
||||||
|
import "go/ast"
|
||||||
|
|
||||||
|
|
||||||
|
// A Type represents a Go type.
|
||||||
|
type Type struct {
|
||||||
|
Form Form
|
||||||
|
Obj *ast.Object // corresponding type name, or nil
|
||||||
|
Scope *ast.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 *ast.Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
|
||||||
|
Expr ast.Expr // corresponding AST expression
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NewType creates a new type of a given form.
|
||||||
|
func NewType(form Form) *Type {
|
||||||
|
return &Type{Form: form, Scope: ast.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",
|
||||||
|
}
|
@ -65,6 +65,7 @@ type typechecker struct {
|
|||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
scanner.ErrorVector
|
scanner.ErrorVector
|
||||||
importer Importer
|
importer Importer
|
||||||
|
globals []*ast.Object // list of global objects
|
||||||
topScope *ast.Scope // current top-most scope
|
topScope *ast.Scope // current top-most scope
|
||||||
cyclemap map[*ast.Object]bool // for cycle detection
|
cyclemap map[*ast.Object]bool // for cycle detection
|
||||||
iota int // current value of iota
|
iota int // current value of iota
|
||||||
@ -94,7 +95,7 @@ phase 1: declare all global objects; also collect all function and method declar
|
|||||||
- report global double declarations
|
- report global double declarations
|
||||||
|
|
||||||
phase 2: bind methods to their receiver base types
|
phase 2: bind methods to their receiver base types
|
||||||
- received base types must be declared in the package, thus for
|
- receiver base types must be declared in the package, thus for
|
||||||
each method a corresponding (unresolved) type must exist
|
each method a corresponding (unresolved) type must exist
|
||||||
- report method double declarations and errors with base types
|
- report method double declarations and errors with base types
|
||||||
|
|
||||||
@ -142,16 +143,16 @@ func (tc *typechecker) checkPackage(pkg *ast.Package) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// phase 3: resolve all global objects
|
// phase 3: resolve all global objects
|
||||||
// (note that objects with _ name are also in the scope)
|
|
||||||
tc.cyclemap = make(map[*ast.Object]bool)
|
tc.cyclemap = make(map[*ast.Object]bool)
|
||||||
for _, obj := range tc.topScope.Objects {
|
for _, obj := range tc.globals {
|
||||||
tc.resolve(obj)
|
tc.resolve(obj)
|
||||||
}
|
}
|
||||||
assert(len(tc.cyclemap) == 0)
|
assert(len(tc.cyclemap) == 0)
|
||||||
|
|
||||||
// 4: sequentially typecheck function and method bodies
|
// 4: sequentially typecheck function and method bodies
|
||||||
for _, f := range funcs {
|
for _, f := range funcs {
|
||||||
tc.checkBlock(f.Body.List, f.Name.Obj.Type)
|
ftype, _ := f.Name.Obj.Type.(*Type)
|
||||||
|
tc.checkBlock(f.Body.List, ftype)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg.Scope = tc.topScope
|
pkg.Scope = tc.topScope
|
||||||
@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, name := range s.Names {
|
for _, name := range s.Names {
|
||||||
tc.decl(ast.Con, name, s, iota)
|
tc.globals = append(tc.globals, tc.decl(ast.Con, name, s, iota))
|
||||||
}
|
}
|
||||||
case token.VAR:
|
case token.VAR:
|
||||||
for _, name := range s.Names {
|
for _, name := range s.Names {
|
||||||
tc.decl(ast.Var, name, s, 0)
|
tc.globals = append(tc.globals, tc.decl(ast.Var, name, s, 0))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
|||||||
iota++
|
iota++
|
||||||
case *ast.TypeSpec:
|
case *ast.TypeSpec:
|
||||||
obj := tc.decl(ast.Typ, s.Name, s, 0)
|
obj := tc.decl(ast.Typ, s.Name, s, 0)
|
||||||
|
tc.globals = append(tc.globals, obj)
|
||||||
// give all type objects an unresolved type so
|
// give all type objects an unresolved type so
|
||||||
// that we can collect methods in the type scope
|
// that we can collect methods in the type scope
|
||||||
typ := ast.NewType(ast.Unresolved)
|
typ := NewType(Unresolved)
|
||||||
obj.Type = typ
|
obj.Type = typ
|
||||||
typ.Obj = obj
|
typ.Obj = obj
|
||||||
default:
|
default:
|
||||||
@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
|||||||
|
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
if d.Recv == nil {
|
if d.Recv == nil {
|
||||||
tc.decl(ast.Fun, d.Name, d, 0)
|
tc.globals = append(tc.globals, tc.decl(ast.Fun, d.Name, d, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
|
|||||||
} else if obj.Kind != ast.Typ {
|
} else if obj.Kind != ast.Typ {
|
||||||
tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
|
tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
|
||||||
} else {
|
} else {
|
||||||
typ := obj.Type
|
typ := obj.Type.(*Type)
|
||||||
assert(typ.Form == ast.Unresolved)
|
assert(typ.Form == Unresolved)
|
||||||
scope = typ.Scope
|
scope = typ.Scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
|
|||||||
func (tc *typechecker) resolve(obj *ast.Object) {
|
func (tc *typechecker) resolve(obj *ast.Object) {
|
||||||
// check for declaration cycles
|
// check for declaration cycles
|
||||||
if tc.cyclemap[obj] {
|
if tc.cyclemap[obj] {
|
||||||
tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name)
|
tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
|
||||||
obj.Kind = ast.Bad
|
obj.Kind = ast.Bad
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// resolve non-type objects
|
// resolve non-type objects
|
||||||
typ := obj.Type
|
typ, _ := obj.Type.(*Type)
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
switch obj.Kind {
|
switch obj.Kind {
|
||||||
case ast.Bad:
|
case ast.Bad:
|
||||||
@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
|||||||
|
|
||||||
case ast.Var:
|
case ast.Var:
|
||||||
tc.declVar(obj)
|
tc.declVar(obj)
|
||||||
//obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
|
obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
|
||||||
|
|
||||||
case ast.Fun:
|
case ast.Fun:
|
||||||
obj.Type = ast.NewType(ast.Function)
|
obj.Type = NewType(Function)
|
||||||
t := obj.Decl.(*ast.FuncDecl).Type
|
t := obj.Decl.(*ast.FuncDecl).Type
|
||||||
tc.declSignature(obj.Type, nil, t.Params, t.Results)
|
tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// type objects have non-nil types when resolve is called
|
// type objects have non-nil types when resolve is called
|
||||||
@ -300,34 +302,36 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolve type objects
|
// resolve type objects
|
||||||
if typ.Form == ast.Unresolved {
|
if typ.Form == Unresolved {
|
||||||
tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
|
tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
|
||||||
|
|
||||||
// provide types for all methods
|
// provide types for all methods
|
||||||
for _, obj := range typ.Scope.Objects {
|
for _, obj := range typ.Scope.Objects {
|
||||||
if obj.Kind == ast.Fun {
|
if obj.Kind == ast.Fun {
|
||||||
assert(obj.Type == nil)
|
assert(obj.Type == nil)
|
||||||
obj.Type = ast.NewType(ast.Method)
|
obj.Type = NewType(Method)
|
||||||
f := obj.Decl.(*ast.FuncDecl)
|
f := obj.Decl.(*ast.FuncDecl)
|
||||||
t := f.Type
|
t := f.Type
|
||||||
tc.declSignature(obj.Type, f.Recv, t.Params, t.Results)
|
tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *ast.Type) {
|
func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
|
||||||
tc.openScope()
|
tc.openScope()
|
||||||
defer tc.closeScope()
|
defer tc.closeScope()
|
||||||
|
|
||||||
// inject function/method parameters into block scope, if any
|
// inject function/method parameters into block scope, if any
|
||||||
if ftype != nil {
|
if ftype != nil {
|
||||||
for _, par := range ftype.Params.Objects {
|
for _, par := range ftype.Params.Objects {
|
||||||
|
if par.Name != "_" {
|
||||||
obj := tc.topScope.Insert(par)
|
obj := tc.topScope.Insert(par)
|
||||||
assert(obj == par) // ftype has no double declarations
|
assert(obj == par) // ftype has no double declarations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, stmt := range body {
|
for _, stmt := range body {
|
||||||
tc.checkStmt(stmt)
|
tc.checkStmt(stmt)
|
||||||
@ -362,8 +366,8 @@ func (tc *typechecker) declFields(scope *ast.Scope, fields *ast.FieldList, ref b
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.FieldList) {
|
func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) {
|
||||||
assert((typ.Form == ast.Method) == (recv != nil))
|
assert((typ.Form == Method) == (recv != nil))
|
||||||
typ.Params = ast.NewScope(nil)
|
typ.Params = ast.NewScope(nil)
|
||||||
tc.declFields(typ.Params, recv, true)
|
tc.declFields(typ.Params, recv, true)
|
||||||
tc.declFields(typ.Params, params, true)
|
tc.declFields(typ.Params, params, true)
|
||||||
@ -371,7 +375,7 @@ func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.F
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) {
|
func (tc *typechecker) typeFor(def *Type, x ast.Expr, ref bool) (typ *Type) {
|
||||||
x = unparen(x)
|
x = unparen(x)
|
||||||
|
|
||||||
// type name
|
// type name
|
||||||
@ -381,10 +385,10 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
|||||||
if obj.Kind != ast.Typ {
|
if obj.Kind != ast.Typ {
|
||||||
tc.Errorf(t.Pos(), "%s is not a type", t.Name)
|
tc.Errorf(t.Pos(), "%s is not a type", t.Name)
|
||||||
if def == nil {
|
if def == nil {
|
||||||
typ = ast.NewType(ast.BadType)
|
typ = NewType(BadType)
|
||||||
} else {
|
} else {
|
||||||
typ = def
|
typ = def
|
||||||
typ.Form = ast.BadType
|
typ.Form = BadType
|
||||||
}
|
}
|
||||||
typ.Expr = x
|
typ.Expr = x
|
||||||
return
|
return
|
||||||
@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
|||||||
if !ref {
|
if !ref {
|
||||||
tc.resolve(obj) // check for cycles even if type resolved
|
tc.resolve(obj) // check for cycles even if type resolved
|
||||||
}
|
}
|
||||||
typ = obj.Type
|
typ = obj.Type.(*Type)
|
||||||
|
|
||||||
if def != nil {
|
if def != nil {
|
||||||
// new type declaration: copy type structure
|
// new type declaration: copy type structure
|
||||||
@ -410,7 +414,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
|||||||
// type literal
|
// type literal
|
||||||
typ = def
|
typ = def
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
typ = ast.NewType(ast.BadType)
|
typ = NewType(BadType)
|
||||||
}
|
}
|
||||||
typ.Expr = x
|
typ.Expr = x
|
||||||
|
|
||||||
@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
|||||||
if debug {
|
if debug {
|
||||||
fmt.Println("qualified identifier unimplemented")
|
fmt.Println("qualified identifier unimplemented")
|
||||||
}
|
}
|
||||||
typ.Form = ast.BadType
|
typ.Form = BadType
|
||||||
|
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
typ.Form = ast.Pointer
|
typ.Form = Pointer
|
||||||
typ.Elt = tc.typeFor(nil, t.X, true)
|
typ.Elt = tc.typeFor(nil, t.X, true)
|
||||||
|
|
||||||
case *ast.ArrayType:
|
case *ast.ArrayType:
|
||||||
if t.Len != nil {
|
if t.Len != nil {
|
||||||
typ.Form = ast.Array
|
typ.Form = Array
|
||||||
// TODO(gri) compute the real length
|
// TODO(gri) compute the real length
|
||||||
// (this may call resolve recursively)
|
// (this may call resolve recursively)
|
||||||
(*typ).N = 42
|
(*typ).N = 42
|
||||||
} else {
|
} else {
|
||||||
typ.Form = ast.Slice
|
typ.Form = Slice
|
||||||
}
|
}
|
||||||
typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
|
typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
|
||||||
|
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
typ.Form = ast.Struct
|
typ.Form = Struct
|
||||||
tc.declFields(typ.Scope, t.Fields, false)
|
tc.declFields(typ.Scope, t.Fields, false)
|
||||||
|
|
||||||
case *ast.FuncType:
|
case *ast.FuncType:
|
||||||
typ.Form = ast.Function
|
typ.Form = Function
|
||||||
tc.declSignature(typ, nil, t.Params, t.Results)
|
tc.declSignature(typ, nil, t.Params, t.Results)
|
||||||
|
|
||||||
case *ast.InterfaceType:
|
case *ast.InterfaceType:
|
||||||
typ.Form = ast.Interface
|
typ.Form = Interface
|
||||||
tc.declFields(typ.Scope, t.Methods, true)
|
tc.declFields(typ.Scope, t.Methods, true)
|
||||||
|
|
||||||
case *ast.MapType:
|
case *ast.MapType:
|
||||||
typ.Form = ast.Map
|
typ.Form = Map
|
||||||
typ.Key = tc.typeFor(nil, t.Key, true)
|
typ.Key = tc.typeFor(nil, t.Key, true)
|
||||||
typ.Elt = tc.typeFor(nil, t.Value, true)
|
typ.Elt = tc.typeFor(nil, t.Value, true)
|
||||||
|
|
||||||
case *ast.ChanType:
|
case *ast.ChanType:
|
||||||
typ.Form = ast.Channel
|
typ.Form = Channel
|
||||||
typ.N = uint(t.Dir)
|
typ.N = uint(t.Dir)
|
||||||
typ.Elt = tc.typeFor(nil, t.Value, true)
|
typ.Elt = tc.typeFor(nil, t.Value, true)
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
|
|||||||
|
|
||||||
|
|
||||||
func testFilter(f *os.FileInfo) bool {
|
func testFilter(f *os.FileInfo) bool {
|
||||||
return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.'
|
return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ func init() {
|
|||||||
Universe = ast.NewScope(nil)
|
Universe = ast.NewScope(nil)
|
||||||
|
|
||||||
// basic types
|
// basic types
|
||||||
for n, name := range ast.BasicTypes {
|
for n, name := range BasicTypes {
|
||||||
typ := ast.NewType(ast.Basic)
|
typ := NewType(Basic)
|
||||||
typ.N = n
|
typ.N = n
|
||||||
obj := ast.NewObj(ast.Typ, name)
|
obj := ast.NewObj(ast.Typ, name)
|
||||||
obj.Type = typ
|
obj.Type = typ
|
||||||
|
Loading…
Reference in New Issue
Block a user