Skip to content

Commit 51e6e9e

Browse files
committed
extract needless_return_with_question_mark
1 parent a8eb936 commit 51e6e9e

File tree

2 files changed

+64
-55
lines changed

2 files changed

+64
-55
lines changed

clippy_lints/src/returns/mod.rs

Lines changed: 4 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res};
3-
use rustc_errors::Applicability;
4-
use rustc_hir::LangItem::ResultErr;
51
use rustc_hir::intravisit::FnKind;
6-
use rustc_hir::{Block, Body, ExprKind, FnDecl, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind};
7-
use rustc_lint::{LateContext, LateLintPass, LintContext};
8-
use rustc_middle::ty::adjustment::Adjust;
2+
use rustc_hir::{Block, Body, FnDecl, Stmt};
3+
use rustc_lint::{LateContext, LateLintPass};
94
use rustc_session::declare_lint_pass;
105
use rustc_span::Span;
116
use rustc_span::def_id::LocalDefId;
127

138
mod let_and_return;
149
mod needless_return;
10+
mod needless_return_with_question_mark;
1511

1612
declare_clippy_lint! {
1713
/// ### What it does
@@ -121,56 +117,9 @@ declare_clippy_lint! {
121117

122118
declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]);
123119

124-
/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This
125-
/// is the case when the enclosing block expression is coerced to some other type, which only works
126-
/// because of the never-ness of `return` expressions
127-
fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool {
128-
cx.tcx
129-
.hir_parent_iter(stmt_hir_id)
130-
.find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None })
131-
.is_some_and(|e| {
132-
cx.typeck_results()
133-
.expr_adjustments(e)
134-
.iter()
135-
.any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny))
136-
})
137-
}
138-
139120
impl<'tcx> LateLintPass<'tcx> for Return {
140121
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
141-
if !stmt.span.in_external_macro(cx.sess().source_map())
142-
&& let StmtKind::Semi(expr) = stmt.kind
143-
&& let ExprKind::Ret(Some(ret)) = expr.kind
144-
// return Err(...)? desugars to a match
145-
// over a Err(...).branch()
146-
// which breaks down to a branch call, with the callee being
147-
// the constructor of the Err variant
148-
&& let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind
149-
&& let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind
150-
&& let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind
151-
&& is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
152-
153-
// Ensure this is not the final stmt, otherwise removing it would cause a compile error
154-
&& let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
155-
&& let ItemKind::Fn { body, .. } = item.kind
156-
&& let block = cx.tcx.hir_body(body).value
157-
&& let ExprKind::Block(block, _) = block.kind
158-
&& !is_inside_let_else(cx.tcx, expr)
159-
&& let [.., final_stmt] = block.stmts
160-
&& final_stmt.hir_id != stmt.hir_id
161-
&& !is_from_proc_macro(cx, expr)
162-
&& !stmt_needs_never_type(cx, stmt.hir_id)
163-
{
164-
span_lint_and_sugg(
165-
cx,
166-
NEEDLESS_RETURN_WITH_QUESTION_MARK,
167-
expr.span.until(ret.span),
168-
"unneeded `return` statement with `?` operator",
169-
"remove it",
170-
String::new(),
171-
Applicability::MachineApplicable,
172-
);
173-
}
122+
needless_return_with_question_mark::check_stmt(cx, stmt);
174123
}
175124

176125
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res};
3+
use rustc_errors::Applicability;
4+
use rustc_hir::LangItem::ResultErr;
5+
use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind};
6+
use rustc_lint::{LateContext, LintContext};
7+
use rustc_middle::ty::adjustment::Adjust;
8+
9+
use super::NEEDLESS_RETURN_WITH_QUESTION_MARK;
10+
11+
pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
12+
if !stmt.span.in_external_macro(cx.sess().source_map())
13+
&& let StmtKind::Semi(expr) = stmt.kind
14+
&& let ExprKind::Ret(Some(ret)) = expr.kind
15+
// return Err(...)? desugars to a match
16+
// over a Err(...).branch()
17+
// which breaks down to a branch call, with the callee being
18+
// the constructor of the Err variant
19+
&& let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind
20+
&& let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind
21+
&& let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind
22+
&& is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
23+
24+
// Ensure this is not the final stmt, otherwise removing it would cause a compile error
25+
&& let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
26+
&& let ItemKind::Fn { body, .. } = item.kind
27+
&& let block = cx.tcx.hir_body(body).value
28+
&& let ExprKind::Block(block, _) = block.kind
29+
&& !is_inside_let_else(cx.tcx, expr)
30+
&& let [.., final_stmt] = block.stmts
31+
&& final_stmt.hir_id != stmt.hir_id
32+
&& !is_from_proc_macro(cx, expr)
33+
&& !stmt_needs_never_type(cx, stmt.hir_id)
34+
{
35+
span_lint_and_sugg(
36+
cx,
37+
NEEDLESS_RETURN_WITH_QUESTION_MARK,
38+
expr.span.until(ret.span),
39+
"unneeded `return` statement with `?` operator",
40+
"remove it",
41+
String::new(),
42+
Applicability::MachineApplicable,
43+
);
44+
}
45+
}
46+
47+
/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed.
48+
/// This is the case when the enclosing block expression is coerced to some other type,
49+
/// which only works because of the never-ness of `return` expressions
50+
fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool {
51+
cx.tcx
52+
.hir_parent_iter(stmt_hir_id)
53+
.find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None })
54+
.is_some_and(|e| {
55+
cx.typeck_results()
56+
.expr_adjustments(e)
57+
.iter()
58+
.any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny))
59+
})
60+
}

0 commit comments

Comments
 (0)