// checkFile performs a static semantic analysis check on the given file. func compileFile(path string, output io.Writer, goccLexer bool) error { // Lexical analysis // Syntactic analysis // Semantic analysis // Intermediate representation generation // Create lexer for the input. buf, err := ioutilx.ReadFile(path) if err != nil { return errutil.Err(err) } if path == "-" { path = "<stdin>" } fmt.Fprintf(os.Stderr, "Compiling %q\n", path) var s parser.Scanner if goccLexer { s = goccscanner.NewFromBytes(buf) } else { s = handscanner.NewFromBytes(buf) } // Parse input. p := parser.NewParser() f, err := p.Parse(s) if err != nil { if err, ok := err.(*goccerrors.Error); ok { // Unwrap Gocc error. return parser.NewError(err) } return errutil.Err(err) } file := f.(*ast.File) input := string(buf) src := semerrors.NewSource(path, input) info, err := sem.Check(file) if err != nil { if err, ok := err.(*errutil.ErrInfo); ok { // Unwrap errutil error. if err, ok := err.Err.(*semerrors.Error); ok { // Unwrap semantic analysis error, and add input source information. err.Src = src return err } } return errutil.Err(err) } // Generate LLVM IR module based on the syntax tree of the given file. module := irgen.Gen(file, info) if _, err := fmt.Fprint(output, module); err != nil { return errutil.Err(err) } return nil }
// checkFile performs a static semantic analysis check on the given file. func checkFile(path string, goccLexer bool) error { // Lexical analysis // Syntactic analysis (skip function bodies) // Top-level declarations; used for forward-declarations. // Syntactic analysis (including function bodies) // NOTE: "For each method body, we rewind the lexer to the point where the // method body began and parse the method body." // // ref: https://blogs.msdn.microsoft.com/ericlippert/2010/02/04/how-many-passes/ // Semantic analysis // Create lexer for the input. buf, err := ioutilx.ReadFile(path) if err != nil { return errutil.Err(err) } if path == "-" { path = "<stdin>" } fmt.Fprintf(os.Stderr, "Checking %q\n", path) var s parser.Scanner if goccLexer { s = goccscanner.NewFromBytes(buf) } else { s = handscanner.NewFromBytes(buf) } // Parse input. p := parser.NewParser() f, err := p.Parse(s) if err != nil { if err, ok := err.(*goccerrors.Error); ok { // Unwrap Gocc error. return parser.NewError(err) } return errutil.Err(err) } file := f.(*ast.File) input := string(buf) src := semerrors.NewSource(path, input) if _, err := sem.Check(file); err != nil { if err, ok := err.(*errutil.ErrInfo); ok { // Unwrap errutil error. if err, ok := err.Err.(*semerrors.Error); ok { // Unwrap semantic analysis error, and add input source information. err.Src = src return err } } return errutil.Err(err) } return nil }
func TestCheckValid(t *testing.T) { var golden = []struct { path string }{ {path: "../testdata/quiet/semantic/s01.c"}, {path: "../testdata/quiet/semantic/s03.c"}, {path: "../testdata/quiet/semantic/s04.c"}, {path: "../testdata/quiet/semantic/s05.c"}, {path: "../testdata/quiet/semantic/s06.c"}, {path: "../testdata/extra/semantic/missing-return-main.c"}, {path: "../testdata/extra/semantic/tentative-var-def.c"}, {path: "../testdata/extra/semantic/variable-sized-array-arg.c"}, {path: "../testdata/extra/semantic/nested-function-def.c"}, } errors.UseColor = false for _, g := range golden { buf, err := ioutil.ReadFile(g.path) if err != nil { t.Errorf("%q: %v", g.path, err) continue } input := string(buf) s := scanner.NewFromString(input) src := errors.NewSource(g.path, input) p := parser.NewParser() file, err := p.Parse(s) if err != nil { t.Error(err) continue } f := file.(*ast.File) if _, err := sem.Check(f); err != nil { if e, ok := err.(*errutil.ErrInfo); ok { // Unwrap errutil error. err = e.Err if e, ok := err.(*errors.Error); ok { // Unwrap semantic error. e.Src = src } } t.Errorf("%q: unexpected error: `%v`", g.path, err.Error()) } } }
// parseFile parses the given file and pretty-prints its abstract syntax tree to // standard output, optionally using the Gocc generated lexer. func parseFile(path string, goccLexer bool) error { // Create lexer for the input. buf, err := ioutilx.ReadFile(path) if err != nil { return errutil.Err(err) } if path == "-" { fmt.Fprintln(os.Stderr, "Parsing from standard input") } else { fmt.Fprintf(os.Stderr, "Parsing %q\n", path) } var s parser.Scanner if goccLexer { s = goccscanner.NewFromBytes(buf) } else { s = handscanner.NewFromBytes(buf) } // Parse input. p := parser.NewParser() file, err := p.Parse(s) if err != nil { if err, ok := err.(*errors.Error); ok { // Unwrap Gocc error. return parser.NewError(err) } return errutil.Err(err) } f := file.(*ast.File) for _, decl := range f.Decls { fmt.Println("=== [ Top-level declaration ] ===") fmt.Println() fmt.Printf("decl type: %T\n", decl) fmt.Println() fmt.Println("decl:", decl) fmt.Println() pretty.Print(decl) fmt.Println() spew.Print(decl) fmt.Println() fmt.Println() } return nil }
func TestGen(t *testing.T) { golden := []struct { path string want string }{ // Identifier usage. { path: "../testdata/extra/irgen/int_ident_use.c", want: "../testdata/extra/irgen/int_ident_use.ll", }, { path: "../testdata/extra/irgen/int_ident_def.c", want: "../testdata/extra/irgen/int_ident_def.ll", }, { path: "../testdata/extra/irgen/array_ident_use.c", want: "../testdata/extra/irgen/array_ident_use.ll", }, // NOTE: Not part of grammar for uC. //{ // path: "../testdata/extra/irgen/array_ident_def.c", // want: "../testdata/extra/irgen/array_ident_def.ll", //}, { path: "../testdata/extra/irgen/index_expr_use.c", want: "../testdata/extra/irgen/index_expr_use.ll", }, { path: "../testdata/extra/irgen/index_expr_def.c", want: "../testdata/extra/irgen/index_expr_def.ll", }, { path: "../testdata/extra/irgen/global_index_expr_use.c", want: "../testdata/extra/irgen/global_index_expr_use.ll", }, { path: "../testdata/extra/irgen/global_index_expr_def.c", want: "../testdata/extra/irgen/global_index_expr_def.ll", }, // Global variable declarations. { path: "../testdata/extra/irgen/global_def.c", want: "../testdata/extra/irgen/global_def.ll", }, { path: "../testdata/extra/irgen/tentative_def.c", want: "../testdata/extra/irgen/tentative_def.ll", }, // Return statements. { path: "../testdata/extra/irgen/void_ret.c", want: "../testdata/extra/irgen/void_ret.ll", }, { path: "../testdata/extra/irgen/implicit_void_ret.c", want: "../testdata/extra/irgen/implicit_void_ret.ll", }, { path: "../testdata/extra/irgen/int_ret.c", want: "../testdata/extra/irgen/int_ret.ll", }, { path: "../testdata/extra/irgen/expr_ret.c", want: "../testdata/extra/irgen/expr_ret.ll", }, { path: "../testdata/extra/irgen/global_ret.c", want: "../testdata/extra/irgen/global_ret.ll", }, // Local variable declarations. { path: "../testdata/extra/irgen/local_def.c", want: "../testdata/extra/irgen/local_def.ll", }, // If and if-else statements. { path: "../testdata/extra/irgen/if_stmt.c", want: "../testdata/extra/irgen/if_stmt.ll", }, { path: "../testdata/extra/irgen/if_else_stmt.c", want: "../testdata/extra/irgen/if_else_stmt.ll", }, // Parenthesized expressions. { path: "../testdata/extra/irgen/paren_expr.c", want: "../testdata/extra/irgen/paren_expr.ll", }, // Unary expressions. { path: "../testdata/extra/irgen/unary_expr_sub.c", want: "../testdata/extra/irgen/unary_expr_sub.ll", }, { path: "../testdata/extra/irgen/unary_expr_not.c", want: "../testdata/extra/irgen/unary_expr_not.ll", }, // Binary expressions. { path: "../testdata/extra/irgen/binary_expr_add.c", want: "../testdata/extra/irgen/binary_expr_add.ll", }, { path: "../testdata/extra/irgen/binary_expr_sub.c", want: "../testdata/extra/irgen/binary_expr_sub.ll", }, { path: "../testdata/extra/irgen/binary_expr_mul.c", want: "../testdata/extra/irgen/binary_expr_mul.ll", }, { path: "../testdata/extra/irgen/binary_expr_div.c", want: "../testdata/extra/irgen/binary_expr_div.ll", }, { path: "../testdata/extra/irgen/binary_expr_lt.c", want: "../testdata/extra/irgen/binary_expr_lt.ll", }, { path: "../testdata/extra/irgen/binary_expr_gt.c", want: "../testdata/extra/irgen/binary_expr_gt.ll", }, { path: "../testdata/extra/irgen/binary_expr_le.c", want: "../testdata/extra/irgen/binary_expr_le.ll", }, { path: "../testdata/extra/irgen/binary_expr_ge.c", want: "../testdata/extra/irgen/binary_expr_ge.ll", }, { path: "../testdata/extra/irgen/binary_expr_ne.c", want: "../testdata/extra/irgen/binary_expr_ne.ll", }, { path: "../testdata/extra/irgen/binary_expr_eq.c", want: "../testdata/extra/irgen/binary_expr_eq.ll", }, { path: "../testdata/extra/irgen/binary_expr_land.c", want: "../testdata/extra/irgen/binary_expr_land.ll", }, { path: "../testdata/extra/irgen/binary_expr_assign.c", want: "../testdata/extra/irgen/binary_expr_assign.ll", }, { path: "../testdata/extra/irgen/while_stmt.c", want: "../testdata/extra/irgen/while_stmt.ll", }, // Function parameters. { path: "../testdata/extra/irgen/func_param.c", want: "../testdata/extra/irgen/func_param.ll", }, // Nested variable declarations. { path: "../testdata/extra/irgen/nested_var_decl.c", want: "../testdata/extra/irgen/nested_var_decl.ll", }, // Call expressions. { path: "../testdata/extra/irgen/call_expr.c", want: "../testdata/extra/irgen/call_expr.ll", }, { path: "../testdata/extra/irgen/call_expr_cast.c", want: "../testdata/extra/irgen/call_expr_cast.ll", }, // NOTE: Correct output. The only difference is that Clang emits all // alloca instructions at the beginning of the entry block. Thus, disabled // for now. //{ // path: "../testdata/extra/irgen/call_expr_multi_args.c", // want: "../testdata/extra/irgen/call_expr_multi_args.ll", //}, // NOTE: Correct output. The only difference is that Clang emits all // alloca instructions at the beginning of the entry block. Thus, disabled // for now. //{ // path: "../testdata/extra/irgen/call_expr_multi_args_cast.c", // want: "../testdata/extra/irgen/call_expr_multi_args_cast.ll", //}, // Expression statements. { path: "../testdata/extra/irgen/expr_stmt.c", want: "../testdata/extra/irgen/expr_stmt.ll", }, // Index expressions. { path: "../testdata/extra/irgen/index_expr.c", want: "../testdata/extra/irgen/index_expr.ll", }, // Array arguments. { path: "../testdata/extra/irgen/array_arg.c", want: "../testdata/extra/irgen/array_arg.ll", }, // Array parameters. // TODO: Re-enable; https://github.com/mewmew/uc/issues/73 { path: "../testdata/extra/irgen/array_param.c", want: "../testdata/extra/irgen/array_param.ll", }, // Bug fixes. { path: "../testdata/extra/irgen/issue_68_nested_if.c", want: "../testdata/extra/irgen/issue_68_nested_if.ll", }, { path: "../testdata/extra/irgen/issue_68_nested_if_while.c", want: "../testdata/extra/irgen/issue_68_nested_if_while.ll", }, // TODO: Re-enable when better support for logical AND expressions have // been implemented. //{ // path: "../testdata/extra/irgen/issue_68_if_land.c", // want: "../testdata/extra/irgen/issue_68_if_land.ll", //}, { path: "../testdata/extra/irgen/issue_68_while_land.c", want: "../testdata/extra/irgen/issue_68_while_land.ll", }, // NOTE: The test case "issue_70_if_ret.c" no longer panics on SetTerm // with a nil basic block. The test case still doesn't pass, but this is // simply because Clang allocates a dedicated variable for return values, // which differs from how uclang does. Thus, the test case remains // disabled for now. //{ // path: "../testdata/extra/irgen/issue_70_if_ret.c", // want: "../testdata/extra/irgen/issue_70_if_ret.ll", //}, // NOTE: The test case "issue_70_while_ret.c" no longer panics on SetTerm // with a nil basic block. The test case still doesn't pass, but this is // simply because Clang allocates a dedicated variable for return values, // which differs from how uclang does. Thus, the test case remains // disabled for now. //{ // path: "../testdata/extra/irgen/issue_70_while_ret.c", // want: "../testdata/extra/irgen/issue_70_while_ret.ll", //}, { path: "../testdata/extra/irgen/issue_69_trunc_arg.c", want: "../testdata/extra/irgen/issue_69_trunc_arg.ll", }, // TODO: Re-enable; https://github.com/mewmew/uc/issues/73 { path: "../testdata/extra/irgen/issue_73_pointer_pointer_use.c", want: "../testdata/extra/irgen/issue_73_pointer_pointer_use.ll", }, // TODO: Re-enable; https://github.com/mewmew/uc/issues/73 { path: "../testdata/extra/irgen/issue_73_pointer_pointer_ref.c", want: "../testdata/extra/irgen/issue_73_pointer_pointer_ref.ll", }, { path: "../testdata/extra/irgen/global_array_arg.c", want: "../testdata/extra/irgen/global_array_arg.ll", }, { path: "../testdata/extra/irgen/global_array_ident_use.c", want: "../testdata/extra/irgen/global_array_ident_use.ll", }, // NOTE: Not part of grammar for uC. // { // path: "../testdata/extra/irgen/global_array_ident_def.c", // want: "../testdata/extra/irgen/global_array_ident_def.ll", // }, { path: "../testdata/extra/irgen/global_array_param.c", want: "../testdata/extra/irgen/global_array_param.ll", }, } for _, g := range golden { // Lex input. buf, err := ioutil.ReadFile(g.path) if err != nil { t.Errorf("%q: %v", g.path, err) continue } input := string(buf) s := scanner.NewFromString(input) // Parse input. p := parser.NewParser() f, err := p.Parse(s) if err != nil { t.Errorf("%q: parse error: %v", g.path, err) continue } file := f.(*ast.File) // Verify input. info, err := sem.Check(file) if err != nil { t.Errorf("%q: semantic analysis error: %v", g.path, err) continue } // Generate IR. // TODO: Remove debug output. fmt.Printf("\n=== [ %s ] ====================================\n\n", g.want) module := irgen.Gen(file, info) // Compare generated module against gold standard. buf, err = ioutil.ReadFile(g.want) if err != nil { t.Errorf("%q: %v", g.path, err) continue } got, want := module.String(), string(buf) if got != want { t.Errorf("%q: module mismatch; expected `%v`, got `%v`", g.path, want, got) // TODO: Remove debug output. fmt.Println("### FAIL:", g.path) continue } // TODO: Remove debug output. fmt.Println("PASS:", g.path) } }
// checkFile performs a static semantic analysis check on the given file. func compileFile(path string, outputPath string, goccLexer bool) error { // Lexical analysis // Syntactic analysis // Semantic analysis // Intermediate representation generation // Create lexer for the input. buf, err := ioutilx.ReadFile(path) if err != nil { return errutil.Err(err) } if path == "-" { path = "<stdin>" } fmt.Fprintf(os.Stderr, "Compiling %q\n", path) var s parser.Scanner if goccLexer { s = goccscanner.NewFromBytes(buf) } else { s = handscanner.NewFromBytes(buf) } // Parse input. p := parser.NewParser() f, err := p.Parse(s) if err != nil { if err, ok := err.(*goccerrors.Error); ok { // Unwrap Gocc error. return parser.NewError(err) } return errutil.Err(err) } file := f.(*ast.File) input := string(buf) src := semerrors.NewSource(path, input) info, err := sem.Check(file) if err != nil { if err, ok := err.(*errutil.ErrInfo); ok { // Unwrap errutil error. if err, ok := err.Err.(*semerrors.Error); ok { // Unwrap semantic analysis error, and add input source information. err.Src = src return err } } return errutil.Err(err) } // Generate LLVM IR module based on the syntax tree of the given file. module := irgen.Gen(file, info) // Add path to uc lib. lib, err := goutil.SrcDir("github.com/mewmew/uc/testdata") if err != nil { return errutil.Err(err) } lib = filepath.Join(lib, "uc.ll") // Link and create binary through clang clang := exec.Command("clang", "-o", outputPath, "-x", "ir", lib, "-") clang.Stdin = strings.NewReader(module.String()) clang.Stderr = os.Stderr clang.Stdout = os.Stdout if err := clang.Run(); err != nil { return errutil.Err(err) } return nil }
func TestParser(t *testing.T) { var golden = []struct { path string want *ast.File }{ { path: "../../testdata/quiet/lexer/l05.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 0, Name: "int", }, Lparen: 8, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 9, Name: "void", }, }, }, Rparen: 13, }, FuncName: &ast.Ident{ NamePos: 4, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 15, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 19, Name: "int", }, VarName: &ast.Ident{ NamePos: 23, Name: "i", }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 28, Kind: token.IntLit, Val: "1", }, OpPos: 29, Op: token.Ne, Y: &ast.UnaryExpr{ OpPos: 31, Op: token.Not, X: &ast.BasicLit{ ValPos: 32, Kind: token.IntLit, Val: "3", }, }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 37, Kind: token.IntLit, Val: "4", }, OpPos: 38, Op: token.Land, Y: &ast.ParenExpr{ Lparen: 40, X: &ast.BasicLit{ ValPos: 41, Kind: token.IntLit, Val: "6", }, Rparen: 42, }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 47, Kind: token.IntLit, Val: "7", }, OpPos: 48, Op: token.Mul, Y: &ast.BasicLit{ ValPos: 50, Kind: token.IntLit, Val: "8", }, }, OpPos: 51, Op: token.Add, Y: &ast.BasicLit{ ValPos: 52, Kind: token.IntLit, Val: "10", }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.ParenExpr{ Lparen: 58, X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 59, Kind: token.IntLit, Val: "11", }, OpPos: 61, Op: token.Sub, Y: &ast.BasicLit{ ValPos: 62, Kind: token.IntLit, Val: "12", }, }, Rparen: 64, }, OpPos: 65, Op: token.Add, Y: &ast.ParenExpr{ Lparen: 66, X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 67, Kind: token.IntLit, Val: "12", }, OpPos: 69, Op: token.Div, Y: &ast.BasicLit{ ValPos: 70, Kind: token.IntLit, Val: "16", }, }, Rparen: 72, }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 77, Kind: token.IntLit, Val: "17", }, OpPos: 79, Op: token.Le, Y: &ast.BasicLit{ ValPos: 81, Kind: token.IntLit, Val: "18", }, }, OpPos: 84, Op: token.Lt, Y: &ast.UnaryExpr{ OpPos: 85, Op: token.Sub, X: &ast.BasicLit{ ValPos: 86, Kind: token.IntLit, Val: "20", }, }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 92, Name: "i", }, OpPos: 93, Op: token.Assign, Y: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 94, Kind: token.IntLit, Val: "21", }, OpPos: 96, Op: token.Eq, Y: &ast.BasicLit{ ValPos: 98, Kind: token.IntLit, Val: "22", }, }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 104, Kind: token.IntLit, Val: "25", }, OpPos: 107, Op: token.Ge, Y: &ast.BasicLit{ ValPos: 109, Kind: token.IntLit, Val: "27", }, }, OpPos: 111, Op: token.Gt, Y: &ast.BasicLit{ ValPos: 112, Kind: token.IntLit, Val: "28", }, }, }, }, Rbrace: 116, }, }, }, }, }, { path: "../../testdata/quiet/parser/p01.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 0, Name: "int", }, Lparen: 8, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 9, Name: "void", }, }, }, Rparen: 13, }, FuncName: &ast.Ident{ NamePos: 4, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 15, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 19, Name: "int", }, VarName: &ast.Ident{ NamePos: 23, Name: "x", }, }, &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 28, Name: "int", }, VarName: &ast.Ident{ NamePos: 32, Name: "y", }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 37, Name: "x", }, OpPos: 39, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 41, Kind: token.IntLit, Val: "42", }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 47, Name: "x", }, OpPos: 48, Op: token.Assign, Y: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 49, Name: "y", }, OpPos: 50, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 51, Kind: token.IntLit, Val: "4711", }, }, }, }, }, Rbrace: 57, }, }, }, }, }, { path: "../../testdata/quiet/parser/p02.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 1, Name: "int", }, Lparen: 9, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 10, Name: "void", }, }, }, Rparen: 14, }, FuncName: &ast.Ident{ NamePos: 5, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 16, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 20, Name: "int", }, VarName: &ast.Ident{ NamePos: 24, Name: "x", }, }, &ast.EmptyStmt{ Semicolon: 29, }, &ast.WhileStmt{ While: 33, Cond: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 40, Name: "x", }, OpPos: 41, Op: token.Lt, Y: &ast.BasicLit{ ValPos: 42, Kind: token.IntLit, Val: "10", }, }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 46, Name: "x", }, OpPos: 48, Op: token.Assign, Y: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 50, Name: "x", }, OpPos: 52, Op: token.Add, Y: &ast.BasicLit{ ValPos: 54, Kind: token.IntLit, Val: "3", }, }, }, }, }, &ast.IfStmt{ If: 60, Cond: &ast.BasicLit{ ValPos: 64, Kind: token.IntLit, Val: "1", }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 67, Name: "x", }, OpPos: 69, Op: token.Assign, Y: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 71, Name: "x", }, OpPos: 73, Op: token.Add, Y: &ast.BasicLit{ ValPos: 75, Kind: token.IntLit, Val: "3", }, }, }, }, }, }, Rbrace: 78, }, }, }, }, }, { path: "../../testdata/quiet/parser/p03.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 1, Name: "int", }, Lparen: 9, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 10, Name: "void", }, }, }, Rparen: 14, }, FuncName: &ast.Ident{ NamePos: 5, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 16, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 20, Name: "int", }, VarName: &ast.Ident{ NamePos: 24, Name: "x", }, }, &ast.IfStmt{ If: 29, Cond: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 33, Kind: token.IntLit, Val: "1", }, OpPos: 34, Op: token.Lt, Y: &ast.BasicLit{ ValPos: 35, Kind: token.IntLit, Val: "2", }, }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 38, Name: "x", }, OpPos: 40, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 42, Kind: token.IntLit, Val: "1", }, }, }, Else: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 53, Name: "x", }, OpPos: 55, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 57, Kind: token.IntLit, Val: "2", }, }, }, }, }, Rbrace: 60, }, }, }, }, }, { path: "../../testdata/quiet/parser/p04.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 0, Name: "int", }, Lparen: 8, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 9, Name: "void", }, }, }, Rparen: 13, }, FuncName: &ast.Ident{ NamePos: 4, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 15, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 19, Name: "int", }, VarName: &ast.Ident{ NamePos: 23, Name: "x", }, }, &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 28, Name: "int", }, VarName: &ast.Ident{ NamePos: 32, Name: "y", }, }, &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 37, Name: "int", }, VarName: &ast.Ident{ NamePos: 41, Name: "z", }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 49, Name: "x", }, OpPos: 50, Op: token.Sub, Y: &ast.Ident{ NamePos: 51, Name: "y", }, }, OpPos: 52, Op: token.Sub, Y: &ast.Ident{ NamePos: 53, Name: "z", }, }, OpPos: 54, Op: token.Sub, Y: &ast.BasicLit{ ValPos: 55, Kind: token.IntLit, Val: "42", }, }, }, &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.BinaryExpr{ X: &ast.UnaryExpr{ OpPos: 90, Op: token.Not, X: &ast.Ident{ NamePos: 91, Name: "x", }, }, OpPos: 93, Op: token.Mul, Y: &ast.Ident{ NamePos: 95, Name: "y", }, }, OpPos: 97, Op: token.Add, Y: &ast.Ident{ NamePos: 99, Name: "z", }, }, OpPos: 101, Op: token.Lt, Y: &ast.Ident{ NamePos: 103, Name: "x", }, }, OpPos: 105, Op: token.Ne, Y: &ast.BinaryExpr{ X: &ast.BasicLit{ ValPos: 108, Kind: token.IntLit, Val: "42", }, OpPos: 111, Op: token.Lt, Y: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 113, Name: "x", }, OpPos: 115, Op: token.Add, Y: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 117, Name: "y", }, OpPos: 119, Op: token.Mul, Y: &ast.UnaryExpr{ OpPos: 121, Op: token.Not, X: &ast.Ident{ NamePos: 122, Name: "x", }, }, }, }, }, }, }, }, Rbrace: 164, }, }, }, }, }, { path: "../../testdata/quiet/parser/p05.c", want: &ast.File{ Decls: []ast.Decl{ &ast.VarDecl{ VarType: &ast.ArrayType{ Elem: &ast.Ident{ NamePos: 0, Name: "int", }, Lbracket: 5, Len: 10, Rbracket: 8, }, VarName: &ast.Ident{ NamePos: 4, Name: "c", }, }, &ast.VarDecl{ VarType: &ast.ArrayType{ Elem: &ast.Ident{ NamePos: 11, Name: "char", }, Lbracket: 17, Len: 10, Rbracket: 20, }, VarName: &ast.Ident{ NamePos: 16, Name: "d", }, }, &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 24, Name: "void", }, Lparen: 30, Params: []*ast.VarDecl{ { VarType: &ast.ArrayType{ Elem: &ast.Ident{ NamePos: 31, Name: "int", }, Lbracket: 36, Rbracket: 37, }, VarName: &ast.Ident{ NamePos: 35, Name: "h", }, }, { VarType: &ast.ArrayType{ Elem: &ast.Ident{ NamePos: 40, Name: "char", }, Lbracket: 46, Rbracket: 47, }, VarName: &ast.Ident{ NamePos: 45, Name: "i", }, }, }, Rparen: 48, }, FuncName: &ast.Ident{ NamePos: 29, Name: "f", }, Body: &ast.BlockStmt{ Lbrace: 50, Rbrace: 52, }, }, &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 56, Name: "int", }, Lparen: 64, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 65, Name: "void", }, }, }, Rparen: 69, }, FuncName: &ast.Ident{ NamePos: 60, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 71, Items: []ast.BlockItem{ &ast.EmptyStmt{ Semicolon: 75, }, }, Rbrace: 77, }, }, }, }, }, { path: "../../testdata/quiet/parser/p06.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 0, Name: "void", }, Lparen: 6, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 7, Name: "void", }, }, }, Rparen: 11, }, FuncName: &ast.Ident{ NamePos: 5, Name: "f", }, Body: &ast.BlockStmt{ Lbrace: 13, Items: []ast.BlockItem{ &ast.ReturnStmt{ Return: 17, }, }, Rbrace: 25, }, }, &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 28, Name: "int", }, Lparen: 33, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 34, Name: "void", }, }, }, Rparen: 38, }, FuncName: &ast.Ident{ NamePos: 32, Name: "g", }, Body: &ast.BlockStmt{ Lbrace: 40, Items: []ast.BlockItem{ &ast.ReturnStmt{ Return: 44, Result: &ast.BasicLit{ ValPos: 51, Kind: token.IntLit, Val: "42", }, }, }, Rbrace: 55, }, }, &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 58, Name: "int", }, Lparen: 66, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 67, Name: "void", }, }, }, Rparen: 71, }, FuncName: &ast.Ident{ NamePos: 62, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 72, Items: []ast.BlockItem{ &ast.ExprStmt{ X: &ast.CallExpr{ Name: &ast.Ident{ NamePos: 76, Name: "f", }, Lparen: 77, Rparen: 78, }, }, &ast.ExprStmt{ X: &ast.CallExpr{ Name: &ast.Ident{ NamePos: 83, Name: "g", }, Lparen: 84, Rparen: 85, }, }, }, Rbrace: 88, }, }, }, }, }, { path: "../../testdata/quiet/parser/p07.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 0, Name: "int", }, Lparen: 8, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 9, Name: "void", }, }, }, Rparen: 13, }, FuncName: &ast.Ident{ NamePos: 4, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 14, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 18, Name: "int", }, VarName: &ast.Ident{ NamePos: 22, Name: "x", }, }, &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 27, Name: "int", }, VarName: &ast.Ident{ NamePos: 31, Name: "y", }, }, &ast.IfStmt{ If: 37, Cond: &ast.Ident{ NamePos: 40, Name: "x", }, Body: &ast.WhileStmt{ While: 43, Cond: &ast.Ident{ NamePos: 50, Name: "y", }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 53, Name: "x", }, OpPos: 54, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 55, Kind: token.IntLit, Val: "42", }, }, }, }, }, &ast.WhileStmt{ While: 64, Cond: &ast.Ident{ NamePos: 70, Name: "x", }, Body: &ast.IfStmt{ If: 73, Cond: &ast.Ident{ NamePos: 76, Name: "y", }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 79, Name: "x", }, OpPos: 80, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 81, Kind: token.IntLit, Val: "42", }, }, }, }, }, }, Rbrace: 85, }, }, }, }, }, { path: "../../testdata/quiet/parser/p08.c", want: &ast.File{ Decls: []ast.Decl{ &ast.FuncDecl{ FuncType: &ast.FuncType{ Result: &ast.Ident{ NamePos: 70, Name: "int", }, Lparen: 78, Params: []*ast.VarDecl{ { VarType: &ast.Ident{ NamePos: 79, Name: "void", }, }, }, Rparen: 83, }, FuncName: &ast.Ident{ NamePos: 74, Name: "main", }, Body: &ast.BlockStmt{ Lbrace: 84, Items: []ast.BlockItem{ &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 88, Name: "int", }, VarName: &ast.Ident{ NamePos: 92, Name: "x", }, }, &ast.VarDecl{ VarType: &ast.Ident{ NamePos: 97, Name: "int", }, VarName: &ast.Ident{ NamePos: 101, Name: "y", }, }, &ast.IfStmt{ If: 107, Cond: &ast.Ident{ NamePos: 110, Name: "x", }, Body: &ast.IfStmt{ If: 118, Cond: &ast.Ident{ NamePos: 122, Name: "y", }, Body: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 125, Name: "x", }, OpPos: 127, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 129, Kind: token.IntLit, Val: "4711", }, }, }, Else: &ast.ExprStmt{ X: &ast.BinaryExpr{ X: &ast.Ident{ NamePos: 144, Name: "x", }, OpPos: 145, Op: token.Assign, Y: &ast.BasicLit{ ValPos: 146, Kind: token.IntLit, Val: "42", }, }, }, }, }, }, Rbrace: 150, }, }, }, }, }, } for _, g := range golden { log.Println("path:", g.path) s, err := scanner.Open(g.path) if err != nil { t.Error(err) continue } p := parser.NewParser() file, err := p.Parse(s) if err != nil { t.Error(err) continue } got := file.(*ast.File) if !reflect.DeepEqual(got, g.want) { t.Errorf("%q: AST mismatch; expected %#v, got %#v", g.path, g.want, got) fmt.Println(pretty.Diff(g.want, got)) } } }
func TestParserError(t *testing.T) { var golden = []struct { path string want string }{ { path: "../../testdata/incorrect/parser/pe01.c", want: `102: unexpected ")", expected ["!" "(" "-" "char_lit" "ident" "int_lit"]`, }, { path: "../../testdata/incorrect/parser/pe02.c", want: `112: unexpected "}", expected ["!=" "&&" "*" "+" "-" "/" ";" "<" "<=" "=" "==" ">" ">="]`, }, { path: "../../testdata/incorrect/parser/pe03.c", want: `129: unexpected "}", expected ["!" "(" "-" ";" "char_lit" "ident" "if" "int_lit" "return" "while" "{"]`, }, { path: "../../testdata/incorrect/parser/pe04.c", want: `111: unexpected "a", expected ["!=" "&&" "(" "*" "+" "-" "/" ";" "<" "<=" "=" "==" ">" ">=" "["]`, }, { path: "../../testdata/incorrect/parser/pe05.c", want: `71: unexpected "else", expected ["ident"]`, }, { path: "../../testdata/incorrect/parser/pe06.c", want: `73: unexpected "b", expected ["(" ";" "["]`, }, { path: "../../testdata/incorrect/parser/pe07.c", want: `72: unexpected ",", expected ["(" ";" "["]`, }, { path: "../../testdata/incorrect/parser/pe08.c", want: `86: unexpected "42", expected [";" "{"]`, }, { // TODO: The ';' at offset 80 in pe09.c shuold probably be a '{', as // indicated by the comment "// '}' missing " // // Update this test case if the test file is fixed. path: "../../testdata/incorrect/parser/pe09.c", want: `87: unexpected ";", expected ["$" "ident" "typedef"]`, }, { path: "../../testdata/incorrect/parser/pe10.c", want: `135: unexpected ")", expected ["!" "(" "-" "char_lit" "ident" "int_lit"]`, }, { path: "../../testdata/incorrect/parser/pe11.c", want: `70: unexpected "(", expected ["ident"]`, }, { path: "../../testdata/incorrect/parser/pe12.c", want: `77: unexpected "{", expected ["(" ";" "["]`, }, { path: "../../testdata/incorrect/parser/pe13.c", // Note, empty parameter list is explicitly allowed by the parser, // and act the same as a void parameter, as per design decisions. // // References. // https://github.com/mewmew/uc/issues/46 // https://github.com/mewmew/uc/issues/50 // https://github.com/mewmew/uc/issues/33 want: "", }, { path: "../../testdata/incorrect/parser/pe14.c", // Note, nested procedures is explicitly allowed by the parser, as the // validation is postponed to the semantic analysis checker. // // References. // https://github.com/mewmew/uc/issues/38 // https://github.com/mewmew/uc/issues/43 want: "", }, } for _, g := range golden { log.Println("path:", g.path) s, err := scanner.Open(g.path) if err != nil { t.Error(err) continue } p := parser.NewParser() _, err = p.Parse(s) got := "" if err != nil { if e, ok := err.(*errors.Error); ok { // Unwrap Gocc error. err = parser.NewError(e) } got = err.Error() } if got != g.want { t.Errorf("%q: error mismatch; expected `%v`, got `%v`", g.path, g.want, got) } } }
func TestCheckError(t *testing.T) { var golden = []struct { path string want string }{ { path: "../testdata/quiet/semantic/s02.c", want: `(../testdata/quiet/semantic/s02.c:3) error: missing return at end of non-void function "foo" ; } ^`, }, { path: "../testdata/incorrect/semantic/se01.c", want: `(../testdata/incorrect/semantic/se01.c:5) error: undeclared identifier "b" a = a + b; // Variable 'b' not defined ^`, }, { path: "../testdata/incorrect/semantic/se02.c", want: `(../testdata/incorrect/semantic/se02.c:5) error: undeclared identifier "foo" a = foo(a); // Function 'foo' not defined ^`, }, { path: "../testdata/incorrect/semantic/se03.c", want: `(../testdata/incorrect/semantic/se03.c:3) error: undeclared identifier "output" output(0); // Procedure 'output' not defined ^`, }, { path: "../testdata/incorrect/semantic/se04.c", want: `(../testdata/incorrect/semantic/se04.c:5) error: redefinition of "a" with type "char" instead of "int" char a; // Redeclaration of 'a' ^`, }, { path: "../testdata/incorrect/semantic/se05.c", want: `(../testdata/incorrect/semantic/se05.c:5) error: redefinition of "a" with type "void(void)" instead of "int" void a(void) { // Attempt to redefine variable 'a' ^`, }, { path: "../testdata/incorrect/semantic/se06.c", want: `(../testdata/incorrect/semantic/se06.c:7) error: redefinition of "a" int a(int i) { // Redeclaration of 'a' ^`, }, { path: "../testdata/incorrect/semantic/se07.c", want: `(../testdata/incorrect/semantic/se07.c:4) error: returning "int" from a function with incompatible result type "void" return 2 * n; // Attempt to return value from procedure ^`, }, { path: "../testdata/incorrect/semantic/se08.c", want: `(../testdata/incorrect/semantic/se08.c:4) error: returning "void" from a function with incompatible result type "int" return; // Void return from function ^`, }, { path: "../testdata/incorrect/semantic/se09.c", want: `(../testdata/incorrect/semantic/se09.c:6) error: returning "char[1]" from a function with incompatible result type "int" else return x; // Return from function with erroneous type ^`, }, { path: "../testdata/incorrect/semantic/se10.c", want: `(../testdata/incorrect/semantic/se10.c:6) error: invalid operation: n[2] (type "int" does not support indexing) n[2]; // Index an integer ^`, }, { path: "../testdata/incorrect/semantic/se11.c", want: `(../testdata/incorrect/semantic/se11.c:4) error: cannot assign to "a" of type "int(void)" a = 1; // 'a' is not an lval ^`, }, { path: "../testdata/incorrect/semantic/se12.c", want: `(../testdata/incorrect/semantic/se12.c:6) error: cannot call non-function "a" of type "int" a(2); // 'a' is not a function ^`, }, { path: "../testdata/incorrect/semantic/se13.c", want: `(../testdata/incorrect/semantic/se13.c:8) error: invalid operands to binary expression: 1 + foo(0) ("int" and "void") 1 + foo(0); // 'foo' does not return a value ^`, }, { path: "../testdata/incorrect/semantic/se14.c", want: `(../testdata/incorrect/semantic/se14.c:12) error: cannot call non-function "f" of type "int" f(n); // 'f' refers only to the local variable ^`, }, { path: "../testdata/incorrect/semantic/se15.c", want: `(../testdata/incorrect/semantic/se15.c:8) error: calling "q" with too few arguments; expected 3, got 2 1 + q(1, 3); // Too few arguments to function 'q' ^`, }, { path: "../testdata/incorrect/semantic/se16.c", want: `(../testdata/incorrect/semantic/se16.c:9) error: calling "d" with too many arguments; expected 2, got 3 d(1, 2, 3); // Too many arguments to function 'd' ^`, }, { path: "../testdata/incorrect/semantic/se17.c", want: `(../testdata/incorrect/semantic/se17.c:6) error: invalid operation: hello + 1 (type mismatch between "char[5]" and "int") hello+1; // Attempt to use char array in arithmetic. (legal in C) ^`, }, { path: "../testdata/incorrect/semantic/se18.c", want: `(../testdata/incorrect/semantic/se18.c:6) error: cannot assign to "a" of type "char[10]" a = 42; // assign int to array of char ^`, }, { path: "../testdata/incorrect/semantic/se19.c", want: `(../testdata/incorrect/semantic/se19.c:5) error: invalid operation: a == 42 (type mismatch between "char[10]" and "int") if (a==42) ; ^`, }, { path: "../testdata/incorrect/semantic/se20.c", want: `(../testdata/incorrect/semantic/se20.c:7) error: cannot assign to "a" of type "int[10]" a=b; ^`, }, { path: "../testdata/incorrect/semantic/se21.c", want: `(../testdata/incorrect/semantic/se21.c:5) error: returning "char[10]" from a function with incompatible result type "int" return bv; // Return from function with erroneous type ^`, }, { path: "../testdata/incorrect/semantic/se22.c", want: `(../testdata/incorrect/semantic/se22.c:6) error: invalid operation: a + 1 (type mismatch between "char[10]" and "int") a+1; // Attempt to apply arithmetic to array reference ^`, }, { path: "../testdata/incorrect/semantic/se23.c", want: `(../testdata/incorrect/semantic/se23.c:6) error: invalid operation: b[0] (type "int" does not support indexing) return b[0]; //not an array! ^`, }, { path: "../testdata/incorrect/semantic/se24.c", want: `(../testdata/incorrect/semantic/se24.c:6) error: cannot assign to "b" of type "int[10]" b = a; // b cannot be assigned ^`, }, { path: "../testdata/incorrect/semantic/se25.c", want: `(../testdata/incorrect/semantic/se25.c:4) error: cannot assign to "(1 + 2)" of type "int" (1 + 2) = 3; //No assignment here! ^`, }, { path: "../testdata/incorrect/semantic/se26.c", want: `(../testdata/incorrect/semantic/se26.c:9) error: calling "f" with incompatible argument type "char[10]" to parameter of type "int[]" f(a); ^`, }, { path: "../testdata/incorrect/semantic/se27.c", want: `(../testdata/incorrect/semantic/se27.c:4) error: returning "int" from a function with incompatible result type "void" if (1<2) return 2 * n; // Attempt to return value from procedure ^`, }, { path: "../testdata/incorrect/semantic/se28.c", want: `(../testdata/incorrect/semantic/se28.c:5) error: returning "int" from a function with incompatible result type "void" else return 2 * n; // Attempt to return value from procedure ^`, }, { path: "../testdata/incorrect/semantic/se29.c", want: `(../testdata/incorrect/semantic/se29.c:4) error: redefinition of "n" with type "char" instead of "int" char n; ^`, }, { path: "../testdata/incorrect/semantic/se30.c", want: `(../testdata/incorrect/semantic/se30.c:6) error: cannot assign to "a" (type mismatch between "int" and "int[10]") a=b; ^`, }, { path: "../testdata/incorrect/semantic/se31.c", want: `(../testdata/incorrect/semantic/se31.c:5) error: redefinition of "a" with type "void(void)" instead of "int" void a(void); // Attempt to redefine 'a' as extern ^`, }, { path: "../testdata/incorrect/semantic/se32.c", want: `(../testdata/incorrect/semantic/se32.c:6) error: invalid operands to binary expression: 1 + foo(0) ("int" and "void") 1 + foo(0); // 'foo' does not return a value ^`, }, { path: "../testdata/incorrect/semantic/se33.c", want: `(../testdata/incorrect/semantic/se33.c:6) error: calling "q" with too few arguments; expected 3, got 2 1 + q(1, 3); // Too few arguments to function 'q' ^`, }, { path: "../testdata/incorrect/semantic/se34.c", want: `(../testdata/incorrect/semantic/se34.c:6) error: calling "d" with too many arguments; expected 2, got 3 d(1, 2, 3); // Too many arguments to function 'd' ^`, }, // Extra test cases. { path: "../testdata/extra/semantic/extra-void-arg.c", want: `(../testdata/extra/semantic/extra-void-arg.c:4) error: "void" must be the only parameter void f(int a, void) { ^`, }, { path: "../testdata/extra/semantic/incompatible-arg-type.c", want: `(../testdata/extra/semantic/incompatible-arg-type.c:10) error: calling "a" with incompatible argument type "int" to parameter of type "int[]" return a(b); ^`, }, { path: "../testdata/extra/semantic/index-array.c", want: `(../testdata/extra/semantic/index-array.c:7) error: invalid array index; expected integer, got "int[20]" x[y]; ^`, }, { path: "../testdata/extra/semantic/local-var-redef.c", want: `(../testdata/extra/semantic/local-var-redef.c:6) error: redefinition of "x" int x; ^`, }, { path: "../testdata/extra/semantic/missing-return.c", want: `(../testdata/extra/semantic/missing-return.c:10) error: missing return at end of non-void function "f" } ^`, }, { path: "../testdata/extra/semantic/param-redef.c", want: `(../testdata/extra/semantic/param-redef.c:5) error: redefinition of "x" int x; ^`, }, { path: "../testdata/extra/semantic/unnamed-arg.c", want: `(../testdata/extra/semantic/unnamed-arg.c:4) error: parameter name obmitted void f(int) { ^`, }, { path: "../testdata/extra/semantic/variable-sized-array.c", want: `(../testdata/extra/semantic/variable-sized-array.c:5) error: array size or initializer missing for "y" char y[]; ^`, }, { path: "../testdata/extra/semantic/void-array.c", want: `(../testdata/extra/semantic/void-array.c:5) error: invalid element type "void" of array "x" void x[10]; ^`, }, { path: "../testdata/extra/semantic/void-array-arg.c", want: `(../testdata/extra/semantic/void-array-arg.c:4) error: invalid element type "void" of array "x" void f(void x[]) { ^`, }, { path: "../testdata/extra/semantic/void-param.c", want: `(../testdata/extra/semantic/void-param.c:4) error: "x" has invalid type "void" void f(void x) { ^`, }, { path: "../testdata/extra/semantic/void-params.c", want: `(../testdata/extra/semantic/void-params.c:4) error: "void" must be the only parameter void f(void, void) { ^`, }, { path: "../testdata/extra/semantic/void-var.c", want: `(../testdata/extra/semantic/void-var.c:5) error: "x" has invalid type "void" void x; ^`, }, } errors.UseColor = false for _, g := range golden { buf, err := ioutil.ReadFile(g.path) if err != nil { t.Errorf("%q: %v", g.path, err) continue } input := string(buf) s := scanner.NewFromString(input) src := errors.NewSource(g.path, input) p := parser.NewParser() file, err := p.Parse(s) if err != nil { t.Error(err) continue } f := file.(*ast.File) got := "" if _, err := sem.Check(f); err != nil { if e, ok := err.(*errutil.ErrInfo); ok { // Unwrap errutil error. err = e.Err if e, ok := err.(*errors.Error); ok { // Unwrap semantic error. e.Src = src } } got = err.Error() } if got != g.want { t.Errorf("%q: error mismatch; expected `%v`, got `%v`", g.path, g.want, got) } } }