1
0
mirror of https://github.com/golang/go synced 2024-10-01 09:38:36 -06:00

go.tools/go/types: print cyles of recursive type declarations

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/59140043
This commit is contained in:
Robert Griesemer 2014-02-03 10:34:06 -08:00
parent 285f6fe2f6
commit ed1b894ade
4 changed files with 34 additions and 30 deletions

View File

@ -96,6 +96,9 @@ type Config struct {
// If Error != nil, it is called with each error found // If Error != nil, it is called with each error found
// during type checking; err has dynamic type Error. // during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types
// involved in an invalid recursive type declaration) have
// error strings that start with a '\t' character.
Error func(err error) Error func(err error)
// If Import != nil, it is called for each imported package. // If Import != nil, it is called for each imported package.

View File

@ -32,8 +32,8 @@ func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) {
} }
// objDecl type-checks the declaration of obj in its respective file scope. // objDecl type-checks the declaration of obj in its respective file scope.
// See typeDecl for the details on def and cycleOk. // See check.typ for the details on def and path.
func (check *checker) objDecl(obj Object, def *Named, cycle []*TypeName) { func (check *checker) objDecl(obj Object, def *Named, path []*TypeName) {
if obj.Type() != nil { if obj.Type() != nil {
return // already checked - nothing to do return // already checked - nothing to do
} }
@ -71,7 +71,7 @@ func (check *checker) objDecl(obj Object, def *Named, cycle []*TypeName) {
check.decl = d // new package-level var decl check.decl = d // new package-level var decl
check.varDecl(obj, d.lhs, d.typ, d.init) check.varDecl(obj, d.lhs, d.typ, d.init)
case *TypeName: case *TypeName:
check.typeDecl(obj, d.typ, def, cycle) check.typeDecl(obj, d.typ, def, path)
case *Func: case *Func:
check.funcDecl(obj, d) check.funcDecl(obj, d)
default: default:
@ -86,8 +86,7 @@ func (check *checker) objDecl(obj Object, def *Named, cycle []*TypeName) {
func (check *checker) constDecl(obj *Const, typ, init ast.Expr) { func (check *checker) constDecl(obj *Const, typ, init ast.Expr) {
assert(obj.typ == nil) assert(obj.typ == nil)
// TODO(gri) consider using the same cycle detection as for types // TODO(gri) Instead of this code we should rely on initialization cycle detection.
// so that we can print the actual cycle in case of an error
if obj.visited { 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] obj.typ = Typ[Invalid]
@ -121,12 +120,10 @@ func (check *checker) constDecl(obj *Const, typ, init ast.Expr) {
check.iota = nil check.iota = nil
} }
// TODO(gri) document arguments
func (check *checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { func (check *checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
assert(obj.typ == nil) assert(obj.typ == nil)
// TODO(gri) consider using the same cycle detection as for types // TODO(gri) Instead of this code we should rely on initialization cycle detection.
// so that we can print the actual cycle in case of an error
if obj.visited { 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] obj.typ = Typ[Invalid]
@ -195,7 +192,7 @@ func (n *Named) setUnderlying(typ Type) {
} }
} }
func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycle []*TypeName) { func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
assert(obj.typ == nil) assert(obj.typ == nil)
// type declarations cannot use iota // type declarations cannot use iota
@ -206,7 +203,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycle []
obj.typ = named // make sure recursive type declarations terminate obj.typ = named // make sure recursive type declarations terminate
// determine underlying type of named // determine underlying type of named
check.typ(typ, named, append(cycle, obj)) check.typ(typ, named, append(path, obj))
// The underlying type of named may be itself a named type that is // The underlying type of named may be itself a named type that is
// incomplete: // incomplete:

View File

@ -511,7 +511,7 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
} }
obj = cycle[i] obj = cycle[i]
} }
check.errorf(obj.Pos(), "\t%s (cycle start)", obj.Name()) check.errorf(obj.Pos(), "\t%s", obj.Name())
} }
init.mark = -1 // avoid further errors init.mark = -1 // avoid further errors

View File

@ -19,7 +19,7 @@ import (
// If an error occurred, x.mode is set to invalid. // If an error occurred, x.mode is set to invalid.
// For the meaning of def and cycle, see check.typ, below. // For the meaning of def and cycle, see check.typ, below.
// //
func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycle []*TypeName) { func (check *checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeName) {
x.mode = invalid x.mode = invalid
x.expr = e x.expr = e
@ -34,7 +34,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycle []*TypeN
} }
check.recordObject(e, obj) check.recordObject(e, obj)
check.objDecl(obj, def, cycle) check.objDecl(obj, def, path)
typ := obj.Type() typ := obj.Type()
assert(typ != nil) assert(typ != nil)
@ -69,11 +69,16 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycle []*TypeN
obj.used = true obj.used = true
x.mode = typexpr x.mode = typexpr
// check for cycle // check for cycle
// (it's ok to iterate forward because each named type appears at most once in path)
// TODO(gri) consider passing []*Named instead of []*TypeName for cycle // TODO(gri) consider passing []*Named instead of []*TypeName for cycle
for _, prev := range cycle { for i, prev := range path {
if prev == obj { if prev == obj {
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
// TODO(gri) print the actual cycle // print cycle
for _, obj := range path[i:] {
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
}
check.errorf(obj.Pos(), "\t%s", obj.Name())
// maintain x.mode == typexpr despite error // maintain x.mode == typexpr despite error
typ = Typ[Invalid] typ = Typ[Invalid]
break break
@ -113,11 +118,10 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycle []*TypeN
// typ type-checks the type expression e and returns its type, or Typ[Invalid]. // typ type-checks the type expression e and returns its type, or Typ[Invalid].
// If def != nil, e is the type specification for the named type def, declared // If def != nil, e is the type specification for the named type def, declared
// in a type declaration, and def.underlying will be set to the type of e before // in a type declaration, and def.underlying will be set to the type of e before
// any components of e are type-checked. // any components of e are type-checked. Path contains the path of named types
// If cycle is not empty, e (or elements of e) may refer to a named type that is not // referring to this type.
// yet completely set up.
// //
func (check *checker) typ(e ast.Expr, def *Named, cycle []*TypeName) (T Type) { func (check *checker) typ(e ast.Expr, def *Named, path []*TypeName) (T Type) {
if trace { if trace {
check.trace(e.Pos(), "%s", e) check.trace(e.Pos(), "%s", e)
check.indent++ check.indent++
@ -127,7 +131,7 @@ func (check *checker) typ(e ast.Expr, def *Named, cycle []*TypeName) (T Type) {
}() }()
} }
T = check.typInternal(e, def, cycle) T = check.typInternal(e, def, path)
assert(isTyped(T)) assert(isTyped(T))
check.recordTypeAndValue(e, T, nil) check.recordTypeAndValue(e, T, nil)
@ -197,14 +201,14 @@ func (check *checker) funcType(sig *Signature, recv *ast.FieldList, ftyp *ast.Fu
// typInternal drives type checking of types. // typInternal drives type checking of types.
// Must only be called by typ. // Must only be called by typ.
// //
func (check *checker) typInternal(e ast.Expr, def *Named, cycle []*TypeName) Type { func (check *checker) typInternal(e ast.Expr, def *Named, path []*TypeName) Type {
switch e := e.(type) { switch e := e.(type) {
case *ast.BadExpr: case *ast.BadExpr:
// ignore - error reported before // ignore - error reported before
case *ast.Ident: case *ast.Ident:
var x operand var x operand
check.ident(&x, e, def, cycle) check.ident(&x, e, def, path)
switch x.mode { switch x.mode {
case typexpr: case typexpr:
@ -237,14 +241,14 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycle []*TypeName) Typ
} }
case *ast.ParenExpr: case *ast.ParenExpr:
return check.typ(e.X, def, cycle) return check.typ(e.X, def, path)
case *ast.ArrayType: case *ast.ArrayType:
if e.Len != nil { if e.Len != nil {
typ := new(Array) typ := new(Array)
def.setUnderlying(typ) def.setUnderlying(typ)
typ.len = check.arrayLength(e.Len) typ.len = check.arrayLength(e.Len)
typ.elem = check.typ(e.Elt, nil, cycle) typ.elem = check.typ(e.Elt, nil, path)
return typ return typ
} else { } else {
@ -257,7 +261,7 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycle []*TypeName) Typ
case *ast.StructType: case *ast.StructType:
typ := new(Struct) typ := new(Struct)
def.setUnderlying(typ) def.setUnderlying(typ)
check.structType(typ, e, cycle) check.structType(typ, e, path)
return typ return typ
case *ast.StarExpr: case *ast.StarExpr:
@ -275,7 +279,7 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycle []*TypeName) Typ
case *ast.InterfaceType: case *ast.InterfaceType:
typ := new(Interface) typ := new(Interface)
def.setUnderlying(typ) def.setUnderlying(typ)
check.interfaceType(typ, e, def, cycle) check.interfaceType(typ, e, def, path)
return typ return typ
case *ast.MapType: case *ast.MapType:
@ -427,7 +431,7 @@ func (check *checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool
return true return true
} }
func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, cycle []*TypeName) { func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) {
// empty interface: common case // empty interface: common case
if ityp.Methods == nil { if ityp.Methods == nil {
return return
@ -498,7 +502,7 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
for _, e := range embedded { for _, e := range embedded {
pos := e.Pos() pos := e.Pos()
typ := check.typ(e, nil, cycle) typ := check.typ(e, nil, path)
named, _ := typ.(*Named) named, _ := typ.(*Named)
if named == nil { if named == nil {
if typ != Typ[Invalid] { if typ != Typ[Invalid] {
@ -586,7 +590,7 @@ func (check *checker) tag(t *ast.BasicLit) string {
return "" return ""
} }
func (check *checker) structType(styp *Struct, e *ast.StructType, cycle []*TypeName) { func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeName) {
list := e.Fields list := e.Fields
if list == nil { if list == nil {
return return
@ -621,7 +625,7 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, cycle []*TypeN
} }
for _, f := range list.List { for _, f := range list.List {
typ = check.typ(f.Type, nil, cycle) typ = check.typ(f.Type, nil, path)
tag = check.tag(f.Tag) tag = check.tag(f.Tag)
if len(f.Names) > 0 { if len(f.Names) > 0 {
// named fields // named fields