diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 54c874d06c..3d7d85a539 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -145,7 +145,7 @@ pub(super) fn atom_expr( stmt_list(p); m.complete(p, BLOCK_EXPR) } - // test_err gen_blocks + // test gen_blocks 2024 // pub fn main() { // gen { yield ""; }; // async gen { yield ""; }; diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs index e0042fbdec..b4b6759e90 100644 --- a/crates/parser/src/tests.rs +++ b/crates/parser/src/tests.rs @@ -51,7 +51,7 @@ fn lex(text: &str) -> String { fn parse_ok() { for case in TestCase::list("parser/ok") { let _guard = stdx::panic_context::enter(format!("{:?}", case.rs)); - let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text); + let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text, Edition::CURRENT); assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display()); expect_file![case.rast].assert_eq(&actual); } @@ -61,16 +61,16 @@ fn parse_ok() { fn parse_err() { for case in TestCase::list("parser/err") { let _guard = stdx::panic_context::enter(format!("{:?}", case.rs)); - let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text); + let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text, Edition::CURRENT); assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display()); expect_file![case.rast].assert_eq(&actual) } } -fn parse(entry: TopEntryPoint, text: &str) -> (String, bool) { - let lexed = LexedStr::new(Edition::CURRENT, text); +fn parse(entry: TopEntryPoint, text: &str, edition: Edition) -> (String, bool) { + let lexed = LexedStr::new(edition, text); let input = lexed.to_input(); - let output = entry.parse(&input, Edition::CURRENT); + let output = entry.parse(&input, edition); let mut buf = String::new(); let mut errors = Vec::new(); @@ -153,9 +153,19 @@ impl TestCase { #[track_caller] fn run_and_expect_no_errors(path: &str) { + run_and_expect_no_errors_with_edition(path, Edition::CURRENT) +} + +#[track_caller] +fn run_and_expect_errors(path: &str) { + run_and_expect_errors_with_edition(path, Edition::CURRENT) +} + +#[track_caller] +fn run_and_expect_no_errors_with_edition(path: &str, edition: Edition) { let path = PathBuf::from(path); let text = std::fs::read_to_string(&path).unwrap(); - let (actual, errors) = parse(TopEntryPoint::SourceFile, &text); + let (actual, errors) = parse(TopEntryPoint::SourceFile, &text, edition); assert!(!errors, "errors in an OK file {}:\n{actual}", path.display()); let mut p = PathBuf::from(".."); p.push(path); @@ -164,10 +174,10 @@ fn run_and_expect_no_errors(path: &str) { } #[track_caller] -fn run_and_expect_errors(path: &str) { +fn run_and_expect_errors_with_edition(path: &str, edition: Edition) { let path = PathBuf::from(path); let text = std::fs::read_to_string(&path).unwrap(); - let (actual, errors) = parse(TopEntryPoint::SourceFile, &text); + let (actual, errors) = parse(TopEntryPoint::SourceFile, &text, edition); assert!(errors, "no errors in an ERR file {}:\n{actual}", path.display()); let mut p = PathBuf::from(".."); p.push(path); diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs index 49dd9e293b..c56bf0b644 100644 --- a/crates/parser/src/tests/top_entries.rs +++ b/crates/parser/src/tests/top_entries.rs @@ -307,6 +307,6 @@ fn expr() { #[track_caller] fn check(entry: TopEntryPoint, input: &str, expect: expect_test::Expect) { - let (parsed, _errors) = super::parse(entry, input); + let (parsed, _errors) = super::parse(entry, input, crate::Edition::CURRENT); expect.assert_eq(&parsed) } diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index f82d1ed320..aa8210541a 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -1,5 +1,5 @@ mod ok { - use crate::tests::run_and_expect_no_errors; + use crate::tests::*; #[test] fn anonymous_const() { run_and_expect_no_errors("test_data/parser/inline/ok/anonymous_const.rs"); @@ -267,6 +267,13 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/function_where_clause.rs"); } #[test] + fn gen_blocks() { + run_and_expect_no_errors_with_edition( + "test_data/parser/inline/ok/gen_blocks.rs", + crate::Edition::Edition2024, + ); + } + #[test] fn generic_arg() { run_and_expect_no_errors("test_data/parser/inline/ok/generic_arg.rs"); } #[test] fn generic_param_attribute() { @@ -666,7 +673,7 @@ mod ok { fn yield_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/yield_expr.rs"); } } mod err { - use crate::tests::run_and_expect_errors; + use crate::tests::*; #[test] fn angled_path_without_qual() { run_and_expect_errors("test_data/parser/inline/err/angled_path_without_qual.rs"); @@ -708,8 +715,6 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs"); } #[test] - fn gen_blocks() { run_and_expect_errors("test_data/parser/inline/err/gen_blocks.rs"); } - #[test] fn gen_fn() { run_and_expect_errors("test_data/parser/inline/err/gen_fn.rs"); } #[test] fn generic_arg_list_recover() { diff --git a/crates/parser/test_data/parser/inline/err/gen_blocks.rast b/crates/parser/test_data/parser/inline/err/gen_blocks.rast deleted file mode 100644 index 08a85891ed..0000000000 --- a/crates/parser/test_data/parser/inline/err/gen_blocks.rast +++ /dev/null @@ -1,139 +0,0 @@ -SOURCE_FILE - FN - VISIBILITY - PUB_KW "pub" - WHITESPACE " " - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "main" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "gen" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - ERROR - YIELD_KW "yield" - WHITESPACE " " - ERROR - STRING "\"\"" - ERROR - SEMICOLON ";" - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " - ERROR - ASYNC_KW "async" - WHITESPACE " " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "gen" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - ERROR - YIELD_KW "yield" - WHITESPACE " " - ERROR - STRING "\"\"" - ERROR - SEMICOLON ";" - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " - EXPR_STMT - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "gen" - WHITESPACE " " - EXPR_STMT - CLOSURE_EXPR - MOVE_KW "move" - WHITESPACE " " - EXPR_STMT - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - EXPR_STMT - YIELD_EXPR - YIELD_KW "yield" - WHITESPACE " " - LITERAL - STRING "\"\"" - SEMICOLON ";" - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " - ERROR - ASYNC_KW "async" - WHITESPACE " " - EXPR_STMT - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "gen" - WHITESPACE " " - EXPR_STMT - CLOSURE_EXPR - MOVE_KW "move" - WHITESPACE " " - EXPR_STMT - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - EXPR_STMT - YIELD_EXPR - YIELD_KW "yield" - WHITESPACE " " - LITERAL - STRING "\"\"" - SEMICOLON ";" - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" -error 26: expected identifier -error 31: expected COMMA -error 32: expected identifier -error 34: expected COMMA -error 34: expected identifier -error 48: expected fn, trait or impl -error 55: expected identifier -error 60: expected COMMA -error 61: expected identifier -error 63: expected COMMA -error 63: expected identifier -error 75: expected SEMICOLON -error 80: expected `|` -error 80: expected SEMICOLON -error 105: expected fn, trait or impl -error 109: expected SEMICOLON -error 114: expected `|` -error 114: expected SEMICOLON diff --git a/crates/parser/test_data/parser/inline/ok/gen_blocks.rast b/crates/parser/test_data/parser/inline/ok/gen_blocks.rast new file mode 100644 index 0000000000..6e8df9897e --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/gen_blocks.rast @@ -0,0 +1,101 @@ +SOURCE_FILE + FN + COMMENT "// 2024" + WHITESPACE "\n" + VISIBILITY + PUB_KW "pub" + WHITESPACE " " + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + BLOCK_EXPR + GEN_KW "gen" + WHITESPACE " " + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + YIELD_EXPR + YIELD_KW "yield" + WHITESPACE " " + LITERAL + STRING "\"\"" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BLOCK_EXPR + ASYNC_KW "async" + WHITESPACE " " + GEN_KW "gen" + WHITESPACE " " + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + YIELD_EXPR + YIELD_KW "yield" + WHITESPACE " " + LITERAL + STRING "\"\"" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BLOCK_EXPR + GEN_KW "gen" + WHITESPACE " " + MOVE_KW "move" + WHITESPACE " " + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + YIELD_EXPR + YIELD_KW "yield" + WHITESPACE " " + LITERAL + STRING "\"\"" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BLOCK_EXPR + ASYNC_KW "async" + WHITESPACE " " + GEN_KW "gen" + WHITESPACE " " + MOVE_KW "move" + WHITESPACE " " + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + YIELD_EXPR + YIELD_KW "yield" + WHITESPACE " " + LITERAL + STRING "\"\"" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/err/gen_blocks.rs b/crates/parser/test_data/parser/inline/ok/gen_blocks.rs similarity index 94% rename from crates/parser/test_data/parser/inline/err/gen_blocks.rs rename to crates/parser/test_data/parser/inline/ok/gen_blocks.rs index f7687331d6..669b434973 100644 --- a/crates/parser/test_data/parser/inline/err/gen_blocks.rs +++ b/crates/parser/test_data/parser/inline/ok/gen_blocks.rs @@ -1,3 +1,4 @@ +// 2024 pub fn main() { gen { yield ""; }; async gen { yield ""; }; diff --git a/xtask/src/codegen/parser_inline_tests.rs b/xtask/src/codegen/parser_inline_tests.rs index 3a9073b4e4..f9f73df8eb 100644 --- a/xtask/src/codegen/parser_inline_tests.rs +++ b/xtask/src/codegen/parser_inline_tests.rs @@ -34,38 +34,60 @@ pub(crate) fn generate(check: bool) { install_tests(&tests.err, &format!("{PARSER_TEST_DATA_INLINE}/err"), check).unwrap(); if some_file_was_updated { - let _ = fs::File::open(&format!("{PARSER_CRATE_ROOT}/src/tests.rs")) + let _ = fs::File::open(format!("{PARSER_CRATE_ROOT}/src/tests.rs")) .unwrap() .set_modified(SystemTime::now()); - let ok_tests = tests.ok.keys().sorted().map(|k| { - let test_name = quote::format_ident!("{}", k); + let ok_tests = tests.ok.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| { + let test_name = quote::format_ident!("{}", test.name); let test_file = format!("test_data/parser/inline/ok/{test_name}.rs"); + let (test_func, args) = match &test.edition { + Some(edition) => { + let edition = quote::format_ident!("Edition{edition}"); + ( + quote::format_ident!("run_and_expect_no_errors_with_edition"), + quote::quote! {#test_file, crate::Edition::#edition}, + ) + } + None => { + (quote::format_ident!("run_and_expect_no_errors"), quote::quote! {#test_file}) + } + }; quote::quote! { #[test] fn #test_name() { - run_and_expect_no_errors(#test_file); + #test_func(#args); } } }); - let err_tests = tests.err.keys().sorted().map(|k| { - let test_name = quote::format_ident!("{}", k); + let err_tests = tests.err.values().sorted_by(|a, b| a.name.cmp(&b.name)).map(|test| { + let test_name = quote::format_ident!("{}", test.name); let test_file = format!("test_data/parser/inline/err/{test_name}.rs"); + let (test_func, args) = match &test.edition { + Some(edition) => { + let edition = quote::format_ident!("Edition{edition}"); + ( + quote::format_ident!("run_and_expect_errors_with_edition"), + quote::quote! {#test_file, crate::Edition::#edition}, + ) + } + None => (quote::format_ident!("run_and_expect_errors"), quote::quote! {#test_file}), + }; quote::quote! { #[test] fn #test_name() { - run_and_expect_errors(#test_file); + #test_func(#args); } } }); let output = quote::quote! { mod ok { - use crate::tests::run_and_expect_no_errors; + use crate::tests::*; #(#ok_tests)* } mod err { - use crate::tests::run_and_expect_errors; + use crate::tests::*; #(#err_tests)* } }; @@ -107,9 +129,10 @@ fn install_tests(tests: &HashMap, into: &str, check: bool) -> Resu #[derive(Debug)] struct Test { - pub name: String, - pub text: String, - pub kind: TestKind, + name: String, + text: String, + kind: TestKind, + edition: Option, } #[derive(Copy, Clone, Debug)] @@ -120,8 +143,8 @@ enum TestKind { #[derive(Default, Debug)] struct Tests { - pub ok: HashMap, - pub err: HashMap, + ok: HashMap, + err: HashMap, } fn collect_tests(s: &str) -> Vec { @@ -135,14 +158,24 @@ fn collect_tests(s: &str) -> Vec { } else { continue; }; - let text: String = comment_block.contents[1..] - .iter() - .cloned() + let (name, edition) = match *name.split(' ').collect_vec().as_slice() { + [name, edition] => { + assert!(!edition.contains(' ')); + (name.to_owned(), Some(edition.to_owned())) + } + [name] => (name.to_owned(), None), + _ => panic!("invalid test name: {:?}", name), + }; + let text: String = edition + .as_ref() + .map(|edition| format!("// {edition}")) + .into_iter() + .chain(comment_block.contents[1..].iter().cloned()) .chain(iter::once(String::new())) .collect::>() .join("\n"); assert!(!text.trim().is_empty() && text.ends_with('\n')); - res.push(Test { name, text, kind }) + res.push(Test { name, edition, text, kind }) } res } @@ -180,7 +213,9 @@ fn existing_tests(dir: &Path, ok: TestKind) -> Result