Skip to content

Commit a2a87e5

Browse files
authored
Merge branch 'rust-lang:master' into split_assert
2 parents 080748f + 773f77b commit a2a87e5

20 files changed

Lines changed: 590 additions & 317 deletions

clippy_lints/src/methods/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,8 @@ mod unnecessary_join;
134134
mod unnecessary_lazy_eval;
135135
mod unnecessary_literal_unwrap;
136136
mod unnecessary_map_or;
137+
mod unnecessary_map_or_else;
137138
mod unnecessary_min_or_max;
138-
mod unnecessary_option_map_or_else;
139-
mod unnecessary_result_map_or_else;
140139
mod unnecessary_sort_by;
141140
mod unnecessary_to_owned;
142141
mod unwrap_expect_used;
@@ -4347,6 +4346,7 @@ declare_clippy_lint! {
43474346
}
43484347

43494348
declare_clippy_lint! {
4349+
/// ### What it does
43504350
/// Checks for usage of `.map_or_else()` "map closure" for `Option` type.
43514351
///
43524352
/// ### Why is this bad?
@@ -5449,8 +5449,7 @@ impl Methods {
54495449
},
54505450
(sym::map_or_else, [def, map]) => {
54515451
result_map_or_else_none::check(cx, expr, recv, def, map);
5452-
unnecessary_option_map_or_else::check(cx, expr, recv, def, map);
5453-
unnecessary_result_map_or_else::check(cx, expr, recv, def, map);
5452+
unnecessary_map_or_else::check(cx, expr, recv, def, map, call_span);
54545453
},
54555454
(sym::next, []) => {
54565455
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::is_expr_identity_function;
3+
use clippy_utils::res::MaybeDef;
4+
use clippy_utils::source::snippet_with_applicability;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::Expr;
7+
use rustc_lint::LateContext;
8+
use rustc_span::Span;
9+
use rustc_span::symbol::sym;
10+
11+
use super::{UNNECESSARY_OPTION_MAP_OR_ELSE, UNNECESSARY_RESULT_MAP_OR_ELSE};
12+
13+
/// lint use of `_.map_or_else(|err| err, |n| n)` for `Result`s and `Option`s.
14+
pub(super) fn check(
15+
cx: &LateContext<'_>,
16+
expr: &Expr<'_>,
17+
recv: &Expr<'_>,
18+
def_arg: &Expr<'_>,
19+
map_arg: &Expr<'_>,
20+
call_span: Span,
21+
) {
22+
let (symbol, lint) = match cx.typeck_results().expr_ty(recv).opt_diag_name(cx) {
23+
Some(x @ sym::Result) => (x, UNNECESSARY_RESULT_MAP_OR_ELSE),
24+
Some(x @ sym::Option) => (x, UNNECESSARY_OPTION_MAP_OR_ELSE),
25+
_ => return,
26+
};
27+
28+
if is_expr_identity_function(cx, map_arg) {
29+
let msg = format!("unused \"map closure\" when calling `{symbol}::map_or_else` value");
30+
31+
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
32+
let mut applicability = Applicability::MachineApplicable;
33+
let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability);
34+
let sugg = format!("unwrap_or_else({err_snippet})");
35+
36+
diag.span_suggestion_verbose(call_span, "consider using `unwrap_or_else`", sugg, applicability);
37+
});
38+
}
39+
}

clippy_lints/src/methods/unnecessary_option_map_or_else.rs

Lines changed: 0 additions & 111 deletions
This file was deleted.

clippy_lints/src/methods/unnecessary_result_map_or_else.rs

Lines changed: 0 additions & 80 deletions
This file was deleted.

clippy_lints/src/question_mark.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
501501

502502
let mut applicability = Applicability::MachineApplicable;
503503
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
504-
let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_));
504+
let parent = cx.tcx.parent_hir_node(expr.hir_id);
505+
let requires_semi = matches!(parent, Node::Stmt(_)) || cx.typeck_results().expr_ty(expr).is_unit();
505506
let method_call_str = match by_ref {
506507
ByRef::Yes(_, Mutability::Mut) => ".as_mut()",
507508
ByRef::Yes(_, Mutability::Not) => ".as_ref()",
@@ -512,7 +513,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
512513
"{receiver_str}{method_call_str}?{}",
513514
if requires_semi { ";" } else { "" }
514515
);
515-
if is_else_clause(cx.tcx, expr) {
516+
if is_else_clause(cx.tcx, expr) || (requires_semi && !matches!(parent, Node::Stmt(_) | Node::Block(_))) {
516517
sugg = format!("{{ {sugg} }}");
517518
}
518519

clippy_utils/src/lib.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,13 +1840,38 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
18401840
/// * `|[x, y]| [x, y]`
18411841
/// * `|Foo(bar, baz)| Foo(bar, baz)`
18421842
/// * `|Foo { bar, baz }| Foo { bar, baz }`
1843+
/// * `|x| { let y = x; ...; let z = y; z }`
1844+
/// * `|x| { let y = x; ...; let z = y; return z }`
18431845
///
18441846
/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.
1845-
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1847+
fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
18461848
let [param] = func.params else {
18471849
return false;
18481850
};
18491851

1852+
let mut param_pat = param.pat;
1853+
1854+
// Given a sequence of `Stmt`s of the form `let p = e` where `e` is an expr identical to the
1855+
// current `param_pat`, advance the current `param_pat` to `p`.
1856+
//
1857+
// Note: This is similar to `clippy_utils::get_last_chain_binding_hir_id`, but it works
1858+
// directly over a `Pattern` rather than a `HirId`. And it checks for compatibility via
1859+
// `is_expr_identity_of_pat` rather than `HirId` equality
1860+
let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1861+
for stmt in stmts {
1862+
if let StmtKind::Let(local) = stmt.kind
1863+
&& let Some(init) = local.init
1864+
&& is_expr_identity_of_pat(cx, param_pat, init, true)
1865+
{
1866+
param_pat = local.pat;
1867+
} else {
1868+
return false;
1869+
}
1870+
}
1871+
1872+
true
1873+
};
1874+
18501875
let mut expr = func.value;
18511876
loop {
18521877
match expr.kind {
@@ -1875,7 +1900,30 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
18751900
return false;
18761901
}
18771902
},
1878-
_ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1903+
ExprKind::Block(
1904+
&Block {
1905+
stmts, expr: Some(e), ..
1906+
},
1907+
_,
1908+
) => {
1909+
if !advance_param_pat_over_stmts(stmts) {
1910+
return false;
1911+
}
1912+
1913+
expr = e;
1914+
},
1915+
ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1916+
if let Some((last_stmt, stmts)) = stmts.split_last()
1917+
&& advance_param_pat_over_stmts(stmts)
1918+
&& let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1919+
&& let ExprKind::Ret(Some(ret_val)) = e.kind
1920+
{
1921+
expr = ret_val;
1922+
} else {
1923+
return false;
1924+
}
1925+
},
1926+
_ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
18791927
}
18801928
}
18811929
}

tests/ui/eta.fixed

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
clippy::uninlined_format_args,
1414
clippy::useless_vec,
1515
clippy::unnecessary_map_on_constructor,
16-
clippy::needless_lifetimes
16+
clippy::needless_lifetimes,
17+
clippy::unnecessary_option_map_or_else
1718
)]
1819

1920
use std::path::{Path, PathBuf};

tests/ui/eta.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
clippy::uninlined_format_args,
1414
clippy::useless_vec,
1515
clippy::unnecessary_map_on_constructor,
16-
clippy::needless_lifetimes
16+
clippy::needless_lifetimes,
17+
clippy::unnecessary_option_map_or_else
1718
)]
1819

1920
use std::path::{Path, PathBuf};

0 commit comments

Comments
 (0)