Skip to content

Commit 42852a9

Browse files
Guanqun LuLu Guanqun
Guanqun Lu
authored and
Lu Guanqun
committed
lint: add map(f).unwrap_or(a) for Result
1 parent f627ae2 commit 42852a9

File tree

4 files changed

+79
-21
lines changed

4 files changed

+79
-21
lines changed

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
622622
&methods::OPTION_UNWRAP_USED,
623623
&methods::OR_FUN_CALL,
624624
&methods::RESULT_EXPECT_USED,
625+
&methods::RESULT_MAP_UNWRAP_OR,
625626
&methods::RESULT_MAP_UNWRAP_OR_ELSE,
626627
&methods::RESULT_UNWRAP_USED,
627628
&methods::SEARCH_IS_SOME,
@@ -1037,6 +1038,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
10371038
LintId::of(&methods::MAP_FLATTEN),
10381039
LintId::of(&methods::OPTION_MAP_UNWRAP_OR),
10391040
LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE),
1041+
LintId::of(&methods::RESULT_MAP_UNWRAP_OR),
10401042
LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE),
10411043
LintId::of(&misc::USED_UNDERSCORE_BINDING),
10421044
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

Lines changed: 47 additions & 17 deletions
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

Lines changed: 22 additions & 3 deletions
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;
@@ -252,7 +252,7 @@ declare_clippy_lint! {
252252
}
253253

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

272+
declare_clippy_lint! {
273+
/// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)` for `Result`.
274+
///
275+
/// **Why is this bad?** Readability, this can be written more concisely as
276+
/// `_.map_or(_, _)`.
277+
///
278+
/// **Known problems:** The order of the arguments is not in execution order
279+
///
280+
/// **Example:**
281+
/// ```rust
282+
/// # let x: Result<usize, ()> = Ok(1);
283+
/// x.map(|a| a + 1).unwrap_or(0);
284+
/// ```
285+
pub RESULT_MAP_UNWRAP_OR,
286+
pedantic,
287+
"using `Result.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`"
288+
}
289+
272290
declare_clippy_lint! {
273291
/// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`.
274292
///
@@ -1093,6 +1111,7 @@ declare_lint_pass!(Methods => [
10931111
WRONG_PUB_SELF_CONVENTION,
10941112
OK_EXPECT,
10951113
OPTION_MAP_UNWRAP_OR,
1114+
RESULT_MAP_UNWRAP_OR,
10961115
OPTION_MAP_UNWRAP_OR_ELSE,
10971116
RESULT_MAP_UNWRAP_OR_ELSE,
10981117
OPTION_MAP_OR_NONE,
@@ -1147,7 +1166,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
11471166
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
11481167
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
11491168
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
1150-
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
1169+
["unwrap_or", "map"] => map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
11511170
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
11521171
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
11531172
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
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; 340] = [
9+
pub const ALL_LINTS: [Lint; 341] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1708,6 +1708,13 @@ pub const ALL_LINTS: [Lint; 340] = [
17081708
deprecation: None,
17091709
module: "map_unit_fn",
17101710
},
1711+
Lint {
1712+
name: "result_map_unwrap_or",
1713+
group: "pedantic",
1714+
desc: "using `Result.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`",
1715+
deprecation: None,
1716+
module: "methods",
1717+
},
17111718
Lint {
17121719
name: "result_map_unwrap_or_else",
17131720
group: "pedantic",

0 commit comments

Comments
 (0)