1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This file contains tests for Eval.
6
7package types_test
8
9import (
10	"go/ast"
11	"go/importer"
12	"go/parser"
13	"go/token"
14	"internal/testenv"
15	"strings"
16	"testing"
17
18	. "go/types"
19)
20
21func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) {
22	gotTv, err := Eval(fset, pkg, pos, expr)
23	if err != nil {
24		t.Errorf("Eval(%q) failed: %s", expr, err)
25		return
26	}
27	if gotTv.Type == nil {
28		t.Errorf("Eval(%q) got nil type but no error", expr)
29		return
30	}
31
32	// compare types
33	if typ != nil {
34		// we have a type, check identity
35		if !Identical(gotTv.Type, typ) {
36			t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ)
37			return
38		}
39	} else {
40		// we have a string, compare type string
41		gotStr := gotTv.Type.String()
42		if gotStr != typStr {
43			t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr)
44			return
45		}
46	}
47
48	// compare values
49	gotStr := ""
50	if gotTv.Value != nil {
51		gotStr = gotTv.Value.ExactString()
52	}
53	if gotStr != valStr {
54		t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
55	}
56}
57
58func TestEvalBasic(t *testing.T) {
59	fset := token.NewFileSet()
60	for _, typ := range Typ[Bool : String+1] {
61		testEval(t, fset, nil, token.NoPos, typ.Name(), typ, "", "")
62	}
63}
64
65func TestEvalComposite(t *testing.T) {
66	fset := token.NewFileSet()
67	for _, test := range independentTestTypes {
68		testEval(t, fset, nil, token.NoPos, test.src, nil, test.str, "")
69	}
70}
71
72func TestEvalArith(t *testing.T) {
73	var tests = []string{
74		`true`,
75		`false == false`,
76		`12345678 + 87654321 == 99999999`,
77		`10 * 20 == 200`,
78		`(1<<1000)*2 >> 100 == 2<<900`,
79		`"foo" + "bar" == "foobar"`,
80		`"abc" <= "bcd"`,
81		`len([10]struct{}{}) == 2*5`,
82	}
83	fset := token.NewFileSet()
84	for _, test := range tests {
85		testEval(t, fset, nil, token.NoPos, test, Typ[UntypedBool], "", "true")
86	}
87}
88
89func TestEvalPos(t *testing.T) {
90	testenv.MustHaveGoBuild(t)
91
92	// The contents of /*-style comments are of the form
93	//	expr => value, type
94	// where value may be the empty string.
95	// Each expr is evaluated at the position of the comment
96	// and the result is compared with the expected value
97	// and type.
98	var sources = []string{
99		`
100		package p
101		import "fmt"
102		import m "math"
103		const c = 3.0
104		type T []int
105		func f(a int, s string) float64 {
106			fmt.Println("calling f")
107			_ = m.Pi // use package math
108			const d int = c + 1
109			var x int
110			x = a + len(s)
111			return float64(x)
112			/* true => true, untyped bool */
113			/* fmt.Println => , func(a ...interface{}) (n int, err error) */
114			/* c => 3, untyped float */
115			/* T => , p.T */
116			/* a => , int */
117			/* s => , string */
118			/* d => 4, int */
119			/* x => , int */
120			/* d/c => 1, int */
121			/* c/2 => 3/2, untyped float */
122			/* m.Pi < m.E => false, untyped bool */
123		}
124		`,
125		`
126		package p
127		/* c => 3, untyped float */
128		type T1 /* T1 => , p.T1 */ struct {}
129		var v1 /* v1 => , int */ = 42
130		func /* f1 => , func(v1 float64) */ f1(v1 float64) {
131			/* f1 => , func(v1 float64) */
132			/* v1 => , float64 */
133			var c /* c => 3, untyped float */ = "foo" /* c => , string */
134			{
135				var c struct {
136					c /* c => , string */ int
137				}
138				/* c => , struct{c int} */
139				_ = c
140			}
141			_ = func(a, b, c int) /* c => , string */ {
142				/* c => , int */
143			}
144			_ = c
145			type FT /* FT => , p.FT */ interface{}
146		}
147		`,
148		`
149		package p
150		/* T => , p.T */
151		`,
152	}
153
154	fset := token.NewFileSet()
155	var files []*ast.File
156	for i, src := range sources {
157		file, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
158		if err != nil {
159			t.Fatalf("could not parse file %d: %s", i, err)
160		}
161		files = append(files, file)
162	}
163
164	conf := Config{Importer: importer.Default()}
165	pkg, err := conf.Check("p", fset, files, nil)
166	if err != nil {
167		t.Fatal(err)
168	}
169
170	for _, file := range files {
171		for _, group := range file.Comments {
172			for _, comment := range group.List {
173				s := comment.Text
174				if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" {
175					str, typ := split(s[2:len(s)-2], ", ")
176					str, val := split(str, "=>")
177					testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val)
178				}
179			}
180		}
181	}
182}
183
184// split splits string s at the first occurrence of s.
185func split(s, sep string) (string, string) {
186	i := strings.Index(s, sep)
187	return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
188}
189