Skip to content

Commit a790f57

Browse files
committed
Add real suggestion to option_map_unwrap_or
1 parent a865d0a commit a790f57

File tree

3 files changed

+61
-28
lines changed

3 files changed

+61
-28
lines changed

clippy_lints/src/methods/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
10701070
["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
10711071
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
10721072
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
1073-
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
1073+
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
10741074
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
10751075
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
10761076
["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),

clippy_lints/src/methods/option_map_unwrap_or.rs

+27-23
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use crate::utils::paths;
2-
use crate::utils::{is_copy, match_type, snippet, span_lint, span_note_and_lint};
1+
use crate::utils::{paths, span_lint_and_then, snippet_with_applicability, differing_macro_contexts};
2+
use crate::utils::{is_copy, match_type, snippet};
33
use rustc::hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
44
use rustc::hir::{self, *};
55
use rustc::lint::LateContext;
66
use rustc_data_structures::fx::FxHashSet;
77
use syntax_pos::symbol::Symbol;
8+
use syntax::source_map::Span;
9+
use rustc_errors::Applicability;
810

911
use super::OPTION_MAP_UNWRAP_OR;
1012

@@ -14,6 +16,7 @@ pub(super) fn lint<'a, 'tcx>(
1416
expr: &hir::Expr,
1517
map_args: &'tcx [hir::Expr],
1618
unwrap_args: &'tcx [hir::Expr],
19+
map_span: Span,
1720
) {
1821
// lint if the caller of `map()` is an `Option`
1922
if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) {
@@ -39,9 +42,13 @@ pub(super) fn lint<'a, 'tcx>(
3942
}
4043
}
4144

42-
// get snippets for args to map() and unwrap_or()
43-
let map_snippet = snippet(cx, map_args[1].span, "..");
44-
let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
45+
if differing_macro_contexts(unwrap_args[1].span, map_span) {
46+
return;
47+
}
48+
49+
let mut applicability = Applicability::MachineApplicable;
50+
// get snippet for unwrap_or()
51+
let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span,"..", &mut applicability);
4552
// lint message
4653
// comparing the snippet from source to raw text ("None") below is safe
4754
// because we already have checked the type.
@@ -56,24 +63,21 @@ pub(super) fn lint<'a, 'tcx>(
5663
This can be done more directly by calling `{}` instead",
5764
arg, suggest
5865
);
59-
// lint, with note if neither arg is > 1 line and both map() and
60-
// unwrap_or() have the same span
61-
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
62-
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
63-
if same_span && !multiline {
64-
let suggest = if unwrap_snippet == "None" {
65-
format!("and_then({})", map_snippet)
66-
} else {
67-
format!("map_or({}, {})", unwrap_snippet, map_snippet)
68-
};
69-
let note = format!(
70-
"replace `map({}).unwrap_or({})` with `{}`",
71-
map_snippet, unwrap_snippet, suggest
72-
);
73-
span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, &note);
74-
} else if same_span && multiline {
75-
span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
76-
};
66+
67+
span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |db| {
68+
let map_arg_span = map_args[1].span;
69+
70+
db.multipart_suggestion(
71+
"use `map_or` instead",
72+
vec![
73+
(map_span, String::from("map_or")),
74+
(expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")),
75+
(map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet))
76+
],
77+
applicability
78+
);
79+
}
80+
);
7781
}
7882
}
7983

tests/ui/methods.stderr

+33-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ LL | | .unwrap_or(0);
2828
| |____________________________^
2929
|
3030
= note: `-D clippy::option-map-unwrap-or` implied by `-D warnings`
31-
= note: replace `map(|x| x + 1).unwrap_or(0)` with `map_or(0, |x| x + 1)`
31+
help: use `map_or` instead
32+
|
33+
LL | let _ = opt.map_or(0, |x| x + 1);
34+
| ^^^^^^ ^^ --
3235

3336
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
3437
--> $DIR/methods.rs:178:13
@@ -39,6 +42,13 @@ LL | | x + 1
3942
LL | | }
4043
LL | | ).unwrap_or(0);
4144
| |____________________________^
45+
help: use `map_or` instead
46+
|
47+
LL | let _ = opt.map_or(0, |x| {
48+
LL | x + 1
49+
LL | }
50+
LL | );
51+
|
4252

4353
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
4454
--> $DIR/methods.rs:182:13
@@ -49,14 +59,22 @@ LL | | .unwrap_or({
4959
LL | | 0
5060
LL | | });
5161
| |__________________^
62+
help: use `map_or` instead
63+
|
64+
LL | let _ = opt.map_or({
65+
LL | 0
66+
LL | }, |x| x + 1);
67+
|
5268

5369
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
5470
--> $DIR/methods.rs:187:13
5571
|
5672
LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
5773
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
74+
help: use `map_or` instead
5875
|
59-
= note: replace `map(|x| Some(x + 1)).unwrap_or(None)` with `and_then(|x| Some(x + 1))`
76+
LL | let _ = opt.map_or(None, |x| Some(x + 1));
77+
| ^^^^^^ ^^^^^ --
6078

6179
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
6280
--> $DIR/methods.rs:189:13
@@ -67,6 +85,13 @@ LL | | Some(x + 1)
6785
LL | | }
6886
LL | | ).unwrap_or(None);
6987
| |_____________________^
88+
help: use `map_or` instead
89+
|
90+
LL | let _ = opt.map_or(None, |x| {
91+
LL | Some(x + 1)
92+
LL | }
93+
LL | );
94+
|
7095

7196
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
7297
--> $DIR/methods.rs:193:13
@@ -76,16 +101,20 @@ LL | let _ = opt
76101
LL | | .map(|x| Some(x + 1))
77102
LL | | .unwrap_or(None);
78103
| |________________________^
104+
help: use `map_or` instead
79105
|
80-
= note: replace `map(|x| Some(x + 1)).unwrap_or(None)` with `and_then(|x| Some(x + 1))`
106+
LL | .map_or(None, |x| Some(x + 1));
107+
| ^^^^^^ ^^^^^ --
81108

82109
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
83110
--> $DIR/methods.rs:204:13
84111
|
85112
LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
86113
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
114+
help: use `map_or` instead
87115
|
88-
= note: replace `map(|p| format!("{}.", p)).unwrap_or(id)` with `map_or(id, |p| format!("{}.", p))`
116+
LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
117+
| ^^^^^^ ^^^ --
89118

90119
error: called `map(f).unwrap_or_else(g)` on an Option value. This can be done more directly by calling `map_or_else(g, f)` instead
91120
--> $DIR/methods.rs:208:13

0 commit comments

Comments
 (0)