Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 12 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 16 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 12 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/return_outside_function.rs | 98 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
6 files changed, 135 insertions, 7 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 8c70461ff6..40fcb62a19 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -524,6 +524,18 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + ReturnOutsideFunction { + #[type_visitable(ignore)] + expr: ExprId, + #[type_visitable(ignore)] + kind: ReturnKind, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ReturnKind { + ReturnExpr, + BecomeExpr, } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3356c4d78a..df17cc3877 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -43,7 +43,7 @@ use crate::{ }; use super::{ - BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, + BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, ReturnKind, cast::CastCheck, find_breakable, }; @@ -576,7 +576,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), - &Expr::Become { expr } => self.infer_expr_become(expr), + &Expr::Become { expr } => self.infer_expr_become(tgt_expr, expr), Expr::Yield { expr } => { if let Some((resume_ty, yield_ty)) = self.resume_yield_tys { if let Some(expr) = expr { @@ -1454,7 +1454,10 @@ impl<'db> InferenceContext<'_, 'db> { } } None => { - // FIXME: diagnose return outside of function + self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction { + expr: ret, + kind: ReturnKind::ReturnExpr, + }); if let Some(expr) = expr { self.infer_expr_no_expect(expr, ExprIsRead::Yes); } @@ -1463,7 +1466,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } - fn infer_expr_become(&mut self, expr: ExprId) -> Ty<'db> { + fn infer_expr_become(&mut self, tgt_expr: ExprId, expr: ExprId) -> Ty<'db> { match &self.return_coercion { Some(return_coercion) => { let ret_ty = return_coercion.expected_ty(); @@ -1476,7 +1479,10 @@ impl<'db> InferenceContext<'_, 'db> { _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty); } None => { - // FIXME: diagnose `become` outside of functions + self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction { + expr: tgt_expr, + kind: ReturnKind::BecomeExpr, + }); self.infer_expr_no_expect(expr, ExprIsRead::Yes); } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 778dbb0ff2..a9975615be 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -106,7 +106,7 @@ pub use autoderef::autoderef; pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, - PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + PointerCast, ReturnKind, cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; pub use lower::{ diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index cfb967f486..b921a5eef1 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -36,7 +36,7 @@ use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwne pub use hir_def::VariantId; pub use hir_ty::{ - GenericArgsProhibitedReason, IncorrectGenericsLenKind, + GenericArgsProhibitedReason, IncorrectGenericsLenKind, ReturnKind, diagnostics::{CaseType, IncorrectCase}, }; @@ -181,6 +181,7 @@ diagnostics![AnyDiagnostic<'db> -> UnionPatHasRest, UnimplementedTrait<'db>, YieldOutsideCoroutine, + ReturnOutsideFunction, ]; #[derive(Debug)] @@ -689,6 +690,12 @@ pub struct YieldOutsideCoroutine { pub expr: InFile<ExprOrPatPtr>, } +#[derive(Debug)] +pub struct ReturnOutsideFunction { + pub expr: InFile<ExprOrPatPtr>, + pub kind: ReturnKind, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -1131,6 +1138,9 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::YieldOutsideCoroutine { expr } => { YieldOutsideCoroutine { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::ReturnOutsideFunction { expr, kind } => { + ReturnOutsideFunction { expr: expr_syntax(expr)?, kind }.into() + } }) } diff --git a/crates/ide-diagnostics/src/handlers/return_outside_function.rs b/crates/ide-diagnostics/src/handlers/return_outside_function.rs new file mode 100644 index 0000000000..e6620ac543 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/return_outside_function.rs @@ -0,0 +1,98 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: return-outside-of-function +// +// This diagnostic triggers if return or become is used outside of a function body. +pub(crate) fn return_outside_function( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ReturnOutsideFunction, +) -> Diagnostic { + let construct = match d.kind { + hir::ReturnKind::ReturnExpr => "return", + hir::ReturnKind::BecomeExpr => "become", + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0572"), + format!("{construct} statement outside of function body"), + d.expr.map(|it| it.into()), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn return_in_const() { + check_diagnostics( + r#" +const _: () = { + return; + //^^^^^^ error: return statement outside of function body +}; +"#, + ); + } + + #[test] + fn return_in_static() { + check_diagnostics( + r#" +static _S: i32 = { + return 0; + //^^^^^^^^ error: return statement outside of function body + 0 +}; +"#, + ); + } + + #[test] + fn return_in_function_is_correct() { + check_diagnostics( + r#" +fn foo() -> i32 { + if true { return 42; } + 0 +} +"#, + ); + } + + #[test] + fn become_in_const() { + check_diagnostics( + r#" +const _: () = { + become 0; + //^^^^^^^^ error: become statement outside of function body +}; +"#, + ); + } + + #[test] + fn become_in_static() { + check_diagnostics( + r#" +static _S: () = { + become 0; + //^^^^^^^^ error: become statement outside of function body + () +}; +"#, + ); + } + + #[test] + fn become_in_function_is_correct() { + check_diagnostics( + r#" +fn foo() { + if true { become (); } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 8d8ed9acaa..819b644dfa 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -79,6 +79,7 @@ mod handlers { pub(crate) mod remove_trailing_return; pub(crate) mod remove_unnecessary_else; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod return_outside_function; pub(crate) mod trait_impl_incorrect_safety; pub(crate) mod trait_impl_missing_assoc_item; pub(crate) mod trait_impl_orphan; @@ -560,6 +561,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), AnyDiagnostic::YieldOutsideCoroutine(d) => handlers::yield_outside_coroutine::yield_outside_coroutine(&ctx, &d), + AnyDiagnostic::ReturnOutsideFunction(d) => handlers::return_outside_function::return_outside_function(&ctx, &d), }; res.push(d) } |