From d33edb4e9cf1cad1c9ceb7c99859d73993ccba29 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 3 Jan 2021 17:45:49 +0100 Subject: ssr: Allow replacing expressions with statements Now that statements can be matched and replaced (#6587) some usecases require expressions to be replaced with statements as well. This happens when something that can ambiguously be an expression or statement like `if` and loop blocks appear in the last position of a block, as trailing expression. In this case a replacement pattern of the form `if foo(){$a();}==>>$a();` will only substitute `if` blocks in the list of statements but not if they (implicitly) end up in the trailing expression, where they are not wrapped by an EXPR_STMT (but the pattern and template are, as parsing only succeeds for the `stmt ==>> stmt` case). Instead of adding two rules that match an expression - and emit duplicate matching errors - allow the template for expressions to be a statement if it fails to parse as an expression. --- crates/ssr/src/parsing.rs | 16 +++++++++++++--- crates/ssr/src/tests.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) (limited to 'crates/ssr') diff --git a/crates/ssr/src/parsing.rs b/crates/ssr/src/parsing.rs index 289affe90..3d5e4feb7 100644 --- a/crates/ssr/src/parsing.rs +++ b/crates/ssr/src/parsing.rs @@ -73,12 +73,18 @@ impl ParsedRule { placeholders_by_stand_in: pattern.placeholders_by_stand_in(), rules: Vec::new(), }; - builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse)); + + let raw_template_stmt = raw_template.map(ast::Stmt::parse); + if let raw_template_expr @ Some(Ok(_)) = raw_template.map(ast::Expr::parse) { + builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_expr); + } else { + builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_stmt.clone()); + } builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse)); builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse)); builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); - builder.try_add(ast::Stmt::parse(&raw_pattern), raw_template.map(ast::Stmt::parse)); + builder.try_add(ast::Stmt::parse(&raw_pattern), raw_template_stmt); builder.build() } } @@ -89,7 +95,11 @@ struct RuleBuilder { } impl RuleBuilder { - fn try_add(&mut self, pattern: Result, template: Option>) { + fn try_add( + &mut self, + pattern: Result, + template: Option>, + ) { match (pattern, template) { (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule { placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), diff --git a/crates/ssr/src/tests.rs b/crates/ssr/src/tests.rs index c4149a849..db9cb8ca1 100644 --- a/crates/ssr/src/tests.rs +++ b/crates/ssr/src/tests.rs @@ -203,6 +203,53 @@ fn ssr_let_stmt_replace_expr() { ); } +#[test] +fn ssr_blockexpr_replace_stmt_with_stmt() { + assert_ssr_transform( + "if $a() {$b;} ==>> $b;", + "{ + if foo() { + bar(); + } + Ok(()) +}", + expect![[r#"{ + bar(); + Ok(()) +}"#]], + ); +} + +#[test] +fn ssr_blockexpr_match_trailing_expr() { + assert_matches( + "if $a() {$b;}", + "{ + if foo() { + bar(); + } +}", + &["if foo() { + bar(); + }"], + ); +} + +#[test] +fn ssr_blockexpr_replace_trailing_expr_with_stmt() { + assert_ssr_transform( + "if $a() {$b;} ==>> $b;", + "{ + if foo() { + bar(); + } +}", + expect![["{ + bar(); +}"]], + ); +} + #[test] fn ssr_function_to_method() { assert_ssr_transform( -- cgit v1.2.3