Home | History | Annotate | Download | only in types
      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 
      7 package types_test
      8 
      9 import (
     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 
     21 func 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 
     58 func 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 
     65 func 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 
     72 func 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 
     89 func 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.
    185 func 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