Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/convert_closure_to_fn.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/convert_closure_to_fn.rs | 126 |
1 files changed, 75 insertions, 51 deletions
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 9f9ced98d7..acade43397 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -24,7 +24,7 @@ use crate::assist_context::{AssistContext, Assists}; // This converts a closure to a freestanding function, changing all captures to parameters. // // ``` -// # //- minicore: copy +// # //- minicore: copy, fn // # struct String; // # impl String { // # fn new() -> Self {} @@ -90,6 +90,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } }) .collect::<Option<Vec<_>>>()?; + let capture_params_start = params.len(); let mut body = closure.body()?.clone_for_update(); let mut is_gen = false; @@ -152,7 +153,8 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) .map(|(_, _, it)| it.clone()) .unwrap_or_else(|| make::name("fun_name")); let captures = closure_ty.captured_items(ctx.db()); - let capture_tys = closure_ty.capture_types(ctx.db()); + let capture_tys = + captures.iter().map(|capture| capture.captured_ty(ctx.db())).collect::<Vec<_>>(); let mut captures_as_args = Vec::with_capacity(captures.len()); @@ -163,22 +165,28 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) for (capture, capture_ty) in std::iter::zip(&captures, &capture_tys) { // FIXME: Allow configuring the replacement of `self`. - let capture_name = - if capture.local().is_self(ctx.db()) && !capture.has_field_projections() { - make::name("this") - } else { - make::name(&capture.place_to_name(ctx.db())) - }; + let is_self = capture.local().is_self(ctx.db()) && !capture.has_field_projections(); + let capture_name = if is_self { + make::name("this") + } else { + make::name(&capture.place_to_name(ctx.db(), ctx.edition())) + }; closure_mentioned_generic_params.extend(capture_ty.generic_params(ctx.db())); let capture_ty = capture_ty .display_source_code(ctx.db(), module.into(), true) .unwrap_or_else(|_| "_".to_owned()); - params.push(make::param( + let param = make::param( ast::Pat::IdentPat(make::ident_pat(false, false, capture_name.clone_subtree())), make::ty(&capture_ty), - )); + ); + if is_self { + // Always put `this` first. + params.insert(capture_params_start, param); + } else { + params.push(param); + } for capture_usage in capture.usages().sources(ctx.db()) { if capture_usage.file_id() != ctx.file_id() { @@ -188,24 +196,32 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) let capture_usage_source = capture_usage.source(); let capture_usage_source = capture_usage_source.to_node(&body_root); - let expr = match capture_usage_source { + let mut expr = match capture_usage_source { Either::Left(expr) => expr, Either::Right(pat) => { let Some(expr) = expr_of_pat(pat) else { continue }; expr } }; + if !capture_usage.is_ref() { + expr = peel_ref(expr); + } let replacement = wrap_capture_in_deref_if_needed( &expr, &capture_name, capture.kind(), - capture_usage.is_ref(), + matches!(expr, ast::Expr::RefExpr(_)) || capture_usage.is_ref(), ) .clone_for_update(); capture_usages_replacement_map.push((expr, replacement)); } - captures_as_args.push(capture_as_arg(ctx, capture)); + let capture_as_arg = capture_as_arg(ctx, capture); + if is_self { + captures_as_args.insert(0, capture_as_arg); + } else { + captures_as_args.push(capture_as_arg); + } } let (closure_type_params, closure_where_clause) = @@ -463,24 +479,29 @@ fn compute_closure_type_params( (Some(make::generic_param_list(include_params)), where_clause) } +fn peel_parens(mut expr: ast::Expr) -> ast::Expr { + loop { + if ast::ParenExpr::can_cast(expr.syntax().kind()) { + let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) else { break }; + expr = parent; + } else { + break; + } + } + expr +} + +fn peel_ref(mut expr: ast::Expr) -> ast::Expr { + expr = peel_parens(expr); + expr.syntax().parent().and_then(ast::RefExpr::cast).map(Into::into).unwrap_or(expr) +} + fn wrap_capture_in_deref_if_needed( expr: &ast::Expr, capture_name: &ast::Name, capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - fn peel_parens(mut expr: ast::Expr) -> ast::Expr { - loop { - if ast::ParenExpr::can_cast(expr.syntax().kind()) { - let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) else { break }; - expr = parent; - } else { - break; - } - } - expr - } - let capture_name = make::expr_path(make::path_from_text(&capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; @@ -507,8 +528,11 @@ fn wrap_capture_in_deref_if_needed( } fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr { - let place = parse_expr_from_str(&capture.display_place_source_code(ctx.db()), ctx.edition()) - .expect("`display_place_source_code()` produced an invalid expr"); + let place = parse_expr_from_str( + &capture.display_place_source_code(ctx.db(), ctx.edition()), + ctx.edition(), + ) + .expect("`display_place_source_code()` produced an invalid expr"); let needs_mut = match capture.kind() { CaptureKind::SharedRef => false, CaptureKind::MutableRef | CaptureKind::UniqueSharedRef => true, @@ -688,7 +712,7 @@ mod tests { check_assist( convert_closure_to_fn, r#" -//- minicore:copy +//- minicore: copy, fn fn main() { let s = &mut true; let closure = |$0| { *s = false; }; @@ -710,7 +734,7 @@ fn main() { check_assist( convert_closure_to_fn, r#" -//- minicore:copy +//- minicore: copy, fn struct A { a: i32, b: bool } fn main() { let mut a = A { a: 123, b: false }; @@ -740,8 +764,8 @@ fn main() { check_assist( convert_closure_to_fn, r#" -//- minicore:copy -struct A { b: &'static B, c: i32 } +//- minicore: copy, fn +struct A { b: &'static mut B, c: i32 } struct B(bool, i32); struct C; impl C { @@ -756,7 +780,7 @@ impl C { } "#, r#" -struct A { b: &'static B, c: i32 } +struct A { b: &'static mut B, c: i32 } struct B(bool, i32); struct C; impl C { @@ -778,7 +802,7 @@ impl C { check_assist( convert_closure_to_fn, r#" -//- minicore:copy +//- minicore: copy, fn struct A { b: &'static B, c: i32 } struct B(bool, i32); impl A { @@ -795,10 +819,10 @@ struct A { b: &'static B, c: i32 } struct B(bool, i32); impl A { fn foo(&self) { - fn closure(self_b_1: &i32) { - let b = *self_b_1; + fn closure(self_b: &B) { + let b = self_b.1; } - closure(&self.b.1); + closure(self.b); } } "#, @@ -810,7 +834,7 @@ impl A { check_assist( convert_closure_to_fn, r#" -//- minicore: copy, future +//- minicore: copy, future, async_fn fn foo(&self) { let closure = async |$0| 1; closure(); @@ -832,7 +856,7 @@ fn foo(&self) { check_assist( convert_closure_to_fn, r#" -//- minicore: copy, future +//- minicore: copy, future, fn fn foo() { let closure = |$0| async { 1 }; closure(); @@ -878,7 +902,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let closure = |$0| {}; closure(); @@ -898,7 +922,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let a = 1; let closure = |$0| a; @@ -918,7 +942,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let closure = |$0| 'label: {}; closure(); @@ -936,7 +960,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let closure = |$0| { const { () } @@ -956,7 +980,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let closure = |$0| unsafe { }; closure(); @@ -974,7 +998,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { { let closure = |$0| match () { @@ -1049,7 +1073,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn struct A { b: B } struct B(bool, i32); fn foo() { @@ -1066,7 +1090,7 @@ struct B(bool, i32); fn foo() { let mut a = A { b: B(true, 0) }; fn closure(a_b_1: &mut i32) { - let A { b: B(_, ref mut c) } = a_b_1; + let A { b: B(_, ref mut c) } = *a_b_1; } closure(&mut a.b.1); } @@ -1079,7 +1103,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let (mut a, b) = (0.1, "abc"); let closure = |$0p1: i32, p2: &mut bool| { @@ -1107,7 +1131,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let (mut a, b) = (0.1, "abc"); let closure = |$0p1: i32, p2| { @@ -1145,7 +1169,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, fn fn foo() { let (mut a, b) = (0.1, "abc"); let closure = |$0p1: i32, p2| { @@ -1183,7 +1207,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore: copy +//- minicore: copy, from struct Foo<A, B, const C: usize>(A, B); impl<A, B: From<A>, const C: usize> Foo<A, B, C> { fn foo<D, E, F, G>(a: A, b: D) @@ -1244,7 +1268,7 @@ fn foo() { check_assist( convert_closure_to_fn, r#" -//- minicore:copy +//- minicore: copy, fn fn main() { let a = &mut true; let closure = |$0| { |