about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2023-04-06 15:37:53 +0200
committerLukas Wirth <lukastw97@gmail.com>2023-04-06 15:37:53 +0200
commitfbb1bd58806a697ac5e0cae01e0a6bc930ed9dc3 (patch)
tree4da0f7300c8a901af69acdd533eb1a16a9a63e0c
parent0e7117900c06ef2ae7f113f61e08a3234c3aee79 (diff)
downloadrust-fbb1bd58806a697ac5e0cae01e0a6bc930ed9dc3.tar.gz
rust-fbb1bd58806a697ac5e0cae01e0a6bc930ed9dc3.zip
Re-enable controlflow outside loop diagnostic
-rw-r--r--crates/hir-ty/src/infer.rs16
-rw-r--r--crates/hir-ty/src/infer/expr.rs32
-rw-r--r--crates/hir/src/diagnostics.rs8
-rw-r--r--crates/hir/src/lib.rs22
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unreachable_label.rs4
-rw-r--r--crates/ide-diagnostics/src/lib.rs3
8 files changed, 75 insertions, 31 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index ed530408abf..c1012cc3d9c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -189,6 +189,12 @@ pub enum InferenceDiagnostic {
         /// Contains the type the field resolves to
         field_with_same_name: Option<Ty>,
     },
+    // FIXME: This should be emitted in body lowering
+    BreakOutsideOfLoop {
+        expr: ExprId,
+        is_break: bool,
+        bad_value_break: bool,
+    },
     MismatchedArgCount {
         call_expr: ExprId,
         expected: usize,
@@ -490,6 +496,16 @@ fn find_breakable<'c>(
     }
 }
 
+fn find_continuable<'c>(
+    ctxs: &'c mut [BreakableContext],
+    label: Option<LabelId>,
+) -> Option<&'c mut BreakableContext> {
+    match label {
+        Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
+        None => find_breakable(ctxs, label),
+    }
+}
+
 impl<'a> InferenceContext<'a> {
     fn new(
         db: &'a dyn HirDatabase,
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 4e62e41b586..129ff33ae5d 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -25,7 +25,9 @@ use syntax::ast::RangeOp;
 use crate::{
     autoderef::{builtin_deref, deref_by_trait, Autoderef},
     consteval,
-    infer::{coerce::CoerceMany, pat::contains_explicit_ref_binding, BreakableKind},
+    infer::{
+        coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
+    },
     lang_items::lang_items_for_bin_op,
     lower::{
         const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
@@ -457,13 +459,29 @@ impl<'a> InferenceContext<'a> {
                 self.resolver.reset_to_guard(g);
                 ty
             }
-            Expr::Continue { .. } => self.result.standard_types.never.clone(),
+            &Expr::Continue { label } => {
+                if let None = find_continuable(&mut self.breakables, label) {
+                    self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                        expr: tgt_expr,
+                        is_break: false,
+                        bad_value_break: false,
+                    });
+                };
+                self.result.standard_types.never.clone()
+            }
             &Expr::Break { expr, label } => {
                 let val_ty = if let Some(expr) = expr {
                     let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
                         Some(ctxt) => match &ctxt.coerce {
                             Some(coerce) => coerce.expected_ty(),
-                            None => self.err_ty(),
+                            None => {
+                                self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                                    expr: tgt_expr,
+                                    is_break: true,
+                                    bad_value_break: true,
+                                });
+                                self.err_ty()
+                            }
                         },
                         None => self.err_ty(),
                     };
@@ -485,7 +503,13 @@ impl<'a> InferenceContext<'a> {
                         }
                         None => ctxt.may_break = true,
                     },
-                    None => {}
+                    None => {
+                        self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                            expr: tgt_expr,
+                            is_break: true,
+                            bad_value_break: false,
+                        });
+                    }
                 }
                 self.result.standard_types.never.clone()
             }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 605bac61571..f756832f0ff 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -32,6 +32,7 @@ macro_rules! diagnostics {
 }
 
 diagnostics![
+    BreakOutsideOfLoop,
     ExpectedFunction,
     InactiveCode,
     IncorrectCase,
@@ -63,6 +64,13 @@ diagnostics![
 ];
 
 #[derive(Debug)]
+pub struct BreakOutsideOfLoop {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub is_break: bool,
+    pub bad_value_break: bool,
+}
+
+#[derive(Debug)]
 pub struct UnresolvedModule {
     pub decl: InFile<AstPtr<ast::Module>>,
     pub candidates: Box<[String]>,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index cd4d8e17d3b..dbb41b1b667 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -85,13 +85,13 @@ use crate::db::{DefDatabase, HirDatabase};
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase,
-        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
-        MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
-        ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro,
-        UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport,
-        UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro,
-        UnusedMut,
+        AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
+        IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
+        MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
+        PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel,
+        UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
+        UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
+        UnresolvedProcMacro, UnusedMut,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -1483,6 +1483,14 @@ impl DefWithBody {
                         .into(),
                     )
                 }
+                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+                    expr,
+                    is_break,
+                    bad_value_break,
+                } => {
+                    let expr = expr_syntax(expr);
+                    acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
+                }
             }
         }
         for (pat_or_expr, mismatch) in infer.type_mismatches() {
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 114face2dca..89aa437d75d 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -31,12 +31,8 @@ mod tests {
 fn foo() {
     break;
   //^^^^^ error: break outside of loop
-    break 'a;
-  //^^^^^^^^ error: break outside of loop
     continue;
   //^^^^^^^^ error: continue outside of loop
-    continue 'a;
-  //^^^^^^^^^^^ error: continue outside of loop
 }
 "#,
         );
@@ -51,12 +47,8 @@ fn foo() {
         async {
                 break;
               //^^^^^ error: break outside of loop
-                break 'a;
-              //^^^^^^^^ error: break outside of loop
                 continue;
               //^^^^^^^^ error: continue outside of loop
-                continue 'a;
-              //^^^^^^^^^^^ error: continue outside of loop
         };
     }
 }
@@ -73,12 +65,8 @@ fn foo() {
         || {
                 break;
               //^^^^^ error: break outside of loop
-                break 'a;
-              //^^^^^^^^ error: break outside of loop
                 continue;
               //^^^^^^^^ error: continue outside of loop
-                continue 'a;
-              //^^^^^^^^^^^ error: continue outside of loop
         };
     }
 }
@@ -94,9 +82,7 @@ fn foo() {
     'a: loop {
         {
             break;
-            break 'a;
             continue;
-            continue 'a;
         }
     }
 }
@@ -112,9 +98,7 @@ fn foo() {
     'a: loop {
         try {
                 break;
-                break 'a;
                 continue;
-                continue 'a;
         };
     }
 }
@@ -130,11 +114,8 @@ fn foo() {
     'a: {
         break;
       //^^^^^ error: break outside of loop
-        break 'a;
         continue;
       //^^^^^^^^ error: continue outside of loop
-        continue 'a;
-      //^^^^^^^^^^^ error: continue outside of loop
     }
 }
 "#,
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 9d5cbb31bbf..dbedf1e6c1e 100644
--- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -23,8 +23,10 @@ mod tests {
             r#"
 fn foo() {
     break 'a;
+  //^^^^^^^^ error: break outside of loop
         //^^ error: use of undeclared label `'a`
     continue 'a;
+  //^^^^^^^^^^^ error: continue outside of loop
            //^^ error: use of undeclared label `'a`
 }
 "#,
diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
index f09659fa9e1..5933a9b694a 100644
--- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs
+++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -25,8 +25,10 @@ fn foo() {
     'a: loop {
         async {
             break 'a;
+          //^^^^^^^^ error: break outside of loop
                // ^^ error: use of unreachable label `'a`
             continue 'a;
+          //^^^^^^^^^^^ error: continue outside of loop
                   // ^^ error: use of unreachable label `'a`
         };
     }
@@ -43,8 +45,10 @@ fn foo() {
     'a: loop {
         || {
             break 'a;
+          //^^^^^^^^ error: break outside of loop
                // ^^ error: use of unreachable label `'a`
             continue 'a;
+          //^^^^^^^^^^^ error: continue outside of loop
                   // ^^ error: use of unreachable label `'a`
         };
     }
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 5cb1d4e1b82..70116f15a75 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -26,6 +26,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
 
 mod handlers {
+    pub(crate) mod break_outside_of_loop;
     pub(crate) mod expected_function;
     pub(crate) mod inactive_code;
     pub(crate) mod incoherent_impl;
@@ -285,7 +286,7 @@ pub fn diagnostics(
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
             AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
-
+            AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
         };
         res.push(d)
     }