From 8cd6c3be050968988ef89d97b86477664023f7b4 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 9 Jul 2013 21:13:37 -0700 Subject: [PATCH] go.tools/go/types: check for non-func init declarations R=adonovan CC=golang-dev https://golang.org/cl/11075043 --- go/types/resolver.go | 53 ++++++++++++++++++++++-------------- go/types/testdata/decls0.src | 14 +++++++++- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/go/types/resolver.go b/go/types/resolver.go index bf7da01f..f25216dd 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -59,12 +59,29 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { var objList []Object var objMap = make(map[Object]*decl) var methods []*mdecl - var fileScope *Scope // current file scope, used by add + var fileScope *Scope // current file scope, used by collect + + declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr) { + assert(ident.Name == obj.Name()) + + // spec: "A package-scope or file-scope identifier with name init + // may only be declared to be a function with this (func()) signature." + if ident.Name == "init" { + f, _ := obj.(*Func) + if f == nil { + check.callIdent(ident, nil) + check.errorf(ident.Pos(), "cannot declare init - must be func") + return + } + // don't declare init functions in the package scope - they are invisible + f.parent = pkg.scope + check.callIdent(ident, obj) + } else { + check.declare(pkg.scope, ident, obj) + } - 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 { @@ -104,6 +121,10 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { name := imp.name if s.Name != nil { name = s.Name.Name + if name == "init" { + check.errorf(s.Name.Pos(), "cannot declare init - must be func") + continue + } } imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil, imp.complete) @@ -143,13 +164,13 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { // declare all constants for i, name := range s.Names { obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) - check.declare(pkg.scope, name, obj) var init ast.Expr if i < len(last.Values) { init = last.Values[i] } - add(obj, last.Type, init) + + declare(name, obj, last.Type, init) } // arity of lhs and rhs must match @@ -170,7 +191,6 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { for i, name := range s.Names { obj := NewVar(name.Pos(), pkg, name.Name, nil) lhs[i] = obj - check.declare(pkg.scope, name, obj) var init ast.Expr switch len(s.Values) { @@ -185,7 +205,8 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { init = s.Values[i] } } - add(obj, s.Type, init) + + declare(name, obj, s.Type, init) } // report if there are too many initialization expressions @@ -206,8 +227,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - check.declare(pkg.scope, s.Name, obj) - add(obj, s.Type, nil) + declare(s.Name, obj, s.Type, nil) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) @@ -222,14 +242,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { } obj := NewFunc(d.Name.Pos(), pkg, d.Name.Name, nil) obj.decl = d - if obj.name == "init" { - // init functions are not visible - don't declare them in package scope - obj.parent = pkg.scope - check.callIdent(d.Name, obj) - } else { - check.declare(pkg.scope, d.Name, obj) - } - add(obj, nil, nil) + declare(d.Name, obj, nil, nil) default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) @@ -241,8 +254,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { for _, scope := range scopes { for _, obj := range scope.entries { if alt := pkg.scope.Lookup(nil, 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()) + check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) } } } @@ -306,8 +318,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { // 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) - check.topScope = pkg.scope + check.objMap = objMap // indicate that we are checking global declarations (objects may not have a type yet) for _, obj := range objList { if obj.Type() == nil { check.declareObject(obj, nil, false) diff --git a/go/types/testdata/decls0.src b/go/types/testdata/decls0.src index c39fa511..48dc0794 100644 --- a/go/types/testdata/decls0.src +++ b/go/types/testdata/decls0.src @@ -14,6 +14,7 @@ import ( // reflect defines a type "flag" which shows up in the gc export data "reflect" . "reflect" + init /* ERROR "cannot declare init" */ "fmt" ) // reflect.flag must not be visible in this package @@ -21,7 +22,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 import" */ struct{} +type Value /* ERROR "already declared in this file" */ struct{} const pi = 3.1415 @@ -50,6 +51,17 @@ type ( ) +// declarations of init +const _, init /* ERROR "cannot declare init" */ , _ = 0, 1, 2 +type init /* ERROR "cannot declare init" */ struct{} +var _, init /* ERROR "cannot declare init" */ int + +func init() {} + +func _() { const init = 0 } +func _() { type init int } +func _() { var init int } + // invalid array types type ( iA0 [... /* ERROR "invalid use of '...'" */ ]byte