diff --git a/go/types/eval.go b/go/types/eval.go index dfff1118..9232f685 100644 --- a/go/types/eval.go +++ b/go/types/eval.go @@ -41,6 +41,8 @@ func New(str string) Type { // Eval returns the type and, if constant, the value for the // expression or type literal string str evaluated in scope. +// If the expression contains function literals, the function +// bodies are ignored (though they must be syntactically correct). // // If pkg == nil, the Universe scope is used and the provided // scope is ignored. Otherwise, the scope must belong to the diff --git a/go/types/eval_test.go b/go/types/eval_test.go index cf73de38..734f50de 100644 --- a/go/types/eval_test.go +++ b/go/types/eval_test.go @@ -6,16 +6,21 @@ package types -import "testing" +import ( + "go/parser" + "go/token" + "strings" + "testing" +) func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) { gotTyp, gotVal, err := Eval(str, pkg, scope) if err != nil { - t.Errorf("Eval(%s) failed: %s", str, err) + t.Errorf("Eval(%q) failed: %s", str, err) return } if gotTyp == nil { - t.Errorf("Eval(%s) got nil type but no error", str) + t.Errorf("Eval(%q) got nil type but no error", str) return } @@ -23,14 +28,14 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty if typ != nil { // we have a type, check identity if !IsIdentical(gotTyp, typ) { - t.Errorf("Eval(%s) got type %s, want %s", str, gotTyp, typ) + t.Errorf("Eval(%q) got type %s, want %s", str, gotTyp, typ) return } } else { // we have a string, compare type string gotStr := gotTyp.String() if gotStr != typStr { - t.Errorf("Eval(%s) got type %s, want %s", str, gotStr, typStr) + t.Errorf("Eval(%q) got type %s, want %s", str, gotStr, typStr) return } } @@ -41,7 +46,7 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty gotStr = gotVal.String() } if gotStr != valStr { - t.Errorf("Eval(%s) got value %s, want %s", str, gotStr, valStr) + t.Errorf("Eval(%q) got value %s, want %s", str, gotStr, valStr) } } @@ -57,4 +62,81 @@ func TestEvalComposite(t *testing.T) { } } -// TODO(gri) expand +func TestEvalArith(t *testing.T) { + var tests = []string{ + `true`, + `false == false`, + `12345678 + 87654321 == 99999999`, + `10 * 20 == 200`, + `(1<<1000)*2 >> 100 == 2<<900`, + `"foo" + "bar" == "foobar"`, + `"abc" <= "bcd"`, + `len([10]struct{}{}) == 2*5`, + } + for _, test := range tests { + testEval(t, nil, nil, test, Typ[UntypedBool], "", "true") + } +} + +func TestEvalContext(t *testing.T) { + src := ` +package p +import "fmt" +import m "math" +const c = 3.0 +type T []int +func f(a int, s string) float64 { + const d int = c + 1 + var x int + x = a + len(s) + return float64(x) +} +` + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "p", src, 0) + if err != nil { + t.Fatal(err) + } + + pkg, err := Check("p", fset, file) + if err != nil { + t.Fatal(err) + } + + pkgScope := pkg.scope + if n := pkgScope.NumChildren(); n != 1 { + t.Fatalf("got %d file scopes, want 1", n) + } + + fileScope := pkgScope.Child(0) + if n := fileScope.NumChildren(); n != 1 { + t.Fatalf("got %d functions scopes, want 1", n) + } + + funcScope := fileScope.Child(0) + + var tests = []string{ + `true => true, untyped boolean`, + `fmt.Println => , func(a·3 ...interface{}) (n·1 int, err·2 error)`, + `c => 3, untyped float`, + `T => , p.T`, + `a => , int`, + `s => , string`, + `d => 4, int`, + `x => , int`, + `d/c => 1, int`, + `c/2 => 3/2, untyped float`, + `m.Pi < m.E => false, untyped boolean`, + } + for _, test := range tests { + str, typ := split(test, ", ") + str, val := split(str, "=>") + testEval(t, pkg, funcScope, str, nil, typ, val) + } +} + +// split splits string s at the first occurrence of s. +func split(s, sep string) (string, string) { + i := strings.Index(s, sep) + return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):]) +}