|
1 | 1 | use super::ERR_EXPECT;
|
2 | 2 | use clippy_utils::{diagnostics::span_lint_and_help, meets_msrv, msrvs, ty::is_type_diagnostic_item};
|
| 3 | +use clippy_utils::ty::implements_trait; |
3 | 4 | use rustc_lint::LateContext;
|
| 5 | +use rustc_middle::ty; |
| 6 | +use rustc_middle::ty::Ty; |
4 | 7 | use rustc_semver::RustcVersion;
|
5 | 8 | use rustc_span::sym;
|
6 | 9 |
|
7 | 10 | pub(super) fn check(cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, msrv: Option<&RustcVersion>) {
|
8 |
| - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) & meets_msrv(msrv, &msrvs::EXPECT_ERR) { |
9 |
| - span_lint_and_help( |
| 11 | + if_chain! { |
| 12 | + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); |
| 13 | + if meets_msrv(msrv, &msrvs::EXPECT_ERR); |
| 14 | + |
| 15 | + let result_type = cx.typeck_results().expr_ty(recv); |
| 16 | + if let Some(error_type) = get_error_type(cx, result_type); |
| 17 | + if has_debug_impl(error_type, cx); |
| 18 | + |
| 19 | + then { |
| 20 | + span_lint_and_help( |
10 | 21 | cx,
|
11 | 22 | ERR_EXPECT,
|
12 | 23 | expr.span,
|
13 | 24 | "called `.err().expect()` on a `Result` value",
|
14 | 25 | None,
|
15 | 26 | "`.expect_err()` can be called instead",
|
16 | 27 | );
|
| 28 | + } |
17 | 29 | }
|
18 | 30 | }
|
| 31 | + |
| 32 | +/// Given a `Result<T, E>` type, return its error type (`E`). |
| 33 | +fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> { |
| 34 | + match ty.kind() { |
| 35 | + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().nth(1), |
| 36 | + _ => None, |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { |
| 41 | + cx.tcx |
| 42 | + .get_diagnostic_item(sym::Debug) |
| 43 | + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) |
| 44 | +} |
0 commit comments