Example #1
0
File: uclang.go Project: mewmew/uc
// 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
}
Example #2
0
File: usem.go Project: mewmew/uc
// 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
}
Example #3
0
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())
		}
	}
}
Example #4
0
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)
	}
}
Example #5
0
// 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
}
Example #6
0
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)
		}
	}
}