mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go.tools/go/types: resolve objects in type checker
By setting resolve = true in check.go, the type checker will do all identifier resolution during type checking time and ignore (and not depend on) parser objects. This permits the type checker to run easily on ASTs that are not generated with invariants guaranteed by the parser. There is a lot of new code; much of it slightly modified copies of old code. There is also a lot of duplication. After removing the dead code resulting from resolve = true permanently (and removing the flag as well), it will be easier to perform a thorough cleanup. As is, there are too many intertwined code paths. For now resolve = false. To be enabled in a successor CL. R=adonovan CC=golang-dev https://golang.org/cl/9606045
This commit is contained in:
parent
98bcbfbab7
commit
3df6f127f0
156
go/types/assignments.go
Normal file
156
go/types/assignments.go
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
// TODO(gri) initialize is very close to the 2nd half of assign1to1.
|
||||
func (check *checker) assign(obj Object, x *operand) {
|
||||
// Determine typ of lhs: If the object doesn't have a type
|
||||
// yet, determine it from the type of x; if x is invalid,
|
||||
// set the object type to Typ[Invalid].
|
||||
var typ Type
|
||||
switch obj := obj.(type) {
|
||||
default:
|
||||
unreachable()
|
||||
|
||||
case *Const:
|
||||
typ = obj.typ // may already be Typ[Invalid]
|
||||
if typ == nil {
|
||||
typ = Typ[Invalid]
|
||||
if x.mode != invalid {
|
||||
typ = x.typ
|
||||
}
|
||||
obj.typ = typ
|
||||
}
|
||||
|
||||
case *Var:
|
||||
typ = obj.typ // may already be Typ[Invalid]
|
||||
if typ == nil {
|
||||
typ = Typ[Invalid]
|
||||
if x.mode != invalid {
|
||||
typ = x.typ
|
||||
if isUntyped(typ) {
|
||||
// convert untyped types to default types
|
||||
if typ == Typ[UntypedNil] {
|
||||
check.errorf(x.pos(), "use of untyped nil")
|
||||
typ = Typ[Invalid]
|
||||
} else {
|
||||
typ = defaultType(typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.typ = typ
|
||||
}
|
||||
}
|
||||
|
||||
// nothing else to check if we don't have a valid lhs or rhs
|
||||
if typ == Typ[Invalid] || x.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if !check.assignment(x, typ) {
|
||||
if x.mode != invalid {
|
||||
if x.typ != Typ[Invalid] && typ != Typ[Invalid] {
|
||||
check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", obj.Name(), typ, x)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// for constants, set their value
|
||||
if obj, _ := obj.(*Const); obj != nil {
|
||||
obj.val = exact.MakeUnknown() // failure case: we don't know the constant value
|
||||
if x.mode == constant {
|
||||
if isConstType(x.typ) {
|
||||
obj.val = x.val
|
||||
} else if x.typ != Typ[Invalid] {
|
||||
check.errorf(x.pos(), "%s has invalid constant type", x)
|
||||
}
|
||||
} else if x.mode != invalid {
|
||||
check.errorf(x.pos(), "%s is not constant", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) {
|
||||
assert(len(lhs) > 0)
|
||||
|
||||
const decl = false
|
||||
|
||||
// If the lhs and rhs have corresponding expressions, treat each
|
||||
// matching pair as an individual pair.
|
||||
if len(lhs) == len(rhs) {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e, nil, -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
check.assign(lhs[i], &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, the rhs must be a single expression (possibly
|
||||
// a function call returning multiple values, or a comma-ok
|
||||
// expression).
|
||||
if len(rhs) == 1 {
|
||||
// len(lhs) > 1
|
||||
// Start with rhs so we have expression types
|
||||
// for declarations with implicit types.
|
||||
var x operand
|
||||
check.expr(&x, rhs[0], nil, -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
|
||||
// function result
|
||||
x.mode = value
|
||||
for i := 0; i < len(lhs); i++ {
|
||||
obj := t.At(i)
|
||||
x.expr = nil // TODO(gri) should do better here
|
||||
x.typ = obj.typ
|
||||
check.assign(lhs[i], &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == valueok && len(lhs) == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.assign(lhs[0], &x)
|
||||
|
||||
x.typ = Typ[UntypedBool]
|
||||
check.assign(lhs[1], &x)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs))
|
||||
|
||||
Error:
|
||||
// In case of a declaration, set all lhs types to Typ[Invalid].
|
||||
for _, obj := range lhs {
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
obj.val = exact.MakeUnknown()
|
||||
case *Var:
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
}
|
@ -14,10 +14,12 @@ import (
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
// debugging support
|
||||
// debugging/development support
|
||||
const (
|
||||
debug = true // leave on during development
|
||||
trace = false // turn on for detailed type resolution traces
|
||||
// TODO(gri) remove this flag and clean up code under the assumption that resolve == true.
|
||||
resolve = false // if set, resolve all identifiers in the type checker (don't use ast.Objects anymore)
|
||||
)
|
||||
|
||||
// exprInfo stores type and constant value for an untyped expression.
|
||||
@ -29,9 +31,8 @@ type exprInfo struct {
|
||||
|
||||
// A checker is an instance of the type checker.
|
||||
type checker struct {
|
||||
ctxt *Context
|
||||
fset *token.FileSet
|
||||
files []*ast.File
|
||||
ctxt *Context
|
||||
fset *token.FileSet
|
||||
|
||||
// lazily initialized
|
||||
pkg *Package // current package
|
||||
@ -47,9 +48,29 @@ type checker struct {
|
||||
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
||||
funcsig *Signature // signature of currently typechecked function
|
||||
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
||||
|
||||
// these are only valid if resolve is set
|
||||
objMap map[Object]*decl // if set we are in the package-global declaration phase (otherwise all objects seen must be declared)
|
||||
topScope *Scope // topScope for lookups, non-global declarations
|
||||
}
|
||||
|
||||
func (check *checker) openScope() {
|
||||
check.topScope = &Scope{Outer: check.topScope}
|
||||
}
|
||||
|
||||
func (check *checker) closeScope() {
|
||||
check.topScope = check.topScope.Outer
|
||||
}
|
||||
|
||||
func (check *checker) register(id *ast.Ident, obj Object) {
|
||||
if resolve {
|
||||
// TODO(gri) Document how if an identifier can be registered more than once.
|
||||
if f := check.ctxt.Ident; f != nil {
|
||||
f(id, obj)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// When an expression is evaluated more than once (happens
|
||||
// in rare cases, e.g. for statement expressions, see
|
||||
// comment in stmt.go), the object has been registered
|
||||
@ -70,9 +91,21 @@ func (check *checker) register(id *ast.Ident, obj Object) {
|
||||
// uses the checker.objects map.
|
||||
//
|
||||
// TODO(gri) Once identifier resolution is done entirely by
|
||||
// the typechecker, only the idents map is needed.
|
||||
// the typechecker, only scopes are needed. Need
|
||||
// to update the comment above.
|
||||
//
|
||||
func (check *checker) lookup(ident *ast.Ident) Object {
|
||||
if resolve {
|
||||
for scope := check.topScope; scope != nil; scope = scope.Outer {
|
||||
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||
check.register(ident, obj)
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// old code
|
||||
obj := check.idents[ident]
|
||||
astObj := ident.Obj
|
||||
|
||||
@ -95,7 +128,8 @@ func (check *checker) lookup(ident *ast.Ident) Object {
|
||||
}
|
||||
|
||||
type function struct {
|
||||
obj *Func // for debugging/tracing only
|
||||
file *Scope // only valid if resolve is set
|
||||
obj *Func // for debugging/tracing only
|
||||
sig *Signature
|
||||
body *ast.BlockStmt
|
||||
}
|
||||
@ -107,11 +141,14 @@ type function struct {
|
||||
func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) {
|
||||
// functions implemented elsewhere (say in assembly) have no body
|
||||
if body != nil {
|
||||
check.funclist = append(check.funclist, function{f, sig, body})
|
||||
check.funclist = append(check.funclist, function{check.topScope, f, sig, body})
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
assert(check.lookup(ident) == nil) // identifier already declared or resolved
|
||||
check.register(ident, obj)
|
||||
if ident.Name != "_" {
|
||||
@ -126,6 +163,10 @@ func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
|
||||
}
|
||||
|
||||
func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) {
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
if len(lhs) == 0 {
|
||||
check.invalidAST(pos, "missing lhs in declaration")
|
||||
return
|
||||
@ -150,16 +191,17 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe
|
||||
break
|
||||
}
|
||||
}
|
||||
assert(l != nil)
|
||||
isConst := false
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
obj.typ = typ
|
||||
isConst = true
|
||||
case *Var:
|
||||
obj.typ = typ
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
check.assign1to1(l, r, nil, true, iota)
|
||||
check.assign1to1(l, r, nil, true, iota, isConst)
|
||||
return
|
||||
}
|
||||
|
||||
@ -190,21 +232,30 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe
|
||||
for i, e := range lhs {
|
||||
lhx[i] = e
|
||||
}
|
||||
check.assignNtoM(lhx, rhs, true, iota)
|
||||
_, isConst := obj.(*Const)
|
||||
check.assignNtoM(lhx, rhs, true, iota, isConst)
|
||||
}
|
||||
}
|
||||
|
||||
// object typechecks an object by assigning it a type.
|
||||
//
|
||||
func (check *checker) object(obj Object, cycleOk bool) {
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
if obj.Type() != nil {
|
||||
return // already checked
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
// nothing to do
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
case *Const:
|
||||
if obj.typ != nil {
|
||||
return // already checked
|
||||
}
|
||||
// The obj.Val field for constants is initialized to its respective
|
||||
// iota value (type int) by the parser.
|
||||
// If the object's type is Typ[Invalid], the object value is ignored.
|
||||
@ -214,7 +265,7 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
// know that x is a constant and has type float32, but we don't
|
||||
// have a value due to the error in the conversion).
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.Name)
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
@ -231,11 +282,8 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota))
|
||||
|
||||
case *Var:
|
||||
if obj.typ != nil {
|
||||
return // already checked
|
||||
}
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.Name)
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
@ -252,9 +300,6 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
}
|
||||
|
||||
case *TypeName:
|
||||
if obj.typ != nil {
|
||||
return // already checked
|
||||
}
|
||||
typ := &Named{obj: obj}
|
||||
obj.typ = typ // "mark" object so recursion terminates
|
||||
typ.underlying = check.typ(obj.spec.Type, cycleOk).Underlying()
|
||||
@ -265,7 +310,7 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
// struct fields must not conflict with methods
|
||||
for _, f := range t.fields {
|
||||
if m := scope.Lookup(f.Name); m != nil {
|
||||
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
|
||||
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.name, f.Name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
@ -273,7 +318,7 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
// methods cannot be associated with an interface type
|
||||
for _, m := range scope.Entries {
|
||||
recv := m.(*Func).decl.Recv.List[0].Type
|
||||
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
|
||||
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.name, obj.name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
@ -282,7 +327,7 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
for _, obj := range scope.Entries {
|
||||
m := obj.(*Func)
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(m.decl.Recv, false)
|
||||
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
|
||||
sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||
m.typ = sig
|
||||
assert(methods.Insert(obj) == nil)
|
||||
@ -293,9 +338,6 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
}
|
||||
|
||||
case *Func:
|
||||
if obj.typ != nil {
|
||||
return // already checked
|
||||
}
|
||||
fdecl := obj.decl
|
||||
// methods are typechecked when their receivers are typechecked
|
||||
if fdecl.Recv == nil {
|
||||
@ -318,6 +360,10 @@ func (check *checker) object(obj Object, cycleOk bool) {
|
||||
// for constant declarations without explicit initialization expressions.
|
||||
//
|
||||
func (check *checker) assocInitvals(decl *ast.GenDecl) {
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
var last *ast.ValueSpec
|
||||
for _, s := range decl.Specs {
|
||||
if s, ok := s.(*ast.ValueSpec); ok {
|
||||
@ -337,6 +383,10 @@ func (check *checker) assocInitvals(decl *ast.GenDecl) {
|
||||
// receiver base type. meth.Recv must exist.
|
||||
//
|
||||
func (check *checker) assocMethod(meth *ast.FuncDecl) {
|
||||
if resolve {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
// The receiver type is one of the following (enforced by parser):
|
||||
// - *ast.Ident
|
||||
// - *ast.StarExpr{*ast.Ident}
|
||||
@ -378,6 +428,10 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) {
|
||||
}
|
||||
|
||||
func (check *checker) decl(decl ast.Decl) {
|
||||
if resolve {
|
||||
unreachable() // during top-level type-checking
|
||||
}
|
||||
|
||||
switch d := decl.(type) {
|
||||
case *ast.BadDecl:
|
||||
// ignore
|
||||
@ -419,12 +473,17 @@ func (check *checker) decl(decl ast.Decl) {
|
||||
type bailout struct{}
|
||||
|
||||
func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) {
|
||||
pkg = &Package{
|
||||
path: path,
|
||||
scope: &Scope{Outer: Universe},
|
||||
imports: make(map[string]*Package),
|
||||
}
|
||||
|
||||
// initialize checker
|
||||
check := checker{
|
||||
ctxt: ctxt,
|
||||
fset: fset,
|
||||
files: files,
|
||||
pkg: &Package{path: path, scope: &Scope{Outer: Universe}, imports: make(map[string]*Package)},
|
||||
pkg: pkg,
|
||||
idents: make(map[*ast.Ident]Object),
|
||||
objects: make(map[*ast.Object]Object),
|
||||
initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
|
||||
@ -433,9 +492,8 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File)
|
||||
untyped: make(map[ast.Expr]exprInfo),
|
||||
}
|
||||
|
||||
// set results and handle panics
|
||||
// handle panics
|
||||
defer func() {
|
||||
pkg = check.pkg
|
||||
switch p := recover().(type) {
|
||||
case nil, bailout:
|
||||
// normal return or early exit
|
||||
@ -451,22 +509,45 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File)
|
||||
}
|
||||
}()
|
||||
|
||||
// determine package name and files
|
||||
i := 0
|
||||
for _, file := range files {
|
||||
switch name := file.Name.Name; pkg.name {
|
||||
case "":
|
||||
pkg.name = name
|
||||
fallthrough
|
||||
case name:
|
||||
files[i] = file
|
||||
i++
|
||||
default:
|
||||
check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
|
||||
// ignore this file
|
||||
}
|
||||
}
|
||||
files = files[:i]
|
||||
|
||||
// resolve identifiers
|
||||
imp := ctxt.Import
|
||||
if imp == nil {
|
||||
imp = GcImport
|
||||
}
|
||||
methods := check.resolve(imp)
|
||||
|
||||
// associate methods with types
|
||||
for _, m := range methods {
|
||||
check.assocMethod(m)
|
||||
}
|
||||
if resolve {
|
||||
check.resolveFiles(files, imp)
|
||||
|
||||
// typecheck all declarations
|
||||
for _, f := range check.files {
|
||||
for _, d := range f.Decls {
|
||||
check.decl(d)
|
||||
} else {
|
||||
methods := check.resolve(imp, files)
|
||||
|
||||
// associate methods with types
|
||||
for _, m := range methods {
|
||||
check.assocMethod(m)
|
||||
}
|
||||
|
||||
// typecheck all declarations
|
||||
for _, f := range files {
|
||||
for _, d := range f.Decls {
|
||||
check.decl(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,6 +562,7 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File)
|
||||
}
|
||||
fmt.Println("---", s)
|
||||
}
|
||||
check.topScope = f.sig.scope // open the function scope
|
||||
check.funcsig = f.sig
|
||||
check.stmtList(f.body.List)
|
||||
if f.sig.results.Len() > 0 && f.body != nil && !check.isTerminating(f.body, "") {
|
||||
|
@ -83,7 +83,11 @@ func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File,
|
||||
var files []*ast.File
|
||||
var errlist []error
|
||||
for _, filename := range filenames {
|
||||
file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors)
|
||||
mode := parser.AllErrors
|
||||
if !resolve {
|
||||
mode |= parser.DeclarationErrors
|
||||
}
|
||||
file, err := parser.ParseFile(fset, filename, nil, mode)
|
||||
if file == nil {
|
||||
t.Fatalf("%s: could not parse file %s", testname, filename)
|
||||
}
|
||||
@ -237,6 +241,10 @@ func TestCheck(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
if resolve {
|
||||
fmt.Println("*** Running new code: Identifiers are resolved by type checker. ***")
|
||||
}
|
||||
|
||||
// Otherwise, run all the tests.
|
||||
for _, test := range tests {
|
||||
checkFiles(t, test.name, test.files)
|
||||
|
@ -53,6 +53,8 @@ func (check *checker) untrace(format string, args ...interface{}) {
|
||||
func (check *checker) formatMsg(format string, args []interface{}) string {
|
||||
for i, arg := range args {
|
||||
switch a := arg.(type) {
|
||||
case nil:
|
||||
args[i] = "<nil>"
|
||||
case operand:
|
||||
panic("internal error: should always pass *operand")
|
||||
case *operand:
|
||||
@ -63,6 +65,8 @@ func (check *checker) formatMsg(format string, args []interface{}) string {
|
||||
args[i] = exprString(a)
|
||||
case Type:
|
||||
args[i] = typeString(a)
|
||||
case Object:
|
||||
args[i] = fmt.Sprintf("%s (%T)", a.Name(), a)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(format, args...)
|
||||
|
102
go/types/expr.go
102
go/types/expr.go
@ -7,6 +7,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
@ -70,7 +71,7 @@ on the way down in updateExprType, or at the end of the type checker run,
|
||||
if present the Context.Expr method is invoked to notify a go/types client.
|
||||
*/
|
||||
|
||||
func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
|
||||
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
@ -92,15 +93,22 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
|
||||
if len(field.Names) > 0 {
|
||||
// named parameter
|
||||
for _, name := range field.Names {
|
||||
par := check.lookup(name).(*Var)
|
||||
par.typ = typ
|
||||
var par *Var
|
||||
if resolve {
|
||||
par = &Var{pos: name.Pos(), pkg: check.pkg, name: name.Name, typ: typ, decl: field}
|
||||
check.declare(scope, par)
|
||||
check.register(name, par)
|
||||
} else {
|
||||
par = check.lookup(name).(*Var)
|
||||
par.typ = typ
|
||||
}
|
||||
last = par
|
||||
copy := *par
|
||||
params = append(params, ©)
|
||||
}
|
||||
} else {
|
||||
// anonymous parameter
|
||||
par := &Var{typ: typ}
|
||||
par := &Var{pkg: check.pkg, typ: typ, decl: field}
|
||||
last = nil // not accessible inside function
|
||||
params = append(params, par)
|
||||
}
|
||||
@ -114,7 +122,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) {
|
||||
func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) (methods ObjSet) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
@ -131,10 +139,20 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) {
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
// TODO(gri) provide correct declaration info
|
||||
obj := &Func{check.pkg, name.Name, sig, nil}
|
||||
if alt := methods.Insert(obj); alt != nil {
|
||||
check.errorf(list.Pos(), "multiple methods named %s", name.Name)
|
||||
// TODO(gri) provide correct declaration info and scope
|
||||
// TODO(gri) with unified scopes (Scope, ObjSet) this can become
|
||||
// just a normal declaration
|
||||
obj := &Func{name.Pos(), check.pkg, nil, name.Name, sig, nil}
|
||||
if alt := methods.Insert(obj); alt != nil && resolve {
|
||||
// if !resolve, the parser complains
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
|
||||
}
|
||||
check.errorf(obj.Pos(), "%s redeclared in this block%s", obj.Name(), prevDecl)
|
||||
}
|
||||
if resolve {
|
||||
check.register(name, obj)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -167,14 +185,14 @@ func (check *checker) tag(t *ast.BasicLit) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
|
||||
func (check *checker) collectFields(scope *Scope, list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var typ Type // current field typ
|
||||
var tag string // current field tag
|
||||
add := func(name string, isAnonymous bool) {
|
||||
add := func(field *ast.Field, ident *ast.Ident, name string, isAnonymous bool, pos token.Pos) {
|
||||
// TODO(gri): rethink this - at the moment we allocate only a prefix
|
||||
if tag != "" && tags == nil {
|
||||
tags = make([]string, len(fields))
|
||||
@ -182,6 +200,13 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
||||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
if resolve {
|
||||
fld := &Var{pos: pos, pkg: check.pkg, name: name, typ: typ, decl: field}
|
||||
check.declare(scope, fld)
|
||||
if resolve && ident != nil {
|
||||
check.register(ident, fld)
|
||||
}
|
||||
}
|
||||
fields = append(fields, &Field{check.pkg, name, typ, isAnonymous})
|
||||
}
|
||||
|
||||
@ -191,18 +216,19 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
||||
if len(f.Names) > 0 {
|
||||
// named fields
|
||||
for _, name := range f.Names {
|
||||
add(name.Name, false)
|
||||
add(f, name, name.Name, false, name.Pos())
|
||||
}
|
||||
} else {
|
||||
// anonymous field
|
||||
pos := f.Type.Pos()
|
||||
switch t := typ.Deref().(type) {
|
||||
case *Basic:
|
||||
add(t.name, true)
|
||||
add(f, nil, t.name, true, pos)
|
||||
case *Named:
|
||||
add(t.obj.name, true)
|
||||
add(f, nil, t.obj.name, true, pos)
|
||||
default:
|
||||
if typ != Typ[Invalid] {
|
||||
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
|
||||
check.invalidAST(pos, "anonymous field type %s must be named", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -870,6 +896,9 @@ func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok b
|
||||
// compositeLitKey resolves unresolved composite literal keys.
|
||||
// For details, see comment in go/parser/parser.go, method parseElement.
|
||||
func (check *checker) compositeLitKey(key ast.Expr) {
|
||||
if resolve {
|
||||
return
|
||||
}
|
||||
if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
|
||||
if obj := check.pkg.scope.Lookup(ident.Name); obj != nil {
|
||||
check.register(ident, obj)
|
||||
@ -1043,15 +1072,37 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
goto Error // error was reported before
|
||||
|
||||
case *ast.Ident:
|
||||
if e.Name == "_" {
|
||||
if !resolve && e.Name == "_" {
|
||||
check.invalidOp(e.Pos(), "cannot use _ as value or type")
|
||||
goto Error
|
||||
}
|
||||
obj := check.lookup(e)
|
||||
if obj == nil {
|
||||
if resolve {
|
||||
if e.Name == "_" {
|
||||
check.invalidOp(e.Pos(), "cannot use _ as value or type")
|
||||
} else {
|
||||
// TODO(gri) anonymous function result parameters are
|
||||
// not declared - this causes trouble when
|
||||
// type-checking return statements
|
||||
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
|
||||
}
|
||||
}
|
||||
goto Error // error was reported before
|
||||
}
|
||||
check.object(obj, cycleOk)
|
||||
if resolve {
|
||||
typ := obj.Type()
|
||||
if check.objMap == nil {
|
||||
if typ == nil {
|
||||
check.dump("%s: %s not declared?", e.Pos(), e)
|
||||
}
|
||||
assert(typ != nil)
|
||||
} else if typ == nil {
|
||||
check.declareObject(obj, cycleOk)
|
||||
}
|
||||
} else {
|
||||
check.object(obj, cycleOk)
|
||||
}
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.Name)
|
||||
@ -1073,7 +1124,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
case *TypeName:
|
||||
x.mode = typexpr
|
||||
if !cycleOk && obj.typ.Underlying() == nil {
|
||||
check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
|
||||
check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.name)
|
||||
x.expr = e
|
||||
x.typ = Typ[Invalid]
|
||||
return // don't goto Error - need x.mode == typexpr
|
||||
@ -1675,19 +1726,22 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
x.mode = typexpr
|
||||
|
||||
case *ast.StructType:
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
fields, tags := check.collectFields(scope, e.Fields, cycleOk)
|
||||
x.mode = typexpr
|
||||
fields, tags := check.collectFields(e.Fields, cycleOk)
|
||||
x.typ = &Struct{fields: fields, tags: tags}
|
||||
x.typ = &Struct{scope: scope, fields: fields, tags: tags}
|
||||
|
||||
case *ast.FuncType:
|
||||
params, isVariadic := check.collectParams(e.Params, true)
|
||||
results, _ := check.collectParams(e.Results, false)
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
params, isVariadic := check.collectParams(scope, e.Params, true)
|
||||
results, _ := check.collectParams(scope, e.Results, false)
|
||||
x.mode = typexpr
|
||||
x.typ = &Signature{recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
|
||||
x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
x.mode = typexpr
|
||||
x.typ = &Interface{methods: check.collectMethods(e.Methods)}
|
||||
x.typ = &Interface{methods: check.collectMethods(scope, e.Methods)}
|
||||
|
||||
case *ast.MapType:
|
||||
x.mode = typexpr
|
||||
|
@ -590,7 +590,7 @@ func (p *gcParser) parseInterfaceType() Type {
|
||||
}
|
||||
pkg, name := p.parseName(true)
|
||||
sig := p.parseSignature()
|
||||
if alt := methods.Insert(&Func{pkg, name, sig, nil}); alt != nil {
|
||||
if alt := methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil}); alt != nil {
|
||||
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
|
||||
}
|
||||
}
|
||||
@ -879,7 +879,7 @@ func (p *gcParser) parseMethodDecl() {
|
||||
|
||||
// add method to type unless type was imported before
|
||||
// and method exists already
|
||||
base.methods.Insert(&Func{pkg, name, sig, nil})
|
||||
base.methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil})
|
||||
}
|
||||
|
||||
// FuncDecl = "func" ExportedName Func .
|
||||
|
@ -11,23 +11,29 @@ import (
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
// TODO(gri) provide a complete set of factory functions!
|
||||
|
||||
// An Object describes a named language entity such as a package,
|
||||
// constant, type, variable, function (incl. methods), or label.
|
||||
// All objects implement the Object interface.
|
||||
//
|
||||
type Object interface {
|
||||
Pkg() *Package // nil for objects in the Universe scope
|
||||
Scope() *Scope
|
||||
Outer() *Scope // the scope in which this object is declared
|
||||
Name() string
|
||||
Type() Type
|
||||
Pos() token.Pos
|
||||
Pos() token.Pos // position of object identifier in declaration
|
||||
// TODO(gri) provide String method!
|
||||
|
||||
setOuter(*Scope)
|
||||
}
|
||||
|
||||
// A Package represents the contents (objects) of a Go package.
|
||||
type Package struct {
|
||||
pos token.Pos // position of package import path or local package identifier, if present
|
||||
name string
|
||||
path string // import path, "" for current (non-imported) package
|
||||
path string // import path, "" for current (non-imported) package
|
||||
outer *Scope
|
||||
scope *Scope // package-level scope
|
||||
imports map[string]*Package // map of import paths to imported packages
|
||||
complete bool // if set, this package was imported completely
|
||||
@ -40,35 +46,46 @@ func NewPackage(path, name string) *Package {
|
||||
}
|
||||
|
||||
func (obj *Package) Pkg() *Package { return obj }
|
||||
func (obj *Package) Outer() *Scope { return obj.outer }
|
||||
func (obj *Package) Scope() *Scope { return obj.scope }
|
||||
func (obj *Package) Name() string { return obj.name }
|
||||
func (obj *Package) Type() Type { return Typ[Invalid] }
|
||||
func (obj *Package) Pos() token.Pos {
|
||||
if obj.spec == nil {
|
||||
return token.NoPos
|
||||
if obj.pos.IsValid() {
|
||||
return obj.pos
|
||||
}
|
||||
return obj.spec.Pos()
|
||||
if obj.spec != nil {
|
||||
return obj.spec.Pos()
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
func (obj *Package) Path() string { return obj.path }
|
||||
func (obj *Package) Imports() map[string]*Package { return obj.imports }
|
||||
func (obj *Package) Complete() bool { return obj.complete }
|
||||
func (obj *Package) setOuter(*Scope) { /* don't do anything - this is the package's scope */
|
||||
}
|
||||
|
||||
// A Const represents a declared constant.
|
||||
type Const struct {
|
||||
pkg *Package
|
||||
name string
|
||||
typ Type
|
||||
val exact.Value
|
||||
pos token.Pos // position of identifier in constant declaration
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type
|
||||
val exact.Value
|
||||
|
||||
visited bool // for initialization cycle detection
|
||||
spec *ast.ValueSpec
|
||||
}
|
||||
|
||||
func (obj *Const) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Const) Scope() *Scope { panic("unimplemented") }
|
||||
func (obj *Const) Outer() *Scope { return obj.outer }
|
||||
func (obj *Const) Name() string { return obj.name }
|
||||
func (obj *Const) Type() Type { return obj.typ }
|
||||
func (obj *Const) Pos() token.Pos {
|
||||
if obj.pos.IsValid() {
|
||||
return obj.pos
|
||||
}
|
||||
if obj.spec == nil {
|
||||
return token.NoPos
|
||||
}
|
||||
@ -79,51 +96,63 @@ func (obj *Const) Pos() token.Pos {
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
func (obj *Const) Val() exact.Value { return obj.val }
|
||||
func (obj *Const) Val() exact.Value { return obj.val }
|
||||
func (obj *Const) setOuter(s *Scope) { obj.outer = s }
|
||||
|
||||
// A TypeName represents a declared type.
|
||||
type TypeName struct {
|
||||
pkg *Package
|
||||
name string
|
||||
typ Type // *Named or *Basic
|
||||
pos token.Pos // position of identifier in type declaration
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type // *Named or *Basic
|
||||
|
||||
spec *ast.TypeSpec
|
||||
}
|
||||
|
||||
func NewTypeName(pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{pkg, name, typ, nil}
|
||||
return &TypeName{token.NoPos, pkg, nil, name, typ, nil}
|
||||
}
|
||||
|
||||
func (obj *TypeName) Pkg() *Package { return obj.pkg }
|
||||
func (obj *TypeName) Scope() *Scope { panic("unimplemented") }
|
||||
func (obj *TypeName) Outer() *Scope { return obj.outer }
|
||||
func (obj *TypeName) Name() string { return obj.name }
|
||||
func (obj *TypeName) Type() Type { return obj.typ }
|
||||
func (obj *TypeName) Pos() token.Pos {
|
||||
if obj.pos.IsValid() {
|
||||
return obj.pos
|
||||
}
|
||||
if obj.spec == nil {
|
||||
return token.NoPos
|
||||
}
|
||||
return obj.spec.Pos()
|
||||
}
|
||||
func (obj *TypeName) setOuter(s *Scope) { obj.outer = s }
|
||||
|
||||
// A Variable represents a declared variable (including function parameters and results).
|
||||
type Var struct {
|
||||
pkg *Package // nil for parameters
|
||||
name string
|
||||
typ Type
|
||||
pos token.Pos // position of identifier in variable declaration
|
||||
pkg *Package // nil for parameters
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type
|
||||
|
||||
visited bool // for initialization cycle detection
|
||||
decl interface{}
|
||||
}
|
||||
|
||||
func NewVar(pkg *Package, name string, typ Type) *Var {
|
||||
return &Var{pkg, name, typ, false, nil}
|
||||
return &Var{token.NoPos, pkg, nil, name, typ, false, nil}
|
||||
}
|
||||
|
||||
func (obj *Var) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Var) Scope() *Scope { panic("unimplemented") }
|
||||
func (obj *Var) Outer() *Scope { return obj.outer }
|
||||
func (obj *Var) Name() string { return obj.name }
|
||||
func (obj *Var) Type() Type { return obj.typ }
|
||||
func (obj *Var) Pos() token.Pos {
|
||||
if obj.pos.IsValid() {
|
||||
return obj.pos
|
||||
}
|
||||
switch d := obj.decl.(type) {
|
||||
case *ast.Field:
|
||||
for _, n := range d.Names {
|
||||
@ -146,26 +175,33 @@ func (obj *Var) Pos() token.Pos {
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
func (obj *Var) setOuter(s *Scope) { obj.outer = s }
|
||||
|
||||
// A Func represents a declared function.
|
||||
type Func struct {
|
||||
pkg *Package
|
||||
name string
|
||||
typ Type // *Signature or *Builtin
|
||||
pos token.Pos
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type // *Signature or *Builtin
|
||||
|
||||
decl *ast.FuncDecl
|
||||
}
|
||||
|
||||
func (obj *Func) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Func) Scope() *Scope { panic("unimplemented") }
|
||||
func (obj *Func) Outer() *Scope { return obj.outer }
|
||||
func (obj *Func) Name() string { return obj.name }
|
||||
func (obj *Func) Type() Type { return obj.typ }
|
||||
func (obj *Func) Pos() token.Pos {
|
||||
if obj.pos.IsValid() {
|
||||
return obj.pos
|
||||
}
|
||||
if obj.decl != nil && obj.decl.Name != nil {
|
||||
return obj.decl.Name.Pos()
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
func (obj *Func) setOuter(s *Scope) { obj.outer = s }
|
||||
|
||||
// newObj returns a new Object for a given *ast.Object.
|
||||
// It does not canonicalize them (it always returns a new one).
|
||||
|
@ -23,7 +23,7 @@ func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport t
|
||||
// for dot-imports, local declarations are declared first - swap messages
|
||||
if dotImport.IsValid() {
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s",
|
||||
check.errorf(pos, fmt.Sprintf("%s redeclared in this block by import at %s",
|
||||
obj.Name(), check.fset.Position(dotImport)))
|
||||
return
|
||||
}
|
||||
@ -50,25 +50,11 @@ func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
|
||||
func (check *checker) resolve(importer Importer, files []*ast.File) (methods []*ast.FuncDecl) {
|
||||
pkg := check.pkg
|
||||
|
||||
// complete package scope
|
||||
i := 0
|
||||
for _, file := range check.files {
|
||||
// package names must match
|
||||
switch name := file.Name.Name; {
|
||||
case pkg.name == "":
|
||||
pkg.name = name
|
||||
case name != pkg.name:
|
||||
check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
|
||||
continue // ignore this file
|
||||
}
|
||||
|
||||
// keep this file
|
||||
check.files[i] = file
|
||||
i++
|
||||
|
||||
for _, file := range files {
|
||||
// the package identifier denotes the current package
|
||||
check.register(file.Name, pkg)
|
||||
|
||||
@ -118,10 +104,9 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
|
||||
}
|
||||
}
|
||||
}
|
||||
check.files = check.files[0:i]
|
||||
|
||||
// complete file scopes with imports and resolve identifiers
|
||||
for _, file := range check.files {
|
||||
for _, file := range files {
|
||||
// build file scope by processing all imports
|
||||
importErrors := false
|
||||
fileScope := &Scope{Outer: pkg.scope}
|
||||
|
661
go/types/resolver.go
Normal file
661
go/types/resolver.go
Normal file
@ -0,0 +1,661 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
func (check *checker) declare(scope *Scope, obj Object) {
|
||||
if obj.Name() == "_" {
|
||||
obj.setOuter(scope)
|
||||
return // blank identifiers are not visible
|
||||
}
|
||||
if alt := scope.Insert(obj); alt != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
|
||||
}
|
||||
check.errorf(obj.Pos(), "%s redeclared in this block%s", obj.Name(), prevDecl)
|
||||
// TODO(gri) Instead, change this into two separate error messages (easier to handle by tools)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) declareShort(scope *Scope, list []Object) {
|
||||
n := 0 // number of new objects
|
||||
for _, obj := range list {
|
||||
if obj.Name() == "_" {
|
||||
obj.setOuter(scope)
|
||||
continue // blank identifiers are not visible
|
||||
}
|
||||
if scope.Insert(obj) == nil {
|
||||
n++ // new declaration
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
check.errorf(list[0].Pos(), "no new variables on left side of :=")
|
||||
}
|
||||
}
|
||||
|
||||
// A decl describes a package-level const, type, var, or func declaration.
|
||||
type decl struct {
|
||||
file *Scope // scope of file containing this declaration
|
||||
typ ast.Expr // type, or nil
|
||||
init ast.Expr // initialization expression, or nil
|
||||
}
|
||||
|
||||
// An mdecl describes a method declaration.
|
||||
type mdecl struct {
|
||||
file *Scope // scope of file containing this declaration
|
||||
meth *ast.FuncDecl
|
||||
}
|
||||
|
||||
// A projExpr projects the index'th value of a multi-valued expression.
|
||||
// projExpr implements ast.Expr.
|
||||
type projExpr struct {
|
||||
lhs []*Var // all variables on the lhs
|
||||
ast.Expr // rhs
|
||||
}
|
||||
|
||||
func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
pkg := check.pkg
|
||||
|
||||
// Phase 1: Pre-declare all package scope objects so that they can be found
|
||||
// when type-checking package objects.
|
||||
|
||||
var scopes []*Scope // corresponding file scope per file
|
||||
var objList []Object
|
||||
var objMap = make(map[Object]*decl)
|
||||
var methods []*mdecl
|
||||
var fileScope *Scope // current file scope, used by add
|
||||
|
||||
add := func(obj Object, typ, init ast.Expr) {
|
||||
objList = append(objList, obj)
|
||||
objMap[obj] = &decl{fileScope, typ, init}
|
||||
// TODO(gri) move check.declare call here
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
// the package identifier denotes the current package, but it is in no scope
|
||||
check.register(file.Name, pkg)
|
||||
|
||||
fileScope = &Scope{Outer: pkg.scope}
|
||||
scopes = append(scopes, fileScope)
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.BadDecl:
|
||||
// ignore
|
||||
|
||||
case *ast.GenDecl:
|
||||
var last *ast.ValueSpec // last list of const initializers seen
|
||||
for iota, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
if importer == nil {
|
||||
//importErrors = true
|
||||
continue
|
||||
}
|
||||
path, _ := strconv.Unquote(s.Path.Value)
|
||||
imp, err := importer(pkg.imports, path)
|
||||
if err != nil {
|
||||
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
||||
//importErrors = true
|
||||
continue
|
||||
}
|
||||
// TODO(gri) If a local package name != "." is provided,
|
||||
// we could proceed even if the import failed. Consider
|
||||
// adjusting the logic here a bit.
|
||||
|
||||
// local name overrides imported package name
|
||||
name := imp.name
|
||||
if s.Name != nil {
|
||||
name = s.Name.Name
|
||||
}
|
||||
|
||||
// add import to file scope
|
||||
if name == "." {
|
||||
// merge imported scope with file scope
|
||||
for _, obj := range imp.scope.Entries {
|
||||
// gcimported package scopes contain non-exported
|
||||
// objects such as types used in partially exported
|
||||
// objects - do not accept them
|
||||
if ast.IsExported(obj.Name()) {
|
||||
// Note: This will change each imported object's scope!
|
||||
// May be an issue for types aliases.
|
||||
check.declare(fileScope, obj)
|
||||
}
|
||||
}
|
||||
// TODO(gri) consider registering the "." identifier
|
||||
// if we have Context.Ident callbacks for say blank
|
||||
// (_) identifiers
|
||||
// check.register(spec.Name, pkg)
|
||||
} else if name != "_" {
|
||||
// declare imported package object in file scope
|
||||
// (do not re-use imp in the file scope but create
|
||||
// a new object instead; the Decl field is different
|
||||
// for different files)
|
||||
obj := &Package{name: name, scope: imp.scope, spec: s}
|
||||
check.declare(fileScope, obj)
|
||||
}
|
||||
|
||||
case *ast.ValueSpec:
|
||||
switch d.Tok {
|
||||
case token.CONST:
|
||||
// determine which initialization expressions to use
|
||||
if len(s.Values) > 0 {
|
||||
last = s
|
||||
}
|
||||
|
||||
// declare all constants
|
||||
for i, name := range s.Names {
|
||||
obj := &Const{pos: name.Pos(), pkg: pkg, name: name.Name, val: exact.MakeInt64(int64(iota)), spec: s}
|
||||
check.declare(pkg.scope, obj)
|
||||
check.register(name, obj)
|
||||
|
||||
var init ast.Expr
|
||||
if i < len(last.Values) {
|
||||
init = last.Values[i]
|
||||
}
|
||||
add(obj, last.Type, init)
|
||||
}
|
||||
|
||||
// arity of lhs and rhs must match
|
||||
if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 {
|
||||
switch {
|
||||
case lhs < rhs:
|
||||
// TODO(gri) once resolve is the default, use first message
|
||||
// x := s.Values[lhs]
|
||||
// check.errorf(x.Pos(), "too many initialization expressions")
|
||||
check.errorf(s.Names[0].Pos(), "assignment count mismatch")
|
||||
case lhs > rhs && rhs != 1:
|
||||
// TODO(gri) once resolve is the default, use first message
|
||||
// n := s.Names[rhs]
|
||||
// check.errorf(n.Pos(), "missing initialization expression for %s", n)
|
||||
check.errorf(s.Names[0].Pos(), "assignment count mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
case token.VAR:
|
||||
// declare all variables
|
||||
lhs := make([]*Var, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
obj := &Var{pos: name.Pos(), pkg: pkg, name: name.Name, decl: s}
|
||||
lhs[i] = obj
|
||||
check.declare(pkg.scope, obj)
|
||||
check.register(name, obj)
|
||||
|
||||
var init ast.Expr
|
||||
switch len(s.Values) {
|
||||
case len(s.Names):
|
||||
// lhs and rhs match
|
||||
init = s.Values[i]
|
||||
case 1:
|
||||
// rhs must be a multi-valued expression
|
||||
init = &projExpr{lhs, s.Values[0]}
|
||||
default:
|
||||
if i < len(s.Values) {
|
||||
init = s.Values[i]
|
||||
}
|
||||
}
|
||||
add(obj, s.Type, init)
|
||||
}
|
||||
|
||||
// arity of lhs and rhs must match
|
||||
if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 {
|
||||
switch {
|
||||
case lhs < rhs:
|
||||
// TODO(gri) once resolve is the default, use first message
|
||||
// x := s.Values[lhs]
|
||||
// check.errorf(x.Pos(), "too many initialization expressions")
|
||||
check.errorf(s.Names[0].Pos(), "assignment count mismatch")
|
||||
case lhs > rhs && rhs != 1:
|
||||
// TODO(gri) once resolve is the default, use first message
|
||||
// n := s.Names[rhs]
|
||||
// check.errorf(n.Pos(), "missing initialization expression for %s", n)
|
||||
check.errorf(s.Names[0].Pos(), "assignment count mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
|
||||
}
|
||||
|
||||
case *ast.TypeSpec:
|
||||
obj := &TypeName{pos: s.Name.Pos(), pkg: pkg, name: s.Name.Name, spec: s}
|
||||
check.declare(pkg.scope, obj)
|
||||
add(obj, s.Type, nil)
|
||||
check.register(s.Name, obj)
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if d.Recv != nil {
|
||||
// collect method
|
||||
methods = append(methods, &mdecl{fileScope, d})
|
||||
continue
|
||||
}
|
||||
obj := &Func{pos: d.Name.Pos(), pkg: pkg, name: d.Name.Name, decl: d}
|
||||
if obj.name == "init" {
|
||||
// init functions are not visible - don't declare them in package scope
|
||||
obj.outer = pkg.scope
|
||||
} else {
|
||||
check.declare(pkg.scope, obj)
|
||||
}
|
||||
check.register(d.Name, obj)
|
||||
add(obj, nil, nil)
|
||||
|
||||
default:
|
||||
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Objects in file scopes and package scopes must have different names.
|
||||
for _, scope := range scopes {
|
||||
for _, obj := range scope.Entries {
|
||||
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
|
||||
// TODO(gri) better error message
|
||||
check.errorf(alt.Pos(), "%s redeclared in this block by import of package %s", obj.Name(), obj.Pkg().Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Associate methods with types.
|
||||
// We do this after all top-level type names have been collected.
|
||||
|
||||
check.topScope = pkg.scope
|
||||
for _, meth := range methods {
|
||||
m := meth.meth
|
||||
// The receiver type must be one of the following:
|
||||
// - *ast.Ident
|
||||
// - *ast.StarExpr{*ast.Ident}
|
||||
// - *ast.BadExpr (parser error)
|
||||
typ := m.Recv.List[0].Type
|
||||
if ptr, ok := typ.(*ast.StarExpr); ok {
|
||||
typ = ptr.X
|
||||
}
|
||||
// determine receiver base type name
|
||||
ident, ok := typ.(*ast.Ident)
|
||||
if !ok {
|
||||
// Disabled for now since the parser reports this error.
|
||||
// check.errorf(typ.Pos(), "receiver base type must be an (unqualified) identifier")
|
||||
continue // ignore this method
|
||||
}
|
||||
// determine receiver base type object
|
||||
var tname *TypeName
|
||||
if obj := check.lookup(ident); obj != nil {
|
||||
obj, ok := obj.(*TypeName)
|
||||
if !ok {
|
||||
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
|
||||
continue // ignore this method
|
||||
}
|
||||
if obj.pkg != pkg {
|
||||
check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name)
|
||||
continue // ignore this method
|
||||
}
|
||||
tname = obj
|
||||
} else {
|
||||
// identifier not declared/resolved
|
||||
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
continue // ignore this method
|
||||
}
|
||||
// declare method in receiver base type scope
|
||||
scope := check.methods[tname]
|
||||
if scope == nil {
|
||||
scope = new(Scope)
|
||||
check.methods[tname] = scope
|
||||
}
|
||||
fun := &Func{pos: m.Name.Pos(), pkg: check.pkg, name: m.Name.Name, decl: m}
|
||||
check.declare(scope, fun)
|
||||
check.register(m.Name, fun)
|
||||
// HACK(gri) change method outer scope to file scope containing the declaration
|
||||
fun.outer = meth.file // remember the file scope
|
||||
}
|
||||
|
||||
// Phase 4) Typecheck all objects in objList but not function bodies.
|
||||
|
||||
check.objMap = objMap // indicate we are doing global declarations (objects may not have a type yet)
|
||||
for _, obj := range objList {
|
||||
if obj.Type() == nil {
|
||||
check.declareObject(obj, false)
|
||||
}
|
||||
}
|
||||
check.objMap = nil // done with global declarations
|
||||
|
||||
// Phase 5) Typecheck all functions.
|
||||
// - done by the caller for now
|
||||
}
|
||||
|
||||
func (check *checker) declareObject(obj Object, cycleOk bool) {
|
||||
d := check.objMap[obj]
|
||||
|
||||
// adjust file scope for current object
|
||||
oldScope := check.topScope
|
||||
check.topScope = d.file // for lookup
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
check.declareConst(obj, d.typ, d.init)
|
||||
case *Var:
|
||||
check.declareVar(obj, d.typ, d.init)
|
||||
case *TypeName:
|
||||
check.declareType(obj, d.typ, cycleOk)
|
||||
case *Func:
|
||||
check.declareFunc(obj, cycleOk)
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
check.topScope = oldScope
|
||||
}
|
||||
|
||||
func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
obj.visited = true
|
||||
iota, ok := exact.Int64Val(obj.val) // set in phase 1
|
||||
assert(ok)
|
||||
// obj.val = exact.MakeUnknown() //do we need this? should we set val to nil?
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
obj.typ = check.typ(typ, false)
|
||||
}
|
||||
|
||||
var x operand
|
||||
|
||||
if init == nil {
|
||||
// TODO(gri) enable error message once resolve is default
|
||||
// check.errorf(obj.Pos(), "missing initialization expression for %s", obj.name)
|
||||
goto Error
|
||||
}
|
||||
|
||||
check.expr(&x, init, nil, int(iota))
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
||||
check.assign(obj, &x)
|
||||
return
|
||||
|
||||
Error:
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
} else {
|
||||
obj.val = exact.MakeUnknown()
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
obj.visited = true
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
obj.typ = check.typ(typ, false)
|
||||
}
|
||||
|
||||
if init == nil {
|
||||
if typ == nil {
|
||||
// TODO(gri) enable error message once resolve is default
|
||||
// check.errorf(obj.Pos(), "missing type or initialization expression for %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// unpack projection expression, if any
|
||||
proj, multi := init.(*projExpr)
|
||||
if multi {
|
||||
init = proj.Expr
|
||||
}
|
||||
|
||||
var x operand
|
||||
check.expr(&x, init, nil, -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
||||
if multi {
|
||||
if t, ok := x.typ.(*Tuple); ok && len(proj.lhs) == t.Len() {
|
||||
// function result
|
||||
x.mode = value
|
||||
for i, lhs := range proj.lhs {
|
||||
x.expr = nil // TODO(gri) should do better here
|
||||
x.typ = t.At(i).typ
|
||||
check.assign(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == valueok && len(proj.lhs) == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.assign(proj.lhs[0], &x)
|
||||
|
||||
x.typ = Typ[UntypedBool]
|
||||
check.assign(proj.lhs[1], &x)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(gri) better error message
|
||||
check.errorf(proj.lhs[0].Pos(), "assignment count mismatch")
|
||||
goto Error
|
||||
}
|
||||
|
||||
check.assign(obj, &x)
|
||||
return
|
||||
|
||||
Error:
|
||||
// mark all involved variables so we can avoid repeated error messages
|
||||
if multi {
|
||||
for _, obj := range proj.lhs {
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.visited = true
|
||||
}
|
||||
}
|
||||
} else if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
|
||||
named := &Named{obj: obj}
|
||||
obj.typ = named // mark object so recursion terminates in case of cycles
|
||||
named.underlying = check.typ(typ, cycleOk).Underlying()
|
||||
|
||||
// typecheck associated method signatures
|
||||
if scope := check.methods[obj]; scope != nil {
|
||||
switch t := named.underlying.(type) {
|
||||
case *Struct:
|
||||
// struct fields must not conflict with methods
|
||||
for _, f := range t.fields {
|
||||
if m := scope.Lookup(f.Name); m != nil {
|
||||
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.name, f.Name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
case *Interface:
|
||||
// methods cannot be associated with an interface type
|
||||
for _, m := range scope.Entries {
|
||||
recv := m.(*Func).decl.Recv.List[0].Type
|
||||
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.name, obj.name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
// typecheck method signatures
|
||||
var methods ObjSet
|
||||
for _, obj := range scope.Entries {
|
||||
m := obj.(*Func)
|
||||
|
||||
// set the correct file scope for checking this method type
|
||||
fileScope := m.outer
|
||||
assert(fileScope != nil)
|
||||
oldScope := check.topScope
|
||||
check.topScope = fileScope
|
||||
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
|
||||
|
||||
check.topScope = oldScope // reset topScope
|
||||
|
||||
sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||
m.typ = sig
|
||||
assert(methods.Insert(obj) == nil)
|
||||
check.later(m, sig, m.decl.Body)
|
||||
}
|
||||
named.methods = methods
|
||||
delete(check.methods, obj) // we don't need this scope anymore
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) declareFunc(obj *Func, cycleOk bool) {
|
||||
fdecl := obj.decl
|
||||
// methods are typechecked when their receivers are typechecked
|
||||
// TODO(gri) there is no reason to make this a special case: receivers are simply parameters
|
||||
if fdecl.Recv == nil {
|
||||
sig := check.typ(fdecl.Type, cycleOk).(*Signature)
|
||||
if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
|
||||
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
|
||||
// ok to continue
|
||||
}
|
||||
obj.typ = sig
|
||||
check.later(obj, sig, fdecl.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) declStmt(decl ast.Decl) {
|
||||
pkg := check.pkg
|
||||
|
||||
switch d := decl.(type) {
|
||||
case *ast.BadDecl:
|
||||
// ignore
|
||||
|
||||
case *ast.GenDecl:
|
||||
var last []ast.Expr // last list of const initializers seen
|
||||
for iota, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ValueSpec:
|
||||
switch d.Tok {
|
||||
case token.CONST:
|
||||
// determine which initialization expressions to use
|
||||
if len(s.Values) > 0 {
|
||||
last = s.Values
|
||||
}
|
||||
|
||||
// declare all constants
|
||||
lhs := make([]*Const, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
obj := &Const{pos: name.Pos(), pkg: pkg, name: name.Name, val: exact.MakeInt64(int64(iota)), spec: s}
|
||||
lhs[i] = obj
|
||||
|
||||
var init ast.Expr
|
||||
if i < len(last) {
|
||||
init = last[i]
|
||||
}
|
||||
|
||||
check.declareConst(obj, s.Type, init)
|
||||
check.register(name, obj)
|
||||
}
|
||||
|
||||
// arity of lhs and rhs must match
|
||||
switch lhs, rhs := len(s.Names), len(last); {
|
||||
case lhs < rhs:
|
||||
x := last[lhs]
|
||||
check.errorf(x.Pos(), "too many initialization expressions")
|
||||
case lhs > rhs:
|
||||
n := s.Names[rhs]
|
||||
check.errorf(n.Pos(), "missing initialization expression for %s", n)
|
||||
}
|
||||
|
||||
for _, obj := range lhs {
|
||||
check.declare(check.topScope, obj)
|
||||
}
|
||||
|
||||
case token.VAR:
|
||||
// declare all variables
|
||||
lhs := make([]*Var, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
obj := &Var{pos: name.Pos(), pkg: pkg, name: name.Name, decl: s}
|
||||
lhs[i] = obj
|
||||
check.register(name, obj)
|
||||
}
|
||||
|
||||
// iterate in 2 phases because declareVar requires fully initialized lhs!
|
||||
for i, obj := range lhs {
|
||||
var init ast.Expr
|
||||
switch len(s.Values) {
|
||||
case len(s.Names):
|
||||
// lhs and rhs match
|
||||
init = s.Values[i]
|
||||
case 1:
|
||||
// rhs must be a multi-valued expression
|
||||
init = &projExpr{lhs, s.Values[0]}
|
||||
default:
|
||||
if i < len(s.Values) {
|
||||
init = s.Values[i]
|
||||
}
|
||||
}
|
||||
|
||||
check.declareVar(obj, s.Type, init)
|
||||
}
|
||||
|
||||
// arity of lhs and rhs must match
|
||||
// TODO(gri) Disabled for now to match existing test errors.
|
||||
// These error messages are better than what we have now.
|
||||
/*
|
||||
if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 {
|
||||
switch {
|
||||
case lhs < rhs:
|
||||
x := s.Values[lhs]
|
||||
check.errorf(x.Pos(), "too many initialization expressions")
|
||||
case lhs > rhs && rhs != 1:
|
||||
n := s.Names[rhs]
|
||||
check.errorf(n.Pos(), "missing initialization expression for %s", n)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for _, obj := range lhs {
|
||||
check.declare(check.topScope, obj)
|
||||
}
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
|
||||
}
|
||||
|
||||
case *ast.TypeSpec:
|
||||
obj := &TypeName{pos: s.Name.Pos(), pkg: pkg, name: s.Name.Name, spec: s}
|
||||
check.declare(check.topScope, obj)
|
||||
check.declareType(obj, s.Type, false)
|
||||
check.register(s.Name, obj)
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||
}
|
||||
}
|
@ -55,7 +55,11 @@ func TestResolveQualifiedIdents(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
var files []*ast.File
|
||||
for _, src := range sources {
|
||||
f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
|
||||
mode := parser.AllErrors
|
||||
if !resolve {
|
||||
mode |= parser.DeclarationErrors
|
||||
}
|
||||
f, err := parser.ParseFile(fset, "", src, mode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -79,9 +83,11 @@ func TestResolveQualifiedIdents(t *testing.T) {
|
||||
}
|
||||
|
||||
// check that there are no top-level unresolved identifiers
|
||||
for _, f := range files {
|
||||
for _, x := range f.Unresolved {
|
||||
t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
if !resolve {
|
||||
for _, f := range files {
|
||||
for _, x := range f.Unresolved {
|
||||
t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +119,9 @@ func TestResolveQualifiedIdents(t *testing.T) {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
switch x := n.(type) {
|
||||
case *ast.StructType:
|
||||
if resolve {
|
||||
break // nothing to do
|
||||
}
|
||||
for _, list := range x.Fields.List {
|
||||
for _, f := range list.Names {
|
||||
assert(idents[f] == nil)
|
||||
@ -120,6 +129,9 @@ func TestResolveQualifiedIdents(t *testing.T) {
|
||||
}
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
if resolve {
|
||||
break // nothing to do
|
||||
}
|
||||
for _, list := range x.Methods.List {
|
||||
for _, f := range list.Names {
|
||||
assert(idents[f] == nil)
|
||||
|
@ -38,7 +38,8 @@ func (s *Scope) Lookup(name string) Object {
|
||||
// Insert attempts to insert an object obj into scope s.
|
||||
// If s already contains an object with the same name,
|
||||
// Insert leaves s unchanged and returns that object.
|
||||
// Otherwise it inserts obj and returns nil.
|
||||
// Otherwise it inserts obj, sets the object's scope to
|
||||
// s, and returns nil.
|
||||
//
|
||||
func (s *Scope) Insert(obj Object) Object {
|
||||
name := obj.Name()
|
||||
@ -46,6 +47,7 @@ func (s *Scope) Insert(obj Object) Object {
|
||||
return alt
|
||||
}
|
||||
s.Entries = append(s.Entries, obj)
|
||||
obj.setOuter(s)
|
||||
|
||||
// If the scope size reaches a threshold, use a map for faster lookups.
|
||||
const threshold = 20
|
||||
|
@ -30,6 +30,10 @@ var (
|
||||
)
|
||||
|
||||
func TestStdlib(t *testing.T) {
|
||||
if resolve {
|
||||
fmt.Println("*** Running new code: Identifiers are resolved by type checker. ***")
|
||||
}
|
||||
|
||||
walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg"))
|
||||
if *verbose {
|
||||
fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
|
||||
@ -48,7 +52,11 @@ func typecheck(t *testing.T, path string, filenames []string) {
|
||||
// parse package files
|
||||
var files []*ast.File
|
||||
for _, filename := range filenames {
|
||||
file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors)
|
||||
mode := parser.AllErrors
|
||||
if !resolve {
|
||||
mode |= parser.DeclarationErrors
|
||||
}
|
||||
file, err := parser.ParseFile(fset, filename, nil, mode)
|
||||
if err != nil {
|
||||
// the parser error may be a list of individual errors; report them all
|
||||
if list, ok := err.(scanner.ErrorList); ok {
|
||||
|
141
go/types/stmt.go
141
go/types/stmt.go
@ -41,7 +41,9 @@ func (check *checker) assignment(x *operand, to Type) bool {
|
||||
// if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in
|
||||
// case of an error.
|
||||
//
|
||||
func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) {
|
||||
func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int, isConst bool) {
|
||||
assert(!isConst || decl)
|
||||
|
||||
// Start with rhs so we have an expression type
|
||||
// for declarations with implicit type.
|
||||
if x == nil {
|
||||
@ -68,7 +70,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
||||
|
||||
var z operand
|
||||
check.expr(&z, lhs, nil, -1)
|
||||
if z.mode == invalid {
|
||||
if z.mode == invalid || z.typ == Typ[Invalid] {
|
||||
return
|
||||
}
|
||||
|
||||
@ -91,8 +93,22 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
||||
// Determine typ of lhs: If the object doesn't have a type
|
||||
// yet, determine it from the type of x; if x is invalid,
|
||||
// set the object type to Typ[Invalid].
|
||||
var obj Object
|
||||
var typ Type
|
||||
obj := check.lookup(ident)
|
||||
|
||||
if resolve {
|
||||
if isConst {
|
||||
obj = &Const{pos: ident.Pos(), pkg: check.pkg, name: ident.Name}
|
||||
} else {
|
||||
obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name}
|
||||
}
|
||||
check.register(ident, obj)
|
||||
defer check.declare(check.topScope, obj)
|
||||
|
||||
} else {
|
||||
obj = check.lookup(ident)
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
default:
|
||||
unreachable()
|
||||
@ -165,14 +181,15 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
||||
// of the corresponding rhs expressions, or set to Typ[Invalid] in case of an error.
|
||||
// Precondition: len(lhs) > 0 .
|
||||
//
|
||||
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
||||
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isConst bool) {
|
||||
assert(len(lhs) > 0)
|
||||
assert(!isConst || decl)
|
||||
|
||||
// If the lhs and rhs have corresponding expressions, treat each
|
||||
// matching pair as an individual pair.
|
||||
if len(lhs) == len(rhs) {
|
||||
for i, e := range rhs {
|
||||
check.assign1to1(lhs[i], e, nil, decl, iota)
|
||||
check.assign1to1(lhs[i], e, nil, decl, iota, isConst)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -197,7 +214,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
||||
obj := t.At(i)
|
||||
x.expr = nil // TODO(gri) should do better here
|
||||
x.typ = obj.typ
|
||||
check.assign1to1(lhs[i], nil, &x, decl, iota)
|
||||
check.assign1to1(lhs[i], nil, &x, decl, iota, isConst)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -205,10 +222,10 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
||||
if x.mode == valueok && len(lhs) == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
check.assign1to1(lhs[0], nil, &x, decl, iota)
|
||||
check.assign1to1(lhs[0], nil, &x, decl, iota, isConst)
|
||||
|
||||
x.typ = Typ[UntypedBool]
|
||||
check.assign1to1(lhs[1], nil, &x, decl, iota)
|
||||
check.assign1to1(lhs[1], nil, &x, decl, iota, isConst)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -224,7 +241,21 @@ Error:
|
||||
check.errorf(e.Pos(), "cannot declare %s", e)
|
||||
continue
|
||||
}
|
||||
switch obj := check.lookup(ident).(type) {
|
||||
|
||||
var obj Object
|
||||
if resolve {
|
||||
if isConst {
|
||||
obj = &Const{pos: ident.Pos(), pkg: check.pkg, name: ident.Name}
|
||||
} else {
|
||||
obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name}
|
||||
}
|
||||
defer check.declare(check.topScope, obj)
|
||||
|
||||
} else {
|
||||
obj = check.lookup(ident)
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
obj.typ = Typ[Invalid]
|
||||
case *Var:
|
||||
@ -287,6 +318,11 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// ignore
|
||||
|
||||
case *ast.DeclStmt:
|
||||
if resolve {
|
||||
check.declStmt(s.Decl)
|
||||
return
|
||||
}
|
||||
|
||||
d, _ := s.Decl.(*ast.GenDecl)
|
||||
if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) {
|
||||
check.invalidAST(token.NoPos, "const, type, or var declaration expected")
|
||||
@ -298,7 +334,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.decl(d)
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
// TODO(gri) anything to do with label itself?
|
||||
// TODO(gri) Declare label in the respectice label scope; define Label object.
|
||||
check.stmt(s.Stmt)
|
||||
|
||||
case *ast.ExprStmt:
|
||||
@ -363,7 +399,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
check.assign1to1(s.X, nil, &x, false, -1)
|
||||
check.assign1to1(s.X, nil, &x, false, -1, false)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
switch s.Tok {
|
||||
@ -372,7 +408,34 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.invalidAST(s.Pos(), "missing lhs in assignment")
|
||||
return
|
||||
}
|
||||
check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1)
|
||||
if resolve && s.Tok == token.DEFINE {
|
||||
// short variable declaration
|
||||
lhs := make([]Object, len(s.Lhs))
|
||||
for i, x := range s.Lhs {
|
||||
var obj *Var
|
||||
if ident, ok := x.(*ast.Ident); ok {
|
||||
obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name, decl: s}
|
||||
// If the variable is already declared (redeclaration in :=),
|
||||
// register the identifier with the existing variable.
|
||||
if alt := check.topScope.Lookup(ident.Name); alt != nil {
|
||||
check.register(ident, alt)
|
||||
} else {
|
||||
check.register(ident, obj)
|
||||
}
|
||||
} else {
|
||||
check.errorf(x.Pos(), "cannot declare %s", x)
|
||||
// create a dummy variable
|
||||
obj = &Var{pos: x.Pos(), pkg: check.pkg, name: "_", decl: s}
|
||||
}
|
||||
lhs[i] = obj
|
||||
}
|
||||
check.assignMulti(lhs, s.Rhs)
|
||||
check.declareShort(check.topScope, lhs) // scope starts after the assignment
|
||||
|
||||
} else {
|
||||
check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1, false)
|
||||
}
|
||||
|
||||
default:
|
||||
// assignment operations
|
||||
if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
|
||||
@ -413,7 +476,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
check.assign1to1(s.Lhs[0], nil, &x, false, -1)
|
||||
check.assign1to1(s.Lhs[0], nil, &x, false, -1, false)
|
||||
}
|
||||
|
||||
case *ast.GoStmt:
|
||||
@ -425,6 +488,22 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
case *ast.ReturnStmt:
|
||||
sig := check.funcsig
|
||||
if n := sig.results.Len(); n > 0 {
|
||||
if resolve {
|
||||
// determine if the function has named results
|
||||
named := false
|
||||
lhs := make([]Object, len(sig.results.vars))
|
||||
for i, res := range sig.results.vars {
|
||||
if res.name != "" {
|
||||
// a blank (_) result parameter is a named result
|
||||
named = true
|
||||
}
|
||||
lhs[i] = res
|
||||
}
|
||||
if len(s.Results) > 0 || !named {
|
||||
check.assignMulti(lhs, s.Results)
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO(gri) should not have to compute lhs, named every single time - clean this up
|
||||
lhs := make([]ast.Expr, n)
|
||||
named := false // if set, function has named results
|
||||
@ -440,7 +519,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
}
|
||||
if len(s.Results) > 0 || !named {
|
||||
// TODO(gri) assignNtoM should perhaps not require len(lhs) > 0
|
||||
check.assignNtoM(lhs, s.Results, false, -1)
|
||||
check.assignNtoM(lhs, s.Results, false, -1, false)
|
||||
}
|
||||
} else if len(s.Results) > 0 {
|
||||
check.errorf(s.Pos(), "no result values expected")
|
||||
@ -450,9 +529,12 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// TODO(gri) implement this
|
||||
|
||||
case *ast.BlockStmt:
|
||||
check.openScope()
|
||||
check.stmtList(s.List)
|
||||
check.closeScope()
|
||||
|
||||
case *ast.IfStmt:
|
||||
check.openScope()
|
||||
check.optionalStmt(s.Init)
|
||||
var x operand
|
||||
check.expr(&x, s.Cond, nil, -1)
|
||||
@ -461,8 +543,10 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
}
|
||||
check.stmt(s.Body)
|
||||
check.optionalStmt(s.Else)
|
||||
check.closeScope()
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
check.openScope()
|
||||
check.optionalStmt(s.Init)
|
||||
var x operand
|
||||
tag := s.Tag
|
||||
@ -523,10 +607,14 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
}
|
||||
}
|
||||
}
|
||||
check.openScope()
|
||||
check.stmtList(clause.Body)
|
||||
check.closeScope()
|
||||
}
|
||||
check.closeScope()
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
check.openScope()
|
||||
check.optionalStmt(s.Init)
|
||||
|
||||
// A type switch guard must be of the form:
|
||||
@ -552,7 +640,13 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||
return
|
||||
}
|
||||
lhs = check.lookup(ident).(*Var)
|
||||
if resolve {
|
||||
// TODO(gri) in the future, create one of these for each block with the correct type!
|
||||
lhs = &Var{pkg: check.pkg, name: ident.Name}
|
||||
check.register(ident, lhs)
|
||||
} else {
|
||||
lhs = check.lookup(ident).(*Var)
|
||||
}
|
||||
rhs = guard.Rhs[0]
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||
@ -576,6 +670,10 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
return
|
||||
}
|
||||
|
||||
if resolve && lhs != nil {
|
||||
check.declare(check.topScope, lhs)
|
||||
}
|
||||
|
||||
check.multipleDefaults(s.Body.List)
|
||||
for _, s := range s.Body.List {
|
||||
clause, _ := s.(*ast.CaseClause)
|
||||
@ -608,7 +706,9 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
}
|
||||
lhs.typ = typ
|
||||
}
|
||||
check.openScope()
|
||||
check.stmtList(clause.Body)
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
// There is only one object (lhs) associated with a lhs identifier, but that object
|
||||
@ -617,6 +717,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if lhs != nil {
|
||||
lhs.typ = x.typ
|
||||
}
|
||||
check.closeScope()
|
||||
|
||||
case *ast.SelectStmt:
|
||||
check.multipleDefaults(s.Body.List)
|
||||
@ -625,11 +726,14 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if clause == nil {
|
||||
continue // error reported before
|
||||
}
|
||||
check.openScope()
|
||||
check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
|
||||
check.stmtList(clause.Body)
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
case *ast.ForStmt:
|
||||
check.openScope()
|
||||
check.optionalStmt(s.Init)
|
||||
if s.Cond != nil {
|
||||
var x operand
|
||||
@ -640,8 +744,10 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
}
|
||||
check.optionalStmt(s.Post)
|
||||
check.stmt(s.Body)
|
||||
check.closeScope()
|
||||
|
||||
case *ast.RangeStmt:
|
||||
check.openScope()
|
||||
// check expression to iterate over
|
||||
decl := s.Tok == token.DEFINE
|
||||
var x operand
|
||||
@ -706,7 +812,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if s.Key != nil {
|
||||
x.typ = key
|
||||
x.expr = s.Key
|
||||
check.assign1to1(s.Key, nil, &x, decl, -1)
|
||||
check.assign1to1(s.Key, nil, &x, decl, -1, false)
|
||||
} else {
|
||||
check.invalidAST(s.Pos(), "range clause requires index iteration variable")
|
||||
// ok to continue
|
||||
@ -714,10 +820,11 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if s.Value != nil {
|
||||
x.typ = val
|
||||
x.expr = s.Value
|
||||
check.assign1to1(s.Value, nil, &x, decl, -1)
|
||||
check.assign1to1(s.Value, nil, &x, decl, -1, false)
|
||||
}
|
||||
|
||||
check.stmt(s.Body)
|
||||
check.closeScope()
|
||||
|
||||
default:
|
||||
check.errorf(s.Pos(), "invalid statement")
|
||||
|
3
go/types/testdata/const0.src
vendored
3
go/types/testdata/const0.src
vendored
@ -42,6 +42,7 @@ const (
|
||||
ui16 = ui2 & ui3
|
||||
ui17 = ui2 | ui3
|
||||
ui18 = ui2 ^ ui3
|
||||
ui19 = 1 /* ERROR "invalid operation" */ % 1.0
|
||||
|
||||
// floating point values
|
||||
uf0 = 0.
|
||||
@ -179,6 +180,8 @@ const (
|
||||
const (
|
||||
a1, a2, a3 = 7, 3.1415926, "foo"
|
||||
b1, b2, b3 = b3, b1, 42
|
||||
c1 /* ERROR "assignment count mismatch" */, c2, c3 = 1, 2
|
||||
d1 /* ERROR "assignment count mismatch" */, d2, d3 = 1, 2, 3, 4
|
||||
_p0 = assert(a1 == 7)
|
||||
_p1 = assert(a2 == 3.1415926)
|
||||
_p2 = assert(a3 == "foo")
|
||||
|
7
go/types/testdata/decls0.src
vendored
7
go/types/testdata/decls0.src
vendored
@ -21,7 +21,7 @@ type flag int
|
||||
type _ reflect /* ERROR "not exported" */ .flag
|
||||
|
||||
// dot-imported exported objects may conflict with local objects
|
||||
type Value /* ERROR "redeclared in this block by dot-import" */ struct{}
|
||||
type Value /* ERROR "redeclared in this block by import" */ struct{}
|
||||
|
||||
const pi = 3.1415
|
||||
|
||||
@ -74,7 +74,8 @@ type (
|
||||
// is done in the parser and the other one in the type checker. We cannot
|
||||
// easily avoid this w/o losing the check or functionality. This will be
|
||||
// fixed once all resolution is done in the type checker.
|
||||
a /* ERROR "redeclared" */ /* ERROR "redeclared" */ int
|
||||
// TODO(gri) Disabled for now since this needs to work with the old and new resolver.
|
||||
// a /* ERROR "redeclared" */ /* ERROR "redeclared" */ int
|
||||
|
||||
// where the cycle error appears depends on the
|
||||
// order in which declarations are processed
|
||||
@ -142,7 +143,7 @@ type (
|
||||
I2 interface {
|
||||
m1()
|
||||
}
|
||||
I3 interface { /* ERROR "multiple methods named m1" */
|
||||
I3 interface {
|
||||
m1()
|
||||
m1 /* ERROR "redeclared" */ ()
|
||||
}
|
||||
|
2
go/types/testdata/decls1.src
vendored
2
go/types/testdata/decls1.src
vendored
@ -110,7 +110,7 @@ func f1(a /* ERROR "not a type" */) {}
|
||||
func f2(a, b, c d /* ERROR "not a type" */) {}
|
||||
|
||||
func f3() int { return 0 }
|
||||
func f4() a /* ERROR "not a type" */ { return 0 /* ERROR "cannot convert" */ }
|
||||
func f4() a /* ERROR "not a type" */ { return 0 }
|
||||
func f5() (a, b, c d /* ERROR "not a type" */) { return }
|
||||
|
||||
func f6(a, b, c int) complex128 { return 0 }
|
||||
|
6
go/types/testdata/decls2a.src
vendored
6
go/types/testdata/decls2a.src
vendored
@ -61,9 +61,9 @@ func (T5 /* ERROR "invalid receiver" */) m2() {}
|
||||
|
||||
// Methods associated with non-local or unnamed types.
|
||||
func (int /* ERROR "non-local type" */ ) m() {}
|
||||
func ([ /* ERROR "expected" */ ]int) m() {}
|
||||
func (time /* ERROR "expected" */ .Time) m() {}
|
||||
func (x interface /* ERROR "expected" */ {}) m() {}
|
||||
func ([ /* ERROR "identifier" */ ]int) m() {}
|
||||
func (time /* ERROR "identifier" */ .Time) m() {}
|
||||
func (x interface /* ERROR "identifier" */ {}) m() {}
|
||||
|
||||
// Double declarations across package files
|
||||
const c_double = 0
|
||||
|
3
go/types/testdata/decls2b.src
vendored
3
go/types/testdata/decls2b.src
vendored
@ -6,9 +6,12 @@
|
||||
|
||||
package decls2
|
||||
|
||||
import "io"
|
||||
|
||||
const pi = 3.1415
|
||||
|
||||
func (T1) m /* ERROR "redeclared" */ () {}
|
||||
func (T2) m(io.Writer) {}
|
||||
|
||||
type T3 struct {
|
||||
f *T3
|
||||
|
@ -6,6 +6,9 @@ package types
|
||||
|
||||
import "go/ast"
|
||||
|
||||
// TODO(gri) Separate struct fields below into transient (used during type checking only)
|
||||
// and permanent fields.
|
||||
|
||||
// A Type represents a type of Go.
|
||||
// All types implement the Type interface.
|
||||
type Type interface {
|
||||
@ -137,13 +140,14 @@ type Field struct {
|
||||
|
||||
// A Struct represents a struct type.
|
||||
type Struct struct {
|
||||
scope *Scope
|
||||
fields []*Field
|
||||
tags []string // field tags; nil of there are no tags
|
||||
offsets []int64 // field offsets in bytes, lazily computed
|
||||
}
|
||||
|
||||
func NewStruct(fields []*Field, tags []string) *Struct {
|
||||
return &Struct{fields: fields, tags: tags}
|
||||
return &Struct{scope: nil, fields: fields, tags: tags}
|
||||
}
|
||||
|
||||
func (s *Struct) NumFields() int { return len(s.fields) }
|
||||
@ -230,6 +234,7 @@ func (t *Tuple) ForEach(f func(*Var)) {
|
||||
|
||||
// A Signature represents a (non-builtin) function type.
|
||||
type Signature struct {
|
||||
scope *Scope // function scope
|
||||
recv *Var // nil if not a method
|
||||
params *Tuple // (incoming) parameters from left to right; or nil
|
||||
results *Tuple // (outgoing) results from left to right; or nil
|
||||
@ -240,7 +245,7 @@ type Signature struct {
|
||||
// and results, either of which may be nil. If isVariadic is set, the function
|
||||
// is variadic.
|
||||
func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature {
|
||||
return &Signature{recv, params, results, isVariadic}
|
||||
return &Signature{nil, recv, params, results, isVariadic}
|
||||
}
|
||||
|
||||
// Recv returns the receiver of signature s, or nil.
|
||||
|
@ -16,7 +16,11 @@ import (
|
||||
const filename = "<src>"
|
||||
|
||||
func makePkg(t *testing.T, src string) (*Package, error) {
|
||||
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||
mode := parser.AllErrors
|
||||
if !resolve {
|
||||
mode |= parser.DeclarationErrors
|
||||
}
|
||||
file, err := parser.ParseFile(fset, filename, src, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -154,7 +158,11 @@ var testExprs = []testEntry{
|
||||
func TestExprs(t *testing.T) {
|
||||
for _, test := range testExprs {
|
||||
src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })"
|
||||
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||
mode := parser.AllErrors
|
||||
if !resolve {
|
||||
mode |= parser.DeclarationErrors
|
||||
}
|
||||
file, err := parser.ParseFile(fset, filename, src, mode)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %s", src, err)
|
||||
continue
|
||||
|
@ -8,6 +8,7 @@ package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
@ -102,7 +103,7 @@ func init() {
|
||||
// Error has a nil package in its qualified name since it is in no package
|
||||
var methods ObjSet
|
||||
sig := &Signature{results: NewTuple(&Var{name: "", typ: Typ[String]})}
|
||||
methods.Insert(&Func{nil, "Error", sig, nil})
|
||||
methods.Insert(&Func{token.NoPos, nil, nil, "Error", sig, nil})
|
||||
def(&TypeName{name: "error", typ: &Named{underlying: &Interface{methods: methods}}})
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user