...

Source file src/go/types/eval_test.go

Documentation: go/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  	"fmt"
		11  	"go/ast"
		12  	"go/importer"
		13  	"go/parser"
		14  	"go/token"
		15  	"internal/testenv"
		16  	"strings"
		17  	"testing"
		18  
		19  	. "go/types"
		20  )
		21  
		22  func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) {
		23  	gotTv, err := Eval(fset, pkg, pos, expr)
		24  	if err != nil {
		25  		t.Errorf("Eval(%q) failed: %s", expr, err)
		26  		return
		27  	}
		28  	if gotTv.Type == nil {
		29  		t.Errorf("Eval(%q) got nil type but no error", expr)
		30  		return
		31  	}
		32  
		33  	// compare types
		34  	if typ != nil {
		35  		// we have a type, check identity
		36  		if !Identical(gotTv.Type, typ) {
		37  			t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ)
		38  			return
		39  		}
		40  	} else {
		41  		// we have a string, compare type string
		42  		gotStr := gotTv.Type.String()
		43  		if gotStr != typStr {
		44  			t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr)
		45  			return
		46  		}
		47  	}
		48  
		49  	// compare values
		50  	gotStr := ""
		51  	if gotTv.Value != nil {
		52  		gotStr = gotTv.Value.ExactString()
		53  	}
		54  	if gotStr != valStr {
		55  		t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
		56  	}
		57  }
		58  
		59  func TestEvalBasic(t *testing.T) {
		60  	fset := token.NewFileSet()
		61  	for _, typ := range Typ[Bool : String+1] {
		62  		testEval(t, fset, nil, token.NoPos, typ.Name(), typ, "", "")
		63  	}
		64  }
		65  
		66  func TestEvalComposite(t *testing.T) {
		67  	fset := token.NewFileSet()
		68  	for _, test := range independentTestTypes {
		69  		testEval(t, fset, nil, token.NoPos, test.src, nil, test.str, "")
		70  	}
		71  }
		72  
		73  func TestEvalArith(t *testing.T) {
		74  	var tests = []string{
		75  		`true`,
		76  		`false == false`,
		77  		`12345678 + 87654321 == 99999999`,
		78  		`10 * 20 == 200`,
		79  		`(1<<500)*2 >> 100 == 2<<400`,
		80  		`"foo" + "bar" == "foobar"`,
		81  		`"abc" <= "bcd"`,
		82  		`len([10]struct{}{}) == 2*5`,
		83  	}
		84  	fset := token.NewFileSet()
		85  	for _, test := range tests {
		86  		testEval(t, fset, nil, token.NoPos, test, Typ[UntypedBool], "", "true")
		87  	}
		88  }
		89  
		90  func TestEvalPos(t *testing.T) {
		91  	testenv.MustHaveGoBuild(t)
		92  
		93  	// The contents of /*-style comments are of the form
		94  	//	expr => value, type
		95  	// where value may be the empty string.
		96  	// Each expr is evaluated at the position of the comment
		97  	// and the result is compared with the expected value
		98  	// and type.
		99  	var sources = []string{
	 100  		`
	 101  		package p
	 102  		import "fmt"
	 103  		import m "math"
	 104  		const c = 3.0
	 105  		type T []int
	 106  		func f(a int, s string) float64 {
	 107  			fmt.Println("calling f")
	 108  			_ = m.Pi // use package math
	 109  			const d int = c + 1
	 110  			var x int
	 111  			x = a + len(s)
	 112  			return float64(x)
	 113  			/* true => true, untyped bool */
	 114  			/* fmt.Println => , func(a ...interface{}) (n int, err error) */
	 115  			/* c => 3, untyped float */
	 116  			/* T => , p.T */
	 117  			/* a => , int */
	 118  			/* s => , string */
	 119  			/* d => 4, int */
	 120  			/* x => , int */
	 121  			/* d/c => 1, int */
	 122  			/* c/2 => 3/2, untyped float */
	 123  			/* m.Pi < m.E => false, untyped bool */
	 124  		}
	 125  		`,
	 126  		`
	 127  		package p
	 128  		/* c => 3, untyped float */
	 129  		type T1 /* T1 => , p.T1 */ struct {}
	 130  		var v1 /* v1 => , int */ = 42
	 131  		func /* f1 => , func(v1 float64) */ f1(v1 float64) {
	 132  			/* f1 => , func(v1 float64) */
	 133  			/* v1 => , float64 */
	 134  			var c /* c => 3, untyped float */ = "foo" /* c => , string */
	 135  			{
	 136  				var c struct {
	 137  					c /* c => , string */ int
	 138  				}
	 139  				/* c => , struct{c int} */
	 140  				_ = c
	 141  			}
	 142  			_ = func(a, b, c int) /* c => , string */ {
	 143  				/* c => , int */
	 144  			}
	 145  			_ = c
	 146  			type FT /* FT => , p.FT */ interface{}
	 147  		}
	 148  		`,
	 149  		`
	 150  		package p
	 151  		/* T => , p.T */
	 152  		`,
	 153  		`
	 154  		package p
	 155  		import "io"
	 156  		type R = io.Reader
	 157  		func _() {
	 158  			/* interface{R}.Read => , func(_ interface{io.Reader}, p []byte) (n int, err error) */
	 159  			_ = func() {
	 160  				/* interface{io.Writer}.Write => , func(_ interface{io.Writer}, p []byte) (n int, err error) */
	 161  				type io interface {} // must not shadow io in line above
	 162  			}
	 163  			type R interface {} // must not shadow R in first line of this function body
	 164  		}
	 165  		`,
	 166  	}
	 167  
	 168  	fset := token.NewFileSet()
	 169  	var files []*ast.File
	 170  	for i, src := range sources {
	 171  		file, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
	 172  		if err != nil {
	 173  			t.Fatalf("could not parse file %d: %s", i, err)
	 174  		}
	 175  		files = append(files, file)
	 176  	}
	 177  
	 178  	conf := Config{Importer: importer.Default()}
	 179  	pkg, err := conf.Check("p", fset, files, nil)
	 180  	if err != nil {
	 181  		t.Fatal(err)
	 182  	}
	 183  
	 184  	for _, file := range files {
	 185  		for _, group := range file.Comments {
	 186  			for _, comment := range group.List {
	 187  				s := comment.Text
	 188  				if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" {
	 189  					str, typ := split(s[2:len(s)-2], ", ")
	 190  					str, val := split(str, "=>")
	 191  					testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val)
	 192  				}
	 193  			}
	 194  		}
	 195  	}
	 196  }
	 197  
	 198  // split splits string s at the first occurrence of s.
	 199  func split(s, sep string) (string, string) {
	 200  	i := strings.Index(s, sep)
	 201  	return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
	 202  }
	 203  
	 204  func TestCheckExpr(t *testing.T) {
	 205  	testenv.MustHaveGoBuild(t)
	 206  
	 207  	// Each comment has the form /* expr => object */:
	 208  	// expr is an identifier or selector expression that is passed
	 209  	// to CheckExpr at the position of the comment, and object is
	 210  	// the string form of the object it denotes.
	 211  	const src = `
	 212  package p
	 213  
	 214  import "fmt"
	 215  
	 216  const c = 3.0
	 217  type T []int
	 218  type S struct{ X int }
	 219  
	 220  func f(a int, s string) S {
	 221  	/* fmt.Println => func fmt.Println(a ...interface{}) (n int, err error) */
	 222  	/* fmt.Stringer.String => func (fmt.Stringer).String() string */
	 223  	fmt.Println("calling f")
	 224  
	 225  	var fmt struct{ Println int }
	 226  	/* fmt => var fmt struct{Println int} */
	 227  	/* fmt.Println => field Println int */
	 228  	/* f(1, "").X => field X int */
	 229  	fmt.Println = 1
	 230  
	 231  	/* append => builtin append */
	 232  
	 233  	/* new(S).X => field X int */
	 234  
	 235  	return S{}
	 236  }`
	 237  
	 238  	fset := token.NewFileSet()
	 239  	f, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
	 240  	if err != nil {
	 241  		t.Fatal(err)
	 242  	}
	 243  
	 244  	conf := Config{Importer: importer.Default()}
	 245  	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
	 246  	if err != nil {
	 247  		t.Fatal(err)
	 248  	}
	 249  
	 250  	checkExpr := func(pos token.Pos, str string) (Object, error) {
	 251  		expr, err := parser.ParseExprFrom(fset, "eval", str, 0)
	 252  		if err != nil {
	 253  			return nil, err
	 254  		}
	 255  
	 256  		info := &Info{
	 257  			Uses:			 make(map[*ast.Ident]Object),
	 258  			Selections: make(map[*ast.SelectorExpr]*Selection),
	 259  		}
	 260  		if err := CheckExpr(fset, pkg, pos, expr, info); err != nil {
	 261  			return nil, fmt.Errorf("CheckExpr(%q) failed: %s", str, err)
	 262  		}
	 263  		switch expr := expr.(type) {
	 264  		case *ast.Ident:
	 265  			if obj, ok := info.Uses[expr]; ok {
	 266  				return obj, nil
	 267  			}
	 268  		case *ast.SelectorExpr:
	 269  			if sel, ok := info.Selections[expr]; ok {
	 270  				return sel.Obj(), nil
	 271  			}
	 272  			if obj, ok := info.Uses[expr.Sel]; ok {
	 273  				return obj, nil // qualified identifier
	 274  			}
	 275  		}
	 276  		return nil, fmt.Errorf("no object for %s", str)
	 277  	}
	 278  
	 279  	for _, group := range f.Comments {
	 280  		for _, comment := range group.List {
	 281  			s := comment.Text
	 282  			if len(s) >= 4 && strings.HasPrefix(s, "/*") && strings.HasSuffix(s, "*/") {
	 283  				pos := comment.Pos()
	 284  				expr, wantObj := split(s[2:len(s)-2], "=>")
	 285  				obj, err := checkExpr(pos, expr)
	 286  				if err != nil {
	 287  					t.Errorf("%s: %s", fset.Position(pos), err)
	 288  					continue
	 289  				}
	 290  				if obj.String() != wantObj {
	 291  					t.Errorf("%s: checkExpr(%s) = %s, want %v",
	 292  						fset.Position(pos), expr, obj, wantObj)
	 293  				}
	 294  			}
	 295  		}
	 296  	}
	 297  }
	 298  

View as plain text