mirror of
https://github.com/golang/go
synced 2024-10-01 05:28:33 -06:00
go.tools/go/types: cleanup init expr checks
- factored our arity checks - started more systematic tests for const/var decls - better error messages - fixed several corner case errors R=adonovan CC=golang-dev https://golang.org/cl/11137043
This commit is contained in:
parent
32f601bfbe
commit
3a75f78acd
@ -51,6 +51,8 @@ var tests = [][]string{
|
||||
{"testdata/decls2a.src", "testdata/decls2b.src"},
|
||||
{"testdata/decls3.src"},
|
||||
{"testdata/const0.src"},
|
||||
{"testdata/constdecl.src"},
|
||||
{"testdata/vardecl.src"},
|
||||
{"testdata/expr0.src"},
|
||||
{"testdata/expr1.src"},
|
||||
{"testdata/expr2.src"},
|
||||
|
@ -19,14 +19,14 @@ import (
|
||||
type operandMode int
|
||||
|
||||
const (
|
||||
invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore
|
||||
invalid operandMode = iota // operand is invalid
|
||||
novalue // operand represents no value (result of a function call w/o result)
|
||||
typexpr // operand is a type
|
||||
typexprn // like typexpr; only used to communicate between checker.expr0 and checker.rawExpr
|
||||
constant // operand is a constant; the operand's typ is a Basic type
|
||||
variable // operand is an addressable variable
|
||||
value // operand is a computed value
|
||||
valueok // like mode == value, but operand may be used in a comma,ok expression
|
||||
valueok // like value, but operand may be used in a comma,ok expression
|
||||
)
|
||||
|
||||
var operandModeString = [...]string{
|
||||
@ -42,7 +42,8 @@ var operandModeString = [...]string{
|
||||
|
||||
// An operand represents an intermediate value during type checking.
|
||||
// Operands have an (addressing) mode, the expression evaluating to
|
||||
// the operand, the operand's type, and for constants a constant value.
|
||||
// the operand, the operand's type, and a value if mode == constant.
|
||||
// The zero value of operand is a ready to use invalid operand.
|
||||
//
|
||||
type operand struct {
|
||||
mode operandMode
|
||||
|
@ -49,6 +49,38 @@ type projExpr struct {
|
||||
ast.Expr // rhs
|
||||
}
|
||||
|
||||
// arityMatch checks that the lhs and rhs of a const or var decl
|
||||
// have the appropriate number of names and init exprs. For const
|
||||
// decls, init is the value spec providing the init exprs; for
|
||||
// var decls, init is nil (the init exprs are in s in this case).
|
||||
func (check *checker) arityMatch(s, init *ast.ValueSpec) {
|
||||
l := len(s.Names)
|
||||
r := len(s.Values)
|
||||
if init != nil {
|
||||
r = len(init.Values)
|
||||
}
|
||||
|
||||
switch {
|
||||
case init == nil && r == 0:
|
||||
// var decl w/o init expr
|
||||
if s.Type == nil {
|
||||
check.errorf(s.Pos(), "missing type or init expr")
|
||||
}
|
||||
case l < r:
|
||||
if l < len(s.Values) {
|
||||
// init exprs from s
|
||||
n := s.Values[l]
|
||||
check.errorf(n.Pos(), "extra init expr %s", n)
|
||||
} else {
|
||||
// init exprs "inherited"
|
||||
check.errorf(s.Pos(), "extra init expr at %s", init.Pos())
|
||||
}
|
||||
case l > r && (init != nil || r != 1):
|
||||
n := s.Names[r]
|
||||
check.errorf(n.Pos(), "missing init expr for %s", n)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
pkg := check.pkg
|
||||
|
||||
@ -100,7 +132,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// ignore
|
||||
|
||||
case *ast.GenDecl:
|
||||
var last *ast.ValueSpec // last list of const initializers seen
|
||||
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
|
||||
for iota, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
@ -110,7 +142,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
path, _ := strconv.Unquote(s.Path.Value)
|
||||
imp, err := importer(pkg.imports, path)
|
||||
if imp == nil && err == nil {
|
||||
err = errors.New("Context.Import returned niil")
|
||||
err = errors.New("Context.Import returned nil")
|
||||
}
|
||||
if err != nil {
|
||||
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
||||
@ -157,8 +189,11 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
switch d.Tok {
|
||||
case token.CONST:
|
||||
// determine which initialization expressions to use
|
||||
if len(s.Values) > 0 {
|
||||
switch {
|
||||
case s.Type != nil || len(s.Values) > 0:
|
||||
last = s
|
||||
case last == nil:
|
||||
last = new(ast.ValueSpec) // make sure last exists
|
||||
}
|
||||
|
||||
// declare all constants
|
||||
@ -173,17 +208,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
declare(name, 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:
|
||||
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)
|
||||
}
|
||||
}
|
||||
check.arityMatch(s, last)
|
||||
|
||||
case token.VAR:
|
||||
// declare all variables
|
||||
@ -199,6 +224,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
init = s.Values[i]
|
||||
case 1:
|
||||
// rhs must be a multi-valued expression
|
||||
// (lhs may not be fully set up yet, but
|
||||
// that's fine because declare simply collects
|
||||
// the information for later processing.)
|
||||
init = &projExpr{lhs, s.Values[0]}
|
||||
default:
|
||||
if i < len(s.Values) {
|
||||
@ -209,17 +237,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
declare(name, obj, s.Type, init)
|
||||
}
|
||||
|
||||
// report if there are too many initialization expressions
|
||||
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)
|
||||
}
|
||||
}
|
||||
check.arityMatch(s, nil)
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
|
||||
@ -377,27 +395,13 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
|
||||
obj.typ = check.typ(typ, nil, false)
|
||||
}
|
||||
|
||||
// check initialization
|
||||
var x operand
|
||||
|
||||
if init == nil {
|
||||
goto Error // error reported before
|
||||
if init != nil {
|
||||
check.expr(&x, init)
|
||||
}
|
||||
|
||||
check.expr(&x, init)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
||||
check.initConst(obj, &x)
|
||||
check.iota = nil
|
||||
return
|
||||
|
||||
Error:
|
||||
if obj.typ == nil {
|
||||
obj.typ = Typ[Invalid]
|
||||
} else {
|
||||
obj.val = exact.MakeUnknown()
|
||||
}
|
||||
check.iota = nil
|
||||
}
|
||||
|
||||
@ -602,56 +606,49 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
// ignore
|
||||
|
||||
case *ast.GenDecl:
|
||||
var last []ast.Expr // last list of const initializers seen
|
||||
var last *ast.ValueSpec // last ValueSpec with type or init exprs 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
|
||||
// determine which init exprs to use
|
||||
switch {
|
||||
case s.Type != nil || len(s.Values) > 0:
|
||||
last = s
|
||||
case last == nil:
|
||||
last = new(ast.ValueSpec) // make sure last exists
|
||||
}
|
||||
|
||||
// declare all constants
|
||||
lhs := make([]*Const, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
|
||||
check.callIdent(name, obj)
|
||||
lhs[i] = obj
|
||||
|
||||
var init ast.Expr
|
||||
if i < len(last) {
|
||||
init = last[i]
|
||||
if i < len(last.Values) {
|
||||
init = last.Values[i]
|
||||
}
|
||||
|
||||
check.declareConst(obj, s.Type, init)
|
||||
check.declareConst(obj, last.Type, init)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
check.arityMatch(s, last)
|
||||
|
||||
for _, obj := range lhs {
|
||||
check.declare(check.topScope, nil, obj)
|
||||
for i, name := range s.Names {
|
||||
check.declare(check.topScope, name, lhs[i])
|
||||
}
|
||||
|
||||
case token.VAR:
|
||||
// declare all variables
|
||||
// For declareVar called with a projExpr we need the fully
|
||||
// initialized lhs. Compute it in a separate pre-pass.
|
||||
lhs := make([]*Var, len(s.Names))
|
||||
for i, name := range s.Names {
|
||||
obj := NewVar(name.Pos(), pkg, name.Name, nil)
|
||||
check.callIdent(name, obj)
|
||||
lhs[i] = obj
|
||||
lhs[i] = NewVar(name.Pos(), pkg, name.Name, nil)
|
||||
}
|
||||
|
||||
// iterate in 2 phases because declareVar requires fully initialized lhs!
|
||||
// declare all variables
|
||||
for i, obj := range lhs {
|
||||
var init ast.Expr
|
||||
switch len(s.Values) {
|
||||
@ -670,20 +667,10 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
check.declareVar(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:
|
||||
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)
|
||||
}
|
||||
}
|
||||
check.arityMatch(s, nil)
|
||||
|
||||
for _, obj := range lhs {
|
||||
check.declare(check.topScope, nil, obj)
|
||||
for i, name := range s.Names {
|
||||
check.declare(check.topScope, name, lhs[i])
|
||||
}
|
||||
|
||||
default:
|
||||
|
8
go/types/testdata/const0.src
vendored
8
go/types/testdata/const0.src
vendored
@ -180,8 +180,8 @@ const (
|
||||
const (
|
||||
a1, a2, a3 = 7, 3.1415926, "foo"
|
||||
b1, b2, b3 = b3, b1, 42
|
||||
c1, c2, c3 /* ERROR "missing initialization" */ = 1, 2
|
||||
d1, d2, d3 = 1, 2, 3, 4 /* ERROR "too many initialization" */
|
||||
c1, c2, c3 /* ERROR "missing init expr for c3" */ = 1, 2
|
||||
d1, d2, d3 = 1, 2, 3, 4 /* ERROR "extra init expr 4" */
|
||||
_p0 = assert(a1 == 7)
|
||||
_p1 = assert(a2 == 3.1415926)
|
||||
_p2 = assert(a3 == "foo")
|
||||
@ -194,8 +194,8 @@ func _() {
|
||||
const (
|
||||
a1, a2, a3 = 7, 3.1415926, "foo"
|
||||
b1, b2, b3 = b3, b1, 42
|
||||
c1, c2, c3 /* ERROR "missing initialization" */ = 1, 2
|
||||
d1, d2, d3 = 1, 2, 3, 4 /* ERROR "too many initialization" */
|
||||
c1, c2, c3 /* ERROR "missing init expr for c3" */ = 1, 2
|
||||
d1, d2, d3 = 1, 2, 3, 4 /* ERROR "extra init expr 4" */
|
||||
_p0 = assert(a1 == 7)
|
||||
_p1 = assert(a2 == 3.1415926)
|
||||
_p2 = assert(a3 == "foo")
|
||||
|
85
go/types/testdata/constdecl.src
vendored
Normal file
85
go/types/testdata/constdecl.src
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
// 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 constdecl
|
||||
|
||||
import "math"
|
||||
|
||||
var v int
|
||||
|
||||
// Const decls must be initialized by constants.
|
||||
const _ = v /* ERROR "not constant" */
|
||||
const _ = math /* ERROR "not constant" */ .Sin(0)
|
||||
const _ = int /* ERROR "not an expression" */
|
||||
|
||||
func _() {
|
||||
const _ = v /* ERROR "not constant" */
|
||||
const _ = math /* ERROR "not constant" */ .Sin(0)
|
||||
const _ = int /* ERROR "not an expression" */
|
||||
}
|
||||
|
||||
// Identifier and expression arity must match.
|
||||
const _ /* ERROR "missing init expr for _" */
|
||||
const _ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
const _ /* ERROR "missing init expr for _" */ int
|
||||
const _ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
const (
|
||||
_ /* ERROR "missing init expr for _" */
|
||||
_ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
_ /* ERROR "missing init expr for _" */ int
|
||||
_ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
)
|
||||
|
||||
const (
|
||||
_ = 1
|
||||
_
|
||||
_, _ /* ERROR "missing init expr for _" */
|
||||
_
|
||||
)
|
||||
|
||||
const (
|
||||
_, _ = 1, 2
|
||||
_, _
|
||||
_ /* ERROR "extra init expr at" */
|
||||
_, _
|
||||
_, _, _ /* ERROR "missing init expr for _" */
|
||||
_, _
|
||||
)
|
||||
|
||||
func _() {
|
||||
const _ /* ERROR "missing init expr for _" */
|
||||
const _ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
const _ /* ERROR "missing init expr for _" */ int
|
||||
const _ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
const (
|
||||
_ /* ERROR "missing init expr for _" */
|
||||
_ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
|
||||
_ /* ERROR "missing init expr for _" */ int
|
||||
_ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
)
|
||||
|
||||
const (
|
||||
_ = 1
|
||||
_
|
||||
_, _ /* ERROR "missing init expr for _" */
|
||||
_
|
||||
)
|
||||
|
||||
const (
|
||||
_, _ = 1, 2
|
||||
_, _
|
||||
_ /* ERROR "extra init expr at" */
|
||||
_, _
|
||||
_, _, _ /* ERROR "missing init expr for _" */
|
||||
_, _
|
||||
)
|
||||
}
|
||||
|
||||
// TODO(gri) move extra tests from testdata/const0.src into here
|
8
go/types/testdata/decls1.src
vendored
8
go/types/testdata/decls1.src
vendored
@ -100,15 +100,15 @@ var (
|
||||
// Multiple assignment expressions
|
||||
var (
|
||||
m1a, m1b = 1, 2
|
||||
m2a, m2b, m2c /* ERROR "missing initialization" */ = 1, 2
|
||||
m3a, m3b = 1, 2, 3 /* ERROR "too many initialization" */
|
||||
m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2
|
||||
m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */
|
||||
)
|
||||
|
||||
func _() {
|
||||
var (
|
||||
m1a, m1b = 1, 2
|
||||
m2a, m2b, m2c /* ERROR "missing initialization" */ = 1, 2
|
||||
m3a, m3b = 1, 2, 3 /* ERROR "too many initialization" */
|
||||
m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2
|
||||
m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */
|
||||
)
|
||||
}
|
||||
|
||||
|
44
go/types/testdata/vardecl.src
vendored
Normal file
44
go/types/testdata/vardecl.src
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 vardecl
|
||||
|
||||
func f() {}
|
||||
|
||||
// Var decls must have a type or an initializer.
|
||||
var _ int
|
||||
var _, _ int
|
||||
|
||||
var _ /* ERROR "missing type or init expr" */
|
||||
var _ /* ERROR "missing type or init expr" */, _
|
||||
var _ /* ERROR "missing type or init expr" */, _, _
|
||||
|
||||
// The initializer must be an expression.
|
||||
var _ = int /* ERROR "not an expression" */
|
||||
var _ = f /* ERROR "used as value" */ ()
|
||||
|
||||
// Identifier and expression arity must match.
|
||||
var _, _ = 1, 2
|
||||
var _ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
var _ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here
|
||||
var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2
|
||||
|
||||
var _, _ int = 1, 2
|
||||
var _ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
var _ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here
|
||||
var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||
|
||||
var (
|
||||
_, _ = 1, 2
|
||||
_ = 1, 2 /* ERROR "extra init expr 2" */
|
||||
_ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here
|
||||
_, _, _ /* ERROR "missing init expr for _" */ = 1, 2
|
||||
|
||||
_, _ int = 1, 2
|
||||
_ int = 1, 2 /* ERROR "extra init expr 2" */
|
||||
_ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here
|
||||
_, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
|
||||
)
|
||||
|
||||
// TODO(gri) consolidate other var decl checks in this file
|
Loading…
Reference in New Issue
Block a user