mirror of
https://github.com/golang/go
synced 2024-11-18 22:14:56 -07:00
go.tools/go/types: init order depends on source order
Apply sorting consistently to object->decl maps. Fixes golang/go#7131. R=adonovan CC=golang-codereviews https://golang.org/cl/52890043
This commit is contained in:
parent
b75a5a4b65
commit
e1c6a8ff95
@ -349,6 +349,21 @@ func TestInitOrder(t *testing.T) {
|
|||||||
{`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{
|
{`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{
|
||||||
"y = 1", "x = T.m",
|
"y = 1", "x = T.m",
|
||||||
}},
|
}},
|
||||||
|
// test case for issue 7131
|
||||||
|
{`package main
|
||||||
|
|
||||||
|
var counter int
|
||||||
|
func next() int { counter++; return counter }
|
||||||
|
|
||||||
|
var _ = makeOrder()
|
||||||
|
func makeOrder() []int { return []int{f, b, d, e, c, a} }
|
||||||
|
|
||||||
|
var a = next()
|
||||||
|
var b, c = next(), next()
|
||||||
|
var d, e, f = next(), next(), next()
|
||||||
|
`, []string{
|
||||||
|
"a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -114,13 +115,12 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
// base type names.
|
// base type names.
|
||||||
|
|
||||||
var (
|
var (
|
||||||
objList []Object // list of package-level objects, in source order
|
|
||||||
objMap = make(map[Object]*declInfo) // corresponding declaration info
|
objMap = make(map[Object]*declInfo) // corresponding declaration info
|
||||||
initMap = make(map[Object]*declInfo) // declaration info for variables with initializers
|
initMap = make(map[Object]*declInfo) // declaration info for variables with initializers
|
||||||
)
|
)
|
||||||
|
|
||||||
// declare declares obj in the package scope, records its ident -> obj mapping,
|
// declare declares obj in the package scope, records its ident -> obj mapping,
|
||||||
// and updates objList and objMap. The object must not be a function or method.
|
// and updates objMap. The object must not be a function or method.
|
||||||
declare := func(ident *ast.Ident, obj Object, d *declInfo) {
|
declare := func(ident *ast.Ident, obj Object, d *declInfo) {
|
||||||
assert(ident.Name == obj.Name())
|
assert(ident.Name == obj.Name())
|
||||||
|
|
||||||
@ -132,7 +132,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check.declare(pkg.scope, ident, obj)
|
check.declare(pkg.scope, ident, obj)
|
||||||
objList = append(objList, obj)
|
|
||||||
objMap[obj] = d
|
objMap[obj] = d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +355,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objList = append(objList, obj)
|
|
||||||
info := &declInfo{file: fileScope, fdecl: d}
|
info := &declInfo{file: fileScope, fdecl: d}
|
||||||
objMap[obj] = info
|
objMap[obj] = info
|
||||||
// remember obj if it has a body (= initializer)
|
// remember obj if it has a body (= initializer)
|
||||||
@ -381,16 +379,15 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 3: Typecheck all objects in objList, but not function bodies.
|
// Phase 3: Typecheck all objects in objMap, but not function bodies.
|
||||||
|
|
||||||
check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet)
|
check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet)
|
||||||
check.initMap = initMap
|
check.initMap = initMap
|
||||||
for _, obj := range objList {
|
for _, obj := range objectsOf(check.objMap) {
|
||||||
if obj.Type() == nil {
|
if obj.Type() == nil {
|
||||||
check.objDecl(obj, nil, false)
|
check.objDecl(obj, nil, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.objMap = nil // done with package-level declarations
|
|
||||||
|
|
||||||
// At this point we may have a non-empty check.methods map; this means that not all
|
// At this point we may have a non-empty check.methods map; this means that not all
|
||||||
// entries were deleted at the end of typeDecl because the respective receiver base
|
// entries were deleted at the end of typeDecl because the respective receiver base
|
||||||
@ -409,9 +406,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
// Note: must happen after checking all functions because function bodies
|
// Note: must happen after checking all functions because function bodies
|
||||||
// may introduce dependencies
|
// may introduce dependencies
|
||||||
|
|
||||||
// For source order and reproducible error messages,
|
for _, obj := range objectsOf(initMap) {
|
||||||
// iterate through objList rather than initMap.
|
|
||||||
for _, obj := range objList {
|
|
||||||
if obj, _ := obj.(*Var); obj != nil {
|
if obj, _ := obj.(*Var); obj != nil {
|
||||||
if init := initMap[obj]; init != nil {
|
if init := initMap[obj]; init != nil {
|
||||||
// provide non-nil path for re-use of underlying array
|
// provide non-nil path for re-use of underlying array
|
||||||
@ -419,7 +414,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objList = nil // not needed anymore
|
|
||||||
check.initMap = nil // not needed anymore
|
check.initMap = nil // not needed anymore
|
||||||
|
|
||||||
// Phase 6: Check for declared but not used packages and function variables.
|
// Phase 6: Check for declared but not used packages and function variables.
|
||||||
@ -467,6 +461,30 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Instead of this support function, introduce type
|
||||||
|
// that combines an map[Object]*declInfo and []Object so that
|
||||||
|
// we can encapsulate this and don't have to depend on correct
|
||||||
|
// Pos() information.
|
||||||
|
|
||||||
|
// objectsOf returns the keys of m in source order.
|
||||||
|
func objectsOf(m map[Object]*declInfo) []Object {
|
||||||
|
list := make([]Object, len(m))
|
||||||
|
i := 0
|
||||||
|
for obj := range m {
|
||||||
|
list[i] = obj
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort(inSourceOrder(list))
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// inSourceOrder implements the sort.Sort interface.
|
||||||
|
type inSourceOrder []Object
|
||||||
|
|
||||||
|
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) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
// dependencies recursively traverses the initialization dependency graph in a depth-first
|
// dependencies recursively traverses the initialization dependency graph in a depth-first
|
||||||
// manner and appends the encountered variables in postorder to the Info.InitOrder list.
|
// manner and appends the encountered variables in postorder to the Info.InitOrder list.
|
||||||
// As a result, that list ends up being sorted topologically in the order of dependencies.
|
// As a result, that list ends up being sorted topologically in the order of dependencies.
|
||||||
@ -530,7 +548,8 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
|
|||||||
|
|
||||||
path = append(path, obj) // len(path) > 0
|
path = append(path, obj) // len(path) > 0
|
||||||
init.mark = len(path) // init.mark > 0
|
init.mark = len(path) // init.mark > 0
|
||||||
for obj, dep := range init.deps {
|
for _, obj := range objectsOf(init.deps) {
|
||||||
|
dep := init.deps[obj]
|
||||||
check.dependencies(obj, dep, path)
|
check.dependencies(obj, dep, path)
|
||||||
}
|
}
|
||||||
init.mark = -1 // init.mark < 0
|
init.mark = -1 // init.mark < 0
|
||||||
|
Loading…
Reference in New Issue
Block a user