1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:34:40 -07:00

go/types: use file not parse order for init order computation

The type-checker depended on (token.Pos) position information of
the presented files to determine source order. That information
is determined by the parse order of the files rather than the
order in which the files are presented to the type-checker.

Introduced an order number strictly determined by the file
order as presented to the type-checker and the AST structure
of each file; thus providing source order information even in
the absence of (token.Pos) position information.

Added test case (provided by adonovan).

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/151160043
This commit is contained in:
Robert Griesemer 2014-10-01 09:56:28 -07:00
parent 4fd305d5dc
commit a7f9d5d4f8
5 changed files with 59 additions and 11 deletions

View File

@ -588,6 +588,39 @@ func TestInitOrderInfo(t *testing.T) {
} }
} }
func TestMultiFileInitOrder(t *testing.T) {
fset := token.NewFileSet()
mustParse := func(src string) *ast.File {
f, err := parser.ParseFile(fset, "main", src, 0)
if err != nil {
t.Fatal(err)
}
return f
}
fileA := mustParse(`package main; var a = 1`)
fileB := mustParse(`package main; var b = 2`)
// The initialization order must not depend on the parse
// order of the files, only on the presentation order to
// the type-checker.
for _, test := range []struct {
files []*ast.File
want string
}{
{[]*ast.File{fileA, fileB}, "[a = 1 b = 2]"},
{[]*ast.File{fileB, fileA}, "[b = 2 a = 1]"},
} {
var info Info
if _, err := new(Config).Check("main", fset, test.files, &info); err != nil {
t.Fatal(err)
}
if got := fmt.Sprint(info.InitOrder); got != test.want {
t.Fatalf("got %s; want %s", got, test.want)
}
}
}
func TestFiles(t *testing.T) { func TestFiles(t *testing.T) {
var sources = []string{ var sources = []string{
"package p; type T struct{}; func (T) m1() {}", "package p; type T struct{}; func (T) m1() {}",

View File

@ -205,8 +205,8 @@ func (a nodeQueue) Swap(i, j int) {
func (a nodeQueue) Less(i, j int) bool { func (a nodeQueue) Less(i, j int) bool {
x, y := a[i], a[j] x, y := a[i], a[j]
// nodes are prioritized by number of incoming dependencies (1st key) // nodes are prioritized by number of incoming dependencies (1st key)
// and source positions (2nd key) // and source order (2nd key)
return x.in < y.in || x.in == y.in && x.obj.Pos() < y.obj.Pos() return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
} }
func (a *nodeQueue) Push(x interface{}) { func (a *nodeQueue) Push(x interface{}) {

View File

@ -31,9 +31,18 @@ type Object interface {
// String returns a human-readable string of the object. // String returns a human-readable string of the object.
String() string String() string
// order reflects a package-level object's source order: if object
// a is before object b in the source, then a.order() < b.order().
// order returns a value > 0 for package-level objects; it returns
// 0 for all other objects (including objects in file scopes).
order() uint32
// isUsed reports whether the object was marked as 'used'. // isUsed reports whether the object was marked as 'used'.
isUsed() bool isUsed() bool
// setOrder sets the order number of the object. It must be > 0.
setOrder(uint32)
// setParent sets the parent scope of the object. // setParent sets the parent scope of the object.
setParent(*Scope) setParent(*Scope)
@ -73,6 +82,8 @@ type object struct {
pkg *Package pkg *Package
name string name string
typ Type typ Type
order_ uint32
used bool used bool
} }
@ -85,8 +96,10 @@ func (obj *object) Exported() bool { return ast.IsExported(obj.name) }
func (obj *object) Id() string { return Id(obj.pkg, obj.name) } func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
func (obj *object) String() string { panic("abstract") } func (obj *object) String() string { panic("abstract") }
func (obj *object) isUsed() bool { return obj.used } func (obj *object) order() uint32 { return obj.order_ }
func (obj *object) isUsed() bool { return obj.used }
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
func (obj *object) setParent(parent *Scope) { obj.parent = parent } func (obj *object) setParent(parent *Scope) { obj.parent = parent }
func (obj *object) sameId(pkg *Package, name string) bool { func (obj *object) sameId(pkg *Package, name string) bool {
@ -118,7 +131,7 @@ type PkgName struct {
} }
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName { func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}, imported} return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, false}, imported}
} }
// Imported returns the package that was imported. // Imported returns the package that was imported.
@ -134,7 +147,7 @@ type Const struct {
} }
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const { func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const {
return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val} return &Const{object: object{nil, pos, pkg, name, typ, 0, false}, val: val}
} }
func (obj *Const) Val() exact.Value { return obj.val } func (obj *Const) Val() exact.Value { return obj.val }
@ -145,7 +158,7 @@ type TypeName struct {
} }
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, false}} return &TypeName{object{nil, pos, pkg, name, typ, 0, false}}
} }
// A Variable represents a declared variable (including function parameters and results, and struct fields). // A Variable represents a declared variable (including function parameters and results, and struct fields).
@ -158,15 +171,15 @@ type Var struct {
} }
func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, false}} return &Var{object: object{nil, pos, pkg, name, typ, 0, false}}
} }
func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, true}} // parameters are always 'used' return &Var{object: object{nil, pos, pkg, name, typ, 0, true}} // parameters are always 'used'
} }
func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, false}, anonymous: anonymous, isField: true} return &Var{object: object{nil, pos, pkg, name, typ, 0, false}, anonymous: anonymous, isField: true}
} }
func (obj *Var) Anonymous() bool { return obj.anonymous } func (obj *Var) Anonymous() bool { return obj.anonymous }
@ -186,7 +199,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
if sig != nil { if sig != nil {
typ = sig typ = sig
} }
return &Func{object{nil, pos, pkg, name, typ, false}} return &Func{object{nil, pos, pkg, name, typ, 0, false}}
} }
// FullName returns the package- or receiver-type-qualified name of // FullName returns the package- or receiver-type-qualified name of

View File

@ -123,5 +123,5 @@ func orderedSetObjects(set map[Object]bool) []Object {
type inSourceOrder []Object type inSourceOrder []Object
func (a inSourceOrder) Len() int { return len(a) } func (a inSourceOrder) Len() int { return len(a) }
func (a inSourceOrder) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@ -110,6 +110,7 @@ func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
check.declare(check.pkg.scope, ident, obj) check.declare(check.pkg.scope, ident, obj)
check.objMap[obj] = d check.objMap[obj] = d
obj.setOrder(uint32(len(check.objMap)))
} }
// collectObjects collects all file and package objects and inserts them // collectObjects collects all file and package objects and inserts them
@ -341,6 +342,7 @@ func (check *Checker) collectObjects() {
} }
info := &declInfo{file: fileScope, fdecl: d} info := &declInfo{file: fileScope, fdecl: d}
check.objMap[obj] = info check.objMap[obj] = info
obj.setOrder(uint32(len(check.objMap)))
default: default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)