1 use rustc_errors
::Diagnostic
;
3 use rustc_lint
::{LateContext, LintContext}
;
4 use rustc_middle
::lint
::in_external_macro
;
5 use rustc_middle
::ty
::{self, Ty}
;
6 use rustc_span
::{sym, Span}
;
8 use clippy_utils
::diagnostics
::{span_lint_and_help, span_lint_and_then}
;
9 use clippy_utils
::trait_ref_of_method
;
10 use clippy_utils
::ty
::{approx_ty_size, is_type_diagnostic_item}
;
12 use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}
;
14 /// The type of the `Err`-variant in a `std::result::Result` returned by the
16 fn result_err_ty
<'tcx
>(
17 cx
: &LateContext
<'tcx
>,
18 decl
: &hir
::FnDecl
<'tcx
>,
19 id
: hir
::def_id
::LocalDefId
,
21 ) -> Option
<(&'tcx hir
::Ty
<'tcx
>, Ty
<'tcx
>)> {
22 if !in_external_macro(cx
.sess(), item_span
)
23 && let hir
::FnRetTy
::Return(hir_ty
) = decl
.output
24 && let ty
= cx
.tcx
.erase_late_bound_regions(cx
.tcx
.fn_sig(id
).output())
25 && is_type_diagnostic_item(cx
, ty
, sym
::Result
)
26 && let ty
::Adt(_
, substs
) = ty
.kind()
28 let err_ty
= substs
.type_at(1);
29 Some((hir_ty
, err_ty
))
35 pub(super) fn check_item
<'tcx
>(cx
: &LateContext
<'tcx
>, item
: &hir
::Item
<'tcx
>, large_err_threshold
: u64) {
36 if let hir
::ItemKind
::Fn(ref sig
, _generics
, _
) = item
.kind
37 && let Some((hir_ty
, err_ty
)) = result_err_ty(cx
, sig
.decl
, item
.def_id
, item
.span
)
39 if cx
.access_levels
.is_exported(item
.def_id
) {
40 let fn_header_span
= item
.span
.with_hi(sig
.decl
.output
.span().hi());
41 check_result_unit_err(cx
, err_ty
, fn_header_span
);
43 check_result_large_err(cx
, err_ty
, hir_ty
.span
, large_err_threshold
);
47 pub(super) fn check_impl_item
<'tcx
>(cx
: &LateContext
<'tcx
>, item
: &hir
::ImplItem
<'tcx
>, large_err_threshold
: u64) {
48 // Don't lint if method is a trait's implementation, we can't do anything about those
49 if let hir
::ImplItemKind
::Fn(ref sig
, _
) = item
.kind
50 && let Some((hir_ty
, err_ty
)) = result_err_ty(cx
, sig
.decl
, item
.def_id
, item
.span
)
51 && trait_ref_of_method(cx
, item
.def_id
).is_none()
53 if cx
.access_levels
.is_exported(item
.def_id
) {
54 let fn_header_span
= item
.span
.with_hi(sig
.decl
.output
.span().hi());
55 check_result_unit_err(cx
, err_ty
, fn_header_span
);
57 check_result_large_err(cx
, err_ty
, hir_ty
.span
, large_err_threshold
);
61 pub(super) fn check_trait_item
<'tcx
>(cx
: &LateContext
<'tcx
>, item
: &hir
::TraitItem
<'tcx
>, large_err_threshold
: u64) {
62 if let hir
::TraitItemKind
::Fn(ref sig
, _
) = item
.kind
{
63 let fn_header_span
= item
.span
.with_hi(sig
.decl
.output
.span().hi());
64 if let Some((hir_ty
, err_ty
)) = result_err_ty(cx
, sig
.decl
, item
.def_id
, item
.span
) {
65 if cx
.access_levels
.is_exported(item
.def_id
) {
66 check_result_unit_err(cx
, err_ty
, fn_header_span
);
68 check_result_large_err(cx
, err_ty
, hir_ty
.span
, large_err_threshold
);
73 fn check_result_unit_err(cx
: &LateContext
<'_
>, err_ty
: Ty
<'_
>, fn_header_span
: Span
) {
79 "this returns a `Result<_, ()>`",
81 "use a custom `Error` type instead",
86 fn check_result_large_err
<'tcx
>(cx
: &LateContext
<'tcx
>, err_ty
: Ty
<'tcx
>, hir_ty_span
: Span
, large_err_threshold
: u64) {
87 let ty_size
= approx_ty_size(cx
, err_ty
);
88 if ty_size
>= large_err_threshold
{
93 "the `Err`-variant returned from this function is very large",
94 |diag
: &mut Diagnostic
| {
95 diag
.span_label(hir_ty_span
, format
!("the `Err`-variant is at least {ty_size} bytes"));
96 diag
.help(format
!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));