Skip to content

Commit 8eafcb4

Browse files
committed
manual_is_variant_of: detect .map_or(false, f) as well
1 parent 753629b commit 8eafcb4

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
lines changed

clippy_lints/src/methods/manual_is_variant_and.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub(super) fn check<'tcx>(
1515
map_arg: &'tcx rustc_hir::Expr<'_>,
1616
map_span: Span,
1717
msrv: &Msrv,
18+
is_map_or: bool,
1819
) {
1920
// Don't lint if:
2021

@@ -23,15 +24,15 @@ pub(super) fn check<'tcx>(
2324
return;
2425
}
2526

26-
// 2. the caller of `map()` is neither `Option` nor `Result`
27+
// 2. the caller of `map()` or `map_or()` is neither `Option` nor `Result`
2728
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option);
2829
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result);
2930
if !is_option && !is_result {
3031
return;
3132
}
3233

3334
// 3. the caller of `unwrap_or_default` is neither `Option<bool>` nor `Result<bool, _>`
34-
if !cx.typeck_results().expr_ty(expr).is_bool() {
35+
if !is_map_or && !cx.typeck_results().expr_ty(expr).is_bool() {
3536
return;
3637
}
3738

@@ -40,12 +41,17 @@ pub(super) fn check<'tcx>(
4041
return;
4142
}
4243

43-
let lint_msg = if is_option {
44-
"called `map(<f>).unwrap_or_default()` on an `Option` value"
44+
let call_msg = if is_map_or {
45+
"map_or(false, <f>)"
4546
} else {
46-
"called `map(<f>).unwrap_or_default()` on a `Result` value"
47+
"map(<f>).unwrap_or_default()"
4748
};
48-
let suggestion = if is_option { "is_some_and" } else { "is_ok_and" };
49+
let (ty, suggestion) = if is_option {
50+
("an `Option`", "is_some_and")
51+
} else {
52+
("a `Result`", "is_ok_and")
53+
};
54+
let lint_msg = format!("called `{call_msg}` on {ty} value");
4955

5056
span_lint_and_sugg(
5157
cx,

clippy_lints/src/methods/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ use clippy_utils::macros::FormatArgsStorage;
140140
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
141141
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
142142
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
143+
use rustc_ast::ast;
143144
use rustc_data_structures::fx::FxHashSet;
144145
use rustc_hir as hir;
145146
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
@@ -3889,6 +3890,8 @@ declare_clippy_lint! {
38893890
/// # let result: Result<usize, ()> = Ok(1);
38903891
/// option.map(|a| a > 10).unwrap_or_default();
38913892
/// result.map(|a| a > 10).unwrap_or_default();
3893+
/// option.map_or(false, |a| a > 10);
3894+
/// result.map_or(false, |a| a > 10);
38923895
/// ```
38933896
/// Use instead:
38943897
/// ```no_run
@@ -3900,7 +3903,7 @@ declare_clippy_lint! {
39003903
#[clippy::version = "1.77.0"]
39013904
pub MANUAL_IS_VARIANT_AND,
39023905
pedantic,
3903-
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
3906+
"calling `.map(f).unwrap_or_default()` or `.map_or(false, f)` instead of `is_some_and(f)` or `is_ok_and(f)`"
39043907
}
39053908

39063909
declare_clippy_lint! {
@@ -4839,6 +4842,11 @@ impl Methods {
48394842
option_map_or_none::check(cx, expr, recv, def, map);
48404843
manual_ok_or::check(cx, expr, recv, def, map);
48414844
option_map_or_err_ok::check(cx, expr, recv, def, map);
4845+
if let ExprKind::Lit(lit) = def.kind
4846+
&& lit.node == ast::LitKind::Bool(false)
4847+
{
4848+
manual_is_variant_and::check(cx, expr, recv, map, span, &self.msrv, true);
4849+
}
48424850
},
48434851
("map_or_else", [def, map]) => {
48444852
result_map_or_else_none::check(cx, expr, recv, def, map);
@@ -5052,7 +5060,7 @@ impl Methods {
50525060
},
50535061
("unwrap_or_default", []) => {
50545062
if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) {
5055-
manual_is_variant_and::check(cx, expr, m_recv, arg, span, &self.msrv);
5063+
manual_is_variant_and::check(cx, expr, m_recv, arg, span, &self.msrv, false);
50565064
}
50575065
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
50585066
},

tests/ui/manual_is_variant_and.fixed

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ fn option_methods() {
1919
let _ = opt
2020
.is_some_and(|x| x > 1);
2121

22+
let _ = opt.is_some_and(|x| x > 1);
23+
2224
// won't fix because the return type of the closure is not `bool`
2325
let _ = opt.map(|x| x + 1).unwrap_or_default();
2426

@@ -37,6 +39,8 @@ fn result_methods() {
3739
});
3840
let _ = res.is_ok_and(|x| x > 1);
3941

42+
let _ = res.is_ok_and(|x| x > 1);
43+
4044
// won't fix because the return type of the closure is not `bool`
4145
let _ = res.map(|x| x + 1).unwrap_or_default();
4246

tests/ui/manual_is_variant_and.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ fn option_methods() {
2323
.map(|x| x > 1)
2424
.unwrap_or_default();
2525

26+
let _ = opt.map_or(false, |x| x > 1);
27+
2628
// won't fix because the return type of the closure is not `bool`
2729
let _ = opt.map(|x| x + 1).unwrap_or_default();
2830

@@ -43,6 +45,8 @@ fn result_methods() {
4345
let _ = res.map(|x| x > 1)
4446
.unwrap_or_default();
4547

48+
let _ = res.map_or(false, |x| x > 1);
49+
4650
// won't fix because the return type of the closure is not `bool`
4751
let _ = res.map(|x| x + 1).unwrap_or_default();
4852

tests/ui/manual_is_variant_and.stderr

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,20 @@ LL | .map(|x| x > 1)
4141
LL | | .unwrap_or_default();
4242
| |____________________________^ help: use: `is_some_and(|x| x > 1)`
4343

44+
error: called `map_or(false, <f>)` on an `Option` value
45+
--> tests/ui/manual_is_variant_and.rs:26:17
46+
|
47+
LL | let _ = opt.map_or(false, |x| x > 1);
48+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)`
49+
4450
error: called `map(<f>).unwrap_or_default()` on an `Option` value
45-
--> tests/ui/manual_is_variant_and.rs:30:18
51+
--> tests/ui/manual_is_variant_and.rs:32:18
4652
|
4753
LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
4854
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)`
4955

5056
error: called `map(<f>).unwrap_or_default()` on a `Result` value
51-
--> tests/ui/manual_is_variant_and.rs:39:17
57+
--> tests/ui/manual_is_variant_and.rs:41:17
5258
|
5359
LL | let _ = res.map(|x| {
5460
| _________________^
@@ -65,18 +71,24 @@ LL ~ });
6571
|
6672

6773
error: called `map(<f>).unwrap_or_default()` on a `Result` value
68-
--> tests/ui/manual_is_variant_and.rs:43:17
74+
--> tests/ui/manual_is_variant_and.rs:45:17
6975
|
7076
LL | let _ = res.map(|x| x > 1)
7177
| _________________^
7278
LL | | .unwrap_or_default();
7379
| |____________________________^ help: use: `is_ok_and(|x| x > 1)`
7480

81+
error: called `map_or(false, <f>)` on a `Result` value
82+
--> tests/ui/manual_is_variant_and.rs:48:17
83+
|
84+
LL | let _ = res.map_or(false, |x| x > 1);
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(|x| x > 1)`
86+
7587
error: called `map(<f>).unwrap_or_default()` on a `Result` value
76-
--> tests/ui/manual_is_variant_and.rs:50:18
88+
--> tests/ui/manual_is_variant_and.rs:54:18
7789
|
7890
LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
7991
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)`
8092

81-
error: aborting due to 8 previous errors
93+
error: aborting due to 10 previous errors
8294

0 commit comments

Comments
 (0)