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