1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::snippet
;
3 use clippy_utils
::ty
::is_type_diagnostic_item
;
4 use clippy_utils
::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
8 use rustc_lint
::LateContext
;
10 use rustc_semver
::RustcVersion
;
13 use super::OPTION_AS_REF_DEREF
;
15 /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
16 pub(super) fn check
<'tcx
>(
17 cx
: &LateContext
<'tcx
>,
19 as_ref_recv
: &hir
::Expr
<'_
>,
20 map_arg
: &hir
::Expr
<'_
>,
22 msrv
: Option
<RustcVersion
>,
24 if !meets_msrv(msrv
, msrvs
::OPTION_AS_DEREF
) {
28 let same_mutability
= |m
| (is_mut
&& m
== &hir
::Mutability
::Mut
) || (!is_mut
&& m
== &hir
::Mutability
::Not
);
30 let option_ty
= cx
.typeck_results().expr_ty(as_ref_recv
);
31 if !is_type_diagnostic_item(cx
, option_ty
, sym
::Option
) {
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
,
44 &paths
::VEC_AS_MUT_SLICE
,
47 let is_deref
= match map_arg
.kind
{
48 hir
::ExprKind
::Path(ref expr_qpath
) => cx
49 .qpath_res(expr_qpath
, map_arg
.hir_id
)
51 .map_or(false, |fun_def_id
| {
52 deref_aliases
.iter().any(|path
| match_def_path(cx
, fun_def_id
, path
))
54 hir
::ExprKind
::Closure { body, .. }
=> {
55 let closure_body
= cx
.tcx
.hir().body(body
);
56 let closure_expr
= peel_blocks(&closure_body
.value
);
58 match &closure_expr
.kind
{
59 hir
::ExprKind
::MethodCall(_
, args
, _
) => {
62 if path_to_local_id(&args
[0], closure_body
.params
[0].pat
.hir_id
);
65 .expr_adjustments(&args
[0])
68 .collect
::<Box
<[_
]>>();
69 if let [ty
::adjustment
::Adjust
::Deref(None
), ty
::adjustment
::Adjust
::Borrow(_
)] = *adj
;
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
))
78 hir
::ExprKind
::AddrOf(hir
::BorrowKind
::Ref
, m
, inner
) if same_mutability(m
) => {
80 if let hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, inner1
) = inner
.kind
;
81 if let hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, inner2
) = inner1
.kind
;
83 path_to_local_id(inner2
, closure_body
.params
[0].pat
.hir_id
)
96 let current_method
= if is_mut
{
97 format
!(".as_mut().map({})", snippet(cx
, map_arg
.span
, ".."))
99 format
!(".as_ref().map({})", snippet(cx
, map_arg
.span
, ".."))
101 let method_hint
= if is_mut { "as_deref_mut" }
else { "as_deref" }
;
102 let hint
= format
!("{}.{}()", snippet(cx
, as_ref_recv
.span
, ".."), method_hint
);
103 let suggestion
= format
!("try using {} instead", method_hint
);
106 "called `{0}` on an Option value. This can be done more directly \
107 by calling `{1}` instead",
117 Applicability
::MachineApplicable
,