]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | use clippy_utils::source::snippet; | |
3 | use clippy_utils::ty::is_type_diagnostic_item; | |
a2a8927a | 4 | use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; |
f20569fa XL |
5 | use if_chain::if_chain; |
6 | use rustc_errors::Applicability; | |
7 | use rustc_hir as hir; | |
8 | use rustc_lint::LateContext; | |
9 | use rustc_middle::ty; | |
10 | use rustc_semver::RustcVersion; | |
11 | use rustc_span::sym; | |
12 | ||
13 | use super::OPTION_AS_REF_DEREF; | |
14 | ||
f20569fa XL |
15 | /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s |
16 | pub(super) fn check<'tcx>( | |
17 | cx: &LateContext<'tcx>, | |
18 | expr: &hir::Expr<'_>, | |
cdc7bbd5 XL |
19 | as_ref_recv: &hir::Expr<'_>, |
20 | map_arg: &hir::Expr<'_>, | |
f20569fa | 21 | is_mut: bool, |
923072b8 | 22 | msrv: Option<RustcVersion>, |
f20569fa | 23 | ) { |
923072b8 | 24 | if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { |
f20569fa XL |
25 | return; |
26 | } | |
27 | ||
28 | let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); | |
29 | ||
cdc7bbd5 | 30 | let option_ty = cx.typeck_results().expr_ty(as_ref_recv); |
c295e0f8 | 31 | if !is_type_diagnostic_item(cx, option_ty, sym::Option) { |
f20569fa XL |
32 | return; |
33 | } | |
34 | ||
35 | let deref_aliases: [&[&str]; 9] = [ | |
36 | &paths::DEREF_TRAIT_METHOD, | |
37 | &paths::DEREF_MUT_TRAIT_METHOD, | |
38 | &paths::CSTRING_AS_C_STR, | |
39 | &paths::OS_STRING_AS_OS_STR, | |
40 | &paths::PATH_BUF_AS_PATH, | |
41 | &paths::STRING_AS_STR, | |
42 | &paths::STRING_AS_MUT_STR, | |
43 | &paths::VEC_AS_SLICE, | |
44 | &paths::VEC_AS_MUT_SLICE, | |
45 | ]; | |
46 | ||
cdc7bbd5 | 47 | let is_deref = match map_arg.kind { |
f20569fa | 48 | hir::ExprKind::Path(ref expr_qpath) => cx |
cdc7bbd5 | 49 | .qpath_res(expr_qpath, map_arg.hir_id) |
f20569fa XL |
50 | .opt_def_id() |
51 | .map_or(false, |fun_def_id| { | |
52 | deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) | |
53 | }), | |
064997fb | 54 | hir::ExprKind::Closure(&hir::Closure { body, .. }) => { |
923072b8 | 55 | let closure_body = cx.tcx.hir().body(body); |
a2a8927a | 56 | let closure_expr = peel_blocks(&closure_body.value); |
f20569fa XL |
57 | |
58 | match &closure_expr.kind { | |
5099ac24 | 59 | hir::ExprKind::MethodCall(_, args, _) => { |
f20569fa XL |
60 | if_chain! { |
61 | if args.len() == 1; | |
62 | if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id); | |
63 | let adj = cx | |
64 | .typeck_results() | |
65 | .expr_adjustments(&args[0]) | |
66 | .iter() | |
67 | .map(|x| &x.kind) | |
68 | .collect::<Box<[_]>>(); | |
69 | if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; | |
70 | then { | |
71 | let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); | |
72 | deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) | |
73 | } else { | |
74 | false | |
75 | } | |
76 | } | |
77 | }, | |
cdc7bbd5 | 78 | hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { |
f20569fa | 79 | if_chain! { |
cdc7bbd5 XL |
80 | if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; |
81 | if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; | |
f20569fa XL |
82 | then { |
83 | path_to_local_id(inner2, closure_body.params[0].pat.hir_id) | |
84 | } else { | |
85 | false | |
86 | } | |
87 | } | |
88 | }, | |
89 | _ => false, | |
90 | } | |
91 | }, | |
92 | _ => false, | |
93 | }; | |
94 | ||
95 | if is_deref { | |
96 | let current_method = if is_mut { | |
cdc7bbd5 | 97 | format!(".as_mut().map({})", snippet(cx, map_arg.span, "..")) |
f20569fa | 98 | } else { |
cdc7bbd5 | 99 | format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) |
f20569fa XL |
100 | }; |
101 | let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; | |
cdc7bbd5 | 102 | let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); |
f20569fa XL |
103 | let suggestion = format!("try using {} instead", method_hint); |
104 | ||
105 | let msg = format!( | |
106 | "called `{0}` on an Option value. This can be done more directly \ | |
107 | by calling `{1}` instead", | |
108 | current_method, hint | |
109 | ); | |
110 | span_lint_and_sugg( | |
111 | cx, | |
112 | OPTION_AS_REF_DEREF, | |
113 | expr.span, | |
114 | &msg, | |
115 | &suggestion, | |
116 | hint, | |
117 | Applicability::MachineApplicable, | |
118 | ); | |
119 | } | |
120 | } |