From f06f1b81bb503e1ffa3b0f622706f315e126ca8c Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 17 Feb 2025 23:36:44 +0900 Subject: [PATCH] Migrate some leftovers in `add_missing_match_arms` --- .../src/handlers/add_missing_match_arms.rs | 63 ++++--- crates/syntax/src/ast/make.rs | 2 +- .../src/ast/syntax_factory/constructors.rs | 156 ++++++++++++++++++ 3 files changed, 196 insertions(+), 25 deletions(-) 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 4a9e2256e9..eec1e648c0 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,4 +1,6 @@ use std::iter::{self, Peekable}; +use std::ops::Deref; +use std::rc::Rc; use either::Either; use hir::{sym, Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics}; @@ -76,6 +78,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let cfg = ctx.config.import_path_config(); + // As `make` is borrowed by the closure that builds `missing_pats`, this is needed + // to satisfy the borrow checker. + let make = Rc::new(SyntaxFactory::new()); + let make_weak = Rc::downgrade(&make); + let module = ctx.sema.scope(expr.syntax())?.module(); let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( Peekable>>, @@ -92,8 +99,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let missing_pats = variants .into_iter() .filter_map(|variant| { + let make = make_weak.upgrade()?; Some(( - build_pat(ctx, module, variant, cfg)?, + build_pat(ctx, make, module, variant, cfg)?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -140,14 +148,17 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .multi_cartesian_product() .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation)) - .map(|variants| { + .filter_map(|variants| { let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = - variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); + let patterns = variants.into_iter().filter_map(|variant| { + make_weak.upgrade().and_then(|make| build_pat(ctx, make, module, variant, cfg)) + }); - (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) + make_weak + .upgrade() + .map(|make| (ast::Pat::from(make.tuple_pat(patterns)), is_hidden)) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); ( @@ -172,13 +183,17 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .multi_cartesian_product() .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation)) - .map(|variants| { + .filter_map(|variants| { let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = - variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); - (ast::Pat::from(make::slice_pat(patterns)), is_hidden) + let patterns = variants.into_iter().filter_map(|variant| { + make_weak.upgrade().and_then(|make| build_pat(ctx, make, module, variant, cfg)) + }); + + make_weak + .upgrade() + .map(|make| (ast::Pat::from(make.slice_pat(patterns)), is_hidden)) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); ( @@ -203,8 +218,6 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) "Fill match arms", ctx.sema.original_range(match_expr.syntax()).range, |builder| { - let make = SyntaxFactory::new(); - // having any hidden variants means that we need a catch-all arm needs_catch_all_arm |= has_hidden_variants; @@ -243,7 +256,7 @@ 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(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); } @@ -290,7 +303,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } } - editor.add_mappings(make.finish_with_mappings()); + editor.add_mappings(Rc::into_inner(make).unwrap().finish_with_mappings()); builder.add_file_edits(ctx.file_id(), editor); }, ) @@ -445,6 +458,7 @@ fn resolve_array_of_enum_def( fn build_pat( ctx: &AssistContext<'_>, + make: impl Deref, module: hir::Module, var: ExtendedVariant, cfg: ImportPathConfig, @@ -455,31 +469,32 @@ fn build_pat( let edition = module.krate().edition(db); let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); let fields = var.fields(db); - let pat = match var.kind(db) { + let pat: ast::Pat = match var.kind(db) { hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::new(); let pats = fields.into_iter().map(|f| { let name = name_generator.for_type(&f.ty(db), db, edition); match name { - Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(), - None => make::wildcard_pat().into(), + Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(), + None => make.wildcard_pat().into(), } }); - make::tuple_struct_pat(path, pats).into() + make.tuple_struct_pat(path, pats).into() } hir::StructKind::Record => { - let pats = fields + let fields = fields .into_iter() - .map(|f| make::name(f.name(db).as_str())) - .map(|name| make::ext::simple_ident_pat(name).into()); - make::record_pat(path, pats).into() + .map(|f| make.name_ref(f.name(db).as_str())) + .map(|name_ref| make.record_pat_field_shorthand(name_ref)); + 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) } - ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), - ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), + ExtendedVariant::True => Some(ast::Pat::from(make.literal_pat("true"))), + ExtendedVariant::False => Some(ast::Pat::from(make.literal_pat("false"))), } } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 9dc2d83253..231c21c38f 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -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} }}: ()))")) } -/// 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 { return from_text(&path.to_string()); fn from_text(text: &str) -> ast::Pat { diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 572622db54..044d68b528 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -156,6 +156,32 @@ impl SyntaxFactory { make::literal_pat(text).clone_for_update() } + pub fn slice_pat(&self, pats: impl IntoIterator) -> 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) -> 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( &self, path: ast::Path, @@ -174,6 +200,96 @@ impl SyntaxFactory { 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, + rest_pat: Option, + ) -> 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( &self, statements: impl IntoIterator, @@ -214,6 +330,21 @@ impl SyntaxFactory { make::expr_empty_block().clone_for_update() } + pub fn expr_paren(&self, expr: ast::Expr) -> ast::ParenExpr { + // FIXME: `make::expr_paren` should return a `MethodCallExpr`, 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) -> ast::TupleExpr { let (fields, input) = iterator_input(fields); let ast = make::expr_tuple(fields).clone_for_update(); @@ -292,6 +423,31 @@ impl SyntaxFactory { 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) -> ast::ArgList { let (args, input) = iterator_input(args); let ast = make::arg_list(args).clone_for_update();