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() } }
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") } }
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 }
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)) } } }
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)) } } }
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) } } }
// 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) } } }