Skip to content

Commit 7d5fff4

Browse files
Merge pull request rust-lang#22813 from Yashb404/master
feat: Add handler for E0572
2 parents 78fe740 + d777f0c commit 7d5fff4

6 files changed

Lines changed: 135 additions & 7 deletions

File tree

crates/hir-ty/src/infer.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,18 @@ pub enum InferenceDiagnostic {
524524
#[type_visitable(ignore)]
525525
expr: ExprId,
526526
},
527+
ReturnOutsideFunction {
528+
#[type_visitable(ignore)]
529+
expr: ExprId,
530+
#[type_visitable(ignore)]
531+
kind: ReturnKind,
532+
},
533+
}
534+
535+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
536+
pub enum ReturnKind {
537+
ReturnExpr,
538+
BecomeExpr,
527539
}
528540

529541
#[derive(Debug, PartialEq, Eq, Clone)]

crates/hir-ty/src/infer/expr.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use crate::{
4343
};
4444

4545
use super::{
46-
BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
46+
BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, ReturnKind,
4747
cast::CastCheck, find_breakable,
4848
};
4949

@@ -576,7 +576,7 @@ impl<'db> InferenceContext<'_, 'db> {
576576
self.types.types.never
577577
}
578578
&Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr),
579-
&Expr::Become { expr } => self.infer_expr_become(expr),
579+
&Expr::Become { expr } => self.infer_expr_become(tgt_expr, expr),
580580
Expr::Yield { expr } => {
581581
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys {
582582
if let Some(expr) = expr {
@@ -1454,7 +1454,10 @@ impl<'db> InferenceContext<'_, 'db> {
14541454
}
14551455
}
14561456
None => {
1457-
// FIXME: diagnose return outside of function
1457+
self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction {
1458+
expr: ret,
1459+
kind: ReturnKind::ReturnExpr,
1460+
});
14581461
if let Some(expr) = expr {
14591462
self.infer_expr_no_expect(expr, ExprIsRead::Yes);
14601463
}
@@ -1463,7 +1466,7 @@ impl<'db> InferenceContext<'_, 'db> {
14631466
self.types.types.never
14641467
}
14651468

1466-
fn infer_expr_become(&mut self, expr: ExprId) -> Ty<'db> {
1469+
fn infer_expr_become(&mut self, tgt_expr: ExprId, expr: ExprId) -> Ty<'db> {
14671470
match &self.return_coercion {
14681471
Some(return_coercion) => {
14691472
let ret_ty = return_coercion.expected_ty();
@@ -1476,7 +1479,10 @@ impl<'db> InferenceContext<'_, 'db> {
14761479
_ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty);
14771480
}
14781481
None => {
1479-
// FIXME: diagnose `become` outside of functions
1482+
self.push_diagnostic(InferenceDiagnostic::ReturnOutsideFunction {
1483+
expr: tgt_expr,
1484+
kind: ReturnKind::BecomeExpr,
1485+
});
14801486
self.infer_expr_no_expect(expr, ExprIsRead::Yes);
14811487
}
14821488
}

crates/hir-ty/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub use autoderef::autoderef;
106106
pub use infer::{
107107
Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind,
108108
InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref,
109-
PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply,
109+
PointerCast, ReturnKind, cast::CastError, could_coerce, could_unify, could_unify_deeply,
110110
infer_query_with_inspect,
111111
};
112112
pub use lower::{

crates/hir/src/diagnostics.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwne
3636

3737
pub use hir_def::VariantId;
3838
pub use hir_ty::{
39-
GenericArgsProhibitedReason, IncorrectGenericsLenKind,
39+
GenericArgsProhibitedReason, IncorrectGenericsLenKind, ReturnKind,
4040
diagnostics::{CaseType, IncorrectCase},
4141
};
4242

@@ -181,6 +181,7 @@ diagnostics![AnyDiagnostic<'db> ->
181181
UnionPatHasRest,
182182
UnimplementedTrait<'db>,
183183
YieldOutsideCoroutine,
184+
ReturnOutsideFunction,
184185
];
185186

186187
#[derive(Debug)]
@@ -689,6 +690,12 @@ pub struct YieldOutsideCoroutine {
689690
pub expr: InFile<ExprOrPatPtr>,
690691
}
691692

693+
#[derive(Debug)]
694+
pub struct ReturnOutsideFunction {
695+
pub expr: InFile<ExprOrPatPtr>,
696+
pub kind: ReturnKind,
697+
}
698+
692699
impl<'db> AnyDiagnostic<'db> {
693700
pub(crate) fn body_validation_diagnostic(
694701
db: &'db dyn HirDatabase,
@@ -1131,6 +1138,9 @@ impl<'db> AnyDiagnostic<'db> {
11311138
&InferenceDiagnostic::YieldOutsideCoroutine { expr } => {
11321139
YieldOutsideCoroutine { expr: expr_syntax(expr)? }.into()
11331140
}
1141+
&InferenceDiagnostic::ReturnOutsideFunction { expr, kind } => {
1142+
ReturnOutsideFunction { expr: expr_syntax(expr)?, kind }.into()
1143+
}
11341144
})
11351145
}
11361146

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
2+
3+
// Diagnostic: return-outside-of-function
4+
//
5+
// This diagnostic triggers if return or become is used outside of a function body.
6+
pub(crate) fn return_outside_function(
7+
ctx: &DiagnosticsContext<'_, '_>,
8+
d: &hir::ReturnOutsideFunction,
9+
) -> Diagnostic {
10+
let construct = match d.kind {
11+
hir::ReturnKind::ReturnExpr => "return",
12+
hir::ReturnKind::BecomeExpr => "become",
13+
};
14+
Diagnostic::new_with_syntax_node_ptr(
15+
ctx,
16+
DiagnosticCode::RustcHardError("E0572"),
17+
format!("{construct} statement outside of function body"),
18+
d.expr.map(|it| it.into()),
19+
)
20+
}
21+
22+
#[cfg(test)]
23+
mod tests {
24+
use crate::tests::check_diagnostics;
25+
26+
#[test]
27+
fn return_in_const() {
28+
check_diagnostics(
29+
r#"
30+
const _: () = {
31+
return;
32+
//^^^^^^ error: return statement outside of function body
33+
};
34+
"#,
35+
);
36+
}
37+
38+
#[test]
39+
fn return_in_static() {
40+
check_diagnostics(
41+
r#"
42+
static _S: i32 = {
43+
return 0;
44+
//^^^^^^^^ error: return statement outside of function body
45+
0
46+
};
47+
"#,
48+
);
49+
}
50+
51+
#[test]
52+
fn return_in_function_is_correct() {
53+
check_diagnostics(
54+
r#"
55+
fn foo() -> i32 {
56+
if true { return 42; }
57+
0
58+
}
59+
"#,
60+
);
61+
}
62+
63+
#[test]
64+
fn become_in_const() {
65+
check_diagnostics(
66+
r#"
67+
const _: () = {
68+
become 0;
69+
//^^^^^^^^ error: become statement outside of function body
70+
};
71+
"#,
72+
);
73+
}
74+
75+
#[test]
76+
fn become_in_static() {
77+
check_diagnostics(
78+
r#"
79+
static _S: () = {
80+
become 0;
81+
//^^^^^^^^ error: become statement outside of function body
82+
()
83+
};
84+
"#,
85+
);
86+
}
87+
88+
#[test]
89+
fn become_in_function_is_correct() {
90+
check_diagnostics(
91+
r#"
92+
fn foo() {
93+
if true { become (); }
94+
}
95+
"#,
96+
);
97+
}
98+
}

crates/ide-diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ mod handlers {
7979
pub(crate) mod remove_trailing_return;
8080
pub(crate) mod remove_unnecessary_else;
8181
pub(crate) mod replace_filter_map_next_with_find_map;
82+
pub(crate) mod return_outside_function;
8283
pub(crate) mod trait_impl_incorrect_safety;
8384
pub(crate) mod trait_impl_missing_assoc_item;
8485
pub(crate) mod trait_impl_orphan;
@@ -560,6 +561,7 @@ pub fn semantic_diagnostics(
560561
AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d),
561562
AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d),
562563
AnyDiagnostic::YieldOutsideCoroutine(d) => handlers::yield_outside_coroutine::yield_outside_coroutine(&ctx, &d),
564+
AnyDiagnostic::ReturnOutsideFunction(d) => handlers::return_outside_function::return_outside_function(&ctx, &d),
563565
};
564566
res.push(d)
565567
}

0 commit comments

Comments
 (0)