Skip to content

Commit

Permalink
Auto merge of #16460 - davidsemakula:trailing-return-diagnostic, r=Ve…
Browse files Browse the repository at this point in the history
…ykril

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: #10970
Completes: #11020
  • Loading branch information
bors committed Feb 8, 2024
2 parents e071834 + 602acfc commit e418c90
Show file tree
Hide file tree
Showing 7 changed files with 479 additions and 11 deletions.
43 changes: 43 additions & 0 deletions crates/hir-ty/src/diagnostics/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub enum BodyValidationDiagnostic {
match_expr: ExprId,
uncovered_patterns: String,
},
RemoveTrailingReturn {
return_expr: ExprId,
},
RemoveUnnecessaryElse {
if_expr: ExprId,
},
Expand Down Expand Up @@ -75,6 +78,10 @@ impl ExprValidator {
let body = db.body(self.owner);
let mut filter_map_next_checker = None;

if matches!(self.owner, DefWithBodyId::FunctionId(_)) {
self.check_for_trailing_return(body.body_expr, &body);
}

for (id, expr) in body.exprs.iter() {
if let Some((variant, missed_fields, true)) =
record_literal_missing_fields(db, &self.infer, id, expr)
Expand All @@ -93,12 +100,16 @@ impl ExprValidator {
Expr::Call { .. } | Expr::MethodCall { .. } => {
self.validate_call(db, id, expr, &mut filter_map_next_checker);
}
Expr::Closure { body: body_expr, .. } => {
self.check_for_trailing_return(*body_expr, &body);
}
Expr::If { .. } => {
self.check_for_unnecessary_else(id, expr, &body);
}
_ => {}
}
}

for (id, pat) in body.pats.iter() {
if let Some((variant, missed_fields, true)) =
record_pattern_missing_fields(db, &self.infer, id, pat)
Expand Down Expand Up @@ -244,6 +255,38 @@ impl ExprValidator {
pattern
}

fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
match &body.exprs[body_expr] {
Expr::Block { statements, tail, .. } => {
let last_stmt = tail.or_else(|| match statements.last()? {
Statement::Expr { expr, .. } => Some(*expr),
_ => None,
});
if let Some(last_stmt) = last_stmt {
self.check_for_trailing_return(last_stmt, body);
}
}
Expr::If { then_branch, else_branch, .. } => {
self.check_for_trailing_return(*then_branch, body);
if let Some(else_branch) = else_branch {
self.check_for_trailing_return(*else_branch, body);
}
}
Expr::Match { arms, .. } => {
for arm in arms.iter() {
let MatchArm { expr, .. } = arm;
self.check_for_trailing_return(*expr, body);
}
}
Expr::Return { .. } => {
self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn {
return_expr: body_expr,
});
}
_ => (),
}
}

fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, body: &Body) {
if let Expr::If { condition: _, then_branch, else_branch } = expr {
if else_branch.is_none() {
Expand Down
21 changes: 20 additions & 1 deletion crates/hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ diagnostics![
NoSuchField,
PrivateAssocItem,
PrivateField,
ReplaceFilterMapNextWithFindMap,
RemoveTrailingReturn,
RemoveUnnecessaryElse,
ReplaceFilterMapNextWithFindMap,
TraitImplIncorrectSafety,
TraitImplMissingAssocItems,
TraitImplOrphan,
Expand Down Expand Up @@ -343,6 +344,11 @@ pub struct TraitImplRedundantAssocItems {
pub assoc_item: (Name, AssocItem),
}

#[derive(Debug)]
pub struct RemoveTrailingReturn {
pub return_expr: InFile<AstPtr<ast::ReturnExpr>>,
}

#[derive(Debug)]
pub struct RemoveUnnecessaryElse {
pub if_expr: InFile<AstPtr<ast::IfExpr>>,
Expand Down Expand Up @@ -450,6 +456,19 @@ impl AnyDiagnostic {
Err(SyntheticSyntax) => (),
}
}
BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
// Filters out desugared return expressions (e.g. desugared try operators).
if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
return Some(
RemoveTrailingReturn {
return_expr: InFile::new(source_ptr.file_id, ptr),
}
.into(),
);
}
}
}
BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
if let Ok(source_ptr) = source_map.expr_syntax(if_expr) {
if let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>() {
Expand Down
Loading

0 comments on commit e418c90

Please sign in to comment.