Repository: rust-lang/rust-analyzer
Pull request #18045 (2024-09-04 09:48:17)

fix: Fix lowering of for loops dropping the loop block


Patch:
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index abf7895829..145afc1b61 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -737,7 +737,7 @@ impl ExprCollector<'_> {
     /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
     /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
     fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
-        let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
+        let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else {
             return self.collect_block(e);
         };
         let label = self
@@ -840,10 +840,10 @@ impl ExprCollector<'_> {
     fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
         let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| {
             Some((
-                LangItem::IntoIterIntoIter.path(self.db, self.krate)?,
-                LangItem::IteratorNext.path(self.db, self.krate)?,
-                LangItem::OptionSome.path(self.db, self.krate)?,
-                LangItem::OptionNone.path(self.db, self.krate)?,
+                self.lang_path(LangItem::IntoIterIntoIter)?,
+                self.lang_path(LangItem::IteratorNext)?,
+                self.lang_path(LangItem::OptionSome)?,
+                self.lang_path(LangItem::OptionNone)?,
             ))
         })() else {
             // Some of the needed lang items are missing, so we can't desugar
@@ -896,6 +896,15 @@ impl ExprCollector<'_> {
             Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
             syntax_ptr,
         );
+        let loop_inner = self.alloc_expr(
+            Expr::Block {
+                id: None,
+                statements: Box::default(),
+                tail: Some(loop_inner),
+                label: None,
+            },
+            syntax_ptr,
+        );
         let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr);
         let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
         let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
@@ -923,10 +932,10 @@ impl ExprCollector<'_> {
     fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
         let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| {
             Some((
-                LangItem::TryTraitBranch.path(self.db, self.krate)?,
-                LangItem::ControlFlowContinue.path(self.db, self.krate)?,
-                LangItem::ControlFlowBreak.path(self.db, self.krate)?,
-                LangItem::TryTraitFromResidual.path(self.db, self.krate)?,
+                self.lang_path(LangItem::TryTraitBranch)?,
+                self.lang_path(LangItem::ControlFlowContinue)?,
+                self.lang_path(LangItem::ControlFlowBreak)?,
+                self.lang_path(LangItem::TryTraitFromResidual)?,
             ))
         })() else {
             // Some of the needed lang items are missing, so we can't desugar
@@ -2053,6 +2062,10 @@ impl ExprCollector<'_> {
         })
     }
     // endregion: format
+
+    fn lang_path(&self, lang: LangItem) -> Option<Path> {
+        lang.path(self.db, self.krate)
+    }
 }
 
 fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {


Test patch:
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index 38fc01d8fc..dd3e79c874 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -142,6 +142,41 @@ mod m {
     );
 }
 
+#[test]
+fn desugar_for_loop() {
+    let (db, body, def) = lower(
+        r#"
+//- minicore: iterator
+fn main() {
+    for ident in 0..10 {
+        foo();
+        bar()
+    }
+}
+"#,
+    );
+
+    expect![[r#"
+        fn main() -> () {
+            match builtin#lang(into_iter)(
+                (0) ..(10) ,
+            ) {
+                mut <ra@gennew>11 => loop {
+                    match builtin#lang(next)(
+                        &mut <ra@gennew>11,
+                    ) {
+                        builtin#lang(None) => break,
+                        builtin#lang(Some)(ident) => {
+                            foo();
+                            bar()
+                        },
+                    }
+                },
+            }
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
+
 #[test]
 fn desugar_builtin_format_args() {
     let (db, body, def) = lower(
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 5454a496ba..5a6161f942 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -206,6 +206,7 @@ fn expr_macro_def_expanded_in_various_places() {
             100..119 'for _ ...!() {}': ()
             100..119 'for _ ...!() {}': ()
             100..119 'for _ ...!() {}': ()
+            100..119 'for _ ...!() {}': ()
             104..105 '_': IntoIterator::Item<isize>
             117..119 '{}': ()
             124..134 '|| spam!()': impl Fn() -> isize
@@ -299,6 +300,7 @@ fn expr_macro_rules_expanded_in_various_places() {
             114..133 'for _ ...!() {}': ()
             114..133 'for _ ...!() {}': ()
             114..133 'for _ ...!() {}': ()
+            114..133 'for _ ...!() {}': ()
             118..119 '_': IntoIterator::Item<isize>
             131..133 '{}': ()
             138..148 '|| spam!()': impl Fn() -> isize
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index 0ccbcf63e2..5c63cd00f9 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -371,6 +371,7 @@ fn diverging_expression_3_break() {
             151..172 'for a ...eak; }': ()
             151..172 'for a ...eak; }': ()
             151..172 'for a ...eak; }': ()
+            151..172 'for a ...eak; }': ()
             155..156 'a': {unknown}
             160..161 'b': {unknown}
             162..172 '{ break; }': ()
@@ -387,6 +388,7 @@ fn diverging_expression_3_break() {
             237..250 'for a in b {}': ()
             237..250 'for a in b {}': ()
             237..250 'for a in b {}': ()
+            237..250 'for a in b {}': ()
             241..242 'a': {unknown}
             246..247 'b': {unknown}
             248..250 '{}': ()
@@ -402,6 +404,7 @@ fn diverging_expression_3_break() {
             315..337 'for a ...urn; }': ()
             315..337 'for a ...urn; }': ()
             315..337 'for a ...urn; }': ()
+            315..337 'for a ...urn; }': ()
             319..320 'a': {unknown}
             324..325 'b': {unknown}
             326..337 '{ return; }': ()
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 57866acc06..51c27f8714 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -57,6 +57,7 @@ fn infer_pattern() {
             101..151 'for (e...     }': ()
             101..151 'for (e...     }': ()
             101..151 'for (e...     }': ()
+            101..151 'for (e...     }': ()
             105..111 '(e, f)': ({unknown}, {unknown})
             106..107 'e': {unknown}
             109..110 'f': {unknown}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 4df10025f8..db289f430e 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -275,6 +275,7 @@ fn infer_std_crash_5() {
             32..320 'for co...     }': ()
             32..320 'for co...     }': ()
             32..320 'for co...     }': ()
+            32..320 'for co...     }': ()
             36..43 'content': {unknown}
             47..60 'doesnt_matter': {unknown}
             61..320 '{     ...     }': ()
@@ -1244,6 +1245,7 @@ fn test() {
             16..66 'for _ ...     }': ()
             16..66 'for _ ...     }': ()
             16..66 'for _ ...     }': ()
+            16..66 'for _ ...     }': ()
             20..21 '_': IntoIterator::Item<()>
             25..39 '{ let x = 0; }': ()
             31..32 'x': i32