Re-enable controlflow outside loop diagnostic

This commit is contained in:
Lukas Wirth 2023-04-06 15:37:53 +02:00
parent 0e7117900c
commit fbb1bd5880
8 changed files with 75 additions and 31 deletions

View file

@ -189,6 +189,12 @@ pub enum InferenceDiagnostic {
/// Contains the type the field resolves to /// Contains the type the field resolves to
field_with_same_name: Option<Ty>, 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 { MismatchedArgCount {
call_expr: ExprId, call_expr: ExprId,
expected: usize, 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> { impl<'a> InferenceContext<'a> {
fn new( fn new(
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,

View file

@ -25,7 +25,9 @@ use syntax::ast::RangeOp;
use crate::{ use crate::{
autoderef::{builtin_deref, deref_by_trait, Autoderef}, autoderef::{builtin_deref, deref_by_trait, Autoderef},
consteval, 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, lang_items::lang_items_for_bin_op,
lower::{ lower::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode, 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); self.resolver.reset_to_guard(g);
ty 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 } => { &Expr::Break { expr, label } => {
let val_ty = if let Some(expr) = expr { let val_ty = if let Some(expr) = expr {
let opt_coerce_to = match find_breakable(&mut self.breakables, label) { let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce { Some(ctxt) => match &ctxt.coerce {
Some(coerce) => coerce.expected_ty(), 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(), None => self.err_ty(),
}; };
@ -485,7 +503,13 @@ impl<'a> InferenceContext<'a> {
} }
None => ctxt.may_break = true, 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() self.result.standard_types.never.clone()
} }

View file

@ -32,6 +32,7 @@ macro_rules! diagnostics {
} }
diagnostics![ diagnostics![
BreakOutsideOfLoop,
ExpectedFunction, ExpectedFunction,
InactiveCode, InactiveCode,
IncorrectCase, IncorrectCase,
@ -62,6 +63,13 @@ diagnostics![
UnusedMut, UnusedMut,
]; ];
#[derive(Debug)]
pub struct BreakOutsideOfLoop {
pub expr: InFile<AstPtr<ast::Expr>>,
pub is_break: bool,
pub bad_value_break: bool,
}
#[derive(Debug)] #[derive(Debug)]
pub struct UnresolvedModule { pub struct UnresolvedModule {
pub decl: InFile<AstPtr<ast::Module>>, pub decl: InFile<AstPtr<ast::Module>>,

View file

@ -85,13 +85,13 @@ use crate::db::{DefDatabase, HirDatabase};
pub use crate::{ pub use crate::{
attrs::{HasAttrs, Namespace}, attrs::{HasAttrs, Namespace},
diagnostics::{ diagnostics::{
AnyDiagnostic, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields, IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel,
UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
UnusedMut, UnresolvedProcMacro, UnusedMut,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@ -1483,6 +1483,14 @@ impl DefWithBody {
.into(), .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() { for (pat_or_expr, mismatch) in infer.type_mismatches() {

View file

@ -31,12 +31,8 @@ mod tests {
fn foo() { fn foo() {
break; break;
//^^^^^ error: break outside of loop //^^^^^ error: break outside of loop
break 'a;
//^^^^^^^^ error: break outside of loop
continue; continue;
//^^^^^^^^ error: continue outside of loop //^^^^^^^^ error: continue outside of loop
continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
} }
"#, "#,
); );
@ -51,12 +47,8 @@ fn foo() {
async { async {
break; break;
//^^^^^ error: break outside of loop //^^^^^ error: break outside of loop
break 'a;
//^^^^^^^^ error: break outside of loop
continue; continue;
//^^^^^^^^ error: continue outside of loop //^^^^^^^^ error: continue outside of loop
continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
}; };
} }
} }
@ -73,12 +65,8 @@ fn foo() {
|| { || {
break; break;
//^^^^^ error: break outside of loop //^^^^^ error: break outside of loop
break 'a;
//^^^^^^^^ error: break outside of loop
continue; continue;
//^^^^^^^^ error: continue outside of loop //^^^^^^^^ error: continue outside of loop
continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
}; };
} }
} }
@ -94,9 +82,7 @@ fn foo() {
'a: loop { 'a: loop {
{ {
break; break;
break 'a;
continue; continue;
continue 'a;
} }
} }
} }
@ -112,9 +98,7 @@ fn foo() {
'a: loop { 'a: loop {
try { try {
break; break;
break 'a;
continue; continue;
continue 'a;
}; };
} }
} }
@ -130,11 +114,8 @@ fn foo() {
'a: { 'a: {
break; break;
//^^^^^ error: break outside of loop //^^^^^ error: break outside of loop
break 'a;
continue; continue;
//^^^^^^^^ error: continue outside of loop //^^^^^^^^ error: continue outside of loop
continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
} }
} }
"#, "#,

View file

@ -23,8 +23,10 @@ mod tests {
r#" r#"
fn foo() { fn foo() {
break 'a; break 'a;
//^^^^^^^^ error: break outside of loop
//^^ error: use of undeclared label `'a` //^^ error: use of undeclared label `'a`
continue 'a; continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
//^^ error: use of undeclared label `'a` //^^ error: use of undeclared label `'a`
} }
"#, "#,

View file

@ -25,8 +25,10 @@ fn foo() {
'a: loop { 'a: loop {
async { async {
break 'a; break 'a;
//^^^^^^^^ error: break outside of loop
// ^^ error: use of unreachable label `'a` // ^^ error: use of unreachable label `'a`
continue 'a; continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
// ^^ error: use of unreachable label `'a` // ^^ error: use of unreachable label `'a`
}; };
} }
@ -43,8 +45,10 @@ fn foo() {
'a: loop { 'a: loop {
|| { || {
break 'a; break 'a;
//^^^^^^^^ error: break outside of loop
// ^^ error: use of unreachable label `'a` // ^^ error: use of unreachable label `'a`
continue 'a; continue 'a;
//^^^^^^^^^^^ error: continue outside of loop
// ^^ error: use of unreachable label `'a` // ^^ error: use of unreachable label `'a`
}; };
} }

View file

@ -26,6 +26,7 @@
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
mod handlers { mod handlers {
pub(crate) mod break_outside_of_loop;
pub(crate) mod expected_function; pub(crate) mod expected_function;
pub(crate) mod inactive_code; pub(crate) mod inactive_code;
pub(crate) mod incoherent_impl; pub(crate) mod incoherent_impl;
@ -285,7 +286,7 @@ pub fn diagnostics(
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), 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::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::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) res.push(d)
} }