Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs53
-rw-r--r--crates/hir/src/diagnostics.rs1
-rw-r--r--crates/hir/src/lib.rs6
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs17
5 files changed, 48 insertions, 31 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index ba18d0c5ea..f41c4afaf5 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -182,7 +182,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
NoSuchField { expr: ExprId },
- BreakOutsideOfLoop { expr: ExprId },
+ BreakOutsideOfLoop { expr: ExprId, is_break: bool },
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index a29e15ec5c..09d8320252 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -120,19 +120,16 @@ impl<'a> InferenceContext<'a> {
let ty = match label {
Some(_) => {
let break_ty = self.table.new_type_var();
- let (ctx, ty) = self.with_breakable_ctx(break_ty.clone(), *label, |this| {
- this.infer_block(
- tgt_expr,
- statements,
- *tail,
- &Expectation::has_type(break_ty),
- )
- });
- if ctx.may_break {
- ctx.coerce.complete()
- } else {
- ty
- }
+ let (breaks, ty) =
+ self.with_breakable_ctx(break_ty.clone(), *label, |this| {
+ this.infer_block(
+ tgt_expr,
+ statements,
+ *tail,
+ &Expectation::has_type(break_ty),
+ )
+ });
+ breaks.unwrap_or(ty)
}
None => self.infer_block(tgt_expr, statements, *tail, expected),
};
@@ -164,15 +161,16 @@ impl<'a> InferenceContext<'a> {
}
&Expr::Loop { body, label } => {
let ty = self.table.new_type_var();
- let (ctx, ()) = self.with_breakable_ctx(ty, label, |this| {
+ let (breaks, ()) = self.with_breakable_ctx(ty, label, |this| {
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
});
- if ctx.may_break {
- self.diverges = Diverges::Maybe;
- ctx.coerce.complete()
- } else {
- TyKind::Never.intern(Interner)
+ match breaks {
+ Some(breaks) => {
+ self.diverges = Diverges::Maybe;
+ breaks
+ }
+ None => TyKind::Never.intern(Interner),
}
}
&Expr::While { condition, body, label } => {
@@ -194,7 +192,7 @@ impl<'a> InferenceContext<'a> {
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
self.infer_pat(pat, &pat_ty, BindingMode::default());
- let (_ctx, ()) = self.with_breakable_ctx(self.err_ty(), label, |this| {
+ self.with_breakable_ctx(self.err_ty(), label, |this| {
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
});
@@ -356,7 +354,15 @@ impl<'a> InferenceContext<'a> {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
}
- Expr::Continue { .. } => TyKind::Never.intern(Interner),
+ Expr::Continue { label } => {
+ if let None = find_breakable(&mut self.breakables, label.as_ref()) {
+ self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+ expr: tgt_expr,
+ is_break: false,
+ });
+ };
+ TyKind::Never.intern(Interner)
+ }
Expr::Break { expr, label } => {
let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
Some(ctxt) => {
@@ -384,6 +390,7 @@ impl<'a> InferenceContext<'a> {
} else {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
+ is_break: true,
});
};
@@ -1462,13 +1469,13 @@ impl<'a> InferenceContext<'a> {
ty: Ty,
label: Option<LabelId>,
cb: impl FnOnce(&mut Self) -> T,
- ) -> (BreakableContext, T) {
+ ) -> (Option<Ty>, T) {
self.breakables.push({
let label = label.map(|label| self.body[label].name.clone());
BreakableContext { may_break: false, coerce: CoerceMany::new(ty), label }
});
let res = cb(self);
let ctx = self.breakables.pop().expect("breakable stack broken");
- (ctx, res)
+ (ctx.may_break.then(|| ctx.coerce.complete()), res)
}
}
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 50374f4b3f..5edc16d8bc 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -124,6 +124,7 @@ pub struct NoSuchField {
#[derive(Debug)]
pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>,
+ pub is_break: bool,
}
#[derive(Debug)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 6dccf2ed20..e4bb63a864 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1216,11 +1216,11 @@ impl DefWithBody {
let field = source_map.field_syntax(*expr);
acc.push(NoSuchField { field }.into())
}
- hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
+ &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
let expr = source_map
- .expr_syntax(*expr)
+ .expr_syntax(expr)
.expect("break outside of loop in synthetic syntax");
- acc.push(BreakOutsideOfLoop { expr }.into())
+ acc.push(BreakOutsideOfLoop { expr, is_break }.into())
}
hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
match source_map.expr_syntax(*call_expr) {
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 d12594a4ce..59203106ef 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -7,9 +7,10 @@ pub(crate) fn break_outside_of_loop(
ctx: &DiagnosticsContext<'_>,
d: &hir::BreakOutsideOfLoop,
) -> Diagnostic {
+ let construct = if d.is_break { "break" } else { "continue" };
Diagnostic::new(
"break-outside-of-loop",
- "break outside of loop",
+ format!("{construct} outside of loop"),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
)
}
@@ -19,11 +20,19 @@ mod tests {
use crate::tests::check_diagnostics;
#[test]
- fn break_outside_of_loop() {
+ fn outside_of_loop() {
check_diagnostics(
r#"
-fn foo() { break; }
- //^^^^^ error: break outside of loop
+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
+}
"#,
);
}