1
0
mirror of https://github.com/golang/go synced 2024-11-18 17:54:57 -07:00

go.tools/go/types: improved operand printing in error messages

Also:
- removed operand.val hack for built-in encoding; added id field
- minor cleanups

R=adonovan
CC=golang-dev
https://golang.org/cl/13840046
This commit is contained in:
Robert Griesemer 2013-09-24 12:22:04 -07:00
parent 37f76edde8
commit bce88d26ea
7 changed files with 139 additions and 47 deletions

View File

@ -405,8 +405,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) {
}
default:
check.invalidAST(call.Pos(), "unknown builtin id %d", id)
goto Error
panic("unreachable")
}
x.expr = call

View File

@ -9,19 +9,6 @@ package types
import (
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact"
)
// exprKind describes the kind of an expression;
// the kind determines if an expression is valid
// in 'statement context'.
type exprKind int
const (
conversion exprKind = iota
expression
statement
)
func (check *checker) call(x *operand, e *ast.CallExpr) exprKind {
@ -61,8 +48,8 @@ func (check *checker) call(x *operand, e *ast.CallExpr) exprKind {
return conversion
case builtin:
id, _ := exact.Int64Val(x.val)
check.builtin(x, e, builtinId(id))
id := x.id
check.builtin(x, e, id)
return predeclaredFuncs[id].kind
default:
@ -239,7 +226,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
case *Builtin:
x.mode = builtin
x.typ = exp.typ
x.val = exact.MakeInt64(int64(exp.id))
x.id = exp.id
default:
unreachable()
}

View File

@ -45,6 +45,7 @@ var (
// Each tests entry is list of files belonging to the same package.
var tests = [][]string{
{"testdata/errors.src"},
{"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
{"testdata/cycles.src"},
{"testdata/decls0.src"},

View File

@ -14,15 +14,6 @@ import (
"code.google.com/p/go.tools/go/exact"
)
// TODO(gri) Internal cleanups
// - don't print error messages referring to invalid types (they are likely spurious errors)
// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values?
// - rethink error handling: should all callers check if x.mode == valid after making a call?
// - consider storing error messages in invalid operands for better error messages/debugging output
// TODO(gri) Test issues
// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks)
/*
Basic algorithm:
@ -840,6 +831,16 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64
return max
}
// exprKind describes the kind of an expression; the kind
// determines if an expression is valid in 'statement context'.
type exprKind int
const (
conversion exprKind = iota
expression
statement
)
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occurred, x.mode is set to invalid.
// If hint != nil, it is the type of a composite literal element.

View File

@ -8,7 +8,6 @@ package types
import (
"bytes"
"fmt"
"go/ast"
"go/token"
@ -30,20 +29,20 @@ const (
)
var operandModeString = [...]string{
invalid: "invalid",
invalid: "invalid operand",
novalue: "no value",
builtin: "builtin",
builtin: "built-in",
typexpr: "type",
constant: "constant",
variable: "variable",
value: "value",
valueok: "value,ok",
valueok: "value, ok",
}
// 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 a value if mode == constant
// or builtin. The built-in id is encoded as an exact Int64 in val.
// the operand, the operand's type, a value for constants, and an id
// for built-in functions.
// The zero value of operand is a ready to use invalid operand.
//
type operand struct {
@ -51,6 +50,7 @@ type operand struct {
expr ast.Expr
typ Type
val exact.Value
id builtinId
}
// pos returns the position of the expression corresponding to x.
@ -64,29 +64,95 @@ func (x *operand) pos() token.Pos {
return x.expr.Pos()
}
// Operand string formats
// (not all "untyped" cases can appear due to the type system,
// but they fall out naturally here)
//
// mode format
//
// invalid <expr> ( <mode> )
// novalue <expr> ( <mode> )
// builtin <expr> ( <mode> )
// typexpr <expr> ( <mode> )
//
// constant <expr> (<untyped kind> <mode> )
// constant <expr> ( <mode> of type <typ>)
// constant <expr> (<untyped kind> <mode> <val> )
// constant <expr> ( <mode> <val> of type <typ>)
//
// variable <expr> (<untyped kind> <mode> )
// variable <expr> ( <mode> of type <typ>)
//
// value <expr> (<untyped kind> <mode> )
// value <expr> ( <mode> of type <typ>)
//
// valueok <expr> (<untyped kind> <mode> )
// valueok <expr> ( <mode> of type <typ>)
//
func (x *operand) String() string {
if x.mode == invalid {
return "invalid operand"
}
var buf bytes.Buffer
var expr string
if x.expr != nil {
buf.WriteString(exprString(x.expr))
expr = exprString(x.expr)
} else {
switch x.mode {
case builtin:
expr = predeclaredFuncs[x.id].name
case typexpr:
expr = typeString(x.typ)
case constant:
expr = x.val.String()
}
}
// <expr> (
if expr != "" {
buf.WriteString(expr)
buf.WriteString(" (")
}
buf.WriteString(operandModeString[x.mode])
if x.mode == constant {
format := " %v"
if isString(x.typ) {
format = " %q"
// <untyped kind>
hasType := false
switch x.mode {
case invalid, novalue, builtin, typexpr:
// no type
default:
// has type
if isUntyped(x.typ) {
buf.WriteString(x.typ.(*Basic).name)
buf.WriteByte(' ')
break
}
fmt.Fprintf(&buf, format, x.val)
hasType = true
}
if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) {
fmt.Fprintf(&buf, " of type %s", typeString(x.typ))
// <mode>
buf.WriteString(operandModeString[x.mode])
// <val>
if x.mode == constant {
if s := x.val.String(); s != expr {
buf.WriteByte(' ')
buf.WriteString(s)
}
}
if x.expr != nil {
// <typ>
if hasType {
if x.typ != Typ[Invalid] {
buf.WriteString(" of type ")
writeType(&buf, x.typ)
} else {
buf.WriteString(" with invalid type")
}
}
// )
if expr != "" {
buf.WriteByte(')')
}
return buf.String()
}

38
go/types/testdata/errors.src vendored Normal file
View File

@ -0,0 +1,38 @@
// 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.
// testing precise operand formatting in error messages
// (matching messages are regular expressions, hence the \'s)
package errors
func f(x int, m map[string]int) {
// no values
_ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m)
// built-ins
_ = println /* ERROR "println \(built-in\) must be called" */
// types
_ = complex128 /* ERROR "complex128 \(type\) is not an expression" */
// constants
const c1 = 991
const c2 float32 = 0.5
0 /* ERROR "0 \(untyped integer constant\) is not used" */
c1 /* ERROR "c1 \(untyped integer constant 991\) is not used" */
c2 /* ERROR "c2 \(constant 1/2 of type float32\) is not used" */
c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2
// variables
x /* ERROR "x \(variable of type int\) is not used" */
// values
x /* ERROR "x != x \(untyped boolean value\) is not used" */ != x
x /* ERROR "x \+ x \(value of type int\) is not used" */ + x
// value, ok's
const s = "foo"
m /* ERROR "m\[s\] \(value, ok of type int\) is not used" */ [s]
}

View File

@ -95,7 +95,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
case *Builtin:
obj.used = true // for built-ins defined by package unsafe
x.mode = builtin
x.val = exact.MakeInt64(int64(obj.id))
x.id = obj.id
default:
unreachable()