]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
Auto merge of #82680 - jturner314:div_euclid-docs, r=JohnTitor
[rust.git] / src / tools / clippy / clippy_lints / src / methods / map_unwrap_or.rs
1 use crate::utils::usage::mutated_variables;
2 use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg};
3 use rustc_errors::Applicability;
4 use rustc_hir as hir;
5 use rustc_lint::LateContext;
6 use rustc_semver::RustcVersion;
7 use rustc_span::symbol::sym;
8
9 use super::MAP_UNWRAP_OR;
10
11 const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
12
13 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
14 /// Return true if lint triggered
15 pub(super) fn check<'tcx>(
16     cx: &LateContext<'tcx>,
17     expr: &'tcx hir::Expr<'_>,
18     map_args: &'tcx [hir::Expr<'_>],
19     unwrap_args: &'tcx [hir::Expr<'_>],
20     msrv: Option<&RustcVersion>,
21 ) -> bool {
22     if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
23         return false;
24     }
25     // lint if the caller of `map()` is an `Option`
26     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
27     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
28
29     if is_option || is_result {
30         // Don't make a suggestion that may fail to compile due to mutably borrowing
31         // the same variable twice.
32         let map_mutated_vars = mutated_variables(&map_args[0], cx);
33         let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
34         if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
35             if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
36                 return false;
37             }
38         } else {
39             return false;
40         }
41
42         // lint message
43         let msg = if is_option {
44             "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
45             `map_or_else(<g>, <f>)` instead"
46         } else {
47             "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
48             `.map_or_else(<g>, <f>)` instead"
49         };
50         // get snippets for args to map() and unwrap_or_else()
51         let map_snippet = snippet(cx, map_args[1].span, "..");
52         let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
53         // lint, with note if neither arg is > 1 line and both map() and
54         // unwrap_or_else() have the same span
55         let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
56         let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
57         if same_span && !multiline {
58             let var_snippet = snippet(cx, map_args[0].span, "..");
59             span_lint_and_sugg(
60                 cx,
61                 MAP_UNWRAP_OR,
62                 expr.span,
63                 msg,
64                 "try this",
65                 format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
66                 Applicability::MachineApplicable,
67             );
68             return true;
69         } else if same_span && multiline {
70             span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
71             return true;
72         }
73     }
74
75     false
76 }