1
Fork 0

Auto merge of #86454 - tlyu:refactor-unsized-suggestions, r=davidtwco

Refactor unsized suggestions

`@rustbot` label +A-diagnostics +A-traits +A-typesystem +C-cleanup +T-compiler
This commit is contained in:
bors 2021-09-03 08:51:21 +00:00
commit e4e4179539

View file

@ -16,6 +16,8 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder,
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::GenericParam;
use rustc_hir::Item;
use rustc_hir::Node; use rustc_hir::Node;
use rustc_middle::mir::abstract_const::NotConstEvaluatable; use rustc_middle::mir::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::ExpectedFound;
@ -1138,6 +1140,20 @@ trait InferCtxtPrivExt<'tcx> {
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
); );
fn maybe_suggest_unsized_generics(
&self,
err: &mut DiagnosticBuilder<'tcx>,
span: Span,
node: Node<'hir>,
);
fn maybe_indirection_for_unsized(
&self,
err: &mut DiagnosticBuilder<'tcx>,
item: &'hir Item<'hir>,
param: &'hir GenericParam<'hir>,
) -> bool;
fn is_recursive_obligation( fn is_recursive_obligation(
&self, &self,
obligated_types: &mut Vec<&ty::TyS<'tcx>>, obligated_types: &mut Vec<&ty::TyS<'tcx>>,
@ -1816,7 +1832,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
) => (pred, item_def_id, span), ) => (pred, item_def_id, span),
_ => return, _ => return,
}; };
debug!(
"suggest_unsized_bound_if_applicable: pred={:?} item_def_id={:?} span={:?}",
pred, item_def_id, span
);
let node = match ( let node = match (
self.tcx.hir().get_if_local(item_def_id), self.tcx.hir().get_if_local(item_def_id),
Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), Some(pred.def_id()) == self.tcx.lang_items().sized_trait(),
@ -1824,80 +1843,105 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
(Some(node), true) => node, (Some(node), true) => node,
_ => return, _ => return,
}; };
self.maybe_suggest_unsized_generics(err, span, node);
}
fn maybe_suggest_unsized_generics(
&self,
err: &mut DiagnosticBuilder<'tcx>,
span: Span,
node: Node<'hir>,
) {
let generics = match node.generics() { let generics = match node.generics() {
Some(generics) => generics, Some(generics) => generics,
None => return, None => return,
}; };
for param in generics.params { let sized_trait = self.tcx.lang_items().sized_trait();
if param.span != span debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params);
|| param.bounds.iter().any(|bound| { debug!("maybe_suggest_unsized_generics: generics.where_clause={:?}", generics.where_clause);
bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) let param = generics
== self.tcx.lang_items().sized_trait() .params
}) .iter()
{ .filter(|param| param.span == span)
continue; .filter(|param| {
} // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
match node { // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
hir::Node::Item( param
item .bounds
@ .iter()
hir::Item { .all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait)
kind: })
hir::ItemKind::Enum(..) .next();
| hir::ItemKind::Struct(..) let param = match param {
| hir::ItemKind::Union(..), Some(param) => param,
.. _ => return,
}, };
) => { debug!("maybe_suggest_unsized_generics: param={:?}", param);
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a match node {
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` hir::Node::Item(
// is not. item
let mut visitor = FindTypeParam { @
param: param.name.ident().name, hir::Item {
invalid_spans: vec![], // Only suggest indirection for uses of type parameters in ADTs.
nested: false, kind:
}; hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
visitor.visit_item(item); ..
if !visitor.invalid_spans.is_empty() { },
let mut multispan: MultiSpan = param.span.into(); ) => {
multispan.push_span_label( if self.maybe_indirection_for_unsized(err, item, param) {
param.span, return;
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
);
for sp in visitor.invalid_spans {
multispan.push_span_label(
sp,
format!(
"...if indirection were used here: `Box<{}>`",
param.name.ident(),
),
);
}
err.span_help(
multispan,
&format!(
"you could relax the implicit `Sized` bound on `{T}` if it were \
used through indirection like `&{T}` or `Box<{T}>`",
T = param.name.ident(),
),
);
return;
}
} }
_ => {}
} }
let (span, separator) = match param.bounds { _ => {}
[] => (span.shrink_to_hi(), ":"), };
[.., bound] => (bound.span().shrink_to_hi(), " +"), // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
}; let (span, separator) = match param.bounds {
err.span_suggestion_verbose( [] => (span.shrink_to_hi(), ":"),
span, [.., bound] => (bound.span().shrink_to_hi(), " +"),
"consider relaxing the implicit `Sized` restriction", };
format!("{} ?Sized", separator), err.span_suggestion_verbose(
Applicability::MachineApplicable, span,
); "consider relaxing the implicit `Sized` restriction",
return; format!("{} ?Sized", separator),
Applicability::MachineApplicable,
);
}
fn maybe_indirection_for_unsized(
&self,
err: &mut DiagnosticBuilder<'tcx>,
item: &'hir Item<'hir>,
param: &'hir GenericParam<'hir>,
) -> bool {
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
let mut visitor =
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
visitor.visit_item(item);
if visitor.invalid_spans.is_empty() {
return false;
} }
let mut multispan: MultiSpan = param.span.into();
multispan.push_span_label(
param.span,
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
);
for sp in visitor.invalid_spans {
multispan.push_span_label(
sp,
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
);
}
err.span_help(
multispan,
&format!(
"you could relax the implicit `Sized` bound on `{T}` if it were \
used through indirection like `&{T}` or `Box<{T}>`",
T = param.name.ident(),
),
);
true
} }
fn is_recursive_obligation( fn is_recursive_obligation(
@ -1948,6 +1992,7 @@ impl<'v> Visitor<'v> for FindTypeParam {
if path.segments.len() == 1 && path.segments[0].ident.name == self.param => if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
{ {
if !self.nested { if !self.nested {
debug!("FindTypeParam::visit_ty: ty={:?}", ty);
self.invalid_spans.push(ty.span); self.invalid_spans.push(ty.span);
} }
} }