mirror of
https://github.com/golang/go
synced 2024-11-19 23:14:47 -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),
|
||||
Val2("i, x := 2, f", "i", 2, "x", 1.0),
|
||||
// Various errors
|
||||
CErr("1 := 2", "left side of := must be a name"),
|
||||
CErr("1 := 2", "expected identifier"),
|
||||
CErr("c, a := 1, 1", "cannot assign"),
|
||||
// Unpacking
|
||||
Val2("x, y := oneTwo()", "x", 1, "y", 2),
|
||||
|
@ -937,11 +937,13 @@ 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
|
||||
Unresolved []*Ident // unresolved global identifiers
|
||||
Comments []*CommentGroup // list of all comments in the source file
|
||||
}
|
||||
|
||||
|
||||
@ -959,7 +961,7 @@ func (f *File) End() token.Pos {
|
||||
//
|
||||
type Package struct {
|
||||
Name string // package name
|
||||
Scope *Scope // package scope; or nil
|
||||
Scope *Scope // package scope
|
||||
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
|
||||
// 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,108 @@ 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.
|
||||
// If the scope does not contain an object with that name yet,
|
||||
// Insert inserts the object and returns it. Otherwise, Insert
|
||||
// leaves the scope unchanged and returns the object found in
|
||||
// the scope instead.
|
||||
//
|
||||
func (s *Scope) Insert(obj *Object) *Object {
|
||||
alt := s.Lookup(obj.Name)
|
||||
if alt == nil {
|
||||
s.append(obj)
|
||||
func (s *Scope) Insert(obj *Object) (alt *Object) {
|
||||
if alt = s.Objects[obj.Name]; alt == nil {
|
||||
s.Objects[obj.Name] = obj
|
||||
alt = 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 *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 +146,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] }
|
||||
|
@ -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).
|
||||
// They control the amount of source code parsed and other optional
|
||||
// parser functionality.
|
||||
@ -30,6 +26,7 @@ const (
|
||||
ImportsOnly // parsing stops after import declarations
|
||||
ParseComments // parse comments and add them to AST
|
||||
Trace // print a trace of parsed productions
|
||||
DeclarationErrors // report declaration errors
|
||||
)
|
||||
|
||||
|
||||
@ -46,8 +43,8 @@ type parser struct {
|
||||
|
||||
// Comments
|
||||
comments []*ast.CommentGroup
|
||||
leadComment *ast.CommentGroup // the last lead comment
|
||||
lineComment *ast.CommentGroup // the last line comment
|
||||
leadComment *ast.CommentGroup // last lead comment
|
||||
lineComment *ast.CommentGroup // last line comment
|
||||
|
||||
// Next token
|
||||
pos token.Pos // token position
|
||||
@ -56,6 +53,16 @@ type parser struct {
|
||||
|
||||
// Non-syntactic parser control
|
||||
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) {
|
||||
p.file = fset.AddFile(filename, fset.Base(), len(src))
|
||||
p.scanner.Init(p.file, src, p, scannerMode(mode))
|
||||
|
||||
p.mode = mode
|
||||
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
var x ast.Expr = p.parseIdent()
|
||||
ident := p.parseIdent()
|
||||
p.resolve(ident)
|
||||
var x ast.Expr = ident
|
||||
if p.tok == token.PERIOD {
|
||||
// first identifier is a package identifier
|
||||
p.next()
|
||||
sel := p.parseIdent()
|
||||
x = &ast.SelectorExpr{x, sel}
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
@ -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 {
|
||||
defer un(trace(p, "ParameterList"))
|
||||
}
|
||||
@ -528,7 +646,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
||||
if typ != nil {
|
||||
// IdentifierList Type
|
||||
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 {
|
||||
p.next()
|
||||
}
|
||||
@ -536,7 +658,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
|
||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||
idents := p.parseIdentList()
|
||||
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 {
|
||||
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 {
|
||||
defer un(trace(p, "Parameters"))
|
||||
}
|
||||
@ -563,7 +689,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
|
||||
var params []*ast.Field
|
||||
lparen := p.expect(token.LPAREN)
|
||||
if p.tok != token.RPAREN {
|
||||
params = p.parseParameterList(ellipsisOk)
|
||||
params = p.parseParameterList(scope, ellipsisOk)
|
||||
}
|
||||
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 {
|
||||
defer un(trace(p, "Result"))
|
||||
}
|
||||
|
||||
if p.tok == token.LPAREN {
|
||||
return p.parseParameters(false)
|
||||
return p.parseParameters(scope, false)
|
||||
}
|
||||
|
||||
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 {
|
||||
defer un(trace(p, "Signature"))
|
||||
}
|
||||
|
||||
params = p.parseParameters(true)
|
||||
results = p.parseResult()
|
||||
params = p.parseParameters(scope, true)
|
||||
results = p.parseResult(scope)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (p *parser) parseFuncType() *ast.FuncType {
|
||||
func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
|
||||
if p.trace {
|
||||
defer un(trace(p, "FuncType"))
|
||||
}
|
||||
|
||||
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 {
|
||||
// method
|
||||
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}
|
||||
} else {
|
||||
// embedded interface
|
||||
typ = x
|
||||
}
|
||||
p.expectSemi()
|
||||
p.expectSemi() // call before accessing 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:
|
||||
return p.parsePointerType()
|
||||
case token.FUNC:
|
||||
return p.parseFuncType()
|
||||
typ, _ := p.parseFuncType()
|
||||
return typ
|
||||
case token.INTERFACE:
|
||||
return p.parseInterfaceType()
|
||||
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 {
|
||||
defer un(trace(p, "Body"))
|
||||
}
|
||||
|
||||
lbrace := p.expect(token.LBRACE)
|
||||
p.topScope = scope // open function scope
|
||||
p.openLabelScope()
|
||||
list := p.parseStmtList()
|
||||
p.closeLabelScope()
|
||||
p.closeScope()
|
||||
rbrace := p.expect(token.RBRACE)
|
||||
|
||||
return &ast.BlockStmt{lbrace, list, rbrace}
|
||||
@ -764,7 +897,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
|
||||
}
|
||||
|
||||
lbrace := p.expect(token.LBRACE)
|
||||
p.openScope()
|
||||
list := p.parseStmtList()
|
||||
p.closeScope()
|
||||
rbrace := p.expect(token.RBRACE)
|
||||
|
||||
return &ast.BlockStmt{lbrace, list, rbrace}
|
||||
@ -779,14 +914,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
|
||||
defer un(trace(p, "FuncTypeOrLit"))
|
||||
}
|
||||
|
||||
typ := p.parseFuncType()
|
||||
typ, scope := p.parseFuncType()
|
||||
if p.tok != token.LBRACE {
|
||||
// function type only
|
||||
return typ
|
||||
}
|
||||
|
||||
p.exprLev++
|
||||
body := p.parseBody()
|
||||
body := p.parseBody(scope)
|
||||
p.exprLev--
|
||||
|
||||
return &ast.FuncLit{typ, body}
|
||||
@ -803,7 +938,9 @@ func (p *parser) parseOperand() ast.Expr {
|
||||
|
||||
switch p.tok {
|
||||
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:
|
||||
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
|
||||
p.next()
|
||||
y := p.parseExprList()
|
||||
if tok == token.DEFINE {
|
||||
p.shortVarDecl(p.makeIdentList(x))
|
||||
}
|
||||
return &ast.AssignStmt{x, pos, tok, y}
|
||||
}
|
||||
|
||||
@ -1216,7 +1356,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
|
||||
colon := p.pos
|
||||
p.next()
|
||||
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")
|
||||
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"))
|
||||
}
|
||||
|
||||
s := &ast.BranchStmt{p.pos, tok, nil}
|
||||
p.expect(tok)
|
||||
pos := p.expect(tok)
|
||||
var label *ast.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()
|
||||
|
||||
return s
|
||||
return &ast.BranchStmt{pos, tok, label}
|
||||
}
|
||||
|
||||
|
||||
@ -1333,6 +1481,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
|
||||
}
|
||||
|
||||
pos := p.expect(token.IF)
|
||||
p.openScope()
|
||||
defer p.closeScope()
|
||||
|
||||
var s ast.Stmt
|
||||
var x ast.Expr
|
||||
@ -1373,7 +1523,6 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
|
||||
defer un(trace(p, "CaseClause"))
|
||||
}
|
||||
|
||||
// SwitchCase
|
||||
pos := p.pos
|
||||
var x []ast.Expr
|
||||
if p.tok == token.CASE {
|
||||
@ -1384,7 +1533,9 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
|
||||
}
|
||||
|
||||
colon := p.expect(token.COLON)
|
||||
p.openScope()
|
||||
body := p.parseStmtList()
|
||||
p.closeScope()
|
||||
|
||||
return &ast.CaseClause{pos, x, colon, body}
|
||||
}
|
||||
@ -1410,7 +1561,6 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
|
||||
defer un(trace(p, "TypeCaseClause"))
|
||||
}
|
||||
|
||||
// TypeSwitchCase
|
||||
pos := p.pos
|
||||
var types []ast.Expr
|
||||
if p.tok == token.CASE {
|
||||
@ -1421,7 +1571,9 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
|
||||
}
|
||||
|
||||
colon := p.expect(token.COLON)
|
||||
p.openScope()
|
||||
body := p.parseStmtList()
|
||||
p.closeScope()
|
||||
|
||||
return &ast.TypeCaseClause{pos, types, colon, body}
|
||||
}
|
||||
@ -1447,6 +1599,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
|
||||
}
|
||||
|
||||
pos := p.expect(token.SWITCH)
|
||||
p.openScope()
|
||||
defer p.closeScope()
|
||||
|
||||
var s1, s2 ast.Stmt
|
||||
if p.tok != token.LBRACE {
|
||||
@ -1497,7 +1651,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||
defer un(trace(p, "CommClause"))
|
||||
}
|
||||
|
||||
// CommCase
|
||||
p.openScope()
|
||||
pos := p.pos
|
||||
var comm ast.Stmt
|
||||
if p.tok == token.CASE {
|
||||
@ -1518,7 +1672,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||
pos := p.pos
|
||||
tok := p.tok
|
||||
var rhs ast.Expr
|
||||
if p.tok == token.ASSIGN || p.tok == token.DEFINE {
|
||||
if tok == token.ASSIGN || tok == token.DEFINE {
|
||||
// RecvStmt with assignment
|
||||
if len(lhs) > 2 {
|
||||
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
|
||||
@ -1527,6 +1681,9 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||
}
|
||||
p.next()
|
||||
rhs = p.parseExpr()
|
||||
if tok == token.DEFINE {
|
||||
p.shortVarDecl(p.makeIdentList(lhs))
|
||||
}
|
||||
} else {
|
||||
// rhs must be single receive operation
|
||||
if len(lhs) > 1 {
|
||||
@ -1552,6 +1709,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
|
||||
|
||||
colon := p.expect(token.COLON)
|
||||
body := p.parseStmtList()
|
||||
p.closeScope()
|
||||
|
||||
return &ast.CommClause{pos, comm, colon, body}
|
||||
}
|
||||
@ -1582,6 +1740,8 @@ func (p *parser) parseForStmt() ast.Stmt {
|
||||
}
|
||||
|
||||
pos := p.expect(token.FOR)
|
||||
p.openScope()
|
||||
defer p.closeScope()
|
||||
|
||||
var s1, s2, s3 ast.Stmt
|
||||
if p.tok != token.LBRACE {
|
||||
@ -1631,18 +1791,16 @@ func (p *parser) parseForStmt() ast.Stmt {
|
||||
return &ast.BadStmt{pos, body.End()}
|
||||
}
|
||||
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}
|
||||
} else {
|
||||
p.errorExpected(s2.Pos(), "range clause")
|
||||
return &ast.BadStmt{pos, body.End()}
|
||||
}
|
||||
} else {
|
||||
// regular for statement
|
||||
return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
|
||||
p.errorExpected(s2.Pos(), "range clause")
|
||||
return &ast.BadStmt{pos, body.End()}
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
// regular for statement
|
||||
return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
|
||||
}
|
||||
|
||||
|
||||
@ -1715,10 +1873,11 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
||||
}
|
||||
|
||||
var ident *ast.Ident
|
||||
if p.tok == token.PERIOD {
|
||||
switch p.tok {
|
||||
case token.PERIOD:
|
||||
ident = &ast.Ident{p.pos, ".", nil}
|
||||
p.next()
|
||||
} else if p.tok == token.IDENT {
|
||||
case token.IDENT:
|
||||
ident = p.parseIdent()
|
||||
}
|
||||
|
||||
@ -1729,7 +1888,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
||||
} else {
|
||||
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}
|
||||
}
|
||||
@ -1747,9 +1906,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
||||
p.expect(token.ASSIGN)
|
||||
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()
|
||||
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)
|
||||
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 {
|
||||
defer un(trace(p, "Receiver"))
|
||||
}
|
||||
|
||||
pos := p.pos
|
||||
par := p.parseParameters(false)
|
||||
par := p.parseParameters(scope, false)
|
||||
|
||||
// must have exactly one receiver
|
||||
if par.NumFields() != 1 {
|
||||
@ -1844,22 +2024,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
|
||||
|
||||
doc := p.leadComment
|
||||
pos := p.expect(token.FUNC)
|
||||
scope := ast.NewScope(p.topScope) // function scope
|
||||
|
||||
var recv *ast.FieldList
|
||||
if p.tok == token.LPAREN {
|
||||
recv = p.parseReceiver()
|
||||
recv = p.parseReceiver(scope)
|
||||
}
|
||||
|
||||
ident := p.parseIdent()
|
||||
params, results := p.parseSignature()
|
||||
|
||||
params, results := p.parseSignature(scope)
|
||||
|
||||
var body *ast.BlockStmt
|
||||
if p.tok == token.LBRACE {
|
||||
body = p.parseBody()
|
||||
body = p.parseBody(scope)
|
||||
}
|
||||
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
|
||||
doc := p.leadComment
|
||||
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()
|
||||
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) {
|
||||
for _, filename := range validFiles {
|
||||
_, err := ParseFile(fset, filename, nil, 0)
|
||||
_, err := ParseFile(fset, filename, nil, DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Errorf("ParseFile(%s): %v", filename, err)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ include ../../../Make.inc
|
||||
TARG=go/typechecker
|
||||
GOFILES=\
|
||||
scope.go\
|
||||
type.go\
|
||||
typechecker.go\
|
||||
universe.go\
|
||||
|
||||
|
@ -2,15 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
import "go/ast"
|
||||
|
||||
|
||||
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.
|
||||
// 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.
|
||||
// (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.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
||||
func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
|
||||
obj := ast.NewObj(kind, name.Name)
|
||||
obj.Decl = decl
|
||||
obj.N = n
|
||||
//obj.N = n
|
||||
name.Obj = obj
|
||||
if alt := scope.Insert(obj); alt != obj {
|
||||
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt))
|
||||
if name.Name != "_" {
|
||||
if alt := scope.Insert(obj); alt != obj {
|
||||
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
// 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.
|
||||
obj = typ.Scope.Lookup(name.Name)
|
||||
if obj == nil {
|
||||
@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec
|
||||
}
|
||||
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) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
|
||||
func (T) _(x, x /* ERROR "already declared" */ int) {}
|
||||
func (T) _() (x, x /* ERROR "already declared" */ int) {}
|
||||
// The following are disabled for now because the typechecker
|
||||
// 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) _() {}
|
||||
|
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
|
||||
scanner.ErrorVector
|
||||
importer Importer
|
||||
globals []*ast.Object // list of global objects
|
||||
topScope *ast.Scope // current top-most scope
|
||||
cyclemap map[*ast.Object]bool // for cycle detection
|
||||
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
|
||||
|
||||
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
|
||||
- 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
|
||||
// (note that objects with _ name are also in the scope)
|
||||
tc.cyclemap = make(map[*ast.Object]bool)
|
||||
for _, obj := range tc.topScope.Objects {
|
||||
for _, obj := range tc.globals {
|
||||
tc.resolve(obj)
|
||||
}
|
||||
assert(len(tc.cyclemap) == 0)
|
||||
|
||||
// 4: sequentially typecheck function and method bodies
|
||||
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
|
||||
@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
||||
}
|
||||
}
|
||||
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:
|
||||
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:
|
||||
panic("unreachable")
|
||||
@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
||||
iota++
|
||||
case *ast.TypeSpec:
|
||||
obj := tc.decl(ast.Typ, s.Name, s, 0)
|
||||
tc.globals = append(tc.globals, obj)
|
||||
// give all type objects an unresolved type so
|
||||
// that we can collect methods in the type scope
|
||||
typ := ast.NewType(ast.Unresolved)
|
||||
typ := NewType(Unresolved)
|
||||
obj.Type = typ
|
||||
typ.Obj = obj
|
||||
default:
|
||||
@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
|
||||
|
||||
case *ast.FuncDecl:
|
||||
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:
|
||||
@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
|
||||
} else if obj.Kind != ast.Typ {
|
||||
tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
|
||||
} else {
|
||||
typ := obj.Type
|
||||
assert(typ.Form == ast.Unresolved)
|
||||
typ := obj.Type.(*Type)
|
||||
assert(typ.Form == Unresolved)
|
||||
scope = typ.Scope
|
||||
}
|
||||
}
|
||||
@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
|
||||
func (tc *typechecker) resolve(obj *ast.Object) {
|
||||
// check for declaration cycles
|
||||
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
|
||||
return
|
||||
}
|
||||
@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
||||
}()
|
||||
|
||||
// resolve non-type objects
|
||||
typ := obj.Type
|
||||
typ, _ := obj.Type.(*Type)
|
||||
if typ == nil {
|
||||
switch obj.Kind {
|
||||
case ast.Bad:
|
||||
@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
||||
|
||||
case ast.Var:
|
||||
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:
|
||||
obj.Type = ast.NewType(ast.Function)
|
||||
obj.Type = NewType(Function)
|
||||
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:
|
||||
// type objects have non-nil types when resolve is called
|
||||
@ -300,32 +302,34 @@ func (tc *typechecker) resolve(obj *ast.Object) {
|
||||
}
|
||||
|
||||
// resolve type objects
|
||||
if typ.Form == ast.Unresolved {
|
||||
if typ.Form == Unresolved {
|
||||
tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
|
||||
|
||||
// provide types for all methods
|
||||
for _, obj := range typ.Scope.Objects {
|
||||
if obj.Kind == ast.Fun {
|
||||
assert(obj.Type == nil)
|
||||
obj.Type = ast.NewType(ast.Method)
|
||||
obj.Type = NewType(Method)
|
||||
f := obj.Decl.(*ast.FuncDecl)
|
||||
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()
|
||||
defer tc.closeScope()
|
||||
|
||||
// inject function/method parameters into block scope, if any
|
||||
if ftype != nil {
|
||||
for _, par := range ftype.Params.Objects {
|
||||
obj := tc.topScope.Insert(par)
|
||||
assert(obj == par) // ftype has no double declarations
|
||||
if par.Name != "_" {
|
||||
obj := tc.topScope.Insert(par)
|
||||
assert(obj == par) // ftype has no double declarations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
assert((typ.Form == ast.Method) == (recv != nil))
|
||||
func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) {
|
||||
assert((typ.Form == Method) == (recv != nil))
|
||||
typ.Params = ast.NewScope(nil)
|
||||
tc.declFields(typ.Params, recv, 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)
|
||||
|
||||
// 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 {
|
||||
tc.Errorf(t.Pos(), "%s is not a type", t.Name)
|
||||
if def == nil {
|
||||
typ = ast.NewType(ast.BadType)
|
||||
typ = NewType(BadType)
|
||||
} else {
|
||||
typ = def
|
||||
typ.Form = ast.BadType
|
||||
typ.Form = BadType
|
||||
}
|
||||
typ.Expr = x
|
||||
return
|
||||
@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
||||
if !ref {
|
||||
tc.resolve(obj) // check for cycles even if type resolved
|
||||
}
|
||||
typ = obj.Type
|
||||
typ = obj.Type.(*Type)
|
||||
|
||||
if def != nil {
|
||||
// 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
|
||||
typ = def
|
||||
if typ == nil {
|
||||
typ = ast.NewType(ast.BadType)
|
||||
typ = NewType(BadType)
|
||||
}
|
||||
typ.Expr = x
|
||||
|
||||
@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
|
||||
if debug {
|
||||
fmt.Println("qualified identifier unimplemented")
|
||||
}
|
||||
typ.Form = ast.BadType
|
||||
typ.Form = BadType
|
||||
|
||||
case *ast.StarExpr:
|
||||
typ.Form = ast.Pointer
|
||||
typ.Form = Pointer
|
||||
typ.Elt = tc.typeFor(nil, t.X, true)
|
||||
|
||||
case *ast.ArrayType:
|
||||
if t.Len != nil {
|
||||
typ.Form = ast.Array
|
||||
typ.Form = Array
|
||||
// TODO(gri) compute the real length
|
||||
// (this may call resolve recursively)
|
||||
(*typ).N = 42
|
||||
} else {
|
||||
typ.Form = ast.Slice
|
||||
typ.Form = Slice
|
||||
}
|
||||
typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
|
||||
|
||||
case *ast.StructType:
|
||||
typ.Form = ast.Struct
|
||||
typ.Form = Struct
|
||||
tc.declFields(typ.Scope, t.Fields, false)
|
||||
|
||||
case *ast.FuncType:
|
||||
typ.Form = ast.Function
|
||||
typ.Form = Function
|
||||
tc.declSignature(typ, nil, t.Params, t.Results)
|
||||
|
||||
case *ast.InterfaceType:
|
||||
typ.Form = ast.Interface
|
||||
typ.Form = Interface
|
||||
tc.declFields(typ.Scope, t.Methods, true)
|
||||
|
||||
case *ast.MapType:
|
||||
typ.Form = ast.Map
|
||||
typ.Form = Map
|
||||
typ.Key = tc.typeFor(nil, t.Key, true)
|
||||
typ.Elt = tc.typeFor(nil, t.Value, true)
|
||||
|
||||
case *ast.ChanType:
|
||||
typ.Form = ast.Channel
|
||||
typ.Form = Channel
|
||||
typ.N = uint(t.Dir)
|
||||
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 {
|
||||
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)
|
||||
|
||||
// basic types
|
||||
for n, name := range ast.BasicTypes {
|
||||
typ := ast.NewType(ast.Basic)
|
||||
for n, name := range BasicTypes {
|
||||
typ := NewType(Basic)
|
||||
typ.N = n
|
||||
obj := ast.NewObj(ast.Typ, name)
|
||||
obj.Type = typ
|
||||
|
Loading…
Reference in New Issue
Block a user