Example #1
0
File: main.go Project: velour/stop
func main() {
	flag.Parse()

	in, err := os.Open(flag.Arg(0))
	if err != nil {
		die(err)
	}
	defer in.Close()

	src, err := ioutil.ReadAll(in)
	if err != nil {
		die(err)
	}

	l := token.NewLexer(in.Name(), string(src))
	p := ast.NewParser(l)
	root, err := ast.Parse(p)
	if err != nil {
		die(err)
	}

	if *gv {
		dot(root)
	} else {
		out := bufio.NewWriter(os.Stdout)
		if err = pretty.Fprint(out, root); err != nil {
			die(err)
		}
		out.Flush()
	}
}
Example #2
0
func TestScopeFind(t *testing.T) {
	src := `
		package testpkg
		const (
			π = 3.1415926535
			name = "eaburns"
		)
		var mayChange int = f()
		var len = 1	// Shadows the predeclared len function.
		func f() int { return 0 }
	`
	p := NewParser(token.NewLexer("", src))
	s, err := pkgDecls([]*File{parseFile(p)})
	if err != nil {
		panic(err)
	}

	tests := []struct {
		id       string
		declType reflect.Type
	}{
		{"π", reflect.TypeOf(&constSpecView{})},
		{"name", reflect.TypeOf(&constSpecView{})},
		{"mayChange", reflect.TypeOf(&varSpecView{})},
		{"len", reflect.TypeOf(&varSpecView{})},
		{"f", reflect.TypeOf(&FunctionDecl{})},
		{"notDefined", nil},
	}
	for _, test := range tests {
		d := s.Find(test.id)
		typ := reflect.TypeOf(d)
		if (d == nil && test.declType != nil) || (d != nil && typ != test.declType) {
			t.Errorf("Find(%s)=%s, want %s", test.id, typ, test.declType)
		}
	}

	// Our testing source file shadowed len, but let's check that it's
	// still findable in the universal scope.
	d := univScope.Find("len")
	typ := reflect.TypeOf(d)
	lenType := reflect.TypeOf(&predeclaredFunc{})
	if d == nil || typ != lenType {
		t.Errorf("Find(len)=%s, want %s", typ, lenType)
	}

	// Rune and byte are aliases for int32 and uint8 respectively;
	// the have the same declaration.
	if univScope.Find("rune") != univScope.Find("int32") {
		t.Errorf("rune is not an alias for int32")
	}
	if univScope.Find("byte") != univScope.Find("uint8") {
		t.Errorf("byte is not an alias for uint8")
	}
}
Example #3
0
func parseSrcFiles(t *testing.T, srcFiles []string) []*File {
	var files []*File
	for _, src := range srcFiles {
		p := NewParser(token.NewLexer("", src))
		file, err := Parse(p)
		if err == nil {
			files = append(files, file)
			continue
		}
		t.Fatalf("Parse(%s), got unexpected error %v", src, err)
		return nil
	}
	return files
}
Example #4
0
func TestCheckTypes(t *testing.T) {
	// The source must contain an identifier α.
	// The test calls Check and compares the type of α to the given Type.
	tests := []struct {
		src string
		t   Type
	}{
		// Literals
		{`package a; const α = 1`, Untyped(IntegerConst)},
		{`package a; const α = 1.0`, Untyped(FloatConst)},
		{`package a; const α = 1.0i`, Untyped(ComplexConst)},
		{`package a; const α = 'a'`, Untyped(RuneConst)},
		{`package a; const α = "Hello, World!"`, Untyped(StringConst)},

		// Identifiers
		{`package a; const α = true`, Untyped(BoolConst)},
		{`package a; const α = false`, Untyped(BoolConst)},
		{`package a; const α = iota`, Untyped(IntegerConst)},
		{`package a; const ( a = iota; α )`, Untyped(IntegerConst)},
		{`package a; const α int = 1`, intType},
		{`package a; const α float64 = 'a'`, float64Type},
		{`package a; const a, b, c, α int = 1, 2, 3, 4`, intType},
		{`package a; const ( a int = iota; α )`, intType},

		// UnaryOps
		{`package a; const α = -1`, Untyped(IntegerConst)},
		{`package a; const α = -1.0`, Untyped(FloatConst)},
		{`package a; const α = -1.0i`, Untyped(ComplexConst)},
		{`package a; const α = -'a'`, Untyped(RuneConst)},
		{`package a; const α = !true`, Untyped(BoolConst)},
		{`package a; const α = -iota`, Untyped(IntegerConst)},
		{`package a; const α = ^1`, Untyped(IntegerConst)},
		{`package a; const α = +1.189`, Untyped(FloatConst)},
		{`package a; const α int = -1`, intType},
		{`package a; const α int = +1.0`, intType},
		{`package a; const α int = ^1`, intType},
		{`package a; const α int = ^1`, intType},
	}
	for _, test := range tests {
		l := token.NewLexer("", test.src)
		p := NewParser(l)
		f := parseFile(p)
		if err := Check([]*File{f}); err != nil {
			t.Errorf("Check(%v), unexpected error: %v", test.src, err)
			continue
		}
		d := f.syms.Find("α")
		if d == nil {
			t.Errorf("Check(%v): failed to find symbol α", test.src)
			continue
		}
		var typ Type
		switch d := d.(type) {
		case *constSpecView:
			typ = d.Type
		case *varSpecView:
			typ = d.Type
		default:
			panic("declaration type not supported")
		}
		if !eq.Deep(typ, test.t) {
			t.Errorf("Check(%v)=%v: α's type is %v, want %v", test.src,
				pretty.String(f), pretty.String(typ), pretty.String(test.t))
		}
	}
}
Example #5
0
func TestConstFolding(t *testing.T) {
	runeNeg97 := intLit("-97")
	runeNeg97.Rune = true

	// The source must contain a const α. The test calls Check on the source and
	// compares the resulting value of α to the given Expression.
	tests := []struct {
		src string
		v   Expression
	}{
		{`package a; const α = 1`, intLit("1")},
		{`package a; const α = 1.0`, floatLit("1.0")},
		{`package a; const α = 1.0i`, imgLit("1.0")},
		{`package a; const α = 'a'`, runeLit('a')},
		{`package a; const α = "Hello, World!"`, strLit("Hello, World!")},
		{`package a; const α = true`, &BoolLiteral{Value: true}},
		{`package a; const α = false`, &BoolLiteral{Value: false}},
		{`package a; const α, b = b, 5`, intLit("5")},
		{`package a; const α = iota`, intLit("0")},
		{`package a; const ( zero = iota; α )`, intLit("1")},
		{`package a; const ( zero = iota; one; α )`, intLit("2")},
		{`package a; const α = +1`, intLit("1")},
		{`package a; const α = -1`, intLit("-1")},
		{`package a; const α = - -1`, intLit("1")},
		{`package a; const α = +'a'`, runeLit('a')},
		{`package a; const α = -'a'`, runeNeg97},
		{`package a; const α = +1.0`, floatLit("1.0")},
		{`package a; const α = - - -1.0`, floatLit("-1.0")},
		{`package a; const α = +1.0i`, imgLit("1.0")},
		{`package a; const α = - - -1.0i`, imgLit("-1.0")},
		{`package a; const α = ^0`, intLit("-1")},
		{`package a; const α = ^1`, intLit("-2")},
		{`package a; const α float64 = ^256`, intLit("-257")},
		{`package a; const α = !true`, &BoolLiteral{Value: false}},
		{`package a; const α = !!true`, &BoolLiteral{Value: true}},
		{`package a; const α = !false`, &BoolLiteral{Value: true}},
		{`package a; const α = !!false`, &BoolLiteral{Value: false}},
		{`package a; const f, α = false, !f`, &BoolLiteral{Value: true}},
	}
	for _, test := range tests {
		l := token.NewLexer("", test.src)
		p := NewParser(l)
		f := parseFile(p)
		if err := Check([]*File{f}); err != nil {
			t.Errorf("Check(%v), unexpected error: %v", test.src, err)
			continue
		}
		d := f.syms.Find("α")
		if d == nil {
			t.Errorf("Check(%v): failed to find symbol α", test.src)
			continue
		}
		a, ok := d.(*constSpecView)
		if !ok {
			t.Errorf("Check(%v): α is not a const", test.src)
			continue
		}
		if !eq.Deep(a.Value, test.v) {
			t.Errorf("Check(%v)=%v: α folded to %v, want %v", test.src,
				pretty.String(f), pretty.String(a.Value), pretty.String(test.v))
		}
	}
}
Example #6
0
func TestCheckErrors(t *testing.T) {
	tests := []struct {
		src  []string
		errs []reflect.Type
	}{
		{[]string{`package a`, `package a`}, []reflect.Type{}},

		// Consts
		{
			[]string{
				`package a
				const (
					complexConst = 5i
					floatConst = 3.1415926535
					intConst = 6
					runeConst = 'α'
					stringConst = "Hello, World!"
					trueConst, falseConst = true, false
				)`,
			},
			[]reflect.Type{},
		},
		{
			[]string{
				`package a; const pi = 3.1415926535`,
				`package a; const π = pi`,
			},
			[]reflect.Type{},
		},
		{
			[]string{
				`package a
				const (
					zero = iota
					one
					two
				)`,
			},
			[]reflect.Type{},
		},
		{[]string{`package a; const i int = 5`}, []reflect.Type{}},
		{[]string{`package a; const f float64 = 5`}, []reflect.Type{}},
		{[]string{`package a; const c complex128 = 5`}, []reflect.Type{}},
		{[]string{`package a; const s string = ""`}, []reflect.Type{}},
		{[]string{`package a; const r rune = 'β'`}, []reflect.Type{}},
		{[]string{`package a; const r int = 'β'`}, []reflect.Type{}},
		{[]string{`package a; const b bool = true`}, []reflect.Type{}},
		{[]string{`package a; const b bool = false`}, []reflect.Type{}},
		{[]string{`package a; const a bool = false; const b bool = a`}, []reflect.Type{}},
		{[]string{`package a; const a int = 0; const b int = a`}, []reflect.Type{}},
		{[]string{`package a; const a int32 = 0; const b rune = a`}, []reflect.Type{}},
		{
			[]string{`package a; const a = undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; const a, b = 1`},
			[]reflect.Type{reflect.TypeOf(AssignCountMismatch{})},
		},
		{
			[]string{`package a; const nilConst = nil`},
			[]reflect.Type{reflect.TypeOf(NotConstant{})},
		},
		{
			[]string{`package a; const a = a`},
			[]reflect.Type{reflect.TypeOf(ConstantLoop{})},
		},
		{
			[]string{
				`package a; const a = b`,
				`package a; const b = c`,
				`package a; const c = a`,
			},
			[]reflect.Type{reflect.TypeOf(ConstantLoop{})},
		},
		{
			[]string{`package a; const i int = ""`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const f float64 = "foo"`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const c complex128 = true`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const s string = 'a'`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const r rune = ""`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const b bool = "hi"`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const b bool = 0`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const i uint8 = 256`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const a uint8 = 0; const b int8 = a`},
			[]reflect.Type{reflect.TypeOf(BadAssign{})},
		},
		{
			[]string{`package a; const c undeclared = 256`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			// Report multiple errors.
			[]string{
				`package a
				const c undeclared = 256
				const d uint8 = 256`,
			},
			[]reflect.Type{
				reflect.TypeOf(Undeclared{}),
				reflect.TypeOf(Unrepresentable{}),
			},
		},
		{
			// Don't repeat errors.
			[]string{
				`package a
				const c = undeclared
				const d = c
				const e = c
				const f = c`,
			},
			[]reflect.Type{
				reflect.TypeOf(Undeclared{}),
			},
		},

		// UnaryOps
		{[]string{`package a; const a float64 = +256`}, []reflect.Type{}},
		{[]string{`package a; const a float64 = -256`}, []reflect.Type{}},
		{[]string{`package a; const a float64 = ^256`}, []reflect.Type{}},
		{[]string{`package a; const a float64 = 1; const b = -a`}, []reflect.Type{}},
		{[]string{`package a; const a complex128 = 1; const b = -a`}, []reflect.Type{}},
		{[]string{`package a; const t bool = true; const f = !t`}, []reflect.Type{}},
		{
			[]string{`package a; const a uint = -undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; const a uint = +"hello"`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const a uint = -"hello"`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const a uint = !0`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const a uint = ^1.0`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const a uint = ^1.1`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const a uint = -1`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const a, b uint = 1, -a`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const a uint = -1.0i`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{`package a; const a uint8 = +256`},
			[]reflect.Type{reflect.TypeOf(Unrepresentable{})},
		},
		{
			[]string{
				`package a
				const a string = "hello"
				const b int = -a`,
			},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{
				`package a
				const a float64 = 1.1
				const b complex128 = 1.1i
				const c, d int = ^a, ^b`,
			},
			[]reflect.Type{
				reflect.TypeOf(InvalidOperation{}),
				reflect.TypeOf(InvalidOperation{}),
			},
		},
		{
			[]string{`package a; const i int = 1; const f = !i`},
			[]reflect.Type{reflect.TypeOf(InvalidOperation{})},
		},
		{
			[]string{`package a; const c = nil`},
			[]reflect.Type{reflect.TypeOf(NotConstant{})},
		},

		// Types
		{[]string{`package a; type T int`}, []reflect.Type{}},
		{
			[]string{`package a; type T undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{[]string{`package a; type T int`}, []reflect.Type{}},
		{
			[]string{`package a; type T undeclared0.undeclared1`},
			[]reflect.Type{
				reflect.TypeOf(Undeclared{}),
				reflect.TypeOf(Undeclared{}),
			},
		},
		{
			[]string{`package a; type T [5]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T [1.0]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T ["hello"]int`},
			[]reflect.Type{reflect.TypeOf(BadArraySize{})},
		},
		{
			// Too big for 64 bits, surely too big for an int.
			[]string{`package a; type T [18446744073709551616]int`},
			[]reflect.Type{reflect.TypeOf(BadArraySize{})},
		},
		{
			[]string{`package a; type T [-1]int`},
			[]reflect.Type{reflect.TypeOf(BadArraySize{})},
		},
		{
			[]string{`package a; type T [1.1]int`},
			[]reflect.Type{reflect.TypeOf(BadArraySize{})},
		},
		{
			[]string{`package a; type T [^1]int`},
			[]reflect.Type{reflect.TypeOf(BadArraySize{})},
		},
		{
			[]string{`package a; const c = 5; type T [c]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; const c = 5; type T [-1]undeclared`},
			[]reflect.Type{
				reflect.TypeOf(BadArraySize{}),
				reflect.TypeOf(Undeclared{}),
			},
		},
		{
			// Don't report both BadArraySize and Undeclared.
			[]string{`package a; const c = 5; type T [undeclared]undeclared`},
			[]reflect.Type{
				reflect.TypeOf(Undeclared{}),
				reflect.TypeOf(Undeclared{}),
			},
		},
		{
			[]string{`package a; type T [undeclared]int`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; type T []int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T []undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; type T [][3][]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T *int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T ***[5]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T *undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; type T map[string]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T map[string]map[string]int`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T map[map[string]int]undeclared`},
			[]reflect.Type{
				reflect.TypeOf(BadMapKey{}),
				reflect.TypeOf(Undeclared{}),
			},
		},
		{
			// Don't report both BadMapKey and Undeclared.
			[]string{`package a; type T map[map[undeclared]int]undeclared`},
			[]reflect.Type{
				reflect.TypeOf(Undeclared{}),
				reflect.TypeOf(Undeclared{}),
			},
		},
		{
			[]string{`package a; type T map[undefined]int`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; type T map[string]undefined`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},
		{
			[]string{`package a; type T map[map[string]int]int`},
			[]reflect.Type{reflect.TypeOf(BadMapKey{})},
		},
		{
			[]string{`
				package a
				type (
					T chan int
					U chan <- int
					V <-chan int
				)`,
			},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T chan undeclared`},
			[]reflect.Type{reflect.TypeOf(Undeclared{})},
		},

		// Recursive Types
		{
			[]string{`package a; type T T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T U; type U T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T U; type U V; type V T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T [5]T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T [5][6][7]T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T U; type U V; type V [1]T`},
			[]reflect.Type{reflect.TypeOf(BadRecursiveType{})},
		},
		{
			[]string{`package a; type T []T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T [][5]T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T U; type U V; type V []T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T *T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T U; type U V; type V *T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T map[T]int`},
			[]reflect.Type{reflect.TypeOf(BadMapKey{})},
		},
		{
			[]string{`package a; type T map[string]T`},
			[]reflect.Type{},
		},
		{
			[]string{`package a; type T chan T`},
			[]reflect.Type{},
		},
	}
	for _, test := range tests {
		want := make(map[reflect.Type]int)
		for _, e := range test.errs {
			want[e]++
		}

		var files []*File
		for _, src := range test.src {
			l := token.NewLexer("", src)
			p := NewParser(l)
			files = append(files, parseFile(p))
		}

		var got []reflect.Type
		if err := Check(files); err != nil {
			for _, e := range err.(errors).All() {
				t := reflect.TypeOf(e)
				want[t]--
				if want[t] == 0 {
					delete(want, t)
				}
				got = append(got, t)
			}
		}
		if len(want) != 0 {
			t.Errorf("Check(%v)=%v, want %v", test.src, got, test.errs)
		}
	}
}
Example #7
0
// BUG(eaburns): Test NilLiteral, BoolLiteral, and ComplexLiteral.
func TestExpressionSource(t *testing.T) {
	tests := []struct{ expr, want string }{
		{`"Hello, World!"`, `"Hello, World!"`},
		{`"αβξ"`, `"αβξ"`},
		{"\x60\\n\x0a\\n\x60", `"\\n\n\\n"`},
		{`'a'`, `'a'`},
		{`'☺'`, `'☺'`},
		{`0`, `0`},
		{`5`, `5`},
		{`0.0`, `0.0`},
		{`5.0`, `5.0`},
		{`3.1415926535`, `3.1415926535`},
		{`1e4`, `10000.0`},
		{`1e-4`, `0.0001`},
		// BUG(eaburns): ComplexLiteral tests don't test ComplexLiterals with a
		// real component, since they are never returned by the parser.
		{`0i`, `0.0i`},
		{`5i`, `5.0i`},
		{`3.1415926535i`, `3.1415926535i`},
		{`a`, `a`},
		{`abc_xyz`, `abc_xyz`},
		{`-1`, `-1`},
		{`+-1`, `+-1`},
		{`<-abc_xyz`, `<-abc_xyz`},
		{`**a`, `**a`},
		{`1 + 1`, `1 + 1`},
		{`1 * abc`, `1 * abc`},
		{`-x / *b`, `-x / *b`},
		{`a + b / c + d`, `a + b / c + d`},
		{`(a + b) / (c + d)`, `(a + b) / (c + d)`},
		{`a()`, `a()`},
		{`a(b, c)`, `a(b, c)`},
		{`a(b, c...)`, `a(b, c...)`},
		{`*a(b, (c + d) / e)`, `*a(b, (c + d) / e)`},
		{`(*int32)(4)`, `(*int32)(4)`},
		{`a.b.c`, `a.b.c`},
		{`a(b, c).d`, `a(b, c).d`},
		{`-a(b, c).d + 12`, `-a(b, c).d + 12`},
		{`a.(b)`, `a.(b)`},
		{`a().(b.c).(e)`, `a().(b.c).(e)`},
		{`a[:]`, `a[:]`},
		{`a[1:]`, `a[1:]`},
		{`a[:2]`, `a[:2]`},
		{`a[1:2]`, `a[1:2]`},
		{`a[1:2:3]`, `a[1:2:3]`},
		{`a[:len(a) - 1]`, `a[:len(a) - 1]`},
		{`a[0]`, `a[0]`},
		{`a[len(a) - 1]`, `a[len(a) - 1]`},
		{`point{a, b}`, `point{a, b}`},
		{`point{x: a, y: b}`, `point{x: a, y: b}`},
		{`(*point){x: a, y: b}`, `(*point){x: a, y: b}`},
		{`[]int`, `[]int`},
		{`[][]int32{}`, `[][]int32{}`},
		{`[2]float64`, `[2]float64`},
		{`[...]float64{0, 0}`, `[...]float64{0, 0}`},
		{`map[string]int`, `map[string]int`},
		{`map[string]*[]float64`, `map[string]*[]float64`},
		{`chan int`, `chan int`},
		{`<-chan int`, `<-chan int`},
		{`chan<- int`, `chan<- int`},
		{`chan<- <-chan int`, `chan<- <-chan int`},
		{`func(){}`, `func(){…}`},
		{`func(int){}`, `func(int){…}`},
		{`func(int)bool{}`, `func(int)bool{…}`},
		{`func(int, float64){}`, `func(int, float64){…}`},
		{`func()(int, int){}`, `func()(int, int){…}`},
		{`func(int, ...float64)(int, int){}`, `func(int, ...float64)(int, int){…}`},
		{`func(x, y int)(z int){}`, `func(x int, y int)(z int){…}`},
		{`func(x, y ...int)(z int, err error){}`, `func(x int, y ...int)(z int, err error){…}`},
		{`struct{ x, y int }{}`, `struct{…}{}`},
		{`interface{ x(); y(int)bool }{}`, `interface{…}{}`},
	}
	for _, test := range tests {
		l := token.NewLexer("", test.expr)
		p := NewParser(l)
		e := parseExpr(p)
		got := e.Source()
		if test.want != got {
			t.Errorf("(%s).Source()=[%s], want [%s]", test.expr, got, test.want)
		}
	}

	// These can't be tested above, because the parser never returns them.
	var zero big.Rat
	nodeTests := []struct {
		expr Expression
		want string
	}{
		{&NilLiteral{}, "nil"},
		{&BoolLiteral{}, "false"},
		{&BoolLiteral{Value: true}, "true"},
		{&ComplexLiteral{Real: &zero, Imaginary: &zero}, "0.0i"},
		{&ComplexLiteral{Real: &zero, Imaginary: big.NewRat(5, 1)}, "5.0i"},
		{&ComplexLiteral{Real: big.NewRat(5, 1), Imaginary: &zero}, "5.0+0.0i"},
		{&ComplexLiteral{Real: big.NewRat(5, 1), Imaginary: big.NewRat(5, 1)}, "5.0+5.0i"},
		{&ComplexLiteral{Real: big.NewRat(5, 1), Imaginary: big.NewRat(-5, 1)}, "5.0-5.0i"},
		{&ComplexLiteral{Real: big.NewRat(-5, 1), Imaginary: big.NewRat(-5, 1)}, "-5.0-5.0i"},
	}
	for _, test := range nodeTests {
		got := test.expr.Source()
		if got != test.want {
			t.Errorf("(%s).Source()=[%s], want [%s]", pretty.String(test.expr), got, test.want)
		}
	}
}