]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/option_map_unwrap_or.rs
39bcf415fe9290928d19fcd7f1abfe0158aabb5f
[rust.git] / clippy_lints / src / methods / option_map_unwrap_or.rs
1 use crate::utils::paths;
2 use crate::utils::{is_copy, match_type, snippet, span_lint, span_note_and_lint};
3 use rustc::hir;
4 use rustc::lint::LateContext;
5
6 use super::OPTION_MAP_UNWRAP_OR;
7
8 /// lint use of `map().unwrap_or()` for `Option`s
9 pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, map_args: &[hir::Expr], unwrap_args: &[hir::Expr]) {
10     // lint if the caller of `map()` is an `Option`
11     let unwrap_ty = cx.tables.expr_ty(&unwrap_args[1]);
12     if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) && is_copy(cx, unwrap_ty) {
13         // get snippets for args to map() and unwrap_or()
14         let map_snippet = snippet(cx, map_args[1].span, "..");
15         let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
16         // lint message
17         // comparing the snippet from source to raw text ("None") below is safe
18         // because we already have checked the type.
19         let arg = if unwrap_snippet == "None" { "None" } else { "a" };
20         let suggest = if unwrap_snippet == "None" {
21             "and_then(f)"
22         } else {
23             "map_or(a, f)"
24         };
25         let msg = &format!(
26             "called `map(f).unwrap_or({})` on an Option value. \
27              This can be done more directly by calling `{}` instead",
28             arg, suggest
29         );
30         // lint, with note if neither arg is > 1 line and both map() and
31         // unwrap_or() have the same span
32         let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
33         let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
34         if same_span && !multiline {
35             let suggest = if unwrap_snippet == "None" {
36                 format!("and_then({})", map_snippet)
37             } else {
38                 format!("map_or({}, {})", unwrap_snippet, map_snippet)
39             };
40             let note = format!(
41                 "replace `map({}).unwrap_or({})` with `{}`",
42                 map_snippet, unwrap_snippet, suggest
43             );
44             span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, &note);
45         } else if same_span && multiline {
46             span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
47         };
48     }
49 }