mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
Diagnose incorrect continue expressions
This commit is contained in:
parent
d6fc4a9ea6
commit
1e66a5a8ce
5 changed files with 48 additions and 31 deletions
|
@ -182,7 +182,7 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum InferenceDiagnostic {
|
pub enum InferenceDiagnostic {
|
||||||
NoSuchField { expr: ExprId },
|
NoSuchField { expr: ExprId },
|
||||||
BreakOutsideOfLoop { expr: ExprId },
|
BreakOutsideOfLoop { expr: ExprId, is_break: bool },
|
||||||
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,19 +120,16 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ty = match label {
|
let ty = match label {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
let break_ty = self.table.new_type_var();
|
let break_ty = self.table.new_type_var();
|
||||||
let (ctx, ty) = self.with_breakable_ctx(break_ty.clone(), *label, |this| {
|
let (breaks, ty) =
|
||||||
this.infer_block(
|
self.with_breakable_ctx(break_ty.clone(), *label, |this| {
|
||||||
tgt_expr,
|
this.infer_block(
|
||||||
statements,
|
tgt_expr,
|
||||||
*tail,
|
statements,
|
||||||
&Expectation::has_type(break_ty),
|
*tail,
|
||||||
)
|
&Expectation::has_type(break_ty),
|
||||||
});
|
)
|
||||||
if ctx.may_break {
|
});
|
||||||
ctx.coerce.complete()
|
breaks.unwrap_or(ty)
|
||||||
} else {
|
|
||||||
ty
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => self.infer_block(tgt_expr, statements, *tail, expected),
|
None => self.infer_block(tgt_expr, statements, *tail, expected),
|
||||||
};
|
};
|
||||||
|
@ -164,15 +161,16 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
&Expr::Loop { body, label } => {
|
&Expr::Loop { body, label } => {
|
||||||
let ty = self.table.new_type_var();
|
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()));
|
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
|
||||||
});
|
});
|
||||||
|
|
||||||
if ctx.may_break {
|
match breaks {
|
||||||
self.diverges = Diverges::Maybe;
|
Some(breaks) => {
|
||||||
ctx.coerce.complete()
|
self.diverges = Diverges::Maybe;
|
||||||
} else {
|
breaks
|
||||||
TyKind::Never.intern(Interner)
|
}
|
||||||
|
None => TyKind::Never.intern(Interner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Expr::While { condition, body, label } => {
|
&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.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
||||||
|
|
||||||
self.infer_pat(pat, &pat_ty, BindingMode::default());
|
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()));
|
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);
|
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())
|
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 } => {
|
Expr::Break { expr, label } => {
|
||||||
let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
|
let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
|
||||||
Some(ctxt) => {
|
Some(ctxt) => {
|
||||||
|
@ -384,6 +390,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||||
expr: tgt_expr,
|
expr: tgt_expr,
|
||||||
|
is_break: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1462,13 +1469,13 @@ impl<'a> InferenceContext<'a> {
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
label: Option<LabelId>,
|
label: Option<LabelId>,
|
||||||
cb: impl FnOnce(&mut Self) -> T,
|
cb: impl FnOnce(&mut Self) -> T,
|
||||||
) -> (BreakableContext, T) {
|
) -> (Option<Ty>, T) {
|
||||||
self.breakables.push({
|
self.breakables.push({
|
||||||
let label = label.map(|label| self.body[label].name.clone());
|
let label = label.map(|label| self.body[label].name.clone());
|
||||||
BreakableContext { may_break: false, coerce: CoerceMany::new(ty), label }
|
BreakableContext { may_break: false, coerce: CoerceMany::new(ty), label }
|
||||||
});
|
});
|
||||||
let res = cb(self);
|
let res = cb(self);
|
||||||
let ctx = self.breakables.pop().expect("breakable stack broken");
|
let ctx = self.breakables.pop().expect("breakable stack broken");
|
||||||
(ctx, res)
|
(ctx.may_break.then(|| ctx.coerce.complete()), res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ pub struct NoSuchField {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BreakOutsideOfLoop {
|
pub struct BreakOutsideOfLoop {
|
||||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
pub is_break: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1216,11 +1216,11 @@ impl DefWithBody {
|
||||||
let field = source_map.field_syntax(*expr);
|
let field = source_map.field_syntax(*expr);
|
||||||
acc.push(NoSuchField { field }.into())
|
acc.push(NoSuchField { field }.into())
|
||||||
}
|
}
|
||||||
hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
|
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
|
||||||
let expr = source_map
|
let expr = source_map
|
||||||
.expr_syntax(*expr)
|
.expr_syntax(expr)
|
||||||
.expect("break outside of loop in synthetic syntax");
|
.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 } => {
|
hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
||||||
match source_map.expr_syntax(*call_expr) {
|
match source_map.expr_syntax(*call_expr) {
|
||||||
|
|
|
@ -7,9 +7,10 @@ pub(crate) fn break_outside_of_loop(
|
||||||
ctx: &DiagnosticsContext<'_>,
|
ctx: &DiagnosticsContext<'_>,
|
||||||
d: &hir::BreakOutsideOfLoop,
|
d: &hir::BreakOutsideOfLoop,
|
||||||
) -> Diagnostic {
|
) -> Diagnostic {
|
||||||
|
let construct = if d.is_break { "break" } else { "continue" };
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
"break-outside-of-loop",
|
"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,
|
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,11 +20,19 @@ mod tests {
|
||||||
use crate::tests::check_diagnostics;
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn break_outside_of_loop() {
|
fn outside_of_loop() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
fn foo() { break; }
|
fn foo() {
|
||||||
//^^^^^ error: break outside of loop
|
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
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue