Skip to content

Commit e418c90

Browse files
committed
Auto merge of rust-lang#16460 - davidsemakula:trailing-return-diagnostic, r=Veykril
feat: Add diagnostic with fix to replace trailing `return <val>;` with `<val>` Works for functions and closures. Ignores desugared return expressions (e.g. from desugared try operators). Fixes: rust-lang#10970 Completes: rust-lang#11020
2 parents e071834 + 602acfc commit e418c90

File tree

7 files changed

+479
-11
lines changed

7 files changed

+479
-11
lines changed

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub enum BodyValidationDiagnostic {
4444
match_expr: ExprId,
4545
uncovered_patterns: String,
4646
},
47+
RemoveTrailingReturn {
48+
return_expr: ExprId,
49+
},
4750
RemoveUnnecessaryElse {
4851
if_expr: ExprId,
4952
},
@@ -75,6 +78,10 @@ impl ExprValidator {
7578
let body = db.body(self.owner);
7679
let mut filter_map_next_checker = None;
7780

81+
if matches!(self.owner, DefWithBodyId::FunctionId(_)) {
82+
self.check_for_trailing_return(body.body_expr, &body);
83+
}
84+
7885
for (id, expr) in body.exprs.iter() {
7986
if let Some((variant, missed_fields, true)) =
8087
record_literal_missing_fields(db, &self.infer, id, expr)
@@ -93,12 +100,16 @@ impl ExprValidator {
93100
Expr::Call { .. } | Expr::MethodCall { .. } => {
94101
self.validate_call(db, id, expr, &mut filter_map_next_checker);
95102
}
103+
Expr::Closure { body: body_expr, .. } => {
104+
self.check_for_trailing_return(*body_expr, &body);
105+
}
96106
Expr::If { .. } => {
97107
self.check_for_unnecessary_else(id, expr, &body);
98108
}
99109
_ => {}
100110
}
101111
}
112+
102113
for (id, pat) in body.pats.iter() {
103114
if let Some((variant, missed_fields, true)) =
104115
record_pattern_missing_fields(db, &self.infer, id, pat)
@@ -244,6 +255,38 @@ impl ExprValidator {
244255
pattern
245256
}
246257

258+
fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
259+
match &body.exprs[body_expr] {
260+
Expr::Block { statements, tail, .. } => {
261+
let last_stmt = tail.or_else(|| match statements.last()? {
262+
Statement::Expr { expr, .. } => Some(*expr),
263+
_ => None,
264+
});
265+
if let Some(last_stmt) = last_stmt {
266+
self.check_for_trailing_return(last_stmt, body);
267+
}
268+
}
269+
Expr::If { then_branch, else_branch, .. } => {
270+
self.check_for_trailing_return(*then_branch, body);
271+
if let Some(else_branch) = else_branch {
272+
self.check_for_trailing_return(*else_branch, body);
273+
}
274+
}
275+
Expr::Match { arms, .. } => {
276+
for arm in arms.iter() {
277+
let MatchArm { expr, .. } = arm;
278+
self.check_for_trailing_return(*expr, body);
279+
}
280+
}
281+
Expr::Return { .. } => {
282+
self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn {
283+
return_expr: body_expr,
284+
});
285+
}
286+
_ => (),
287+
}
288+
}
289+
247290
fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, body: &Body) {
248291
if let Expr::If { condition: _, then_branch, else_branch } = expr {
249292
if else_branch.is_none() {

crates/hir/src/diagnostics.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ diagnostics![
6767
NoSuchField,
6868
PrivateAssocItem,
6969
PrivateField,
70-
ReplaceFilterMapNextWithFindMap,
70+
RemoveTrailingReturn,
7171
RemoveUnnecessaryElse,
72+
ReplaceFilterMapNextWithFindMap,
7273
TraitImplIncorrectSafety,
7374
TraitImplMissingAssocItems,
7475
TraitImplOrphan,
@@ -343,6 +344,11 @@ pub struct TraitImplRedundantAssocItems {
343344
pub assoc_item: (Name, AssocItem),
344345
}
345346

347+
#[derive(Debug)]
348+
pub struct RemoveTrailingReturn {
349+
pub return_expr: InFile<AstPtr<ast::ReturnExpr>>,
350+
}
351+
346352
#[derive(Debug)]
347353
pub struct RemoveUnnecessaryElse {
348354
pub if_expr: InFile<AstPtr<ast::IfExpr>>,
@@ -450,6 +456,19 @@ impl AnyDiagnostic {
450456
Err(SyntheticSyntax) => (),
451457
}
452458
}
459+
BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
460+
if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
461+
// Filters out desugared return expressions (e.g. desugared try operators).
462+
if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
463+
return Some(
464+
RemoveTrailingReturn {
465+
return_expr: InFile::new(source_ptr.file_id, ptr),
466+
}
467+
.into(),
468+
);
469+
}
470+
}
471+
}
453472
BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
454473
if let Ok(source_ptr) = source_map.expr_syntax(if_expr) {
455474
if let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>() {

0 commit comments

Comments
 (0)