From 319611b7382fc4c84170519dade68f4f558a44b1 Mon Sep 17 00:00:00 2001 From: unexge Date: Sat, 29 Oct 2022 23:44:34 +0100 Subject: [PATCH 1/6] Record diverging match arms in `InferenceResult` --- crates/hir-ty/src/infer.rs | 2 ++ crates/hir-ty/src/infer/expr.rs | 5 +++++ crates/hir/src/semantics.rs | 8 ++++++++ crates/hir/src/source_analyzer.rs | 15 +++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 31e56dec625..05776e19219 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -333,6 +333,8 @@ pub struct InferenceResult { assoc_resolutions: FxHashMap, pub diagnostics: Vec, pub type_of_expr: ArenaMap, + /// For each match expr, record diverging arm's expr. + pub diverging_arms: FxHashMap>, /// For each pattern record the type it resolves to. /// /// **Note**: When a pattern type is resolved it may still contain diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index f56108b26c4..3d2e091a0f6 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -375,6 +375,7 @@ impl<'a> InferenceContext<'a> { let matchee_diverges = self.diverges; let mut all_arms_diverge = Diverges::Always; + let mut diverging_arms = Vec::new(); for arm in arms.iter() { self.diverges = Diverges::Maybe; @@ -387,11 +388,15 @@ impl<'a> InferenceContext<'a> { } let arm_ty = self.infer_expr_inner(arm.expr, &expected); + if self.diverges.is_always() { + diverging_arms.push(arm.expr); + } all_arms_diverge &= self.diverges; coerce.coerce(self, Some(arm.expr), &arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; + self.result.diverging_arms.insert(tgt_expr, diverging_arms); coerce.complete() } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 119ec3210e1..2f835657d37 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -481,6 +481,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { self.imp.is_unsafe_ident_pat(ident_pat) } + + pub fn is_diverging_match_arm(&self, match_arm: &ast::MatchArm) -> Option { + self.imp.is_diverging_match_arm(match_arm) + } } impl<'db> SemanticsImpl<'db> { @@ -1421,6 +1425,10 @@ impl<'db> SemanticsImpl<'db> { .map(|ty| ty.original.is_packed(self.db)) .unwrap_or(false) } + + fn is_diverging_match_arm(&self, match_arm: &ast::MatchArm) -> Option { + self.analyze(match_arm.syntax())?.is_diverging_match_arm(self.db, match_arm) + } } fn macro_call_to_macro_id( diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f86c5710053..2e61946a738 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -782,6 +782,21 @@ impl SourceAnalyzer { false } + pub(crate) fn is_diverging_match_arm( + &self, + db: &dyn HirDatabase, + match_arm: &ast::MatchArm, + ) -> Option { + let infer = self.infer.as_ref()?; + let match_expr = match_arm.syntax().ancestors().find_map(ast::MatchExpr::cast)?; + let match_id = self.expr_id(db, &match_expr.into())?; + let diverging_arms = infer.diverging_arms.get(&match_id)?; + let match_arm_expr = match_arm.expr()?; + let match_arm_expr_id = self.expr_id(db, &match_arm_expr)?; + + Some(diverging_arms.contains(&match_arm_expr_id)) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, From 48efc9d30354eeb98b2992c002f753565e2fe07d Mon Sep 17 00:00:00 2001 From: unexge Date: Sat, 29 Oct 2022 23:45:13 +0100 Subject: [PATCH 2/6] Add `Convert match to let-else` assist --- .../src/handlers/convert_match_to_let_else.rs | 400 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + 2 files changed, 402 insertions(+) create mode 100644 crates/ide-assists/src/handlers/convert_match_to_let_else.rs diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs new file mode 100644 index 00000000000..928016daab9 --- /dev/null +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -0,0 +1,400 @@ +use ide_db::defs::{Definition, NameRefClass}; +use syntax::{ + ast::{self, HasName}, + ted, AstNode, SyntaxNode, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; + +// Assist: convert_match_to_let_else +// +// Converts let statement with match initializer to let-else statement. +// +// ``` +// fn foo(opt: Option<()>) { +// let val = $0match opt { +// Some(it) => it, +// None => return, +// }; +// } +// ``` +// -> +// ``` +// fn foo(opt: Option<()>) { +// let Some(val) = opt else { return }; +// } +// ``` +pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?; + let binding = find_binding(let_stmt.pat()?)?; + + let initializer = match let_stmt.initializer() { + Some(ast::Expr::MatchExpr(it)) => it, + _ => return None, + }; + let initializer_expr = initializer.expr()?; + + let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) { + Some(it) => it, + None => return None, + }; + if extracting_arm.guard().is_some() { + cov_mark::hit!(extracting_arm_has_guard); + return None; + } + + let diverging_arm_expr = diverging_arm.expr()?; + let extracting_arm_pat = extracting_arm.pat()?; + let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?; + + acc.add( + AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite), + "Convert match to let-else", + let_stmt.syntax().text_range(), + |builder| { + let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding); + builder.replace( + let_stmt.syntax().text_range(), + format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};") + ) + }, + ) +} + +// Given a pattern, find the name introduced to the surrounding scope. +fn find_binding(pat: ast::Pat) -> Option { + if let ast::Pat::IdentPat(ident) = pat { + Some(ident) + } else { + None + } +} + +// Given a match expression, find extracting and diverging arms. +fn find_arms( + ctx: &AssistContext<'_>, + match_expr: &ast::MatchExpr, +) -> Option<(ast::MatchArm, ast::MatchArm)> { + let arms = match_expr.match_arm_list()?.arms().collect::>(); + if arms.len() != 2 { + return None; + } + + let mut extracting = None; + let mut diverging = None; + for arm in arms { + if ctx.sema.is_diverging_match_arm(&arm)? { + diverging = Some(arm); + } else { + extracting = Some(arm); + } + } + + match (extracting, diverging) { + (Some(extracting), Some(diverging)) => Some((extracting, diverging)), + _ => { + cov_mark::hit!(non_diverging_match); + None + } + } +} + +// Given an extracting arm, find the extracted variable. +fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option { + match arm.expr()? { + ast::Expr::PathExpr(path) => { + let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; + match NameRefClass::classify(&ctx.sema, &name_ref)? { + NameRefClass::Definition(Definition::Local(local)) => { + let source = local.source(ctx.db()).value.left()?; + Some(source.name()?) + } + _ => None, + } + } + _ => { + cov_mark::hit!(extracting_arm_is_not_an_identity_expr); + return None; + } + } +} + +// Rename `extracted` with `binding` in `pat`. +fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode { + let syntax = pat.syntax().clone_for_update(); + let extracted_syntax = syntax.covering_element(extracted.syntax().text_range()); + + // If `extracted` variable is a record field, we should rename it to `binding`, + // otherwise we just need to replace `extracted` with `binding`. + + if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast) + { + if let Some(name_ref) = record_pat_field.field_name() { + ted::replace( + record_pat_field.syntax(), + ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into()) + .syntax() + .clone_for_update(), + ); + } + } else { + ted::replace(extracted_syntax, binding.syntax().clone_for_update()); + } + + syntax +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn should_not_be_applicable_for_non_diverging_match() { + cov_mark::check!(non_diverging_match); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => (), + }; +} +"#, + ); + } + + #[test] + fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() { + cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it + 1, + None => return, + }; +} +"#, + ); + + check_assist_not_applicable( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => { + let _ = 1 + 1; + it + }, + None => return, + }; +} +"#, + ); + } + + #[test] + fn should_not_be_applicable_if_extracting_arm_has_guard() { + cov_mark::check!(extracting_arm_has_guard); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) if 2 > 1 => it, + None => return, + }; +} +"#, + ); + } + + #[test] + fn basic_pattern() { + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => return, + }; +} + "#, + r#" +fn foo(opt: Option<()>) { + let Some(val) = opt else { return }; +} + "#, + ); + } + + #[test] + fn keeps_modifiers() { + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + let ref mut val = $0match opt { + Some(it) => it, + None => return, + }; +} + "#, + r#" +fn foo(opt: Option<()>) { + let Some(ref mut val) = opt else { return }; +} + "#, + ); + } + + #[test] + fn nested_pattern() { + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option>) { + let val = $0match opt { + Some(Ok(it)) => it, + _ => return, + }; +} + "#, + r#" +fn foo(opt: Option>) { + let Some(Ok(val)) = opt else { return }; +} + "#, + ); + } + + #[test] + fn works_with_any_diverging_block() { + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => break, + }; + } +} + "#, + r#" +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { break }; + } +} + "#, + ); + + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => continue, + }; + } +} + "#, + r#" +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { continue }; + } +} + "#, + ); + + check_assist( + convert_match_to_let_else, + r#" +fn panic() -> ! {} + +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => panic(), + }; + } +} + "#, + r#" +fn panic() -> ! {} + +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { panic() }; + } +} + "#, + ); + } + + #[test] + fn struct_pattern() { + check_assist( + convert_match_to_let_else, + r#" +struct Point { + x: i32, + y: i32, +} + +fn foo(opt: Option) { + let val = $0match opt { + Some(Point { x: 0, y }) => y, + _ => return, + }; +} + "#, + r#" +struct Point { + x: i32, + y: i32, +} + +fn foo(opt: Option) { + let Some(Point { x: 0, y: val }) = opt else { return }; +} + "#, + ); + } + + #[test] + fn renames_whole_binding() { + check_assist( + convert_match_to_let_else, + r#" +fn foo(opt: Option) -> Option { + let val = $0match opt { + it @ Some(42) => it, + _ => return None, + }; + val +} + "#, + r#" +fn foo(opt: Option) -> Option { + let val @ Some(42) = opt else { return None }; + val +} + "#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index a07318cefad..387cc631428 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -120,6 +120,7 @@ mod handlers { mod convert_into_to_from; mod convert_iter_for_each_to_for; mod convert_let_else_to_match; + mod convert_match_to_let_else; mod convert_tuple_struct_to_named_struct; mod convert_named_struct_to_tuple_struct; mod convert_to_guarded_return; @@ -220,6 +221,7 @@ mod handlers { convert_iter_for_each_to_for::convert_for_loop_with_for_each, convert_let_else_to_match::convert_let_else_to_match, convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct, + convert_match_to_let_else::convert_match_to_let_else, convert_to_guarded_return::convert_to_guarded_return, convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro, From f0a14346ee79086ecb64bb65ced517568fbfc986 Mon Sep 17 00:00:00 2001 From: unexge Date: Sun, 30 Oct 2022 00:00:53 +0100 Subject: [PATCH 3/6] Update auto generated tests --- crates/ide-assists/src/tests/generated.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 2c4000efe0f..9df029748c0 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -407,6 +407,26 @@ fn main() { ) } +#[test] +fn doctest_convert_match_to_let_else() { + check_doc_test( + "convert_match_to_let_else", + r#####" +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => return, + }; +} +"#####, + r#####" +fn foo(opt: Option<()>) { + let Some(val) = opt else { return }; +} +"#####, + ) +} + #[test] fn doctest_convert_named_struct_to_tuple_struct() { check_doc_test( From 9f1bb17a1bb683a50a62c371803b07144b1bfc5a Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 1 Nov 2022 17:18:13 +0100 Subject: [PATCH 4/6] Import `option` in the tests --- .../src/handlers/convert_match_to_let_else.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 928016daab9..f5dbdd76273 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -86,7 +86,7 @@ fn find_arms( let mut extracting = None; let mut diverging = None; for arm in arms { - if ctx.sema.is_diverging_match_arm(&arm)? { + if ctx.sema.type_of_expr(&arm.expr().unwrap()).unwrap().original().is_never() { diverging = Some(arm); } else { extracting = Some(arm); @@ -159,6 +159,7 @@ mod tests { check_assist_not_applicable( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { let val = $0match opt { Some(it) => it, @@ -175,7 +176,8 @@ fn foo(opt: Option<()>) { check_assist_not_applicable( convert_match_to_let_else, r#" -fn foo(opt: Option<()>) { +//- minicore: option +fn foo(opt: Option) { let val = $0match opt { Some(it) => it + 1, None => return, @@ -187,6 +189,7 @@ fn foo(opt: Option<()>) { check_assist_not_applicable( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { let val = $0match opt { Some(it) => { @@ -206,6 +209,7 @@ fn foo(opt: Option<()>) { check_assist_not_applicable( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { let val = $0match opt { Some(it) if 2 > 1 => it, @@ -221,6 +225,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { let val = $0match opt { Some(it) => it, @@ -241,6 +246,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { let ref mut val = $0match opt { Some(it) => it, @@ -261,6 +267,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option, result fn foo(opt: Option>) { let val = $0match opt { Some(Ok(it)) => it, @@ -281,6 +288,7 @@ fn foo(opt: Option>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { loop { let val = $0match opt { @@ -302,6 +310,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option<()>) { loop { let val = $0match opt { @@ -323,6 +332,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn panic() -> ! {} fn foo(opt: Option<()>) { @@ -351,6 +361,7 @@ fn foo(opt: Option<()>) { check_assist( convert_match_to_let_else, r#" +//- minicore: option struct Point { x: i32, y: i32, @@ -381,6 +392,7 @@ fn foo(opt: Option) { check_assist( convert_match_to_let_else, r#" +//- minicore: option fn foo(opt: Option) -> Option { let val = $0match opt { it @ Some(42) => it, From e110c7889a568cd7ef375796028ce5eaacd81664 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 1 Nov 2022 17:18:17 +0100 Subject: [PATCH 5/6] Revert "Record diverging match arms in `InferenceResult`" This reverts commit 319611b7382fc4c84170519dade68f4f558a44b1. --- crates/hir-ty/src/infer.rs | 2 -- crates/hir-ty/src/infer/expr.rs | 5 ----- crates/hir/src/semantics.rs | 8 -------- crates/hir/src/source_analyzer.rs | 15 --------------- 4 files changed, 30 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 05776e19219..31e56dec625 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -333,8 +333,6 @@ pub struct InferenceResult { assoc_resolutions: FxHashMap, pub diagnostics: Vec, pub type_of_expr: ArenaMap, - /// For each match expr, record diverging arm's expr. - pub diverging_arms: FxHashMap>, /// For each pattern record the type it resolves to. /// /// **Note**: When a pattern type is resolved it may still contain diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3d2e091a0f6..f56108b26c4 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -375,7 +375,6 @@ impl<'a> InferenceContext<'a> { let matchee_diverges = self.diverges; let mut all_arms_diverge = Diverges::Always; - let mut diverging_arms = Vec::new(); for arm in arms.iter() { self.diverges = Diverges::Maybe; @@ -388,15 +387,11 @@ impl<'a> InferenceContext<'a> { } let arm_ty = self.infer_expr_inner(arm.expr, &expected); - if self.diverges.is_always() { - diverging_arms.push(arm.expr); - } all_arms_diverge &= self.diverges; coerce.coerce(self, Some(arm.expr), &arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; - self.result.diverging_arms.insert(tgt_expr, diverging_arms); coerce.complete() } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 2f835657d37..119ec3210e1 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -481,10 +481,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { self.imp.is_unsafe_ident_pat(ident_pat) } - - pub fn is_diverging_match_arm(&self, match_arm: &ast::MatchArm) -> Option { - self.imp.is_diverging_match_arm(match_arm) - } } impl<'db> SemanticsImpl<'db> { @@ -1425,10 +1421,6 @@ impl<'db> SemanticsImpl<'db> { .map(|ty| ty.original.is_packed(self.db)) .unwrap_or(false) } - - fn is_diverging_match_arm(&self, match_arm: &ast::MatchArm) -> Option { - self.analyze(match_arm.syntax())?.is_diverging_match_arm(self.db, match_arm) - } } fn macro_call_to_macro_id( diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 2e61946a738..f86c5710053 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -782,21 +782,6 @@ impl SourceAnalyzer { false } - pub(crate) fn is_diverging_match_arm( - &self, - db: &dyn HirDatabase, - match_arm: &ast::MatchArm, - ) -> Option { - let infer = self.infer.as_ref()?; - let match_expr = match_arm.syntax().ancestors().find_map(ast::MatchExpr::cast)?; - let match_id = self.expr_id(db, &match_expr.into())?; - let diverging_arms = infer.diverging_arms.get(&match_id)?; - let match_arm_expr = match_arm.expr()?; - let match_arm_expr_id = self.expr_id(db, &match_arm_expr)?; - - Some(diverging_arms.contains(&match_arm_expr_id)) - } - fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, From 72d5b456e1435023d9e7bd25174e2f5dbe37f535 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 1 Nov 2022 17:23:32 +0100 Subject: [PATCH 6/6] Fix doc test --- crates/ide-assists/src/handlers/convert_match_to_let_else.rs | 1 + crates/ide-assists/src/tests/generated.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index f5dbdd76273..5bf04a3ad37 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -14,6 +14,7 @@ use crate::{ // Converts let statement with match initializer to let-else statement. // // ``` +// # //- minicore: option // fn foo(opt: Option<()>) { // let val = $0match opt { // Some(it) => it, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 9df029748c0..029d169899b 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -412,6 +412,7 @@ fn doctest_convert_match_to_let_else() { check_doc_test( "convert_match_to_let_else", r#####" +//- minicore: option fn foo(opt: Option<()>) { let val = $0match opt { Some(it) => it,