go/types: modify Eval, EvalNode to return a TypeAndValue

The new interface makes the functions more useful by allowing clients to
check the various properties that TypeAndValue provides.

Change-Id: I8b41a27316081bea24a18ffe6fa1812e809d6f67
Reviewed-on: https://go-review.googlesource.com/2134
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Peter Collingbourne 2014-12-28 11:35:42 -08:00 committed by Robert Griesemer
parent c1ef9c75bf
commit 6370cd3a8e
3 changed files with 17 additions and 30 deletions

View File

@ -248,13 +248,14 @@ func doOneInput(input, filename string) bool {
continue continue
} }
mainFileScope := mainpkg.Object.Scope().Child(0) mainFileScope := mainpkg.Object.Scope().Child(0)
t, _, err = types.EvalNode(prog.Fset, texpr, mainpkg.Object, mainFileScope) tv, err := types.EvalNode(prog.Fset, texpr, mainpkg.Object, mainFileScope)
if err != nil { if err != nil {
ok = false ok = false
// Don't print err since its location is bad. // Don't print err since its location is bad.
e.errorf("'%s' is not a valid type: %s", typstr, err) e.errorf("'%s' is not a valid type: %s", typstr, err)
continue continue
} }
t = tv.Type
} }
e.types = append(e.types, t) e.types = append(e.types, t)
} }

View File

@ -11,8 +11,6 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"golang.org/x/tools/go/exact"
) )
// New is a convenience function to create a new type from a given // New is a convenience function to create a new type from a given
@ -22,11 +20,11 @@ import (
// Position info for objects in the result type is undefined. // Position info for objects in the result type is undefined.
// //
func New(str string) Type { func New(str string) Type {
typ, _, err := Eval(str, nil, nil) tv, err := Eval(str, nil, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return typ return tv.Type
} }
// Eval returns the type and, if constant, the value for the // Eval returns the type and, if constant, the value for the
@ -50,10 +48,10 @@ func New(str string) Type {
// level untyped constants will return an untyped type rather then the // level untyped constants will return an untyped type rather then the
// respective context-specific type. // respective context-specific type.
// //
func Eval(str string, pkg *Package, scope *Scope) (typ Type, val exact.Value, err error) { func Eval(str string, pkg *Package, scope *Scope) (TypeAndValue, error) {
node, err := parser.ParseExpr(str) node, err := parser.ParseExpr(str)
if err != nil { if err != nil {
return nil, nil, err return TypeAndValue{}, err
} }
// Create a file set that looks structurally identical to the // Create a file set that looks structurally identical to the
@ -70,7 +68,7 @@ func Eval(str string, pkg *Package, scope *Scope) (typ Type, val exact.Value, er
// An error is returned if the scope is incorrect // An error is returned if the scope is incorrect
// if the node cannot be evaluated in the scope. // if the node cannot be evaluated in the scope.
// //
func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (typ Type, val exact.Value, err error) { func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (tv TypeAndValue, err error) {
// verify package/scope relationship // verify package/scope relationship
if pkg == nil { if pkg == nil {
scope = Universe scope = Universe
@ -81,7 +79,7 @@ func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (t
} }
// s == nil || s == pkg.scope // s == nil || s == pkg.scope
if s == nil { if s == nil {
return nil, nil, fmt.Errorf("scope does not belong to package %s", pkg.name) return TypeAndValue{}, fmt.Errorf("scope does not belong to package %s", pkg.name)
} }
} }
@ -92,18 +90,6 @@ func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (t
// evaluate node // evaluate node
var x operand var x operand
check.exprOrType(&x, node) check.rawExpr(&x, node, nil)
switch x.mode { return TypeAndValue{x.mode, x.typ, x.val}, nil
case invalid, novalue:
fallthrough
default:
unreachable() // or bailed out with error
case constant:
val = x.val
fallthrough
case typexpr, variable, mapindex, value, commaok:
typ = x.typ
}
return
} }

View File

@ -18,12 +18,12 @@ import (
) )
func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) { func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) {
gotTyp, gotVal, err := Eval(str, pkg, scope) gotTv, err := Eval(str, pkg, scope)
if err != nil { if err != nil {
t.Errorf("Eval(%q) failed: %s", str, err) t.Errorf("Eval(%q) failed: %s", str, err)
return return
} }
if gotTyp == nil { if gotTv.Type == nil {
t.Errorf("Eval(%q) got nil type but no error", str) t.Errorf("Eval(%q) got nil type but no error", str)
return return
} }
@ -31,13 +31,13 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty
// compare types // compare types
if typ != nil { if typ != nil {
// we have a type, check identity // we have a type, check identity
if !Identical(gotTyp, typ) { if !Identical(gotTv.Type, typ) {
t.Errorf("Eval(%q) got type %s, want %s", str, gotTyp, typ) t.Errorf("Eval(%q) got type %s, want %s", str, gotTv.Type, typ)
return return
} }
} else { } else {
// we have a string, compare type string // we have a string, compare type string
gotStr := gotTyp.String() gotStr := gotTv.Type.String()
if gotStr != typStr { if gotStr != typStr {
t.Errorf("Eval(%q) got type %s, want %s", str, gotStr, typStr) t.Errorf("Eval(%q) got type %s, want %s", str, gotStr, typStr)
return return
@ -46,8 +46,8 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty
// compare values // compare values
gotStr := "" gotStr := ""
if gotVal != nil { if gotTv.Value != nil {
gotStr = gotVal.String() gotStr = gotTv.Value.String()
} }
if gotStr != valStr { if gotStr != valStr {
t.Errorf("Eval(%q) got value %s, want %s", str, gotStr, valStr) t.Errorf("Eval(%q) got value %s, want %s", str, gotStr, valStr)