mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-29 19:17:12 +00:00 
			
		
		
		
	Merge pull request #19171 from ShoyuVanilla/migrate-de-morgan-assist
internal: Migrate `apply_demorgan` to `SyntaxEditor`
This commit is contained in:
		
						commit
						f6edb7178e
					
				
					 10 changed files with 351 additions and 75 deletions
				
			
		|  | @ -76,6 +76,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
| 
 | 
 | ||||||
|     let cfg = ctx.config.import_path_config(); |     let cfg = ctx.config.import_path_config(); | ||||||
| 
 | 
 | ||||||
|  |     let make = SyntaxFactory::new(); | ||||||
|  | 
 | ||||||
|     let module = ctx.sema.scope(expr.syntax())?.module(); |     let module = ctx.sema.scope(expr.syntax())?.module(); | ||||||
|     let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( |     let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( | ||||||
|         Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>, |         Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>, | ||||||
|  | @ -93,7 +95,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .filter_map(|variant| { |             .filter_map(|variant| { | ||||||
|                 Some(( |                 Some(( | ||||||
|                     build_pat(ctx, module, variant, cfg)?, |                     build_pat(ctx, &make, module, variant, cfg)?, | ||||||
|                     variant.should_be_hidden(ctx.db(), module.krate()), |                     variant.should_be_hidden(ctx.db(), module.krate()), | ||||||
|                 )) |                 )) | ||||||
|             }) |             }) | ||||||
|  | @ -144,10 +146,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|                 let is_hidden = variants |                 let is_hidden = variants | ||||||
|                     .iter() |                     .iter() | ||||||
|                     .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); |                     .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); | ||||||
|                 let patterns = |                 let patterns = variants | ||||||
|                     variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); |                     .into_iter() | ||||||
|  |                     .filter_map(|variant| build_pat(ctx, &make, module, variant, cfg)); | ||||||
| 
 | 
 | ||||||
|                 (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) |                 (ast::Pat::from(make.tuple_pat(patterns)), is_hidden) | ||||||
|             }) |             }) | ||||||
|             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); |             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); | ||||||
|         ( |         ( | ||||||
|  | @ -176,9 +179,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|                 let is_hidden = variants |                 let is_hidden = variants | ||||||
|                     .iter() |                     .iter() | ||||||
|                     .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); |                     .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); | ||||||
|                 let patterns = |                 let patterns = variants | ||||||
|                     variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); |                     .into_iter() | ||||||
|                 (ast::Pat::from(make::slice_pat(patterns)), is_hidden) |                     .filter_map(|variant| build_pat(ctx, &make, module, variant, cfg)); | ||||||
|  | 
 | ||||||
|  |                 (ast::Pat::from(make.slice_pat(patterns)), is_hidden) | ||||||
|             }) |             }) | ||||||
|             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); |             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); | ||||||
|         ( |         ( | ||||||
|  | @ -203,8 +208,6 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|         "Fill match arms", |         "Fill match arms", | ||||||
|         ctx.sema.original_range(match_expr.syntax()).range, |         ctx.sema.original_range(match_expr.syntax()).range, | ||||||
|         |builder| { |         |builder| { | ||||||
|             let make = SyntaxFactory::new(); |  | ||||||
| 
 |  | ||||||
|             // having any hidden variants means that we need a catch-all arm
 |             // having any hidden variants means that we need a catch-all arm
 | ||||||
|             needs_catch_all_arm |= has_hidden_variants; |             needs_catch_all_arm |= has_hidden_variants; | ||||||
| 
 | 
 | ||||||
|  | @ -243,7 +246,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
| 
 | 
 | ||||||
|             if needs_catch_all_arm && !has_catch_all_arm { |             if needs_catch_all_arm && !has_catch_all_arm { | ||||||
|                 cov_mark::hit!(added_wildcard_pattern); |                 cov_mark::hit!(added_wildcard_pattern); | ||||||
|                 let arm = make.match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo()); |                 let arm = make.match_arm(make.wildcard_pat().into(), None, make::ext::expr_todo()); | ||||||
|                 arms.push(arm); |                 arms.push(arm); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -290,7 +293,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             editor.add_mappings(make.finish_with_mappings()); |             editor.add_mappings(make.take()); | ||||||
|             builder.add_file_edits(ctx.file_id(), editor); |             builder.add_file_edits(ctx.file_id(), editor); | ||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
|  | @ -445,6 +448,7 @@ fn resolve_array_of_enum_def( | ||||||
| 
 | 
 | ||||||
| fn build_pat( | fn build_pat( | ||||||
|     ctx: &AssistContext<'_>, |     ctx: &AssistContext<'_>, | ||||||
|  |     make: &SyntaxFactory, | ||||||
|     module: hir::Module, |     module: hir::Module, | ||||||
|     var: ExtendedVariant, |     var: ExtendedVariant, | ||||||
|     cfg: ImportPathConfig, |     cfg: ImportPathConfig, | ||||||
|  | @ -455,31 +459,32 @@ fn build_pat( | ||||||
|             let edition = module.krate().edition(db); |             let edition = module.krate().edition(db); | ||||||
|             let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); |             let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); | ||||||
|             let fields = var.fields(db); |             let fields = var.fields(db); | ||||||
|             let pat = match var.kind(db) { |             let pat: ast::Pat = match var.kind(db) { | ||||||
|                 hir::StructKind::Tuple => { |                 hir::StructKind::Tuple => { | ||||||
|                     let mut name_generator = suggest_name::NameGenerator::new(); |                     let mut name_generator = suggest_name::NameGenerator::new(); | ||||||
|                     let pats = fields.into_iter().map(|f| { |                     let pats = fields.into_iter().map(|f| { | ||||||
|                         let name = name_generator.for_type(&f.ty(db), db, edition); |                         let name = name_generator.for_type(&f.ty(db), db, edition); | ||||||
|                         match name { |                         match name { | ||||||
|                             Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(), |                             Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(), | ||||||
|                             None => make::wildcard_pat().into(), |                             None => make.wildcard_pat().into(), | ||||||
|                         } |                         } | ||||||
|                     }); |                     }); | ||||||
|                     make::tuple_struct_pat(path, pats).into() |                     make.tuple_struct_pat(path, pats).into() | ||||||
|                 } |                 } | ||||||
|                 hir::StructKind::Record => { |                 hir::StructKind::Record => { | ||||||
|                     let pats = fields |                     let fields = fields | ||||||
|                         .into_iter() |                         .into_iter() | ||||||
|                         .map(|f| make::name(f.name(db).as_str())) |                         .map(|f| make.name_ref(f.name(db).as_str())) | ||||||
|                         .map(|name| make::ext::simple_ident_pat(name).into()); |                         .map(|name_ref| make.record_pat_field_shorthand(name_ref)); | ||||||
|                     make::record_pat(path, pats).into() |                     let fields = make.record_pat_field_list(fields, None); | ||||||
|  |                     make.record_pat_with_fields(path, fields).into() | ||||||
|                 } |                 } | ||||||
|                 hir::StructKind::Unit => make::path_pat(path), |                 hir::StructKind::Unit => make.path_pat(path), | ||||||
|             }; |             }; | ||||||
|             Some(pat) |             Some(pat) | ||||||
|         } |         } | ||||||
|         ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), |         ExtendedVariant::True => Some(ast::Pat::from(make.literal_pat("true"))), | ||||||
|         ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), |         ExtendedVariant::False => Some(ast::Pat::from(make.literal_pat("false"))), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,12 +3,12 @@ use std::collections::VecDeque; | ||||||
| use ide_db::{ | use ide_db::{ | ||||||
|     assists::GroupLabel, |     assists::GroupLabel, | ||||||
|     famous_defs::FamousDefs, |     famous_defs::FamousDefs, | ||||||
|     source_change::SourceChangeBuilder, |  | ||||||
|     syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, |     syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, | ||||||
| }; | }; | ||||||
| use syntax::{ | use syntax::{ | ||||||
|     ast::{self, make, AstNode, Expr::BinExpr, HasArgList}, |     ast::{self, syntax_factory::SyntaxFactory, AstNode, Expr::BinExpr, HasArgList}, | ||||||
|     ted, SyntaxKind, T, |     syntax_editor::{Position, SyntaxEditor}, | ||||||
|  |     SyntaxKind, SyntaxNode, T, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; | ||||||
|  | @ -58,9 +58,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti | ||||||
|         _ => return None, |         _ => return None, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let demorganed = bin_expr.clone_subtree().clone_for_update(); |     let make = SyntaxFactory::new(); | ||||||
|  | 
 | ||||||
|  |     let demorganed = bin_expr.clone_subtree(); | ||||||
|  |     let mut editor = SyntaxEditor::new(demorganed.syntax().clone()); | ||||||
|  |     editor.replace(demorganed.op_token()?, make.token(inv_token)); | ||||||
| 
 | 
 | ||||||
|     ted::replace(demorganed.op_token()?, ast::make::token(inv_token)); |  | ||||||
|     let mut exprs = VecDeque::from([ |     let mut exprs = VecDeque::from([ | ||||||
|         (bin_expr.lhs()?, demorganed.lhs()?), |         (bin_expr.lhs()?, demorganed.lhs()?), | ||||||
|         (bin_expr.rhs()?, demorganed.rhs()?), |         (bin_expr.rhs()?, demorganed.rhs()?), | ||||||
|  | @ -70,35 +73,39 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti | ||||||
|         if let BinExpr(bin_expr) = &expr { |         if let BinExpr(bin_expr) = &expr { | ||||||
|             if let BinExpr(cbin_expr) = &dm { |             if let BinExpr(cbin_expr) = &dm { | ||||||
|                 if op == bin_expr.op_kind()? { |                 if op == bin_expr.op_kind()? { | ||||||
|                     ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token)); |                     editor.replace(cbin_expr.op_token()?, make.token(inv_token)); | ||||||
|                     exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?)); |                     exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?)); | ||||||
|                     exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?)); |                     exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?)); | ||||||
|                 } else { |                 } else { | ||||||
|                     let mut inv = invert_boolean_expression(expr); |                     let mut inv = invert_boolean_expression(&make, expr); | ||||||
|                     if inv.needs_parens_in(dm.syntax().parent()?) { |                     if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) { | ||||||
|                         inv = ast::make::expr_paren(inv).clone_for_update(); |                         inv = make.expr_paren(inv).into(); | ||||||
|                     } |                     } | ||||||
|                     ted::replace(dm.syntax(), inv.syntax()); |                     editor.replace(dm.syntax(), inv.syntax()); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 return None; |                 return None; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update(); |             let mut inv = invert_boolean_expression(&make, dm.clone()); | ||||||
|             if inv.needs_parens_in(dm.syntax().parent()?) { |             if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) { | ||||||
|                 inv = ast::make::expr_paren(inv).clone_for_update(); |                 inv = make.expr_paren(inv).into(); | ||||||
|             } |             } | ||||||
|             ted::replace(dm.syntax(), inv.syntax()); |             editor.replace(dm.syntax(), inv.syntax()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     editor.add_mappings(make.finish_with_mappings()); | ||||||
|  |     let edit = editor.finish(); | ||||||
|  |     let demorganed = ast::Expr::cast(edit.new_root().clone())?; | ||||||
|  | 
 | ||||||
|     acc.add_group( |     acc.add_group( | ||||||
|         &GroupLabel("Apply De Morgan's law".to_owned()), |         &GroupLabel("Apply De Morgan's law".to_owned()), | ||||||
|         AssistId("apply_demorgan", AssistKind::RefactorRewrite), |         AssistId("apply_demorgan", AssistKind::RefactorRewrite), | ||||||
|         "Apply De Morgan's law", |         "Apply De Morgan's law", | ||||||
|         op_range, |         op_range, | ||||||
|         |edit| { |         |builder| { | ||||||
|             let demorganed = ast::Expr::BinExpr(demorganed); |             let make = SyntaxFactory::new(); | ||||||
|             let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); |             let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); | ||||||
|             let neg_expr = paren_expr |             let neg_expr = paren_expr | ||||||
|                 .clone() |                 .clone() | ||||||
|  | @ -107,24 +114,32 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti | ||||||
|                 .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) |                 .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) | ||||||
|                 .map(ast::Expr::PrefixExpr); |                 .map(ast::Expr::PrefixExpr); | ||||||
| 
 | 
 | ||||||
|  |             let mut editor; | ||||||
|             if let Some(paren_expr) = paren_expr { |             if let Some(paren_expr) = paren_expr { | ||||||
|                 if let Some(neg_expr) = neg_expr { |                 if let Some(neg_expr) = neg_expr { | ||||||
|                     cov_mark::hit!(demorgan_double_negation); |                     cov_mark::hit!(demorgan_double_negation); | ||||||
|                     let parent = neg_expr.syntax().parent(); |                     let parent = neg_expr.syntax().parent(); | ||||||
|  |                     editor = builder.make_editor(neg_expr.syntax()); | ||||||
| 
 | 
 | ||||||
|                     if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) { |                     if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) { | ||||||
|                         cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); |                         cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); | ||||||
|                         edit.replace_ast(neg_expr, make::expr_paren(demorganed)); |                         editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax()); | ||||||
|                     } else { |                     } else { | ||||||
|                         edit.replace_ast(neg_expr, demorganed); |                         editor.replace(neg_expr.syntax(), demorganed.syntax()); | ||||||
|                     }; |                     }; | ||||||
|                 } else { |                 } else { | ||||||
|                     cov_mark::hit!(demorgan_double_parens); |                     cov_mark::hit!(demorgan_double_parens); | ||||||
|                     edit.replace_ast(paren_expr.into(), add_bang_paren(demorganed)); |                     editor = builder.make_editor(paren_expr.syntax()); | ||||||
|  | 
 | ||||||
|  |                     editor.replace(paren_expr.syntax(), add_bang_paren(&make, demorganed).syntax()); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 edit.replace_ast(bin_expr.into(), add_bang_paren(demorganed)); |                 editor = builder.make_editor(bin_expr.syntax()); | ||||||
|  |                 editor.replace(bin_expr.syntax(), add_bang_paren(&make, demorganed).syntax()); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             editor.add_mappings(make.finish_with_mappings()); | ||||||
|  |             builder.add_file_edits(ctx.file_id(), editor); | ||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | @ -161,7 +176,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> | ||||||
|     let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; |     let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; | ||||||
| 
 | 
 | ||||||
|     let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; |     let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; | ||||||
|     let closure_body = closure_expr.body()?; |     let closure_body = closure_expr.body()?.clone_for_update(); | ||||||
| 
 | 
 | ||||||
|     let op_range = method_call.syntax().text_range(); |     let op_range = method_call.syntax().text_range(); | ||||||
|     let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); |     let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); | ||||||
|  | @ -170,18 +185,19 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> | ||||||
|         AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite), |         AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite), | ||||||
|         label, |         label, | ||||||
|         op_range, |         op_range, | ||||||
|         |edit| { |         |builder| { | ||||||
|  |             let make = SyntaxFactory::new(); | ||||||
|  |             let mut editor = builder.make_editor(method_call.syntax()); | ||||||
|             // replace the method name
 |             // replace the method name
 | ||||||
|             let new_name = match name.text().as_str() { |             let new_name = match name.text().as_str() { | ||||||
|                 "all" => make::name_ref("any"), |                 "all" => make.name_ref("any"), | ||||||
|                 "any" => make::name_ref("all"), |                 "any" => make.name_ref("all"), | ||||||
|                 _ => unreachable!(), |                 _ => unreachable!(), | ||||||
|             } |             }; | ||||||
|             .clone_for_update(); |             editor.replace(name.syntax(), new_name.syntax()); | ||||||
|             edit.replace_ast(name, new_name); |  | ||||||
| 
 | 
 | ||||||
|             // negate all tail expressions in the closure body
 |             // negate all tail expressions in the closure body
 | ||||||
|             let tail_cb = &mut |e: &_| tail_cb_impl(edit, e); |             let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e); | ||||||
|             walk_expr(&closure_body, &mut |expr| { |             walk_expr(&closure_body, &mut |expr| { | ||||||
|                 if let ast::Expr::ReturnExpr(ret_expr) = expr { |                 if let ast::Expr::ReturnExpr(ret_expr) = expr { | ||||||
|                     if let Some(ret_expr_arg) = &ret_expr.expr() { |                     if let Some(ret_expr_arg) = &ret_expr.expr() { | ||||||
|  | @ -198,15 +214,15 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> | ||||||
|                 .and_then(ast::PrefixExpr::cast) |                 .and_then(ast::PrefixExpr::cast) | ||||||
|                 .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) |                 .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) | ||||||
|             { |             { | ||||||
|                 edit.delete( |                 editor.delete( | ||||||
|                     prefix_expr |                     prefix_expr.op_token().expect("prefix expression always has an operator"), | ||||||
|                         .op_token() |  | ||||||
|                         .expect("prefix expression always has an operator") |  | ||||||
|                         .text_range(), |  | ||||||
|                 ); |                 ); | ||||||
|             } else { |             } else { | ||||||
|                 edit.insert(method_call.syntax().text_range().start(), "!"); |                 editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG)); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             editor.add_mappings(make.finish_with_mappings()); | ||||||
|  |             builder.add_file_edits(ctx.file_id(), editor); | ||||||
|         }, |         }, | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  | @ -233,26 +249,50 @@ fn validate_method_call_expr( | ||||||
|     it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr)) |     it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { | fn tail_cb_impl(editor: &mut SyntaxEditor, make: &SyntaxFactory, e: &ast::Expr) { | ||||||
|     match e { |     match e { | ||||||
|         ast::Expr::BreakExpr(break_expr) => { |         ast::Expr::BreakExpr(break_expr) => { | ||||||
|             if let Some(break_expr_arg) = break_expr.expr() { |             if let Some(break_expr_arg) = break_expr.expr() { | ||||||
|                 for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(edit, e)) |                 for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, make, e)) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         ast::Expr::ReturnExpr(_) => { |         ast::Expr::ReturnExpr(_) => { | ||||||
|             // all return expressions have already been handled by the walk loop
 |             // all return expressions have already been handled by the walk loop
 | ||||||
|         } |         } | ||||||
|         e => { |         e => { | ||||||
|             let inverted_body = invert_boolean_expression(e.clone()); |             let inverted_body = invert_boolean_expression(make, e.clone()); | ||||||
|             edit.replace(e.syntax().text_range(), inverted_body.syntax().text()); |             editor.replace(e.syntax(), inverted_body.syntax()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Add bang and parentheses to the expression.
 | /// Add bang and parentheses to the expression.
 | ||||||
| fn add_bang_paren(expr: ast::Expr) -> ast::Expr { | fn add_bang_paren(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr { | ||||||
|     make::expr_prefix(T![!], make::expr_paren(expr)).into() |     make.expr_prefix(T![!], make.expr_paren(expr).into()).into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn needs_parens_in_place_of( | ||||||
|  |     this: &ast::Expr, | ||||||
|  |     parent: &SyntaxNode, | ||||||
|  |     in_place_of: &ast::Expr, | ||||||
|  | ) -> bool { | ||||||
|  |     assert_eq!(Some(parent), in_place_of.syntax().parent().as_ref()); | ||||||
|  | 
 | ||||||
|  |     let child_idx = parent | ||||||
|  |         .children() | ||||||
|  |         .enumerate() | ||||||
|  |         .find_map(|(i, it)| if &it == in_place_of.syntax() { Some(i) } else { None }) | ||||||
|  |         .unwrap(); | ||||||
|  |     let parent = parent.clone_subtree(); | ||||||
|  |     let subtree_place = parent.children().nth(child_idx).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let mut editor = SyntaxEditor::new(parent); | ||||||
|  |     editor.replace(subtree_place, this.syntax()); | ||||||
|  |     let edit = editor.finish(); | ||||||
|  | 
 | ||||||
|  |     let replaced = edit.new_root().children().nth(child_idx).unwrap(); | ||||||
|  |     let replaced = ast::Expr::cast(replaced).unwrap(); | ||||||
|  |     replaced.needs_parens_in(edit.new_root().clone()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ use syntax::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     utils::{invert_boolean_expression, unwrap_trivial_block}, |     utils::{invert_boolean_expression_legacy, unwrap_trivial_block}, | ||||||
|     AssistContext, AssistId, AssistKind, Assists, |     AssistContext, AssistId, AssistKind, Assists, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -119,7 +119,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> | ||||||
|                     | ast::Expr::WhileExpr(_) |                     | ast::Expr::WhileExpr(_) | ||||||
|                     | ast::Expr::YieldExpr(_) |                     | ast::Expr::YieldExpr(_) | ||||||
|             ); |             ); | ||||||
|             let cond = if invert_cond { invert_boolean_expression(cond) } else { cond }; |             let cond = if invert_cond { invert_boolean_expression_legacy(cond) } else { cond }; | ||||||
|             let cond = if parenthesize { make::expr_paren(cond) } else { cond }; |             let cond = if parenthesize { make::expr_paren(cond) } else { cond }; | ||||||
|             let arg_list = make::arg_list(Some(make::expr_closure(None, closure_body))); |             let arg_list = make::arg_list(Some(make::expr_closure(None, closure_body))); | ||||||
|             let mcall = make::expr_method_call(cond, make::name_ref("then"), arg_list); |             let mcall = make::expr_method_call(cond, make::name_ref("then"), arg_list); | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ use syntax::{ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     assist_context::{AssistContext, Assists}, |     assist_context::{AssistContext, Assists}, | ||||||
|     utils::invert_boolean_expression, |     utils::invert_boolean_expression_legacy, | ||||||
|     AssistId, AssistKind, |     AssistId, AssistKind, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -139,7 +139,7 @@ fn if_expr_to_guarded_return( | ||||||
|                     let new_expr = { |                     let new_expr = { | ||||||
|                         let then_branch = |                         let then_branch = | ||||||
|                             make::block_expr(once(make::expr_stmt(early_expression).into()), None); |                             make::block_expr(once(make::expr_stmt(early_expression).into()), None); | ||||||
|                         let cond = invert_boolean_expression(cond_expr); |                         let cond = invert_boolean_expression_legacy(cond_expr); | ||||||
|                         make::expr_if(cond, then_branch, None).indent(if_indent_level) |                         make::expr_if(cond, then_branch, None).indent(if_indent_level) | ||||||
|                     }; |                     }; | ||||||
|                     new_expr.syntax().clone_for_update() |                     new_expr.syntax().clone_for_update() | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ use syntax::{ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     assist_context::{AssistContext, Assists}, |     assist_context::{AssistContext, Assists}, | ||||||
|     utils::invert_boolean_expression, |     utils::invert_boolean_expression_legacy, | ||||||
|     AssistId, AssistKind, |     AssistId, AssistKind, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -63,7 +63,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) | ||||||
|                 let stmts = iter::once(make::expr_stmt(if_expr.into()).into()); |                 let stmts = iter::once(make::expr_stmt(if_expr.into()).into()); | ||||||
|                 make::block_expr(stmts, None) |                 make::block_expr(stmts, None) | ||||||
|             } else { |             } else { | ||||||
|                 let if_cond = invert_boolean_expression(while_cond); |                 let if_cond = invert_boolean_expression_legacy(while_cond); | ||||||
|                 let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into(); |                 let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into(); | ||||||
|                 let elements = while_body.stmt_list().map_or_else( |                 let elements = while_body.stmt_list().map_or_else( | ||||||
|                     || Either::Left(iter::empty()), |                     || Either::Left(iter::empty()), | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ use syntax::{ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     assist_context::{AssistContext, Assists}, |     assist_context::{AssistContext, Assists}, | ||||||
|     utils::invert_boolean_expression, |     utils::invert_boolean_expression_legacy, | ||||||
|     AssistId, AssistKind, |     AssistId, AssistKind, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -48,7 +48,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { |     acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { | ||||||
|         let flip_cond = invert_boolean_expression(cond.clone()); |         let flip_cond = invert_boolean_expression_legacy(cond.clone()); | ||||||
|         edit.replace_ast(cond, flip_cond); |         edit.replace_ast(cond, flip_cond); | ||||||
| 
 | 
 | ||||||
|         let else_node = else_block.syntax(); |         let else_node = else_block.syntax(); | ||||||
|  |  | ||||||
|  | @ -17,7 +17,9 @@ use syntax::{ | ||||||
|         self, |         self, | ||||||
|         edit::{AstNodeEdit, IndentLevel}, |         edit::{AstNodeEdit, IndentLevel}, | ||||||
|         edit_in_place::{AttrsOwnerEdit, Indent, Removable}, |         edit_in_place::{AttrsOwnerEdit, Indent, Removable}, | ||||||
|         make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, |         make, | ||||||
|  |         syntax_factory::SyntaxFactory, | ||||||
|  |         HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, | ||||||
|     }, |     }, | ||||||
|     ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile, |     ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile, | ||||||
|     SyntaxKind::*, |     SyntaxKind::*, | ||||||
|  | @ -245,11 +247,79 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { | ||||||
|         .unwrap_or_else(|| node.text_range().start()) |         .unwrap_or_else(|| node.text_range().start()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { | pub(crate) fn invert_boolean_expression(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr { | ||||||
|     invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) |     invert_special_case(make, &expr).unwrap_or_else(|| make.expr_prefix(T![!], expr).into()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | // FIXME: Migrate usages of this function to the above function and remove this.
 | ||||||
|  | pub(crate) fn invert_boolean_expression_legacy(expr: ast::Expr) -> ast::Expr { | ||||||
|  |     invert_special_case_legacy(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Expr> { | ||||||
|  |     match expr { | ||||||
|  |         ast::Expr::BinExpr(bin) => { | ||||||
|  |             let op_kind = bin.op_kind()?; | ||||||
|  |             let rev_kind = match op_kind { | ||||||
|  |                 ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated }) => { | ||||||
|  |                     ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: !negated }) | ||||||
|  |                 } | ||||||
|  |                 ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict }) => { | ||||||
|  |                     ast::BinaryOp::CmpOp(ast::CmpOp::Ord { | ||||||
|  |                         ordering: ast::Ordering::Greater, | ||||||
|  |                         strict: !strict, | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |                 ast::BinaryOp::CmpOp(ast::CmpOp::Ord { | ||||||
|  |                     ordering: ast::Ordering::Greater, | ||||||
|  |                     strict, | ||||||
|  |                 }) => ast::BinaryOp::CmpOp(ast::CmpOp::Ord { | ||||||
|  |                     ordering: ast::Ordering::Less, | ||||||
|  |                     strict: !strict, | ||||||
|  |                 }), | ||||||
|  |                 // Parenthesize other expressions before prefixing `!`
 | ||||||
|  |                 _ => { | ||||||
|  |                     return Some( | ||||||
|  |                         make.expr_prefix(T![!], make.expr_paren(expr.clone()).into()).into(), | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             Some(make.expr_bin(bin.lhs()?, rev_kind, bin.rhs()?).into()) | ||||||
|  |         } | ||||||
|  |         ast::Expr::MethodCallExpr(mce) => { | ||||||
|  |             let receiver = mce.receiver()?; | ||||||
|  |             let method = mce.name_ref()?; | ||||||
|  |             let arg_list = mce.arg_list()?; | ||||||
|  | 
 | ||||||
|  |             let method = match method.text().as_str() { | ||||||
|  |                 "is_some" => "is_none", | ||||||
|  |                 "is_none" => "is_some", | ||||||
|  |                 "is_ok" => "is_err", | ||||||
|  |                 "is_err" => "is_ok", | ||||||
|  |                 _ => return None, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into()) | ||||||
|  |         } | ||||||
|  |         ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? { | ||||||
|  |             ast::Expr::ParenExpr(parexpr) => { | ||||||
|  |                 parexpr.expr().map(|e| e.clone_subtree().clone_for_update()) | ||||||
|  |             } | ||||||
|  |             _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()), | ||||||
|  |         }, | ||||||
|  |         ast::Expr::Literal(lit) => match lit.kind() { | ||||||
|  |             ast::LiteralKind::Bool(b) => match b { | ||||||
|  |                 true => Some(ast::Expr::Literal(make.expr_literal("false"))), | ||||||
|  |                 false => Some(ast::Expr::Literal(make.expr_literal("true"))), | ||||||
|  |             }, | ||||||
|  |             _ => None, | ||||||
|  |         }, | ||||||
|  |         _ => None, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> { | ||||||
|     match expr { |     match expr { | ||||||
|         ast::Expr::BinExpr(bin) => { |         ast::Expr::BinExpr(bin) => { | ||||||
|             let bin = bin.clone_for_update(); |             let bin = bin.clone_for_update(); | ||||||
|  |  | ||||||
|  | @ -784,7 +784,7 @@ pub fn record_pat_field_shorthand(name_ref: ast::NameRef) -> ast::RecordPatField | ||||||
|     ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))")) |     ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))")) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
 | /// Returns a `IdentPat` if the path has just one segment, a `PathPat` otherwise.
 | ||||||
| pub fn path_pat(path: ast::Path) -> ast::Pat { | pub fn path_pat(path: ast::Path) -> ast::Pat { | ||||||
|     return from_text(&path.to_string()); |     return from_text(&path.to_string()); | ||||||
|     fn from_text(text: &str) -> ast::Pat { |     fn from_text(text: &str) -> ast::Pat { | ||||||
|  |  | ||||||
|  | @ -33,6 +33,11 @@ impl SyntaxFactory { | ||||||
|         self.mappings.unwrap_or_default().into_inner() |         self.mappings.unwrap_or_default().into_inner() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Take all of the tracked syntax mappings, leaving `SyntaxMapping::default()` in its place, if any.
 | ||||||
|  |     pub fn take(&self) -> SyntaxMapping { | ||||||
|  |         self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> { |     fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> { | ||||||
|         self.mappings.as_ref().map(|it| it.borrow_mut()) |         self.mappings.as_ref().map(|it| it.borrow_mut()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -156,6 +156,32 @@ impl SyntaxFactory { | ||||||
|         make::literal_pat(text).clone_for_update() |         make::literal_pat(text).clone_for_update() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn slice_pat(&self, pats: impl IntoIterator<Item = ast::Pat>) -> ast::SlicePat { | ||||||
|  |         let (pats, input) = iterator_input(pats); | ||||||
|  |         let ast = make::slice_pat(pats).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.pats().map(|it| it.syntax().clone())); | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn tuple_pat(&self, pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { | ||||||
|  |         let (pats, input) = iterator_input(pats); | ||||||
|  |         let ast = make::tuple_pat(pats).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 tuple_struct_pat( |     pub fn tuple_struct_pat( | ||||||
|         &self, |         &self, | ||||||
|         path: ast::Path, |         path: ast::Path, | ||||||
|  | @ -174,6 +200,96 @@ impl SyntaxFactory { | ||||||
|         ast |         ast | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn record_pat_with_fields( | ||||||
|  |         &self, | ||||||
|  |         path: ast::Path, | ||||||
|  |         fields: ast::RecordPatFieldList, | ||||||
|  |     ) -> ast::RecordPat { | ||||||
|  |         let ast = make::record_pat_with_fields(path.clone(), fields.clone()).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_node( | ||||||
|  |                 fields.syntax().clone(), | ||||||
|  |                 ast.record_pat_field_list().unwrap().syntax().clone(), | ||||||
|  |             ); | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn record_pat_field_list( | ||||||
|  |         &self, | ||||||
|  |         fields: impl IntoIterator<Item = ast::RecordPatField>, | ||||||
|  |         rest_pat: Option<ast::RestPat>, | ||||||
|  |     ) -> ast::RecordPatFieldList { | ||||||
|  |         let (fields, input) = iterator_input(fields); | ||||||
|  |         let ast = make::record_pat_field_list(fields, rest_pat.clone()).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())); | ||||||
|  |             if let Some(rest_pat) = rest_pat { | ||||||
|  |                 builder | ||||||
|  |                     .map_node(rest_pat.syntax().clone(), ast.rest_pat().unwrap().syntax().clone()); | ||||||
|  |             } | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn record_pat_field(self, name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField { | ||||||
|  |         let ast = make::record_pat_field(name_ref.clone(), pat.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.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone()); | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn record_pat_field_shorthand(&self, name_ref: ast::NameRef) -> ast::RecordPatField { | ||||||
|  |         let ast = make::record_pat_field_shorthand(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.pat().unwrap().syntax().clone()); | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn path_pat(&self, path: ast::Path) -> ast::Pat { | ||||||
|  |         let ast = make::path_pat(path.clone()).clone_for_update(); | ||||||
|  | 
 | ||||||
|  |         match &ast { | ||||||
|  |             ast::Pat::PathPat(ast) => { | ||||||
|  |                 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.finish(&mut mapping) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             ast::Pat::IdentPat(ast) => { | ||||||
|  |                 if let Some(mut mapping) = self.mappings() { | ||||||
|  |                     let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); | ||||||
|  |                     builder.map_node(path.syntax().clone(), ast.name().unwrap().syntax().clone()); | ||||||
|  |                     builder.finish(&mut mapping) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub fn block_expr( |     pub fn block_expr( | ||||||
|         &self, |         &self, | ||||||
|         statements: impl IntoIterator<Item = ast::Stmt>, |         statements: impl IntoIterator<Item = ast::Stmt>, | ||||||
|  | @ -214,6 +330,21 @@ impl SyntaxFactory { | ||||||
|         make::expr_empty_block().clone_for_update() |         make::expr_empty_block().clone_for_update() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn expr_paren(&self, expr: ast::Expr) -> ast::ParenExpr { | ||||||
|  |         // FIXME: `make::expr_paren` should return a `ParenExpr`, not just an `Expr`
 | ||||||
|  |         let ast::Expr::ParenExpr(ast) = make::expr_paren(expr.clone()).clone_for_update() else { | ||||||
|  |             unreachable!() | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         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_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr { |     pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr { | ||||||
|         let (fields, input) = iterator_input(fields); |         let (fields, input) = iterator_input(fields); | ||||||
|         let ast = make::expr_tuple(fields).clone_for_update(); |         let ast = make::expr_tuple(fields).clone_for_update(); | ||||||
|  | @ -292,6 +423,31 @@ impl SyntaxFactory { | ||||||
|         ast |         ast | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn expr_method_call( | ||||||
|  |         &self, | ||||||
|  |         receiver: ast::Expr, | ||||||
|  |         method: ast::NameRef, | ||||||
|  |         arg_list: ast::ArgList, | ||||||
|  |     ) -> ast::MethodCallExpr { | ||||||
|  |         // FIXME: `make::expr_method_call` should return a `MethodCallExpr`, not just an `Expr`
 | ||||||
|  |         let ast::Expr::MethodCallExpr(ast) = | ||||||
|  |             make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone()) | ||||||
|  |                 .clone_for_update() | ||||||
|  |         else { | ||||||
|  |             unreachable!() | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if let Some(mut mapping) = self.mappings() { | ||||||
|  |             let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); | ||||||
|  |             builder.map_node(receiver.syntax().clone(), ast.receiver().unwrap().syntax().clone()); | ||||||
|  |             builder.map_node(method.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); | ||||||
|  |             builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone()); | ||||||
|  |             builder.finish(&mut mapping); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ast | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList { |     pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList { | ||||||
|         let (args, input) = iterator_input(args); |         let (args, input) = iterator_input(args); | ||||||
|         let ast = make::arg_list(args).clone_for_update(); |         let ast = make::arg_list(args).clone_for_update(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth