Unnamed repository; edit this file 'description' to name the repository.
Re-enable controlflow outside loop diagnostic
Lukas Wirth 2023-04-06
parent 0e71179 · commit fbb1bd5
-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 ed530408ab..c1012cc3d9 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 4e62e41b58..129ff33ae5 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 605bac6157..f756832f0f 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 cd4d8e17d3..dbb41b1b66 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 114face2dc..89aa437d75 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 9d5cbb31bb..dbedf1e6c1 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 f09659fa9e..5933a9b694 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 5cb1d4e1b8..70116f15a7 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)
}