Skip to content

Commit a8eb936

Browse files
committed
extract let_and_return
1 parent a35500e commit a8eb936

File tree

2 files changed

+91
-85
lines changed

2 files changed

+91
-85
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use clippy_utils::diagnostics::span_lint_hir_and_then;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::sugg::has_enclosing_paren;
4+
use clippy_utils::visitors::for_each_expr;
5+
use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg};
6+
use core::ops::ControlFlow;
7+
use rustc_errors::Applicability;
8+
use rustc_hir::{Block, Expr, PatKind, StmtKind};
9+
use rustc_lint::{LateContext, LintContext};
10+
use rustc_middle::ty::GenericArgKind;
11+
use rustc_span::edition::Edition;
12+
13+
use super::LET_AND_RETURN;
14+
15+
pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
16+
// we need both a let-binding stmt and an expr
17+
if let Some(retexpr) = block.expr
18+
&& let Some(stmt) = block.stmts.last()
19+
&& let StmtKind::Let(local) = &stmt.kind
20+
&& local.ty.is_none()
21+
&& cx.tcx.hir_attrs(local.hir_id).is_empty()
22+
&& let Some(initexpr) = &local.init
23+
&& let PatKind::Binding(_, local_id, _, _) = local.pat.kind
24+
&& path_to_local_id(retexpr, local_id)
25+
&& (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr))
26+
&& !initexpr.span.in_external_macro(cx.sess().source_map())
27+
&& !retexpr.span.in_external_macro(cx.sess().source_map())
28+
&& !local.span.from_expansion()
29+
&& !span_contains_cfg(cx, stmt.span.between(retexpr.span))
30+
{
31+
span_lint_hir_and_then(
32+
cx,
33+
LET_AND_RETURN,
34+
retexpr.hir_id,
35+
retexpr.span,
36+
"returning the result of a `let` binding from a block",
37+
|err| {
38+
err.span_label(local.span, "unnecessary `let` binding");
39+
40+
if let Some(src) = initexpr.span.get_source_text(cx) {
41+
let sugg = if binary_expr_needs_parentheses(initexpr) {
42+
if has_enclosing_paren(&src) {
43+
src.to_owned()
44+
} else {
45+
format!("({src})")
46+
}
47+
} else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
48+
if has_enclosing_paren(&src) {
49+
format!("{src} as _")
50+
} else {
51+
format!("({src}) as _")
52+
}
53+
} else {
54+
src.to_owned()
55+
};
56+
err.multipart_suggestion(
57+
"return the expression directly",
58+
vec![(local.span, String::new()), (retexpr.span, sugg)],
59+
Applicability::MachineApplicable,
60+
);
61+
} else {
62+
err.span_help(initexpr.span, "this expression can be directly returned");
63+
}
64+
},
65+
);
66+
}
67+
}
68+
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
69+
for_each_expr(cx, expr, |e| {
70+
if let Some(def_id) = fn_def_id(cx, e)
71+
&& cx
72+
.tcx
73+
.fn_sig(def_id)
74+
.instantiate_identity()
75+
.skip_binder()
76+
.output()
77+
.walk()
78+
.any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
79+
{
80+
ControlFlow::Break(())
81+
} else {
82+
ControlFlow::Continue(())
83+
}
84+
})
85+
.is_some()
86+
}

clippy_lints/src/returns/mod.rs

Lines changed: 5 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
1-
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
2-
use clippy_utils::source::SpanRangeExt;
3-
use clippy_utils::sugg::has_enclosing_paren;
4-
use clippy_utils::visitors::for_each_expr;
5-
use clippy_utils::{
6-
binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
7-
path_to_local_id, span_contains_cfg,
8-
};
9-
use core::ops::ControlFlow;
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};
103
use rustc_errors::Applicability;
114
use rustc_hir::LangItem::ResultErr;
125
use rustc_hir::intravisit::FnKind;
13-
use rustc_hir::{
14-
Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, MatchSource, Node, OwnerNode, PatKind, Stmt, StmtKind,
15-
};
6+
use rustc_hir::{Block, Body, ExprKind, FnDecl, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind};
167
use rustc_lint::{LateContext, LateLintPass, LintContext};
17-
use rustc_middle::ty::GenericArgKind;
188
use rustc_middle::ty::adjustment::Adjust;
199
use rustc_session::declare_lint_pass;
2010
use rustc_span::Span;
2111
use rustc_span::def_id::LocalDefId;
22-
use rustc_span::edition::Edition;
2312

13+
mod let_and_return;
2414
mod needless_return;
2515

2616
declare_clippy_lint! {
@@ -184,57 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
184174
}
185175

186176
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
187-
// we need both a let-binding stmt and an expr
188-
if let Some(retexpr) = block.expr
189-
&& let Some(stmt) = block.stmts.last()
190-
&& let StmtKind::Let(local) = &stmt.kind
191-
&& local.ty.is_none()
192-
&& cx.tcx.hir_attrs(local.hir_id).is_empty()
193-
&& let Some(initexpr) = &local.init
194-
&& let PatKind::Binding(_, local_id, _, _) = local.pat.kind
195-
&& path_to_local_id(retexpr, local_id)
196-
&& (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr))
197-
&& !initexpr.span.in_external_macro(cx.sess().source_map())
198-
&& !retexpr.span.in_external_macro(cx.sess().source_map())
199-
&& !local.span.from_expansion()
200-
&& !span_contains_cfg(cx, stmt.span.between(retexpr.span))
201-
{
202-
span_lint_hir_and_then(
203-
cx,
204-
LET_AND_RETURN,
205-
retexpr.hir_id,
206-
retexpr.span,
207-
"returning the result of a `let` binding from a block",
208-
|err| {
209-
err.span_label(local.span, "unnecessary `let` binding");
210-
211-
if let Some(src) = initexpr.span.get_source_text(cx) {
212-
let sugg = if binary_expr_needs_parentheses(initexpr) {
213-
if has_enclosing_paren(&src) {
214-
src.to_owned()
215-
} else {
216-
format!("({src})")
217-
}
218-
} else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
219-
if has_enclosing_paren(&src) {
220-
format!("{src} as _")
221-
} else {
222-
format!("({src}) as _")
223-
}
224-
} else {
225-
src.to_owned()
226-
};
227-
err.multipart_suggestion(
228-
"return the expression directly",
229-
vec![(local.span, String::new()), (retexpr.span, sugg)],
230-
Applicability::MachineApplicable,
231-
);
232-
} else {
233-
err.span_help(initexpr.span, "this expression can be directly returned");
234-
}
235-
},
236-
);
237-
}
177+
let_and_return::check_block(cx, block)
238178
}
239179

240180
fn check_fn(
@@ -249,23 +189,3 @@ impl<'tcx> LateLintPass<'tcx> for Return {
249189
needless_return::check_fn(cx, kind, body, sp);
250190
}
251191
}
252-
253-
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
254-
for_each_expr(cx, expr, |e| {
255-
if let Some(def_id) = fn_def_id(cx, e)
256-
&& cx
257-
.tcx
258-
.fn_sig(def_id)
259-
.instantiate_identity()
260-
.skip_binder()
261-
.output()
262-
.walk()
263-
.any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
264-
{
265-
ControlFlow::Break(())
266-
} else {
267-
ControlFlow::Continue(())
268-
}
269-
})
270-
.is_some()
271-
}

0 commit comments

Comments
 (0)