diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 24b34f140b..5899ec5a00 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -212,8 +212,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) !hidden }) .map(|(pat, _)| { - make::match_arm(iter::once(pat), None, make::ext::expr_todo()) - .clone_for_update() + make::match_arm(pat, None, make::ext::expr_todo()).clone_for_update() }); let catch_all_arm = new_match_arm_list @@ -243,12 +242,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) if needs_catch_all_arm && !has_catch_all_arm { cov_mark::hit!(added_wildcard_pattern); - let arm = make::match_arm( - iter::once(make::wildcard_pat().into()), - None, - make::ext::expr_todo(), - ) - .clone_for_update(); + let arm = + make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo()) + .clone_for_update(); todo_placeholders.push(arm.expr().unwrap()); added_arms.push(arm); } diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index f178a7e0ce..70fb568005 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -252,7 +252,7 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { /// Add bang and parentheses to the expression. fn add_bang_paren(expr: ast::Expr) -> ast::Expr { - make::expr_prefix(T![!], make::expr_paren(expr)) + make::expr_prefix(T![!], make::expr_paren(expr)).into() } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index f699899066..cbd3979624 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -195,6 +195,7 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { make::tail_only_block_expr(true_expr), Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))), ) + .into() } } diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 79f303b37a..bb04a43cf9 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed( if does_autoderef { return capture_name; } - make::expr_prefix(T![*], capture_name) + make::expr_prefix(T![*], capture_name).into() } fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr { diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 67c72a93da..dd2e9cbcb5 100644 --- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -97,7 +97,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> ); for r in return_exprs { - let t = r.expr().unwrap_or_else(make::expr_unit); + let t = r.expr().unwrap_or_else(make::ext::expr_unit); ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); } diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 434daa279c..0b92beefbc 100644 --- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -60,7 +60,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) .indent(while_indent_level); let block_expr = if is_pattern_cond(while_cond.clone()) { let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); - let stmts = iter::once(make::expr_stmt(if_expr).into()); + let stmts = iter::once(make::expr_stmt(if_expr.into()).into()); make::block_expr(stmts, None) } else { let if_cond = invert_boolean_expression(while_cond); diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 0d1b6af720..967da41c15 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1533,7 +1533,7 @@ impl FlowHandler { .into(), call_expr, ); - make::expr_if(condition.into(), block, None) + make::expr_if(condition.into(), block, None).into() } FlowHandler::IfOption { action } => { let path = make::ext::ident_path("Some"); @@ -1544,7 +1544,7 @@ impl FlowHandler { let action_expr = action.make_result_handler(Some(value)); let action_stmt = make::expr_stmt(action_expr); let then = make::block_expr(iter::once(action_stmt.into()), None); - make::expr_if(cond.into(), then, None) + make::expr_if(cond.into(), then, None).into() } FlowHandler::MatchOption { none } => { let some_name = "value"; @@ -1554,15 +1554,15 @@ impl FlowHandler { let value_pat = make::ext::simple_ident_pat(make::name(some_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(some_name)); - make::match_arm(iter::once(pat.into()), None, value) + make::match_arm(pat.into(), None, value) }; let none_arm = { let path = make::ext::ident_path("None"); let pat = make::path_pat(path); - make::match_arm(iter::once(pat), None, none.make_result_handler(None)) + make::match_arm(pat, None, none.make_result_handler(None)) }; let arms = make::match_arm_list(vec![some_arm, none_arm]); - make::expr_match(call_expr, arms) + make::expr_match(call_expr, arms).into() } FlowHandler::MatchResult { err } => { let ok_name = "value"; @@ -1573,21 +1573,17 @@ impl FlowHandler { let value_pat = make::ext::simple_ident_pat(make::name(ok_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(ok_name)); - make::match_arm(iter::once(pat.into()), None, value) + make::match_arm(pat.into(), None, value) }; let err_arm = { let path = make::ext::ident_path("Err"); let value_pat = make::ext::simple_ident_pat(make::name(err_name)); let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); let value = make::expr_path(make::ext::ident_path(err_name)); - make::match_arm( - iter::once(pat.into()), - None, - err.make_result_handler(Some(value)), - ) + make::match_arm(pat.into(), None, err.make_result_handler(Some(value))) }; let arms = make::match_arm_list(vec![ok_arm, err_arm]); - make::expr_match(call_expr, arms) + make::expr_match(call_expr, arms).into() } } } @@ -1879,7 +1875,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - .iter() .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition)); let expr = make::expr_tuple(exprs); - tail_expr = Some(expr); + tail_expr = Some(expr.into()); } }, }; @@ -1910,7 +1906,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - match &handler { FlowHandler::None => block, FlowHandler::Try { kind } => { - let block = with_default_tail_expr(block, make::expr_unit()); + let block = with_default_tail_expr(block, make::ext::expr_unit()); map_tail_expr(block, |tail_expr| { let constructor = match kind { TryKind::Option => "Some", @@ -1924,7 +1920,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) - FlowHandler::If { .. } => { let controlflow_continue = make::expr_call( make::expr_path(make::path_from_text("ControlFlow::Continue")), - make::arg_list(iter::once(make::expr_unit())), + make::arg_list([make::ext::expr_unit()]), ); with_tail_expr(block, controlflow_continue) } @@ -2127,17 +2123,17 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option) -> Op FlowHandler::None | FlowHandler::Try { .. } => return None, FlowHandler::If { .. } => make::expr_call( make::expr_path(make::path_from_text("ControlFlow::Break")), - make::arg_list(iter::once(make::expr_unit())), + make::arg_list([make::ext::expr_unit()]), ), FlowHandler::IfOption { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); - let args = make::arg_list(iter::once(expr)); + let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); + let args = make::arg_list([expr]); make::expr_call(make::expr_path(make::ext::ident_path("Some")), args) } FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), FlowHandler::MatchResult { .. } => { - let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); - let args = make::arg_list(iter::once(expr)); + let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); + let args = make::arg_list([expr]); make::expr_call(make::expr_path(make::ext::ident_path("Err")), args) } }; diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs index f0c96fe3cb..a487960d8d 100644 --- a/crates/ide-assists/src/handlers/move_guard.rs +++ b/crates/ide-assists/src/handlers/move_guard.rs @@ -61,7 +61,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) }; edit.delete(guard.syntax().text_range()); - edit.replace_ast(arm_expr, if_expr); + edit.replace_ast(arm_expr, if_expr.into()); }, ) } diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 94274f6d17..ae14075c26 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -102,7 +102,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt }; (range, None) }, - _ => (macro_call.syntax().text_range(), Some(make::expr_unit())), + _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())), } } } @@ -152,7 +152,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt exprs => { let exprs = exprs.iter().cloned().map(replace_nested_dbgs); let expr = make::expr_tuple(exprs); - (macro_call.syntax().text_range(), Some(expr)) + (macro_call.syntax().text_range(), Some(expr.into())) } }) } diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index b31d45e6d4..e324d6eaaa 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -1,4 +1,4 @@ -use std::iter::{self, successors}; +use std::iter::successors; use either::Either; use ide_db::{ @@ -8,11 +8,7 @@ use ide_db::{ RootDatabase, }; use syntax::{ - ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, HasName, - }, + ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory, HasName}, AstNode, TextRange, T, }; @@ -108,53 +104,58 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite), format!("Replace if{let_} with match"), available_range, - move |edit| { + move |builder| { + let make = SyntaxFactory::new(); let match_expr = { - let else_arm = make_else_arm(ctx, else_block, &cond_bodies); + let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies); let make_match_arm = |(pat, body): (_, ast::BlockExpr)| { - let body = body.reset_indent().indent(IndentLevel(1)); + let body = make.block_expr(body.statements(), body.tail_expr()); + body.indent(IndentLevel::from(1)); + let body = unwrap_trivial_block(body); match pat { - Either::Left(pat) => { - make::match_arm(iter::once(pat), None, unwrap_trivial_block(body)) + Either::Left(pat) => make.match_arm(pat, None, body), + Either::Right(_) if !pat_seen => { + make.match_arm(make.literal_pat("true").into(), None, body) } - Either::Right(_) if !pat_seen => make::match_arm( - iter::once(make::literal_pat("true").into()), - None, - unwrap_trivial_block(body), - ), - Either::Right(expr) => make::match_arm( - iter::once(make::wildcard_pat().into()), - Some(expr), - unwrap_trivial_block(body), + Either::Right(expr) => make.match_arm( + make.wildcard_pat().into(), + Some(make.match_guard(expr)), + body, ), } }; - let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm)); - let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms)); - match_expr.indent(IndentLevel::from_node(if_expr.syntax())) + let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); + let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)); + match_expr.indent(IndentLevel::from_node(if_expr.syntax())); + match_expr.into() }; let has_preceding_if_expr = if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` - make::block_expr(None, Some(match_expr)).into() + make.block_expr([], Some(match_expr)).into() } else { match_expr }; - edit.replace_ast::(if_expr.into(), expr); + + let mut editor = builder.make_editor(if_expr.syntax()); + editor.replace(if_expr.syntax(), expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } fn make_else_arm( ctx: &AssistContext<'_>, + make: &SyntaxFactory, else_block: Option, conditionals: &[(Either, ast::BlockExpr)], ) -> ast::MatchArm { let (pattern, expr) = if let Some(else_block) = else_block { let pattern = match conditionals { - [(Either::Right(_), _)] => make::literal_pat("false").into(), + [(Either::Right(_), _)] => make.literal_pat("false").into(), [(Either::Left(pat), _)] => match ctx .sema .type_of_pat(pat) @@ -164,24 +165,24 @@ fn make_else_arm( if does_pat_match_variant(pat, &it.sad_pattern()) { it.happy_pattern_wildcard() } else if does_pat_variant_nested_or_literal(ctx, pat) { - make::wildcard_pat().into() + make.wildcard_pat().into() } else { it.sad_pattern() } } - None => make::wildcard_pat().into(), + None => make.wildcard_pat().into(), }, - _ => make::wildcard_pat().into(), + _ => make.wildcard_pat().into(), }; (pattern, unwrap_trivial_block(else_block)) } else { let pattern = match conditionals { - [(Either::Right(_), _)] => make::literal_pat("false").into(), - _ => make::wildcard_pat().into(), + [(Either::Right(_), _)] => make.literal_pat("false").into(), + _ => make.wildcard_pat().into(), }; - (pattern, make::expr_unit()) + (pattern, make.expr_unit()) }; - make::match_arm(iter::once(pattern), None, expr) + make.match_arm(pattern, None, expr) } // Assist: replace_match_with_if_let @@ -247,21 +248,21 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' } _ => " let", }; - let target = match_expr.syntax().text_range(); acc.add( AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), format!("Replace match with if{let_}"), - target, - move |edit| { - fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr { + match_expr.syntax().text_range(), + move |builder| { + let make = SyntaxFactory::new(); + let make_block_expr = |expr: ast::Expr| { // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are // formatted without enclosing braces. If we encounter such block exprs, // wrap them in another BlockExpr. match expr { ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, - expr => make::block_expr(iter::empty(), Some(expr)), + expr => make.block_expr([], Some(expr)), } - } + }; let condition = match if_let_pat { ast::Pat::LiteralPat(p) @@ -272,20 +273,25 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { - make::expr_prefix(T![!], scrutinee) + make.expr_prefix(T![!], scrutinee).into() } - _ => make::expr_let(if_let_pat, scrutinee).into(), + _ => make.expr_let(if_let_pat, scrutinee).into(), }; - let then_block = make_block_expr(then_expr.reset_indent()); + let then_expr = then_expr.clone_for_update(); + then_expr.reindent_to(IndentLevel::single()); + let then_block = make_block_expr(then_expr); let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; - let if_let_expr = make::expr_if( + let if_let_expr = make.expr_if( condition, then_block, else_expr.map(make_block_expr).map(ast::ElseBranch::Block), - ) - .indent(IndentLevel::from_node(match_expr.syntax())); + ); + if_let_expr.indent(IndentLevel::from_node(match_expr.syntax())); - edit.replace_ast::(match_expr.into(), if_let_expr); + let mut editor = builder.make_editor(match_expr.syntax()); + editor.replace(match_expr.syntax(), if_let_expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index c2be4593b9..c071d3022d 100644 --- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -1,12 +1,6 @@ -use std::iter::once; - use ide_db::ty_filter::TryEnum; use syntax::{ - ast::{ - self, - edit::{AstNodeEdit, IndentLevel}, - make, - }, + ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, AstNode, T, }; @@ -47,7 +41,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite), "Replace let with if let", target, - |edit| { + |builder| { + let mut editor = builder.make_editor(let_stmt.syntax()); + let make = SyntaxFactory::new(); let ty = ctx.sema.type_of_expr(&init); let happy_variant = ty .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -55,17 +51,18 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> let pat = match happy_variant { None => original_pat, Some(var_name) => { - make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat)) - .into() + make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into() } }; - let block = - make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); - let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None); - let stmt = make::expr_stmt(if_); + let block = make.block_expr([], None); + block.indent(IndentLevel::from_node(let_stmt.syntax())); + let if_expr = make.expr_if(make.expr_let(pat, init).into(), block, None); + let if_stmt = make.expr_stmt(if_expr.into()); - edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); + editor.replace(let_stmt.syntax(), if_stmt.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.file_id(), editor); }, ) } diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs index 2e26f59d03..88b50543dd 100644 --- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs @@ -71,19 +71,17 @@ pub(crate) fn replace_try_expr_with_match( }; let happy_arm = make::match_arm( - iter::once( - try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), - ), + try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()), None, make::expr_path(make::ext::ident_path("it")), ); - let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr); + let sad_arm = make::match_arm(sad_pat, None, sad_expr); let match_arm_list = make::match_arm_list([happy_arm, sad_arm]); let expr_match = make::expr_match(expr, match_arm_list) .indent(IndentLevel::from_node(qm_kw_parent.syntax())); - edit.replace_ast::(qm_kw_parent.into(), expr_match); + edit.replace_ast::(qm_kw_parent.into(), expr_match.into()); }, ) } diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs index c6cffb5434..6b9f661d4d 100644 --- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -54,13 +54,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let pats_after = pipe_token .siblings_with_tokens(Direction::Next) .filter_map(|it| ast::Pat::cast(it.into_node()?)); - // FIXME: We should add a leading pipe if the original arm has one. - let new_match_arm = make::match_arm( - pats_after, - match_arm.guard().and_then(|guard| guard.condition()), - match_arm_body, - ) - .clone_for_update(); + let new_pat = make::or_pat(pats_after, or_pat.leading_pipe().is_some()); + let new_match_arm = + make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update(); let mut pipe_index = pipe_token.index(); if pipe_token diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs index f3e7f5f416..fd37140e9c 100644 --- a/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/crates/ide-assists/src/handlers/unwrap_block.rs @@ -61,7 +61,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option } } None => { - let empty_tuple = make::expr_tuple([]); + let empty_tuple = make::ext::expr_unit(); make::let_stmt(pattern, ty, Some(empty_tuple)).to_string() } }; diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index e20c4ef09e..78ff441791 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -246,7 +246,7 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { } pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { - invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr)) + invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) } fn invert_special_case(expr: &ast::Expr) -> Option { @@ -262,7 +262,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option { T![>] => T![<=], T![>=] => T![<], // Parenthesize other expressions before prefixing `!` - _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))), + _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone())).into()), }; ted::replace(op_token, make::token(rev_token)); Some(bin.into()) diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 75caf6d49f..a9a889acc2 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -66,7 +66,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let pat = make::record_pat(variant_name.clone(), pats.into_iter()); let fields = make::record_expr_field_list(fields); let record_expr = make::record_expr(variant_name, fields).into(); - arms.push(make::match_arm(Some(pat.into()), None, record_expr)); + arms.push(make::match_arm(pat.into(), None, record_expr)); } // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) } @@ -84,21 +84,21 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); let struct_name = make::expr_path(variant_name); let tuple_expr = make::expr_call(struct_name, make::arg_list(fields)); - arms.push(make::match_arm(Some(pat.into()), None, tuple_expr)); + arms.push(make::match_arm(pat.into(), None, tuple_expr)); } // => match self { Self::Name => Self::Name } None => { let pattern = make::path_pat(variant_name.clone()); let variant_expr = make::expr_path(variant_name); - arms.push(make::match_arm(Some(pattern), None, variant_expr)); + arms.push(make::match_arm(pattern, None, variant_expr)); } } } let match_target = make::expr_path(make::ext::ident_path("self")); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list) + make::expr_match(match_target, list).into() } ast::Adt::Struct(strukt) => { match strukt.field_list() { @@ -190,7 +190,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(), let pat = make::record_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(Some(pat.into()), None, expr)); + arms.push(make::match_arm(pat.into(), None, expr)); } Some(ast::FieldList::TupleFieldList(list)) => { // => f.debug_tuple(name) @@ -223,7 +223,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(), let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); - arms.push(make::match_arm(Some(pat.into()), None, expr)); + arms.push(make::match_arm(pat.into(), None, expr)); } None => { let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into(); @@ -232,7 +232,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let macro_call = make::expr_macro_call(macro_name, args); let variant_name = make::path_pat(variant_name); - arms.push(make::match_arm(Some(variant_name), None, macro_call)); + arms.push(make::match_arm(variant_name, None, macro_call)); } } } @@ -241,7 +241,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); let match_expr = make::expr_match(match_target, list); - let body = make::block_expr(None, Some(match_expr)); + let body = make::block_expr(None, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); Some(()) @@ -485,7 +485,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(Some(tuple.into()), None, expr)); + arms.push(make::match_arm(tuple.into(), None, expr)); } } @@ -518,7 +518,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { - arms.push(make::match_arm(Some(tuple.into()), None, expr)); + arms.push(make::match_arm(tuple.into(), None, expr)); } } None => continue, @@ -538,12 +538,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - } else { eq_check }; - arms.push(make::match_arm(Some(lhs), None, rhs)); + arms.push(make::match_arm(lhs, None, rhs)); } - let match_target = make::expr_tuple(vec![lhs_name, rhs_name]); + let match_target = make::expr_tuple([lhs_name, rhs_name]).into(); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - make::expr_match(match_target, list) + make::expr_match(match_target, list).into() } }; @@ -599,15 +599,15 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) let variant_name = make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?); let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]); - arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block())); + arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block())); arms.push(make::match_arm( - [make::ident_pat(false, false, make::name("ord")).into()], + make::ident_pat(false, false, make::name("ord")).into(), None, make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))), )); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - Some(make::expr_stmt(make::expr_match(match_target, list)).into()) + Some(make::expr_stmt(make::expr_match(match_target, list).into()).into()) } fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { diff --git a/crates/ide-assists/src/utils/ref_field_expr.rs b/crates/ide-assists/src/utils/ref_field_expr.rs index e95b291dd7..d434872ea5 100644 --- a/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/crates/ide-assists/src/utils/ref_field_expr.rs @@ -121,7 +121,7 @@ impl RefData { /// Derefs `expr` and wraps it in parens if necessary pub(crate) fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr { if self.needs_deref { - expr = make::expr_prefix(T![*], expr); + expr = make::expr_prefix(T![*], expr).into(); } if self.needs_parentheses { diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 7b57eea052..5bdc320d57 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -303,7 +303,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { resulting_element = ast::Expr::from(parent_deref_element); - new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt); + new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into(); } if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index de40d638be..579f3ba8b4 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -153,8 +153,8 @@ impl AstNodeEdit for N {} #[test] fn test_increase_indent() { let arm_list = { - let arm = make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit()); - make::match_arm_list(vec![arm.clone(), arm]) + let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit()); + make::match_arm_list([arm.clone(), arm]) }; assert_eq!( arm_list.syntax().to_string(), diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 9466755576..93faeb40c3 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -10,7 +10,7 @@ use crate::{ FormatArgsArg, FormatArgsExpr, MacroDef, Static, TokenTree, }, AstToken, - SyntaxKind::*, + SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, }; @@ -50,6 +50,27 @@ impl From for ElseBranch { } } +impl AstNode for ElseBranch { + fn can_cast(kind: SyntaxKind) -> bool { + ast::BlockExpr::can_cast(kind) || ast::IfExpr::can_cast(kind) + } + + fn cast(syntax: SyntaxNode) -> Option { + if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) { + Some(Self::Block(block_expr)) + } else { + ast::IfExpr::cast(syntax).map(Self::IfExpr) + } + } + + fn syntax(&self) -> &SyntaxNode { + match self { + ElseBranch::Block(block_expr) => block_expr.syntax(), + ElseBranch::IfExpr(if_expr) => if_expr.syntax(), + } + } +} + impl ast::IfExpr { pub fn condition(&self) -> Option { // If the condition is a BlockExpr, check if the then body is missing. diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 282cbc4b3a..a920fe4f35 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -63,6 +63,9 @@ pub mod ext { Some(expr) } + pub fn expr_unit() -> ast::Expr { + expr_tuple([]).into() + } pub fn expr_unreachable() -> ast::Expr { expr_from_text("unreachable!()") } @@ -546,10 +549,6 @@ pub fn hacky_block_expr( ast_from_text(&format!("fn f() {buf}")) } -pub fn expr_unit() -> ast::Expr { - expr_from_text("()") -} - pub fn expr_literal(text: &str) -> ast::Literal { assert_eq!(text.trim(), text); ast_from_text(&format!("fn f() {{ let _ = {text}; }}")) @@ -600,14 +599,14 @@ pub fn expr_try(expr: ast::Expr) -> ast::Expr { pub fn expr_await(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("{expr}.await")) } -pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { +pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { expr_from_text(&format!("match {expr} {match_arm_list}")) } pub fn expr_if( condition: ast::Expr, then_branch: ast::BlockExpr, else_branch: Option, -) -> ast::Expr { +) -> ast::IfExpr { let else_branch = match else_branch { Some(ast::ElseBranch::Block(block)) => format!("else {block}"), Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"), @@ -623,7 +622,7 @@ pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr { expr_from_text(&format!("loop {block}")) } -pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { +pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { let token = token(op); expr_from_text(&format!("{token}{expr}")) } @@ -656,14 +655,14 @@ pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr { pub fn expr_paren(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("({expr})")) } -pub fn expr_tuple(elements: impl IntoIterator) -> ast::Expr { +pub fn expr_tuple(elements: impl IntoIterator) -> ast::TupleExpr { let expr = elements.into_iter().format(", "); expr_from_text(&format!("({expr})")) } pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { expr_from_text(&format!("{lhs} = {rhs}")) } -fn expr_from_text(text: &str) -> ast::Expr { +fn expr_from_text + AstNode>(text: &str) -> E { ast_from_text(&format!("const C: () = {text};")) } pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { @@ -788,15 +787,21 @@ pub fn path_pat(path: ast::Path) -> ast::Pat { } } -pub fn match_arm( - pats: impl IntoIterator, - guard: Option, - expr: ast::Expr, -) -> ast::MatchArm { - let pats_str = pats.into_iter().join(" | "); +/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise. +pub fn or_pat(pats: impl IntoIterator, leading_pipe: bool) -> ast::Pat { + let leading_pipe = if leading_pipe { "| " } else { "" }; + let pats = pats.into_iter().join(" | "); + + return from_text(&format!("{leading_pipe}{pats}")); + fn from_text(text: &str) -> ast::Pat { + ast_from_text(&format!("fn f({text}: ())")) + } +} + +pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) -> ast::MatchArm { return match guard { - Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")), - None => from_text(&format!("{pats_str} => {expr}")), + Some(guard) => from_text(&format!("{pat} {guard} => {expr}")), + None => from_text(&format!("{pat} => {expr}")), }; fn from_text(text: &str) -> ast::MatchArm { @@ -817,6 +822,14 @@ pub fn match_arm_with_guard( } } +pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard { + return from_text(&format!("if {condition}")); + + fn from_text(text: &str) -> ast::MatchGuard { + ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}")) + } +} + pub fn match_arm_list(arms: impl IntoIterator) -> ast::MatchArmList { let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like()); diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index bea6bfeafc..d62c01ba76 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,6 +1,6 @@ //! Wrappers over [`make`] constructors use crate::{ - ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, + ast::{self, make, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, syntax_editor::SyntaxMappingBuilder, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, }; @@ -12,6 +12,10 @@ impl SyntaxFactory { make::name(name).clone_for_update() } + pub fn name_ref(&self, name: &str) -> ast::NameRef { + make::name_ref(name).clone_for_update() + } + pub fn ty(&self, text: &str) -> ast::Type { make::ty(text).clone_for_update() } @@ -46,6 +50,71 @@ impl SyntaxFactory { ast } + pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment { + let ast = make::path_segment(name_ref.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_segment_generics( + &self, + name_ref: ast::NameRef, + generic_arg_list: ast::GenericArgList, + ) -> ast::PathSegment { + let ast::Type::PathType(path) = make::ty(&format!("{name_ref}{generic_arg_list}")) else { + unreachable!(); + }; + + let ast = path.path().unwrap().segment().unwrap().clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + builder.map_node( + generic_arg_list.syntax().clone(), + ast.generic_arg_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { + let ast = make::path_unqualified(segment.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(segment.syntax().clone(), ast.segment().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn path_from_segments( + &self, + segments: impl IntoIterator, + is_abs: bool, + ) -> ast::Path { + let (segments, input) = iterator_input(segments); + let ast = make::path_from_segments(segments, is_abs).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.segments().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat { let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); @@ -58,6 +127,32 @@ impl SyntaxFactory { ast } + pub fn wildcard_pat(&self) -> ast::WildcardPat { + make::wildcard_pat().clone_for_update() + } + + pub fn literal_pat(&self, text: &str) -> ast::LiteralPat { + make::literal_pat(text).clone_for_update() + } + + pub fn tuple_struct_pat( + &self, + path: ast::Path, + fields: impl IntoIterator, + ) -> ast::TupleStructPat { + let (fields, input) = iterator_input(fields); + let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn block_expr( &self, statements: impl IntoIterator, @@ -98,6 +193,19 @@ impl SyntaxFactory { ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() } } + pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr { + let (fields, input) = iterator_input(fields); + let ast = make::expr_tuple(fields).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr { let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() @@ -115,6 +223,10 @@ impl SyntaxFactory { ast } + pub fn expr_literal(&self, text: &str) -> ast::Literal { + make::expr_literal(text).clone_for_update() + } + pub fn expr_path(&self, path: ast::Path) -> ast::Expr { let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else { unreachable!() @@ -129,6 +241,18 @@ impl SyntaxFactory { ast.into() } + pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { + let ast = make::expr_prefix(op, expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update() else { @@ -160,6 +284,125 @@ impl SyntaxFactory { ast } + pub fn expr_if( + &self, + condition: ast::Expr, + then_branch: ast::BlockExpr, + else_branch: Option, + ) -> ast::IfExpr { + let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone()); + builder.map_node( + then_branch.syntax().clone(), + ast.then_branch().unwrap().syntax().clone(), + ); + + if let Some(else_branch) = else_branch { + builder.map_node( + else_branch.syntax().clone(), + ast.else_branch().unwrap().syntax().clone(), + ); + } + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { + let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt { + let ast = make::expr_stmt(expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { + let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.map_node( + match_arm_list.syntax().clone(), + ast.match_arm_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_arm( + &self, + pat: ast::Pat, + guard: Option, + expr: ast::Expr, + ) -> ast::MatchArm { + let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone()); + if let Some(guard) = guard { + builder.map_node(guard.syntax().clone(), ast.guard().unwrap().syntax().clone()); + } + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard { + let ast = make::match_guard(condition.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn match_arm_list( + &self, + match_arms: impl IntoIterator, + ) -> ast::MatchArmList { + let (match_arms, input) = iterator_input(match_arms); + let ast = make::match_arm_list(match_arms).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input.into_iter(), ast.arms().map(|it| it.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn let_stmt( &self, pattern: ast::Pat, @@ -503,11 +746,22 @@ impl SyntaxFactory { make::token(kind) } - pub fn whitespace(&self, text: &str) -> ast::SyntaxToken { + pub fn whitespace(&self, text: &str) -> SyntaxToken { make::tokens::whitespace(text) } } +// `ext` constructors +impl SyntaxFactory { + pub fn expr_unit(&self) -> ast::Expr { + self.expr_tuple([]).into() + } + + pub fn ident_path(&self, ident: &str) -> ast::Path { + self.path_unqualified(self.path_segment(self.name_ref(ident))) + } +} + // We need to collect `input` here instead of taking `impl IntoIterator + Clone`, // because if we took `impl IntoIterator + Clone`, that could be something like an // `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor. diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 992a847663..b82181ae13 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -335,7 +335,7 @@ mod tests { #[test] fn basic_usage() { let root = make::match_arm( - [make::wildcard_pat().into()], + make::wildcard_pat().into(), None, make::expr_tuple([ make::expr_bin_op( @@ -344,7 +344,8 @@ mod tests { make::expr_literal("2").into(), ), make::expr_literal("true").into(), - ]), + ]) + .into(), ); let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); @@ -549,7 +550,7 @@ mod tests { None, None, make::param_list(None, []), - make::block_expr([], Some(make::expr_unit())), + make::block_expr([], Some(make::ext::expr_unit())), Some(make::ret_type(make::ty_unit())), false, false,