Skip to content

Commit a54baad

Browse files
authored
Merge pull request #2112 from topecongiro/issue-2109
Add a suggestion to replace `map(f).unwrap_or(None)` with `and_then(f)`.
2 parents 45e5f28 + 7f4b583 commit a54baad

File tree

3 files changed

+291
-244
lines changed

3 files changed

+291
-244
lines changed

clippy_lints/src/methods.rs

+22-14
Original file line numberDiff line numberDiff line change
@@ -1150,29 +1150,37 @@ fn lint_ok_expect(cx: &LateContext, expr: &hir::Expr, ok_args: &[hir::Expr]) {
11501150
fn lint_map_unwrap_or(cx: &LateContext, expr: &hir::Expr, map_args: &[hir::Expr], unwrap_args: &[hir::Expr]) {
11511151
// lint if the caller of `map()` is an `Option`
11521152
if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) {
1153-
// lint message
1154-
let msg = "called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling \
1155-
`map_or(a, f)` instead";
11561153
// get snippets for args to map() and unwrap_or()
11571154
let map_snippet = snippet(cx, map_args[1].span, "..");
11581155
let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
1156+
// lint message
1157+
// comparing the snippet from source to raw text ("None") below is safe
1158+
// because we already have checked the type.
1159+
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
1160+
let suggest = if unwrap_snippet == "None" { "and_then(f)" } else { "map_or(a, f)" };
1161+
let msg = &format!(
1162+
"called `map(f).unwrap_or({})` on an Option value. \
1163+
This can be done more directly by calling `{}` instead",
1164+
arg,
1165+
suggest
1166+
);
11591167
// lint, with note if neither arg is > 1 line and both map() and
11601168
// unwrap_or() have the same span
11611169
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
11621170
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
11631171
if same_span && !multiline {
1164-
span_note_and_lint(
1165-
cx,
1166-
OPTION_MAP_UNWRAP_OR,
1167-
expr.span,
1168-
msg,
1169-
expr.span,
1170-
&format!(
1171-
"replace `map({0}).unwrap_or({1})` with `map_or({1}, {0})`",
1172-
map_snippet,
1173-
unwrap_snippet
1174-
),
1172+
let suggest = if unwrap_snippet == "None" {
1173+
format!("and_then({})", map_snippet)
1174+
} else {
1175+
format!("map_or({}, {})", unwrap_snippet, map_snippet)
1176+
};
1177+
let note = format!(
1178+
"replace `map({}).unwrap_or({})` with `{}`",
1179+
map_snippet,
1180+
unwrap_snippet,
1181+
suggest
11751182
);
1183+
span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, &note);
11761184
} else if same_span && multiline {
11771185
span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
11781186
};

tests/ui/methods.rs

+10
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ fn option_methods() {
108108
.unwrap_or({
109109
0
110110
});
111+
// single line `map(f).unwrap_or(None)` case
112+
let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
113+
// multiline `map(f).unwrap_or(None)` cases
114+
let _ = opt.map(|x| {
115+
Some(x + 1)
116+
}
117+
).unwrap_or(None);
118+
let _ = opt
119+
.map(|x| Some(x + 1))
120+
.unwrap_or(None);
111121
// macro case
112122
let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
113123

0 commit comments

Comments
 (0)