]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
Rollup merge of #103122 - ouz-a:mir-technical-debt, r=oli-obk
[rust.git] / src / tools / clippy / clippy_lints / src / methods / get_unwrap.rs
1 use super::utils::derefs_to_slice;
2 use clippy_utils::diagnostics::span_lint_and_sugg;
3 use clippy_utils::get_parent_expr;
4 use clippy_utils::source::snippet_with_applicability;
5 use clippy_utils::ty::is_type_diagnostic_item;
6 use if_chain::if_chain;
7 use rustc_errors::Applicability;
8 use rustc_hir as hir;
9 use rustc_lint::LateContext;
10 use rustc_span::sym;
11
12 use super::GET_UNWRAP;
13
14 pub(super) fn check<'tcx>(
15     cx: &LateContext<'tcx>,
16     expr: &hir::Expr<'_>,
17     recv: &'tcx hir::Expr<'tcx>,
18     get_arg: &'tcx hir::Expr<'_>,
19     is_mut: bool,
20 ) {
21     // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
22     // because they do not implement `IndexMut`
23     let mut applicability = Applicability::MachineApplicable;
24     let expr_ty = cx.typeck_results().expr_ty(recv);
25     let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
26     let mut needs_ref;
27     let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() {
28         needs_ref = get_args_str.parse::<usize>().is_ok();
29         "slice"
30     } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) {
31         needs_ref = get_args_str.parse::<usize>().is_ok();
32         "Vec"
33     } else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) {
34         needs_ref = get_args_str.parse::<usize>().is_ok();
35         "VecDeque"
36     } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) {
37         needs_ref = true;
38         "HashMap"
39     } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) {
40         needs_ref = true;
41         "BTreeMap"
42     } else {
43         return; // caller is not a type that we want to lint
44     };
45
46     let mut span = expr.span;
47
48     // Handle the case where the result is immediately dereferenced
49     // by not requiring ref and pulling the dereference into the
50     // suggestion.
51     if_chain! {
52         if needs_ref;
53         if let Some(parent) = get_parent_expr(cx, expr);
54         if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind;
55         then {
56             needs_ref = false;
57             span = parent.span;
58         }
59     }
60
61     let mut_str = if is_mut { "_mut" } else { "" };
62     let borrow_str = if !needs_ref {
63         ""
64     } else if is_mut {
65         "&mut "
66     } else {
67         "&"
68     };
69
70     span_lint_and_sugg(
71         cx,
72         GET_UNWRAP,
73         span,
74         &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"),
75         "try this",
76         format!(
77             "{borrow_str}{}[{get_args_str}]",
78             snippet_with_applicability(cx, recv.span, "..", &mut applicability)
79         ),
80         applicability,
81     );
82 }