1
Fork 0

Auto merge of #100720 - camsteffen:representable, r=cjgillot

Rewrite representability

 * Improve placement of `Box` in the suggestion
 * Multiple items in a cycle emit 1 error instead of an error for each item in the cycle
 * Introduce `representability` query to avoid traversing an item every time it is used.
 * Also introduce `params_in_repr` query to avoid traversing generic items every time it is used.
This commit is contained in:
bors 2022-10-08 11:53:25 +00:00
commit bba9785dd7
61 changed files with 537 additions and 744 deletions

View file

@ -2751,82 +2751,6 @@ impl<'v> Visitor<'v> for FindTypeParam {
}
}
pub fn recursive_type_with_infinite_size_error<'tcx>(
tcx: TyCtxt<'tcx>,
type_def_id: DefId,
spans: Vec<(Span, Option<hir::HirId>)>,
) {
assert!(type_def_id.is_local());
let span = tcx.def_span(type_def_id);
let path = tcx.def_path_str(type_def_id);
let mut err =
struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path);
err.span_label(span, "recursive type has infinite size");
for &(span, _) in &spans {
err.span_label(span, "recursive without indirection");
}
let msg = format!(
"insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable",
path,
);
if spans.len() <= 4 {
// FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
err.multipart_suggestion(
&msg,
spans
.into_iter()
.flat_map(|(span, field_id)| {
if let Some(generic_span) = get_option_generic_from_field_id(tcx, field_id) {
// If we match an `Option` and can grab the span of the Option's generic, then
// suggest boxing the generic arg for a non-null niche optimization.
vec![
(generic_span.shrink_to_lo(), "Box<".to_string()),
(generic_span.shrink_to_hi(), ">".to_string()),
]
} else {
vec![
(span.shrink_to_lo(), "Box<".to_string()),
(span.shrink_to_hi(), ">".to_string()),
]
}
})
.collect(),
Applicability::HasPlaceholders,
);
} else {
err.help(&msg);
}
err.emit();
}
/// Extract the span for the generic type `T` of `Option<T>` in a field definition
fn get_option_generic_from_field_id(tcx: TyCtxt<'_>, field_id: Option<hir::HirId>) -> Option<Span> {
let node = tcx.hir().find(field_id?);
// Expect a field from our field_id
let Some(hir::Node::Field(field_def)) = node
else { bug!("Expected HirId corresponding to FieldDef, found: {:?}", node) };
// Match a type that is a simple QPath with no Self
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = &field_def.ty.kind
else { return None };
// Check if the path we're checking resolves to Option
let hir::def::Res::Def(_, did) = path.res
else { return None };
// Bail if this path doesn't describe `::core::option::Option`
if !tcx.is_diagnostic_item(sym::Option, did) {
return None;
}
// Match a single generic arg in the 0th path segment
let generic_arg = path.segments.last()?.args?.args.get(0)?;
// Take the span out of the type, if it's a type
if let hir::GenericArg::Type(generic_ty) = generic_arg { Some(generic_ty.span) } else { None }
}
/// Summarizes information
#[derive(Clone)]
pub enum ArgKind {