diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index bdc366f6878a..9a7427ea1447 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -596,4 +596,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions { impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api); } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + result::check_expr(cx, expr, self.large_error_threshold, &self.large_error_ignored); + } } diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 04e15a1d8a0e..77fec9371425 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -50,7 +50,7 @@ pub(super) fn check_item<'tcx>( let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored, false); } } @@ -70,7 +70,7 @@ pub(super) fn check_impl_item<'tcx>( let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored, false); } } @@ -87,7 +87,7 @@ pub(super) fn check_trait_item<'tcx>( if cx.effective_visibilities.is_exported(item.owner_id.def_id) { check_result_unit_err(cx, err_ty, fn_header_span, msrv); } - check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored); + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold, large_err_ignored, false); } } } @@ -111,12 +111,15 @@ fn check_result_large_err<'tcx>( hir_ty_span: Span, large_err_threshold: u64, large_err_ignored: &DefIdSet, + is_closure: bool, ) { if let ty::Adt(adt, _) = err_ty.kind() && large_err_ignored.contains(&adt.did()) { return; } + + let subject = if is_closure { "closure" } else { "function" }; if let ty::Adt(adt, subst) = err_ty.kind() && let Some(local_def_id) = adt.did().as_local() && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id) @@ -130,7 +133,7 @@ fn check_result_large_err<'tcx>( cx, RESULT_LARGE_ERR, hir_ty_span, - "the `Err`-variant returned from this function is very large", + format!("the `Err`-variant returned from this {subject} is very large"), |diag| { diag.span_label( def.variants[first_variant.ind].span, @@ -161,7 +164,7 @@ fn check_result_large_err<'tcx>( cx, RESULT_LARGE_ERR, hir_ty_span, - "the `Err`-variant returned from this function is very large", + format!("the `Err`-variant returned from this {subject} is very large"), |diag: &mut Diag<'_, ()>| { diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); @@ -170,3 +173,33 @@ fn check_result_large_err<'tcx>( } } } + +pub(super) fn check_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + large_err_threshold: u64, + large_err_ignored: &DefIdSet, +) { + if let hir::ExprKind::Closure(closure) = expr.kind + && let ty::Closure(_, args) = cx.typeck_results().expr_ty(expr).kind() + && let closure_sig = args.as_closure().sig() + && let Ok(err_binder) = closure_sig.output().try_map_bound(|output_ty| { + if let ty::Adt(adt, args) = output_ty.kind() + && let [_, err_arg] = args.as_slice() + && let Some(err_ty) = err_arg.as_type() + && adt.is_diag_item(cx, sym::Result) + { + return Ok(err_ty); + } + + Err(()) + }) + { + let err_ty = cx.tcx.instantiate_bound_regions_with_erased(err_binder); + let hir_ty_span = match closure.fn_decl.output { + hir::FnRetTy::Return(hir_ty) => hir_ty.span, + hir::FnRetTy::DefaultReturn(_) => expr.span, + }; + check_result_large_err(cx, err_ty, hir_ty_span, large_err_threshold, large_err_ignored, true); + } +} diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index fa57b3f553fc..b4ad050df3b7 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -141,3 +141,12 @@ fn _empty_error() -> Result<(), Empty> { } fn main() {} + +fn issue16249() { + type Large = [u8; 1024]; + + let closure = || -> Result<(), Large> { Ok(()) }; + //~^ result_large_err + let closure = || Ok::<(), Large>(()); + //~^ result_large_err +} diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index 72fbc3f58961..fd39179c61cb 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -104,5 +104,21 @@ LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box>` -error: aborting due to 12 previous errors +error: the `Err`-variant returned from this closure is very large + --> tests/ui/result_large_err.rs:148:25 + | +LL | let closure = || -> Result<(), Large> { Ok(()) }; + | ^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 1024 bytes + | + = help: try reducing the size of `[u8; 1024]`, for example by boxing large elements or replacing it with `Box<[u8; 1024]>` + +error: the `Err`-variant returned from this closure is very large + --> tests/ui/result_large_err.rs:150:19 + | +LL | let closure = || Ok::<(), Large>(()); + | ^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 1024 bytes + | + = help: try reducing the size of `[u8; 1024]`, for example by boxing large elements or replacing it with `Box<[u8; 1024]>` + +error: aborting due to 14 previous errors