1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::snippet_with_applicability
;
3 use clippy_utils
::ty
::is_type_diagnostic_item
;
4 use clippy_utils
::{get_parent_expr, is_lang_ctor, match_def_path, paths}
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::LangItem
::ResultErr
;
8 use rustc_hir
::{Expr, ExprKind, LangItem, MatchSource, QPath}
;
9 use rustc_lint
::LateContext
;
10 use rustc_middle
::ty
::{self, Ty}
;
11 use rustc_span
::{hygiene, sym}
;
15 pub(super) fn check
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>, scrutinee
: &'tcx Expr
<'_
>) {
16 // Looks for a structure like this:
17 // match ::std::ops::Try::into_result(Err(5)) {
18 // ::std::result::Result::Err(err) =>
19 // #[allow(unreachable_code)]
20 // return ::std::ops::Try::from_error(::std::convert::From::from(err)),
21 // ::std::result::Result::Ok(val) =>
22 // #[allow(unreachable_code)]
26 if let ExprKind
::Call(match_fun
, try_args
) = scrutinee
.kind
;
27 if let ExprKind
::Path(ref match_fun_path
) = match_fun
.kind
;
28 if matches
!(match_fun_path
, QPath
::LangItem(LangItem
::TryTraitBranch
, ..));
29 if let Some(try_arg
) = try_args
.get(0);
30 if let ExprKind
::Call(err_fun
, err_args
) = try_arg
.kind
;
31 if let Some(err_arg
) = err_args
.get(0);
32 if let ExprKind
::Path(ref err_fun_path
) = err_fun
.kind
;
33 if is_lang_ctor(cx
, err_fun_path
, ResultErr
);
34 if let Some(return_ty
) = find_return_type(cx
, &expr
.kind
);
40 if let Some(ty
) = result_error_type(cx
, return_ty
) {
44 } else if let Some(ty
) = poll_result_error_type(cx
, return_ty
) {
45 prefix
= "Poll::Ready(Err(";
48 } else if let Some(ty
) = poll_option_result_error_type(cx
, return_ty
) {
49 prefix
= "Poll::Ready(Some(Err(";
56 let expr_err_ty
= cx
.typeck_results().expr_ty(err_arg
);
57 let span
= hygiene
::walk_chain(err_arg
.span
, try_arg
.span
.ctxt());
58 let mut applicability
= Applicability
::MachineApplicable
;
59 let origin_snippet
= snippet_with_applicability(cx
, span
, "_", &mut applicability
);
60 let ret_prefix
= if get_parent_expr(cx
, expr
).map_or(false, |e
| matches
!(e
.kind
, ExprKind
::Ret(_
))) {
65 let suggestion
= if err_ty
== expr_err_ty
{
66 format
!("{}{}{}{}", ret_prefix
, prefix
, origin_snippet
, suffix
)
68 format
!("{}{}{}.into(){}", ret_prefix
, prefix
, origin_snippet
, suffix
)
75 "returning an `Err(_)` with the `?` operator",
84 /// Finds function return type by examining return expressions in match arms.
85 fn find_return_type
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx ExprKind
<'_
>) -> Option
<Ty
<'tcx
>> {
86 if let ExprKind
::Match(_
, arms
, MatchSource
::TryDesugar
) = expr
{
87 for arm
in arms
.iter() {
88 if let ExprKind
::Ret(Some(ret
)) = arm
.body
.kind
{
89 return Some(cx
.typeck_results().expr_ty(ret
));
96 /// Extracts the error type from Result<T, E>.
97 fn result_error_type
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
99 if let ty
::Adt(_
, subst
) = ty
.kind();
100 if is_type_diagnostic_item(cx
, ty
, sym
::Result
);
102 Some(subst
.type_at(1))
109 /// Extracts the error type from Poll<Result<T, E>>.
110 fn poll_result_error_type
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
112 if let ty
::Adt(def
, subst
) = ty
.kind();
113 if match_def_path(cx
, def
.did(), &paths
::POLL
);
114 let ready_ty
= subst
.type_at(0);
116 if let ty
::Adt(ready_def
, ready_subst
) = ready_ty
.kind();
117 if cx
.tcx
.is_diagnostic_item(sym
::Result
, ready_def
.did());
119 Some(ready_subst
.type_at(1))
126 /// Extracts the error type from Poll<Option<Result<T, E>>>.
127 fn poll_option_result_error_type
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
129 if let ty
::Adt(def
, subst
) = ty
.kind();
130 if match_def_path(cx
, def
.did(), &paths
::POLL
);
131 let ready_ty
= subst
.type_at(0);
133 if let ty
::Adt(ready_def
, ready_subst
) = ready_ty
.kind();
134 if cx
.tcx
.is_diagnostic_item(sym
::Option
, ready_def
.did());
135 let some_ty
= ready_subst
.type_at(0);
137 if let ty
::Adt(some_def
, some_subst
) = some_ty
.kind();
138 if cx
.tcx
.is_diagnostic_item(sym
::Result
, some_def
.did());
140 Some(some_subst
.type_at(1))