Skip to content

Commit b373248

Browse files
author
Guanqun Lu
committed
lint: add map(f).unwrap_or(a) for Result
1 parent b4f1769 commit b373248

File tree

4 files changed

+79
-21
lines changed

4 files changed

+79
-21
lines changed

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
611611
&methods::OPTION_UNWRAP_USED,
612612
&methods::OR_FUN_CALL,
613613
&methods::RESULT_EXPECT_USED,
614+
&methods::RESULT_MAP_UNWRAP_OR,
614615
&methods::RESULT_MAP_UNWRAP_OR_ELSE,
615616
&methods::RESULT_UNWRAP_USED,
616617
&methods::SEARCH_IS_SOME,
@@ -1014,6 +1015,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
10141015
LintId::of(&methods::MAP_FLATTEN),
10151016
LintId::of(&methods::OPTION_MAP_UNWRAP_OR),
10161017
LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE),
1018+
LintId::of(&methods::RESULT_MAP_UNWRAP_OR),
10171019
LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE),
10181020
LintId::of(&misc::USED_UNDERSCORE_BINDING),
10191021
LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX),

clippy_lints/src/methods/option_map_unwrap_or.rs renamed to clippy_lints/src/methods/map_unwrap_or.rs

+47-17
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ use rustc::lint::LateContext;
66
use rustc_data_structures::fx::FxHashSet;
77
use syntax_pos::symbol::Symbol;
88

9-
use super::OPTION_MAP_UNWRAP_OR;
9+
use super::{OPTION_MAP_UNWRAP_OR, RESULT_MAP_UNWRAP_OR};
1010

11-
/// lint use of `map().unwrap_or()` for `Option`s
11+
/// lint use of `map().unwrap_or()` for `Option`s and `Result`s
1212
pub(super) fn lint<'a, 'tcx>(
1313
cx: &LateContext<'a, 'tcx>,
1414
expr: &hir::Expr,
1515
map_args: &'tcx [hir::Expr],
1616
unwrap_args: &'tcx [hir::Expr],
1717
) {
18-
// lint if the caller of `map()` is an `Option`
19-
if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) {
18+
let is_option = match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION);
19+
let is_result = match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::RESULT);
20+
21+
if is_option || is_result {
2022
if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) {
2123
// Do not lint if the `map` argument uses identifiers in the `map`
2224
// argument that are also used in the `unwrap_or` argument
@@ -43,19 +45,27 @@ pub(super) fn lint<'a, 'tcx>(
4345
let map_snippet = snippet(cx, map_args[1].span, "..");
4446
let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
4547
// lint message
46-
// comparing the snippet from source to raw text ("None") below is safe
47-
// because we already have checked the type.
48-
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
49-
let suggest = if unwrap_snippet == "None" {
50-
"and_then(f)"
48+
let msg = if is_option {
49+
// comparing the snippet from source to raw text ("None") below is safe
50+
// because we already have checked the type.
51+
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
52+
let suggest = if unwrap_snippet == "None" {
53+
"and_then(f)"
54+
} else {
55+
"map_or(a, f)"
56+
};
57+
58+
format!(
59+
"called `map(f).unwrap_or({})` on an Option value. \
60+
This can be done more directly by calling `{}` instead",
61+
arg, suggest
62+
)
5163
} else {
52-
"map_or(a, f)"
64+
"called `map(f).unwrap_or(a)` on a Result value. \
65+
This can be done more directly by calling `map_or(a, f)` instead"
66+
.to_string()
5367
};
54-
let msg = &format!(
55-
"called `map(f).unwrap_or({})` on an Option value. \
56-
This can be done more directly by calling `{}` instead",
57-
arg, suggest
58-
);
68+
5969
// lint, with note if neither arg is > 1 line and both map() and
6070
// unwrap_or() have the same span
6171
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
@@ -70,9 +80,29 @@ pub(super) fn lint<'a, 'tcx>(
7080
"replace `map({}).unwrap_or({})` with `{}`",
7181
map_snippet, unwrap_snippet, suggest
7282
);
73-
span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, &note);
83+
span_note_and_lint(
84+
cx,
85+
if is_option {
86+
OPTION_MAP_UNWRAP_OR
87+
} else {
88+
RESULT_MAP_UNWRAP_OR
89+
},
90+
expr.span,
91+
&msg,
92+
expr.span,
93+
&note,
94+
);
7495
} else if same_span && multiline {
75-
span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
96+
span_lint(
97+
cx,
98+
if is_option {
99+
OPTION_MAP_UNWRAP_OR
100+
} else {
101+
RESULT_MAP_UNWRAP_OR
102+
},
103+
expr.span,
104+
&msg,
105+
);
76106
};
77107
}
78108
}

clippy_lints/src/methods/mod.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod inefficient_to_string;
22
mod manual_saturating_arithmetic;
3-
mod option_map_unwrap_or;
3+
mod map_unwrap_or;
44
mod unnecessary_filter_map;
55

66
use std::borrow::Cow;
@@ -251,7 +251,7 @@ declare_clippy_lint! {
251251
}
252252

253253
declare_clippy_lint! {
254-
/// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)`.
254+
/// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)` for `Option`.
255255
///
256256
/// **Why is this bad?** Readability, this can be written more concisely as
257257
/// `_.map_or(_, _)`.
@@ -268,6 +268,24 @@ declare_clippy_lint! {
268268
"using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`"
269269
}
270270

271+
declare_clippy_lint! {
272+
/// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)` for `Result`.
273+
///
274+
/// **Why is this bad?** Readability, this can be written more concisely as
275+
/// `_.map_or(_, _)`.
276+
///
277+
/// **Known problems:** The order of the arguments is not in execution order
278+
///
279+
/// **Example:**
280+
/// ```rust
281+
/// # let x: Result<usize, ()> = Ok(1);
282+
/// x.map(|a| a + 1).unwrap_or(0);
283+
/// ```
284+
pub RESULT_MAP_UNWRAP_OR,
285+
pedantic,
286+
"using `Result.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`"
287+
}
288+
271289
declare_clippy_lint! {
272290
/// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`.
273291
///
@@ -1075,6 +1093,7 @@ declare_lint_pass!(Methods => [
10751093
WRONG_PUB_SELF_CONVENTION,
10761094
OK_EXPECT,
10771095
OPTION_MAP_UNWRAP_OR,
1096+
RESULT_MAP_UNWRAP_OR,
10781097
OPTION_MAP_UNWRAP_OR_ELSE,
10791098
RESULT_MAP_UNWRAP_OR_ELSE,
10801099
OPTION_MAP_OR_NONE,
@@ -1128,7 +1147,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
11281147
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
11291148
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
11301149
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
1131-
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
1150+
["unwrap_or", "map"] => map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
11321151
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
11331152
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
11341153
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 333] = [
9+
pub const ALL_LINTS: [Lint; 334] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1687,6 +1687,13 @@ pub const ALL_LINTS: [Lint; 333] = [
16871687
deprecation: None,
16881688
module: "map_unit_fn",
16891689
},
1690+
Lint {
1691+
name: "result_map_unwrap_or",
1692+
group: "pedantic",
1693+
desc: "using `Result.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`",
1694+
deprecation: None,
1695+
module: "methods",
1696+
},
16901697
Lint {
16911698
name: "result_map_unwrap_or_else",
16921699
group: "pedantic",

0 commit comments

Comments
 (0)