diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index ed530408abf5..c1012cc3d9ce 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, }, + // 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, +) -> 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 4e62e41b586b..129ff33ae5db 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 605bac61571f..f756832f0ff8 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, @@ -62,6 +63,13 @@ diagnostics![ UnusedMut, ]; +#[derive(Debug)] +pub struct BreakOutsideOfLoop { + pub expr: InFile>, + pub is_break: bool, + pub bad_value_break: bool, +} + #[derive(Debug)] pub struct UnresolvedModule { pub decl: InFile>, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index cd4d8e17d3bb..dbb41b1b6674 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 114face2dca8..89aa437d75d9 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 9d5cbb31bbfb..dbedf1e6c1ed 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 f09659fa9e1f..5933a9b694a3 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 5cb1d4e1b828..70116f15a756 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) }