Skip to content

Commit cad0d3d

Browse files
committed
Auto merge of rust-lang#9662 - ebobrow:result-large-err, r=dswij
`result_large_err` show largest variants in err msg fixes rust-lang#9538 changelog: Sugg: [`result_large_err`]: Now show largest enum variants in error message
2 parents 3c0ad8a + 80e5856 commit cad0d3d

File tree

5 files changed

+138
-74
lines changed

5 files changed

+138
-74
lines changed

clippy_lints/src/functions/result.rs

+54-14
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use rustc_errors::Diagnostic;
22
use rustc_hir as hir;
33
use rustc_lint::{LateContext, LintContext};
44
use rustc_middle::lint::in_external_macro;
5-
use rustc_middle::ty::{self, Ty};
5+
use rustc_middle::ty::{self, Adt, Ty};
66
use rustc_span::{sym, Span};
77

88
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
99
use clippy_utils::trait_ref_of_method;
10-
use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
10+
use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo};
1111

1212
use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
1313

@@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S
8484
}
8585

8686
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 {
89-
span_lint_and_then(
90-
cx,
91-
RESULT_LARGE_ERR,
92-
hir_ty_span,
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}>`"));
97-
},
98-
);
87+
if_chain! {
88+
if let Adt(adt, subst) = err_ty.kind();
89+
if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local();
90+
if let Some(hir::Node::Item(item)) = cx
91+
.tcx
92+
.hir()
93+
.find_by_def_id(local_def_id);
94+
if let hir::ItemKind::Enum(ref def, _) = item.kind;
95+
then {
96+
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
97+
if variants_size[0].size >= large_err_threshold {
98+
span_lint_and_then(
99+
cx,
100+
RESULT_LARGE_ERR,
101+
hir_ty_span,
102+
"the `Err`-variant returned from this function is very large",
103+
|diag| {
104+
diag.span_label(
105+
def.variants[variants_size[0].ind].span,
106+
format!("the largest variant contains at least {} bytes", variants_size[0].size),
107+
);
108+
109+
for variant in &variants_size[1..] {
110+
if variant.size >= large_err_threshold {
111+
let variant_def = &def.variants[variant.ind];
112+
diag.span_label(
113+
variant_def.span,
114+
format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size),
115+
);
116+
}
117+
}
118+
119+
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
120+
}
121+
);
122+
}
123+
}
124+
else {
125+
let ty_size = approx_ty_size(cx, err_ty);
126+
if ty_size >= large_err_threshold {
127+
span_lint_and_then(
128+
cx,
129+
RESULT_LARGE_ERR,
130+
hir_ty_span,
131+
"the `Err`-variant returned from this function is very large",
132+
|diag: &mut Diagnostic| {
133+
diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
134+
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
135+
},
136+
);
137+
}
138+
}
99139
}
100140
}

clippy_lints/src/large_enum_variant.rs

+10-50
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//! lint when there is a large size difference between variants on an enum
22
33
use clippy_utils::source::snippet_with_applicability;
4-
use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
4+
use clippy_utils::{
5+
diagnostics::span_lint_and_then,
6+
ty::{approx_ty_size, is_copy, AdtVariantInfo},
7+
};
58
use rustc_errors::Applicability;
69
use rustc_hir::{Item, ItemKind};
710
use rustc_lint::{LateContext, LateLintPass};
811
use rustc_middle::lint::in_external_macro;
9-
use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
12+
use rustc_middle::ty::{Adt, Ty};
1013
use rustc_session::{declare_tool_lint, impl_lint_pass};
1114
use rustc_span::source_map::Span;
1215

@@ -72,49 +75,6 @@ impl LargeEnumVariant {
7275
}
7376
}
7477

75-
struct FieldInfo {
76-
ind: usize,
77-
size: u64,
78-
}
79-
80-
struct VariantInfo {
81-
ind: usize,
82-
size: u64,
83-
fields_size: Vec<FieldInfo>,
84-
}
85-
86-
fn variants_size<'tcx>(
87-
cx: &LateContext<'tcx>,
88-
adt: AdtDef<'tcx>,
89-
subst: &'tcx List<GenericArg<'tcx>>,
90-
) -> Vec<VariantInfo> {
91-
let mut variants_size = adt
92-
.variants()
93-
.iter()
94-
.enumerate()
95-
.map(|(i, variant)| {
96-
let mut fields_size = variant
97-
.fields
98-
.iter()
99-
.enumerate()
100-
.map(|(i, f)| FieldInfo {
101-
ind: i,
102-
size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
103-
})
104-
.collect::<Vec<_>>();
105-
fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
106-
107-
VariantInfo {
108-
ind: i,
109-
size: fields_size.iter().map(|info| info.size).sum(),
110-
fields_size,
111-
}
112-
})
113-
.collect::<Vec<_>>();
114-
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
115-
variants_size
116-
}
117-
11878
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
11979

12080
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
@@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
13090
if adt.variants().len() <= 1 {
13191
return;
13292
}
133-
let variants_size = variants_size(cx, *adt, subst);
93+
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
13494

13595
let mut difference = variants_size[0].size - variants_size[1].size;
13696
if difference > self.maximum_size_difference_allowed {
@@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
173133
.fields_size
174134
.iter()
175135
.rev()
176-
.map_while(|val| {
136+
.map_while(|&(ind, size)| {
177137
if difference > self.maximum_size_difference_allowed {
178-
difference = difference.saturating_sub(val.size);
138+
difference = difference.saturating_sub(size);
179139
Some((
180-
fields[val.ind].ty.span,
140+
fields[ind].ty.span,
181141
format!(
182142
"Box<{}>",
183143
snippet_with_applicability(
184144
cx,
185-
fields[val.ind].ty.span,
145+
fields[ind].ty.span,
186146
"..",
187147
&mut applicability
188148
)

clippy_utils/src/ty.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt;
1313
use rustc_lint::LateContext;
1414
use rustc_middle::mir::interpret::{ConstValue, Scalar};
1515
use rustc_middle::ty::{
16-
self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, ParamEnv, Predicate,
17-
PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
18-
TypeVisitor, UintTy, VariantDef, VariantDiscr,
16+
self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv,
17+
Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable,
18+
TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
1919
};
2020
use rustc_middle::ty::{GenericArg, GenericArgKind};
2121
use rustc_span::symbol::Ident;
@@ -845,6 +845,42 @@ pub fn for_each_top_level_late_bound_region<B>(
845845
ty.visit_with(&mut V { index: 0, f })
846846
}
847847

848+
pub struct AdtVariantInfo {
849+
pub ind: usize,
850+
pub size: u64,
851+
852+
/// (ind, size)
853+
pub fields_size: Vec<(usize, u64)>,
854+
}
855+
856+
impl AdtVariantInfo {
857+
/// Returns ADT variants ordered by size
858+
pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List<GenericArg<'tcx>>) -> Vec<Self> {
859+
let mut variants_size = adt
860+
.variants()
861+
.iter()
862+
.enumerate()
863+
.map(|(i, variant)| {
864+
let mut fields_size = variant
865+
.fields
866+
.iter()
867+
.enumerate()
868+
.map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
869+
.collect::<Vec<_>>();
870+
fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
871+
872+
Self {
873+
ind: i,
874+
size: fields_size.iter().map(|(_, size)| size).sum(),
875+
fields_size,
876+
}
877+
})
878+
.collect::<Vec<_>>();
879+
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
880+
variants_size
881+
}
882+
}
883+
848884
/// Gets the struct or enum variant from the given `Res`
849885
pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
850886
match res {

tests/ui/result_large_err.rs

+12
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ impl LargeErrorVariants<()> {
5050
}
5151
}
5252

53+
enum MultipleLargeVariants {
54+
_Biggest([u8; 1024]),
55+
_AlsoBig([u8; 512]),
56+
_Ok(usize),
57+
}
58+
59+
impl MultipleLargeVariants {
60+
fn large_enum_error() -> Result<(), Self> {
61+
Ok(())
62+
}
63+
}
64+
5365
trait TraitForcesLargeError {
5466
fn large_error() -> Result<(), [u8; 512]> {
5567
Ok(())

tests/ui/result_large_err.stderr

+23-7
Original file line numberDiff line numberDiff line change
@@ -42,50 +42,66 @@ LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeErro
4242
error: the `Err`-variant returned from this function is very large
4343
--> $DIR/result_large_err.rs:48:34
4444
|
45+
LL | _Omg([u8; 512]),
46+
| --------------- the largest variant contains at least 512 bytes
47+
...
4548
LL | pub fn large_enum_error() -> Result<(), Self> {
46-
| ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
49+
| ^^^^^^^^^^^^^^^^
4750
|
4851
= help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
4952

5053
error: the `Err`-variant returned from this function is very large
51-
--> $DIR/result_large_err.rs:54:25
54+
--> $DIR/result_large_err.rs:60:30
55+
|
56+
LL | _Biggest([u8; 1024]),
57+
| -------------------- the largest variant contains at least 1024 bytes
58+
LL | _AlsoBig([u8; 512]),
59+
| ------------------- the variant `_AlsoBig` contains at least 512 bytes
60+
...
61+
LL | fn large_enum_error() -> Result<(), Self> {
62+
| ^^^^^^^^^^^^^^^^
63+
|
64+
= help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box<MultipleLargeVariants>`
65+
66+
error: the `Err`-variant returned from this function is very large
67+
--> $DIR/result_large_err.rs:66:25
5268
|
5369
LL | fn large_error() -> Result<(), [u8; 512]> {
5470
| ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
5571
|
5672
= help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
5773

5874
error: the `Err`-variant returned from this function is very large
59-
--> $DIR/result_large_err.rs:73:29
75+
--> $DIR/result_large_err.rs:85:29
6076
|
6177
LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
6278
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
6379
|
6480
= help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
6581

6682
error: the `Err`-variant returned from this function is very large
67-
--> $DIR/result_large_err.rs:82:40
83+
--> $DIR/result_large_err.rs:94:40
6884
|
6985
LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
7086
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
7187
|
7288
= help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
7389

7490
error: the `Err`-variant returned from this function is very large
75-
--> $DIR/result_large_err.rs:91:34
91+
--> $DIR/result_large_err.rs:103:34
7692
|
7793
LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
7894
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
7995
|
8096
= help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
8197

8298
error: the `Err`-variant returned from this function is very large
83-
--> $DIR/result_large_err.rs:95:31
99+
--> $DIR/result_large_err.rs:107:31
84100
|
85101
LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
86102
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
87103
|
88104
= help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
89105

90-
error: aborting due to 11 previous errors
106+
error: aborting due to 12 previous errors
91107

0 commit comments

Comments
 (0)