From 8ae142310cc74aaed51d2ea9bdd4291c3bc5b1b7 2021-12-21 11:58:35 From: MH Date: 2021-12-21 11:58:35 Subject: [PATCH] Add some tests before refactoring variable resolving --- diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 621092216319cdae738794dfffda706abec1aa9b..90e035252ce53b4aaad9cc5d3aba6faed31428fb 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -259,6 +259,9 @@ impl Visitor for PassValidationLinking { } fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult { + // Note that we're not adding the variable to its scope for lookups. + // This is done in the `visit_statement_for_locals_labels_and_in_sync` + // method. let stmt = &ctx.heap[id]; let expr_id = stmt.initial_expr; diff --git a/src/protocol/tests/mod.rs b/src/protocol/tests/mod.rs index ae0f331b905942da4bd4f3c072c5fb71b6d49e1b..0d2f9cfe3d391cc37a4d19dd7c29e0edd1fa786a 100644 --- a/src/protocol/tests/mod.rs +++ b/src/protocol/tests/mod.rs @@ -12,13 +12,13 @@ */ mod utils; -mod parser_after_tokenizing; mod parser_binding; mod parser_imports; mod parser_inference; mod parser_literals; mod parser_monomorphs; -mod parser_types; +mod parser_type_declaration; +mod parser_type_layout; mod parser_validation; mod eval_binding; mod eval_calls; diff --git a/src/protocol/tests/parser_after_tokenizing.rs b/src/protocol/tests/parser_after_tokenizing.rs deleted file mode 100644 index c05ffc0589703b6b2593f44be9c58e3c2a6be505..0000000000000000000000000000000000000000 --- a/src/protocol/tests/parser_after_tokenizing.rs +++ /dev/null @@ -1,109 +0,0 @@ -/// parser_after_tokenizing -/// -/// Simple tests for the lexer. Only tests the lexing of the input source and -/// the resulting AST without relying on the validation/typing pass - -use super::*; - -#[test] -fn test_disallowed_inference() { - Tester::new_single_source_expect_err( - "argument auto inference", - "func thing(auto arg) -> s32 { return 0; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto arg"); - }); - - Tester::new_single_source_expect_err( - "return type auto inference", - "func thing(s32 arg) -> auto { return 0; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto {"); - }); - - Tester::new_single_source_expect_err( - "implicit polymorph argument auto inference", - "func thing(in port) -> s32 { return port; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "in port"); - }); - - Tester::new_single_source_expect_err( - "explicit polymorph argument auto inference", - "func thing(in port) -> s32 { return port; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto> port"); - }); - - Tester::new_single_source_expect_err( - "implicit polymorph return type auto inference", - "func thing(in a, in b) -> in { return a; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "in {"); - }); - - Tester::new_single_source_expect_err( - "explicit polymorph return type auto inference", - "func thing(in a) -> in { return a; }" - ).error(|e| { e - .assert_msg_has(0, "inference is not allowed") - .assert_occurs_at(0, "auto> {"); - }); -} - -#[test] -fn test_simple_struct_definition() { - Tester::new_single_source_expect_ok( - "empty struct", - "struct Foo{}" - ).for_struct("Foo", |t| { t.assert_num_fields(0); }); - - Tester::new_single_source_expect_ok( - "single field, no comma", - "struct Foo{ s32 field }" - ).for_struct("Foo", |t| { t - .assert_num_fields(1) - .for_field("field", |f| { - f.assert_parser_type("s32"); - }); - }); - - Tester::new_single_source_expect_ok( - "single field, with comma", - "struct Foo{ s32 field, }" - ).for_struct("Foo", |t| { t - .assert_num_fields(1) - .for_field("field", |f| { f - .assert_parser_type("s32"); - }); - }); - - Tester::new_single_source_expect_ok( - "multiple fields, no comma", - "struct Foo{ u8 a, s16 b, s32 c }" - ).for_struct("Foo", |t| { t - .assert_num_fields(3) - .for_field("a", |f| { f.assert_parser_type("u8"); }) - .for_field("b", |f| { f.assert_parser_type("s16"); }) - .for_field("c", |f| { f.assert_parser_type("s32"); }); - }); - - Tester::new_single_source_expect_ok( - "multiple fields, with comma", - "struct Foo{ - u8 a, - s16 b, - s32 c, - }" - ).for_struct("Foo", |t| { t - .assert_num_fields(3) - .for_field("a", |f| { f.assert_parser_type("u8"); }) - .for_field("b", |f| { f.assert_parser_type("s16"); }) - .for_field("c", |f| { f.assert_parser_type("s32"); }); - }); -} \ No newline at end of file diff --git a/src/protocol/tests/parser_inference.rs b/src/protocol/tests/parser_inference.rs index 44dcd85a51a0797fed6e79151940b2c57e4bd322..282c19626d328bee46016db1bfc4bd816a387c0c 100644 --- a/src/protocol/tests/parser_inference.rs +++ b/src/protocol/tests/parser_inference.rs @@ -468,6 +468,56 @@ fn test_failed_polymorph_inference() { ); } +#[test] +fn test_disallowed_inference() { + Tester::new_single_source_expect_err( + "argument auto inference", + "func thing(auto arg) -> s32 { return 0; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "auto arg"); + }); + + Tester::new_single_source_expect_err( + "return type auto inference", + "func thing(s32 arg) -> auto { return 0; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "auto {"); + }); + + Tester::new_single_source_expect_err( + "implicit polymorph argument auto inference", + "func thing(in port) -> s32 { return port; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "in port"); + }); + + Tester::new_single_source_expect_err( + "explicit polymorph argument auto inference", + "func thing(in port) -> s32 { return port; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "auto> port"); + }); + + Tester::new_single_source_expect_err( + "implicit polymorph return type auto inference", + "func thing(in a, in b) -> in { return a; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "in {"); + }); + + Tester::new_single_source_expect_err( + "explicit polymorph return type auto inference", + "func thing(in a) -> in { return a; }" + ).error(|e| { e + .assert_msg_has(0, "inference is not allowed") + .assert_occurs_at(0, "auto> {"); + }); +} #[test] fn test_explicit_polymorph_argument() { diff --git a/src/protocol/tests/parser_type_declaration.rs b/src/protocol/tests/parser_type_declaration.rs new file mode 100644 index 0000000000000000000000000000000000000000..b1107cda6e426b7a2c754e1282b7c922b2b6271b --- /dev/null +++ b/src/protocol/tests/parser_type_declaration.rs @@ -0,0 +1,53 @@ +use super::*; + +#[test] +fn test_simple_struct_definition() { + Tester::new_single_source_expect_ok( + "empty struct", + "struct Foo{}" + ).for_struct("Foo", |t| { t.assert_num_fields(0); }); + + Tester::new_single_source_expect_ok( + "single field, no comma", + "struct Foo{ s32 field }" + ).for_struct("Foo", |t| { t + .assert_num_fields(1) + .for_field("field", |f| { + f.assert_parser_type("s32"); + }); + }); + + Tester::new_single_source_expect_ok( + "single field, with comma", + "struct Foo{ s32 field, }" + ).for_struct("Foo", |t| { t + .assert_num_fields(1) + .for_field("field", |f| { f + .assert_parser_type("s32"); + }); + }); + + Tester::new_single_source_expect_ok( + "multiple fields, no comma", + "struct Foo{ u8 a, s16 b, s32 c }" + ).for_struct("Foo", |t| { t + .assert_num_fields(3) + .for_field("a", |f| { f.assert_parser_type("u8"); }) + .for_field("b", |f| { f.assert_parser_type("s16"); }) + .for_field("c", |f| { f.assert_parser_type("s32"); }); + }); + + Tester::new_single_source_expect_ok( + "multiple fields, with comma", + "struct Foo{ + u8 a, + s16 b, + s32 c, + }" + ).for_struct("Foo", |t| { t + .assert_num_fields(3) + .for_field("a", |f| { f.assert_parser_type("u8"); }) + .for_field("b", |f| { f.assert_parser_type("s16"); }) + .for_field("c", |f| { f.assert_parser_type("s32"); }); + }); +} \ No newline at end of file diff --git a/src/protocol/tests/parser_types.rs b/src/protocol/tests/parser_type_layout.rs similarity index 100% rename from src/protocol/tests/parser_types.rs rename to src/protocol/tests/parser_type_layout.rs diff --git a/src/protocol/tests/parser_validation.rs b/src/protocol/tests/parser_validation.rs index 6097189b80e9c09c378adf4323dcd43b49e0ad33..6cc4e99196c67d163d5265831256da171defe96e 100644 --- a/src/protocol/tests/parser_validation.rs +++ b/src/protocol/tests/parser_validation.rs @@ -4,6 +4,8 @@ use super::*; + + #[test] fn test_correct_struct_instance() { Tester::new_single_source_expect_ok( @@ -634,4 +636,110 @@ fn test_incorrect_select_statement() { sync select { auto a = get(rx) -> {} } }" ); +} + +#[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"); + }) +} + +#[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"); + }); } \ No newline at end of file