7147: ssr: Allow replacing expressions with statements r=davidlattimore a=MarijnS95

Depends on #6587

Until that is merged, the diff is https://github.com/MarijnS95/rust-analyzer/compare/stmt..replace-expr-with-stmt

---

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.

---

Another gross change that does not seem to break any tests currently, but perhaps a safeguard should be added to only allow this kind of replacement in blocks by "pushing" the replacement template to the statement list and clearing the trailing expression?

CC @davidlattimore 

Co-authored-by: Marijn Suijten <marijn@traverseresearch.nl>
This commit is contained in:
bors[bot] 2021-01-04 20:36:13 +00:00 committed by GitHub
commit 550c49657e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 3 deletions

View file

@ -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<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<Result<T, ()>>) {
fn try_add<T: AstNode, T2: AstNode>(
&mut self,
pattern: Result<T, ()>,
template: Option<Result<T2, ()>>,
) {
match (pattern, template) {
(Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule {
placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),

View file

@ -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(