aboutsummaryrefslogtreecommitdiff
path: root/src/go/types/expr.go
diff options
context:
space:
mode:
authorRob Findley <rfindley@google.com>2020-10-19 11:51:17 -0400
committerRobert Findley <rfindley@google.com>2020-10-30 14:13:03 +0000
commit733c4af41d2e89d18cfb039e0107336a4c1ec220 (patch)
treedd690132c110447b802de7b1b08c89635df192ca /src/go/types/expr.go
parentf7e26467b4e7ee0bb3219c26e71292ff4aac7da9 (diff)
downloadgo-733c4af41d2e89d18cfb039e0107336a4c1ec220.tar.gz
go-733c4af41d2e89d18cfb039e0107336a4c1ec220.zip
go/types: add internal error codes
Tools using go/types sometimes need to implement special handling for certain errors produced by the type-checker. They can offer suggested fixes, expand the error position to surrounding syntax, highlight related syntax (for example in the case of a declaration cycle), link to additional documentation, group errors by category, or correlate errors with signals produced by other static analysis tools. All these require a notion of error identity. Tools need to be able to reliably determine the nature of an error without re-implementing type checking logic or parsing error messages. This CL is a first-pass at adding such an identifier to types.Error: a (for the moment unexported) field containing one of many declared errorCode constants. A wide variety of error code constants are defined, and assigned to type checker errors according to their 'functional equivalence', meaning that they should be ideally be stable under refactoring. With few exceptions, each error code is documented with an example that produces it. This is enforced by tests. When error codes are exported they will represent quite a large API surface. For this reason, as well as the likelihood that error codes will change at the start, both the code field and the codes themselves are initially unexported. gopls will read these fields using reflection during this experimental phase. Others can obviously do the same, provided they accept the lack of forward compatibility. For #42290 Change-Id: I15e3c2bffd2046c20297b1857057d421f633098a Reviewed-on: https://go-review.googlesource.com/c/go/+/264179 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org> Trust: Robert Griesemer <gri@golang.org> Trust: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/go/types/expr.go')
-rw-r--r--src/go/types/expr.go127
1 files changed, 73 insertions, 54 deletions
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 94d98f0fbb..5f3415a59d 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -68,7 +68,7 @@ var unaryOpPredicates = opPredicates{
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
if pred := m[op]; pred != nil {
if !pred(x.typ) {
- check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
+ check.invalidOp(x.pos(), _UndefinedOp, "operator %s not defined for %s", op, x)
return false
}
} else {
@@ -85,7 +85,7 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
// spec: "As an exception to the addressability
// requirement x may also be a composite literal."
if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
- check.invalidOp(x.pos(), "cannot take address of %s", x)
+ check.invalidOp(x.pos(), _UnaddressableOperand, "cannot take address of %s", x)
x.mode = invalid
return
}
@@ -96,12 +96,12 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
case token.ARROW:
typ, ok := x.typ.Underlying().(*Chan)
if !ok {
- check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
+ check.invalidOp(x.pos(), _InvalidReceive, "cannot receive from non-channel %s", x)
x.mode = invalid
return
}
if typ.dir == SendOnly {
- check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
+ check.invalidOp(x.pos(), _InvalidReceive, "cannot receive from send-only channel %s", x)
x.mode = invalid
return
}
@@ -342,6 +342,7 @@ func (check *Checker) isRepresentable(x *operand, typ *Basic) error {
assert(x.mode == constant_)
if !representableConst(x.val, check, typ, &x.val) {
var msg string
+ var code errorCode
if isNumeric(x.typ) && isNumeric(typ) {
// numeric conversion : error msg
//
@@ -352,13 +353,16 @@ func (check *Checker) isRepresentable(x *operand, typ *Basic) error {
//
if !isInteger(x.typ) && isInteger(typ) {
msg = "%s truncated to %s"
+ code = _TruncatedFloat
} else {
msg = "%s overflows %s"
+ code = _NumericOverflow
}
} else {
msg = "cannot convert %s to %s"
+ code = _InvalidConstVal
}
- return check.newErrorf(x.pos(), msg, x, typ)
+ return check.newErrorf(x.pos(), code, msg, x, typ)
}
return nil
}
@@ -466,7 +470,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
// We already know from the shift check that it is representable
// as an integer if it is a constant.
if !isInteger(typ) {
- check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+ check.invalidOp(x.Pos(), _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
return
}
// Even if we have an integer, if the value is a constant we
@@ -517,7 +521,7 @@ func (check *Checker) canConvertUntyped(x *operand, target Type) error {
check.updateExprType(x.expr, target, false)
}
} else if xkind != tkind {
- return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target)
+ return check.newErrorf(x.pos(), _InvalidUntypedConversion, "cannot convert %s to %s", x, target)
}
return nil
}
@@ -531,7 +535,7 @@ func (check *Checker) canConvertUntyped(x *operand, target Type) error {
} else {
newTarget := check.implicitType(x, target)
if newTarget == nil {
- return check.newErrorf(x.pos(), "cannot convert %s to %s", x, target)
+ return check.newErrorf(x.pos(), _InvalidUntypedConversion, "cannot convert %s to %s", x, target)
}
target = newTarget
}
@@ -608,7 +612,10 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
// spec: "In any comparison, the first operand must be assignable
// to the type of the second operand, or vice versa."
err := ""
- if x.assignableTo(check, y.typ, nil) || y.assignableTo(check, x.typ, nil) {
+ var code errorCode
+ xok, _ := x.assignableTo(check, y.typ, nil)
+ yok, _ := y.assignableTo(check, x.typ, nil)
+ if xok || yok {
defined := false
switch op {
case token.EQL, token.NEQ:
@@ -626,13 +633,15 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
typ = y.typ
}
err = check.sprintf("operator %s not defined for %s", op, typ)
+ code = _UndefinedOp
}
} else {
err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
+ code = _MismatchedTypes
}
if err != "" {
- check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+ check.errorf(x.pos(), code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
x.mode = invalid
return
}
@@ -669,7 +678,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
// as an integer. Nothing to do.
} else {
// shift has no chance
- check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ check.invalidOp(x.pos(), _InvalidShiftOperand, "shifted operand %s must be integer", x)
x.mode = invalid
return
}
@@ -686,7 +695,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
return
}
default:
- check.invalidOp(y.pos(), "shift count %s must be integer", y)
+ check.invalidOp(y.pos(), _InvalidShiftCount, "shift count %s must be integer", y)
x.mode = invalid
return
}
@@ -699,7 +708,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
yval = constant.ToInt(y.val)
assert(yval.Kind() == constant.Int)
if constant.Sign(yval) < 0 {
- check.invalidOp(y.pos(), "negative shift count %s", y)
+ check.invalidOp(y.pos(), _InvalidShiftCount, "negative shift count %s", y)
x.mode = invalid
return
}
@@ -711,7 +720,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
s, ok := constant.Uint64Val(yval)
if !ok || s > shiftBound {
- check.invalidOp(y.pos(), "invalid shift count %s", y)
+ check.invalidOp(y.pos(), _InvalidShiftCount, "invalid shift count %s", y)
x.mode = invalid
return
}
@@ -768,7 +777,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
// non-constant shift - lhs must be an integer
if !isInteger(x.typ) {
- check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ check.invalidOp(x.pos(), _InvalidShiftOperand, "shifted operand %s must be integer", x)
x.mode = invalid
return
}
@@ -832,7 +841,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
- check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ check.invalidOp(x.pos(), _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ)
}
x.mode = invalid
return
@@ -846,7 +855,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
if op == token.QUO || op == token.REM {
// check for zero divisor
if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
- check.invalidOp(y.pos(), "division by zero")
+ check.invalidOp(y.pos(), _DivByZero, "division by zero")
x.mode = invalid
return
}
@@ -856,7 +865,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
re, im := constant.Real(y.val), constant.Imag(y.val)
re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im)
if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 {
- check.invalidOp(y.pos(), "division by zero")
+ check.invalidOp(y.pos(), _DivByZero, "division by zero")
x.mode = invalid
return
}
@@ -909,7 +918,7 @@ func (check *Checker) index(index ast.Expr, max int64) (typ Type, val int64) {
// the index must be of integer type
if !isInteger(x.typ) {
- check.invalidArg(x.pos(), "index %s must be integer", &x)
+ check.invalidArg(x.pos(), _InvalidIndex, "index %s must be integer", &x)
return
}
@@ -919,13 +928,13 @@ func (check *Checker) index(index ast.Expr, max int64) (typ Type, val int64) {
// a constant index i must be in bounds
if constant.Sign(x.val) < 0 {
- check.invalidArg(x.pos(), "index %s must not be negative", &x)
+ check.invalidArg(x.pos(), _InvalidIndex, "index %s must not be negative", &x)
return
}
v, valid := constant.Int64Val(constant.ToInt(x.val))
if !valid || max >= 0 && v >= max {
- check.errorf(x.pos(), "index %s is out of bounds", &x)
+ check.errorf(x.pos(), _InvalidIndex, "index %s is out of bounds", &x)
return
}
@@ -951,12 +960,12 @@ func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64
index = i
validIndex = true
} else {
- check.errorf(e.Pos(), "index %s must be integer constant", kv.Key)
+ check.errorf(e.Pos(), _InvalidLitIndex, "index %s must be integer constant", kv.Key)
}
}
eval = kv.Value
} else if length >= 0 && index >= length {
- check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+ check.errorf(e.Pos(), _OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
} else {
validIndex = true
}
@@ -964,7 +973,7 @@ func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64
// if we have a valid index, check for duplicate entries
if validIndex {
if visited[index] {
- check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+ check.errorf(e.Pos(), _DuplicateLitKey, "duplicate index %d in array or slice literal", index)
}
visited[index] = true
}
@@ -1054,7 +1063,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
// (array composite literals and parameter lists)
- check.error(e.Pos(), "invalid use of '...'")
+ check.error(e.Pos(), _BadDotDotDotSyntax, "invalid use of '...'")
goto Error
case *ast.BasicLit:
@@ -1113,7 +1122,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
default:
// TODO(gri) provide better error messages depending on context
- check.error(e.Pos(), "missing type in composite literal")
+ check.error(e.Pos(), _UntypedLit, "missing type in composite literal")
goto Error
}
@@ -1129,7 +1138,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
- check.error(e.Pos(), "mixture of field:value and value elements in struct literal")
+ check.error(e.Pos(), _MixedStructLit, "mixture of field:value and value elements in struct literal")
continue
}
key, _ := kv.Key.(*ast.Ident)
@@ -1137,12 +1146,12 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// so we don't drop information on the floor
check.expr(x, kv.Value)
if key == nil {
- check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ check.errorf(kv.Pos(), _InvalidLitField, "invalid field name %s in struct literal", kv.Key)
continue
}
i := fieldIndex(utyp.fields, check.pkg, key.Name)
if i < 0 {
- check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ check.errorf(kv.Pos(), _MissingLitField, "unknown field %s in struct literal", key.Name)
continue
}
fld := fields[i]
@@ -1151,7 +1160,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
check.assignment(x, etyp, "struct literal")
// 0 <= i < len(fields)
if visited[i] {
- check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+ check.errorf(kv.Pos(), _DuplicateLitField, "duplicate field name %s in struct literal", key.Name)
continue
}
visited[i] = true
@@ -1160,25 +1169,25 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// no element must have a key
for i, e := range e.Elts {
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
- check.error(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ check.error(kv.Pos(), _MixedStructLit, "mixture of field:value and value elements in struct literal")
continue
}
check.expr(x, e)
if i >= len(fields) {
- check.error(x.pos(), "too many values in struct literal")
+ check.error(x.pos(), _InvalidStructLit, "too many values in struct literal")
break // cannot continue
}
// i < len(fields)
fld := fields[i]
if !fld.Exported() && fld.pkg != check.pkg {
- check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+ check.errorf(x.pos(), _UnexportedLitField, "implicit assignment to unexported field %s in %s literal", fld.name, typ)
continue
}
etyp := fld.typ
check.assignment(x, etyp, "struct literal")
}
if len(e.Elts) < len(fields) {
- check.error(e.Rbrace, "too few values in struct literal")
+ check.error(e.Rbrace, _InvalidStructLit, "too few values in struct literal")
// ok to continue
}
}
@@ -1188,7 +1197,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// This is a stop-gap solution. Should use Checker.objPath to report entire
// path starting with earliest declaration in the source. TODO(gri) fix this.
if utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
@@ -1215,7 +1224,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// Prevent crash if the slice referred to is not yet set up.
// See analogous comment for *Array.
if utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
check.indexedElts(e.Elts, utyp.elem, -1)
@@ -1224,14 +1233,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// Prevent crash if the map referred to is not yet set up.
// See analogous comment for *Array.
if utyp.key == nil || utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
visited := make(map[interface{}][]Type, len(e.Elts))
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
- check.error(e.Pos(), "missing key in map literal")
+ check.error(e.Pos(), _MissingLitKey, "missing key in map literal")
continue
}
check.exprWithHint(x, kv.Key, utyp.key)
@@ -1256,7 +1265,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
visited[xkey] = nil
}
if duplicate {
- check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ check.errorf(x.pos(), _DuplicateLitKey, "duplicate key %s in map literal", x.val)
continue
}
}
@@ -1278,7 +1287,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
}
// if utyp is invalid, an error was reported before
if utyp != Typ[Invalid] {
- check.errorf(e.Pos(), "invalid composite literal type %s", typ)
+ check.errorf(e.Pos(), _InvalidLit, "invalid composite literal type %s", typ)
goto Error
}
}
@@ -1352,7 +1361,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
}
if !valid {
- check.invalidOp(x.pos(), "cannot index %s", x)
+ check.invalidOp(x.pos(), _NonIndexableOperand, "cannot index %s", x)
goto Error
}
@@ -1377,7 +1386,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case *Basic:
if isString(typ) {
if e.Slice3 {
- check.invalidOp(x.pos(), "3-index slice of string")
+ check.invalidOp(x.pos(), _InvalidSliceExpr, "3-index slice of string")
goto Error
}
valid = true
@@ -1395,7 +1404,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
valid = true
length = typ.len
if x.mode != variable {
- check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
+ check.invalidOp(x.pos(), _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
goto Error
}
x.typ = &Slice{elem: typ.elem}
@@ -1413,7 +1422,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
}
if !valid {
- check.invalidOp(x.pos(), "cannot slice %s", x)
+ check.invalidOp(x.pos(), _NonSliceableOperand, "cannot slice %s", x)
goto Error
}
@@ -1421,7 +1430,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// spec: "Only the first index may be omitted; it defaults to 0."
if e.Slice3 && (e.High == nil || e.Max == nil) {
- check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice")
+ check.invalidAST(e.Rbrack, "2nd and 3rd index required in 3-index slice")
goto Error
}
@@ -1458,7 +1467,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
if x > 0 {
for _, y := range ind[i+1:] {
if y >= 0 && x > y {
- check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y)
+ check.errorf(e.Rbrack, _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y)
break L // only report one error, ok to continue
}
}
@@ -1472,12 +1481,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
}
xtyp, _ := x.typ.Underlying().(*Interface)
if xtyp == nil {
- check.invalidOp(x.pos(), "%s is not an interface", x)
+ check.invalidOp(x.pos(), _InvalidAssert, "%s is not an interface", x)
goto Error
}
// x.(type) expressions are handled explicitly in type switches
if e.Type == nil {
- check.invalidAST(e.Pos(), "use of .(type) outside type switch")
+ // Don't use invalidAST because this can occur in the AST produced by
+ // go/parser.
+ check.error(e.Pos(), _BadTypeKeyword, "use of .(type) outside type switch")
goto Error
}
T := check.typ(e.Type)
@@ -1503,7 +1514,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.mode = variable
x.typ = typ.base
} else {
- check.invalidOp(x.pos(), "cannot indirect %s", x)
+ check.invalidOp(x.pos(), _InvalidIndirection, "cannot indirect %s", x)
goto Error
}
}
@@ -1597,7 +1608,7 @@ func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface,
} else {
msg = "missing method " + method.name
}
- check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
+ check.errorf(pos, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
}
func (check *Checker) singleValue(x *operand) {
@@ -1605,7 +1616,7 @@ func (check *Checker) singleValue(x *operand) {
// tuple types are never named - no need for underlying type below
if t, ok := x.typ.(*Tuple); ok {
assert(t.Len() != 1)
- check.errorf(x.pos(), "%d-valued %s where single value is expected", t.Len(), x)
+ check.errorf(x.pos(), _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
x.mode = invalid
}
}
@@ -1624,17 +1635,21 @@ func (check *Checker) expr(x *operand, e ast.Expr) {
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
var msg string
+ var code errorCode
switch x.mode {
default:
return
case novalue:
msg = "%s used as value"
+ code = _TooManyValues
case builtin:
msg = "%s must be called"
+ code = _UncalledBuiltin
case typexpr:
msg = "%s is not an expression"
+ code = _NotAnExpr
}
- check.errorf(x.pos(), msg, x)
+ check.errorf(x.pos(), code, msg, x)
x.mode = invalid
}
@@ -1647,17 +1662,21 @@ func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
check.rawExpr(x, e, hint)
check.singleValue(x)
var msg string
+ var code errorCode
switch x.mode {
default:
return
case novalue:
msg = "%s used as value"
+ code = _TooManyValues
case builtin:
msg = "%s must be called"
+ code = _UncalledBuiltin
case typexpr:
msg = "%s is not an expression"
+ code = _NotAnExpr
}
- check.errorf(x.pos(), msg, x)
+ check.errorf(x.pos(), code, msg, x)
x.mode = invalid
}
@@ -1668,7 +1687,7 @@ func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.singleValue(x)
if x.mode == novalue {
- check.errorf(x.pos(), "%s used as value or type", x)
+ check.errorf(x.pos(), _NotAnExpr, "%s used as value or type", x)
x.mode = invalid
}
}