Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22813 from Yashb404/master
feat: Add handler for E0572
Chayim Refael Friedman 4 weeks ago
parent 78fe740 · parent d777f0c · commit 7d5fff4
-rw-r--r--crates/hir-ty/src/infer.rs12
-rw-r--r--crates/hir-ty/src/infer/expr.rs16
-rw-r--r--crates/hir-ty/src/lib.rs2
-rw-r--r--crates/hir/src/diagnostics.rs12
-rw-r--r--crates/ide-diagnostics/src/handlers/return_outside_function.rs98
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
6 files changed, 135 insertions, 7 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index d103bacb8f..ebcc0bd3f6 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)
}