/// parser_validation.rs /// /// Simple tests for the validation phase use super::*; #[test] fn test_correct_struct_instance() { Tester::new_single_source_expect_ok( "single field", " struct Foo { s32 a } func bar(s32 arg) -> Foo { return Foo{ a: arg }; } " ); Tester::new_single_source_expect_ok( "multiple fields", " struct Foo { s32 a, s32 b } func bar(s32 arg) -> Foo { return Foo{ a: arg, b: arg }; } " ); Tester::new_single_source_expect_ok( "single field, explicit polymorph", " struct Foo{ T field } func bar(s32 arg) -> Foo { return Foo{ field: arg }; } " ); Tester::new_single_source_expect_ok( "single field, implicit polymorph", " struct Foo{ T field } func bar(s32 arg) -> s32 { auto thingo = Foo{ field: arg }; return arg; } " ); Tester::new_single_source_expect_ok( "multiple fields, same explicit polymorph", " struct Pair{ T1 first, T2 second } func bar(s32 arg) -> s32 { auto qux = Pair{ first: arg, second: arg }; return arg; } " ); Tester::new_single_source_expect_ok( "multiple fields, same implicit polymorph", " struct Pair{ T1 first, T2 second } func bar(s32 arg) -> s32 { auto wup = Pair{ first: arg, second: arg }; return arg; } " ); Tester::new_single_source_expect_ok( "multiple fields, different explicit polymorph", " struct Pair{ T1 first, T2 second } func bar(s32 arg1, s8 arg2) -> s32 { auto shoo = Pair{ first: arg1, second: arg2 }; return arg1; } " ); Tester::new_single_source_expect_ok( "multiple fields, different implicit polymorph", " struct Pair{ T1 first, T2 second } func bar(s32 arg1, s8 arg2) -> s32 { auto shrubbery = Pair{ first: arg1, second: arg2 }; return arg1; } " ); } #[test] fn test_incorrect_struct_instance() { Tester::new_single_source_expect_err( "reused field in definition", "struct Foo{ s32 a, s8 a }" ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "a }") .assert_msg_has(0, "defined more than once") .assert_occurs_at(1, "a, ") .assert_msg_has(1, "other struct field"); }); Tester::new_single_source_expect_err( "reused field in instance", " struct Foo{ s32 a, s32 b } func bar() -> s32 { auto foo = Foo{ a: 5, a: 3 }; return 0; } " ).error(|e| { e .assert_occurs_at(0, "a: 3") .assert_msg_has(0, "field is specified more than once"); }); Tester::new_single_source_expect_err( "missing field", " struct Foo { s32 a, s32 b } func bar() -> s32 { auto foo = Foo{ a: 2 }; return 0; } " ).error(|e| { e .assert_occurs_at(0, "Foo{") .assert_msg_has(0, "'b' is missing"); }); Tester::new_single_source_expect_err( "missing fields", " struct Foo { s32 a, s32 b, s32 c } func bar() -> s32 { auto foo = Foo{ a: 2 }; return 0; } " ).error(|e| { e .assert_occurs_at(0, "Foo{") .assert_msg_has(0, "[b, c] are missing"); }); } #[test] fn test_correct_enum_instance() { Tester::new_single_source_expect_ok( "single variant", " enum Foo { A } func bar() -> Foo { return Foo::A; } " ); Tester::new_single_source_expect_ok( "multiple variants", " enum Foo { A=15, B = 0xF } func bar() -> Foo { auto a = Foo::A; return Foo::B; } " ); Tester::new_single_source_expect_ok( "explicit single polymorph", " enum Foo{ A } func bar() -> Foo { return Foo::A; } " ); Tester::new_single_source_expect_ok( "explicit multi-polymorph", " enum Foo{ A, B } func bar() -> Foo { return Foo::B; } " ); } #[test] fn test_incorrect_enum_instance() { Tester::new_single_source_expect_err( "variant name reuse", " enum Foo { A, A } func bar() -> Foo { return Foo::A; } " ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "A }") .assert_msg_has(0, "defined more than once") .assert_occurs_at(1, "A, ") .assert_msg_has(1, "other enum variant is defined here"); }); Tester::new_single_source_expect_err( "undefined variant", " enum Foo { A } func bar() -> Foo { return Foo::B; } " ).error(|e| { e .assert_num(1) .assert_msg_has(0, "variant 'B' does not exist on the enum 'Foo'"); }); } #[test] fn test_correct_union_instance() { Tester::new_single_source_expect_ok( "single tag", " union Foo { A } func bar() -> Foo { return Foo::A; } " ); Tester::new_single_source_expect_ok( "multiple tags", " union Foo { A, B } func bar() -> Foo { return Foo::B; } " ); Tester::new_single_source_expect_ok( "single embedded", " union Foo { A(s32) } func bar() -> Foo { return Foo::A(5); } " ); Tester::new_single_source_expect_ok( "multiple embedded", " union Foo { A(s32), B(s8) } func bar() -> Foo { return Foo::B(2); } " ); Tester::new_single_source_expect_ok( "multiple values in embedded", " union Foo { A(s32, s8) } func bar() -> Foo { return Foo::A(0, 2); } " ); Tester::new_single_source_expect_ok( "mixed tag/embedded", " union OptionInt { None, Some(s32) } func bar() -> OptionInt { return OptionInt::Some(3); } " ); Tester::new_single_source_expect_ok( "single polymorphic var", " union Option { None, Some(T) } func bar() -> Option { return Option::Some(3); }" ); Tester::new_single_source_expect_ok( "multiple polymorphic vars", " union Result { Ok(T), Err(E), } func bar() -> Result { return Result::Ok(3); } " ); Tester::new_single_source_expect_ok( "multiple polymorphic in one variant", " union MaybePair{ None, Some(T1, T2) } func bar() -> MaybePair { return MaybePair::Some(1, 2); } " ); } #[test] fn test_incorrect_union_instance() { Tester::new_single_source_expect_err( "tag-variant name reuse", " union Foo{ A, A } " ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "A }") .assert_msg_has(0, "union variant is defined more than once") .assert_occurs_at(1, "A, ") .assert_msg_has(1, "other union variant"); }); Tester::new_single_source_expect_err( "embedded-variant name reuse", " union Foo{ A(s32), A(s8) } " ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "A(s8)") .assert_msg_has(0, "union variant is defined more than once") .assert_occurs_at(1, "A(s32)") .assert_msg_has(1, "other union variant"); }); Tester::new_single_source_expect_err( "undefined variant", " union Silly{ Thing(s8) } func bar() -> Silly { return Silly::Undefined(5); } " ).error(|e| { e .assert_msg_has(0, "variant 'Undefined' does not exist on the union 'Silly'"); }); Tester::new_single_source_expect_err( "using tag instead of embedded", " union Foo{ A(s32) } func bar() -> Foo { return Foo::A; } " ).error(|e| { e .assert_msg_has(0, "variant 'A' of union 'Foo' expects 1 embedded values, but 0 were"); }); Tester::new_single_source_expect_err( "using embedded instead of tag", " union Foo{ A } func bar() -> Foo { return Foo::A(3); } " ).error(|e| { e .assert_msg_has(0, "The variant 'A' of union 'Foo' expects 0"); }); Tester::new_single_source_expect_err( "wrong embedded value", " union Foo{ A(s32) } func bar() -> Foo { return Foo::A(false); } " ).error(|e| { e .assert_occurs_at(0, "Foo::A") .assert_msg_has(0, "failed to fully resolve") .assert_occurs_at(1, "false") .assert_msg_has(1, "has been resolved to 's32'") .assert_msg_has(1, "has been resolved to 'bool'"); }); } #[test] fn test_correct_tuple_members() { // Tuples with zero members Tester::new_single_source_expect_ok( "single zero-tuple", "struct Foo{ () bar }" ).for_struct("Foo", |s| { s .for_field("bar", |f| { f.assert_parser_type("()"); }) .assert_size_alignment("Foo", 0, 1); }); Tester::new_single_source_expect_ok( "triple zero-tuple", "struct Foo{ () bar, () baz, () qux }" ).for_struct("Foo", |s| { s .assert_size_alignment("Foo", 0, 1); }); // Tuples with one member (which are elided, because due to ambiguity // between a one-tuple literal and a parenthesized expression, we're not // going to be able to construct one-tuples). Tester::new_single_source_expect_ok( "single elided one-tuple", "struct Foo{ (u32) bar }" ).for_struct("Foo", |s| { s .for_field("bar", |f| { f.assert_parser_type("u32"); }) .assert_size_alignment("Foo", 4, 4); }); Tester::new_single_source_expect_ok( "triple elided one-tuple", "struct Foo{ (u8) bar, (u16) baz, (u32) qux }" ).for_struct("Foo", |s| { s .assert_size_alignment("Foo", 8, 4); }); // Tuples with three members Tester::new_single_source_expect_ok( "single three-tuple", "struct Foo{ (u8, u16, u32) bar }" ).for_struct("Foo", |s| { s .for_field("bar", |f| { f.assert_parser_type("(u8,u16,u32)"); }) .assert_size_alignment("Foo", 8, 4); }); Tester::new_single_source_expect_ok( "double three-tuple", "struct Foo{ (u8,u16,u32,) bar, (s8,s16,s32,) baz }" ).for_struct("Foo", |s| { s .for_field("bar", |f| { f.assert_parser_type("(u8,u16,u32)"); }) .for_field("baz", |f| { f.assert_parser_type("(s8,s16,s32)"); }) .assert_size_alignment("Foo", 16, 4); }); } #[test] fn test_incorrect_tuple_member() { // Test not really necessary, but hey, what's a test between friends Tester::new_single_source_expect_err( "unknown tuple member", "struct Foo{ (u32, u32, u32, YouThirstySchmoo) field }" ).error(|e| { e .assert_num(1) .assert_msg_has(0, "unknown type") .assert_occurs_at(0, "YouThirstySchmoo"); }); } #[test] fn test_correct_tuple_polymorph_args() { Tester::new_single_source_expect_ok( "single tuple arg", " union Option{ Some(T), None } func thing() -> u32 { auto a = Option<()>::None; auto b = Option<(u32, u64)>::None; auto c = Option<(Option<(u8, s8)>, Option<(s8, u8)>)>::None; return 0; } " ).for_union("Option", |u| { u .assert_has_monomorph("Option<()>") .assert_has_monomorph("Option<(u32,u64)>") .assert_has_monomorph("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>") .assert_size_alignment("Option<()>", 1, 1, 0, 0) .assert_size_alignment("Option<(u32,u64)>", 24, 8, 0, 0) // (u32, u64) becomes size 16, alignment 8. Hence union tag is aligned to 8 .assert_size_alignment("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>", 7, 1, 0, 0); // inner unions are size 3, alignment 1. Two of those with a tag is size 7 }); } #[test] fn test_incorrect_tuple_polymorph_args() { // Do some mismatching brackets. I don't know what else to test Tester::new_single_source_expect_err( "mismatch angle bracket", " union Option{ Some(T), None } func f() -> u32 { auto a = Option<(u32>)::None; return 0; }" ).error(|e| { e .assert_num(2) .assert_msg_has(0, "closing '>'").assert_occurs_at(0, ">)::None") .assert_msg_has(1, "match this '('").assert_occurs_at(1, "(u32>"); }); Tester::new_single_source_expect_err( "wrongly placed angle", " union O{ S(T), N } func f() -> u32 { auto a = O<()>::None; return 0; } " ).error(|e| { e .assert_num(1) .assert_msg_has(0, "expected typename") .assert_occurs_at(0, " () { () a = (); auto b = a.0; return a; }" ).error(|e| { e .assert_num(1) .assert_msg_has(0, "out of bounds") .assert_occurs_at(0, "a.0"); }); // Make the type checker do some shenanigans before we can decide the tuple // type. Tester::new_single_source_expect_err( "sized tuple", " func determinator((A,B,A) v) -> B { return v.1; } func tester() -> u64 { auto v = (0,1,2); u32 a_u32 = 5; v.2 = a_u32; v.8 = 5; return determinator(v); } " ).error(|e| { e .assert_num(1) .assert_msg_has(0, "out of bounds") .assert_occurs_at(0, "v.8"); }); } #[test] fn test_polymorph_array_types() { Tester::new_single_source_expect_ok( "array of polymorph in struct", " struct Foo { T[] hello } struct Bar { Foo[] world } " ).for_struct("Bar", |s| { s .for_field("world", |f| { f.assert_parser_type("Foo[]"); }); }); Tester::new_single_source_expect_ok( "array of port in struct", " struct Bar { in[] inputs } " ).for_struct("Bar", |s| { s .for_field("inputs", |f| { f.assert_parser_type("in[]"); }); }); } #[test] fn test_correct_modifying_operators() { // Not testing the types, just that it parses Tester::new_single_source_expect_ok( "valid uses", " func f() -> u32 { auto a = 5; a += 2; a -= 2; a *= 2; a /= 2; a %= 2; a <<= 2; a >>= 2; a |= 2; a &= 2; a ^= 2; return a; } " ); } #[test] fn test_incorrect_modifying_operators() { Tester::new_single_source_expect_err( "wrong declaration", "func f() -> u8 { auto a += 2; return a; }" ).error(|e| { e.assert_msg_has(0, "expected '='"); }); Tester::new_single_source_expect_err( "inside function", "func f(u32 a) -> u32 { auto b = 0; auto c = f(a += 2); }" ).error(|e| { e.assert_msg_has(0, "assignments are statements"); }); Tester::new_single_source_expect_err( "inside tuple", "func f(u32 a) -> u32 { auto b = (a += 2, a /= 2); return 0; }" ).error(|e| { e.assert_msg_has(0, "assignments are statements"); }); } #[test] fn test_variable_introduction_in_scope() { Tester::new_single_source_expect_err( "variable use before declaration", "func f() -> u8 { return thing; auto thing = 5; }" ).error(|e| { e.assert_msg_has(0, "unresolved variable"); }); Tester::new_single_source_expect_err( "variable use in declaration", "func f() -> u8 { auto thing = 5 + thing; return thing; }" ).error(|e| { e.assert_msg_has(0, "unresolved variable"); }); Tester::new_single_source_expect_ok( "variable use after declaration", "func f() -> u8 { auto thing = 5; return thing; }" ); Tester::new_single_source_expect_err( "variable use of closed scope", "func f() -> u8 { { auto thing = 5; } return thing; }" ).error(|e| { e.assert_msg_has(0, "unresolved variable"); }); } #[test] fn test_correct_select_statement() { Tester::new_single_source_expect_ok( "guard variable decl", " primitive f() { channel unused -> input; u32 outer_value = 0; sync select { auto in_same_guard = get(input) -> {} // decl A1 auto in_same_gaurd = get(input) -> {} // decl A2 auto in_guard_and_block = get(input) -> {} // decl B1 outer_value = get(input) -> { auto in_guard_and_block = outer_value; } // decl B2 } } " ); Tester::new_single_source_expect_ok( "empty select", "primitive f() { sync select {} }" ); Tester::new_single_source_expect_ok( "mixed uses", " primitive f() { channel unused_output -> input; u32 outer_value = 0; sync select { outer_value = get(input) -> outer_value = 0; auto new_value = get(input) -> { outer_value = new_value; } get(input) + get(input) -> outer_value = 8; get(input) -> {} outer_value %= get(input) -> { outer_value *= outer_value; auto new_value = get(input); outer_value += new_value; } } } " ); } #[test] fn test_incorrect_select_statement() { Tester::new_single_source_expect_err( "outside sync", "primitive f() { select {} }" ).error(|e| { e .assert_num(1) .assert_occurs_at(0, "select") .assert_msg_has(0, "inside sync blocks"); }); Tester::new_single_source_expect_err( "variable in previous block", "primitive f() { channel tx -> rx; u32 a = 0; // this one will be shadowed sync select { auto a = get(rx) -> {} } }" ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "a = get").assert_msg_has(0, "variable name conflicts") .assert_occurs_at(1, "a = 0").assert_msg_has(1, "Previous variable"); }); Tester::new_single_source_expect_err( "put inside arm", "primitive f() { channel a -> b; sync select { put(a) -> {} } }" ).error(|e| { e .assert_occurs_at(0, "put") .assert_msg_has(0, "may not occur"); }); } #[test] fn test_incorrect_goto_statement() { Tester::new_single_source_expect_err( "goto missing var in same scope", "func f() -> u32 { goto exit; auto v = 5; exit: return 0; }" ).error(|e| { e .assert_num(3) .assert_occurs_at(0, "exit;").assert_msg_has(0, "skips over a variable") .assert_occurs_at(1, "exit:").assert_msg_has(1, "jumps to this label") .assert_occurs_at(2, "v = 5").assert_msg_has(2, "skips over this variable"); }); Tester::new_single_source_expect_err( "goto missing var in outer scope", "func f() -> u32 { if (true) { goto exit; } auto v = 0; exit: return 1; }" ).error(|e| { e .assert_num(3) .assert_occurs_at(0, "exit;").assert_msg_has(0, "skips over a variable") .assert_occurs_at(1, "exit:").assert_msg_has(1, "jumps to this label") .assert_occurs_at(2, "v = 0").assert_msg_has(2, "skips over this variable"); }); Tester::new_single_source_expect_err( "goto jumping into scope", "func f() -> u32 { goto nested; { nested: return 0; } return 1; }" ).error(|e| { e .assert_num(1) .assert_occurs_at(0, "nested;") .assert_msg_has(0, "could not find this label"); }); Tester::new_single_source_expect_err( "goto jumping outside sync", "primitive f() { sync { goto exit; } exit: u32 v = 0; }" ).error(|e| { e .assert_num(3) .assert_occurs_at(0, "goto exit;").assert_msg_has(0, "not escape the surrounding sync") .assert_occurs_at(1, "exit: u32 v").assert_msg_has(1, "target of the goto") .assert_occurs_at(2, "sync {").assert_msg_has(2, "jump past this"); }); Tester::new_single_source_expect_err( "goto jumping to select case", "primitive f(in i) { sync select { hello: auto a = get(i) -> i += 1 } goto hello; }" ).error(|e| { e .assert_msg_has(0, "expected '->'"); }); Tester::new_single_source_expect_err( "goto jumping into select case skipping variable", "primitive f(in i) { goto waza; sync select { auto a = get(i) -> { waza: a += 1; } } }" ).error(|e| { e .assert_num(1) .assert_msg_has(0, "not find this label") .assert_occurs_at(0, "waza;"); }); } #[test] fn test_incorrect_while_statement() { // Just testing the error cases caught at compile-time. Other ones need // evaluation testing Tester::new_single_source_expect_err( "break wrong earlier loop", "func f() -> u32 { target: while (true) {} while (true) { break target; } return 0; }" ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "target; }").assert_msg_has(0, "not nested under the target") .assert_occurs_at(1, "target: while").assert_msg_has(1, "is found here"); }); Tester::new_single_source_expect_err( "break wrong later loop", "func f() -> u32 { while (true) { break target; } target: while (true) {} return 0; }" ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "target; }").assert_msg_has(0, "not nested under the target") .assert_occurs_at(1, "target: while").assert_msg_has(1, "is found here"); }); Tester::new_single_source_expect_err( "break outside of sync", "primitive f() { outer: while (true) { //mark sync while(true) { break outer; } } }" ).error(|e| { e .assert_num(3) .assert_occurs_at(0, "break outer;").assert_msg_has(0, "may not escape the surrounding") .assert_occurs_at(1, "while (true) { //mark").assert_msg_has(1, "escapes out of this loop") .assert_occurs_at(2, "sync while").assert_msg_has(2, "escape this synchronous block"); }); }