]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
New upstream version 1.52.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / option_as_ref_deref.rs
CommitLineData
f20569fa
XL
1use 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};
5use if_chain::if_chain;
6use rustc_errors::Applicability;
7use rustc_hir as hir;
8use rustc_lint::LateContext;
9use rustc_middle::ty;
10use rustc_semver::RustcVersion;
11use rustc_span::sym;
12
13use super::OPTION_AS_REF_DEREF;
14
15const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
16
17/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
18pub(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}