Skip to content

Commit e4e4179

Browse files
committed
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
2 parents fbdff7f + 3252432 commit e4e4179

File tree

1 file changed

+112
-67
lines changed
  • compiler/rustc_trait_selection/src/traits/error_reporting

1 file changed

+112
-67
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+112-67
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder,
1616
use rustc_hir as hir;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_hir::intravisit::Visitor;
19+
use rustc_hir::GenericParam;
20+
use rustc_hir::Item;
1921
use rustc_hir::Node;
2022
use rustc_middle::mir::abstract_const::NotConstEvaluatable;
2123
use rustc_middle::ty::error::ExpectedFound;
@@ -1138,6 +1140,20 @@ trait InferCtxtPrivExt<'tcx> {
11381140
obligation: &PredicateObligation<'tcx>,
11391141
);
11401142

1143+
fn maybe_suggest_unsized_generics(
1144+
&self,
1145+
err: &mut DiagnosticBuilder<'tcx>,
1146+
span: Span,
1147+
node: Node<'hir>,
1148+
);
1149+
1150+
fn maybe_indirection_for_unsized(
1151+
&self,
1152+
err: &mut DiagnosticBuilder<'tcx>,
1153+
item: &'hir Item<'hir>,
1154+
param: &'hir GenericParam<'hir>,
1155+
) -> bool;
1156+
11411157
fn is_recursive_obligation(
11421158
&self,
11431159
obligated_types: &mut Vec<&ty::TyS<'tcx>>,
@@ -1816,88 +1832,116 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
18161832
) => (pred, item_def_id, span),
18171833
_ => return,
18181834
};
1819-
1835+
debug!(
1836+
"suggest_unsized_bound_if_applicable: pred={:?} item_def_id={:?} span={:?}",
1837+
pred, item_def_id, span
1838+
);
18201839
let node = match (
18211840
self.tcx.hir().get_if_local(item_def_id),
18221841
Some(pred.def_id()) == self.tcx.lang_items().sized_trait(),
18231842
) {
18241843
(Some(node), true) => node,
18251844
_ => return,
18261845
};
1846+
self.maybe_suggest_unsized_generics(err, span, node);
1847+
}
1848+
1849+
fn maybe_suggest_unsized_generics(
1850+
&self,
1851+
err: &mut DiagnosticBuilder<'tcx>,
1852+
span: Span,
1853+
node: Node<'hir>,
1854+
) {
18271855
let generics = match node.generics() {
18281856
Some(generics) => generics,
18291857
None => return,
18301858
};
1831-
for param in generics.params {
1832-
if param.span != span
1833-
|| param.bounds.iter().any(|bound| {
1834-
bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id())
1835-
== self.tcx.lang_items().sized_trait()
1836-
})
1837-
{
1838-
continue;
1839-
}
1840-
match node {
1841-
hir::Node::Item(
1842-
item
1843-
@
1844-
hir::Item {
1845-
kind:
1846-
hir::ItemKind::Enum(..)
1847-
| hir::ItemKind::Struct(..)
1848-
| hir::ItemKind::Union(..),
1849-
..
1850-
},
1851-
) => {
1852-
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
1853-
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
1854-
// is not.
1855-
let mut visitor = FindTypeParam {
1856-
param: param.name.ident().name,
1857-
invalid_spans: vec![],
1858-
nested: false,
1859-
};
1860-
visitor.visit_item(item);
1861-
if !visitor.invalid_spans.is_empty() {
1862-
let mut multispan: MultiSpan = param.span.into();
1863-
multispan.push_span_label(
1864-
param.span,
1865-
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
1866-
);
1867-
for sp in visitor.invalid_spans {
1868-
multispan.push_span_label(
1869-
sp,
1870-
format!(
1871-
"...if indirection were used here: `Box<{}>`",
1872-
param.name.ident(),
1873-
),
1874-
);
1875-
}
1876-
err.span_help(
1877-
multispan,
1878-
&format!(
1879-
"you could relax the implicit `Sized` bound on `{T}` if it were \
1880-
used through indirection like `&{T}` or `Box<{T}>`",
1881-
T = param.name.ident(),
1882-
),
1883-
);
1884-
return;
1885-
}
1859+
let sized_trait = self.tcx.lang_items().sized_trait();
1860+
debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params);
1861+
debug!("maybe_suggest_unsized_generics: generics.where_clause={:?}", generics.where_clause);
1862+
let param = generics
1863+
.params
1864+
.iter()
1865+
.filter(|param| param.span == span)
1866+
.filter(|param| {
1867+
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
1868+
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
1869+
param
1870+
.bounds
1871+
.iter()
1872+
.all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait)
1873+
})
1874+
.next();
1875+
let param = match param {
1876+
Some(param) => param,
1877+
_ => return,
1878+
};
1879+
debug!("maybe_suggest_unsized_generics: param={:?}", param);
1880+
match node {
1881+
hir::Node::Item(
1882+
item
1883+
@
1884+
hir::Item {
1885+
// Only suggest indirection for uses of type parameters in ADTs.
1886+
kind:
1887+
hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
1888+
..
1889+
},
1890+
) => {
1891+
if self.maybe_indirection_for_unsized(err, item, param) {
1892+
return;
18861893
}
1887-
_ => {}
18881894
}
1889-
let (span, separator) = match param.bounds {
1890-
[] => (span.shrink_to_hi(), ":"),
1891-
[.., bound] => (bound.span().shrink_to_hi(), " +"),
1892-
};
1893-
err.span_suggestion_verbose(
1894-
span,
1895-
"consider relaxing the implicit `Sized` restriction",
1896-
format!("{} ?Sized", separator),
1897-
Applicability::MachineApplicable,
1895+
_ => {}
1896+
};
1897+
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
1898+
let (span, separator) = match param.bounds {
1899+
[] => (span.shrink_to_hi(), ":"),
1900+
[.., bound] => (bound.span().shrink_to_hi(), " +"),
1901+
};
1902+
err.span_suggestion_verbose(
1903+
span,
1904+
"consider relaxing the implicit `Sized` restriction",
1905+
format!("{} ?Sized", separator),
1906+
Applicability::MachineApplicable,
1907+
);
1908+
}
1909+
1910+
fn maybe_indirection_for_unsized(
1911+
&self,
1912+
err: &mut DiagnosticBuilder<'tcx>,
1913+
item: &'hir Item<'hir>,
1914+
param: &'hir GenericParam<'hir>,
1915+
) -> bool {
1916+
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
1917+
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
1918+
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
1919+
let mut visitor =
1920+
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
1921+
visitor.visit_item(item);
1922+
if visitor.invalid_spans.is_empty() {
1923+
return false;
1924+
}
1925+
let mut multispan: MultiSpan = param.span.into();
1926+
multispan.push_span_label(
1927+
param.span,
1928+
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
1929+
);
1930+
for sp in visitor.invalid_spans {
1931+
multispan.push_span_label(
1932+
sp,
1933+
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
18981934
);
1899-
return;
19001935
}
1936+
err.span_help(
1937+
multispan,
1938+
&format!(
1939+
"you could relax the implicit `Sized` bound on `{T}` if it were \
1940+
used through indirection like `&{T}` or `Box<{T}>`",
1941+
T = param.name.ident(),
1942+
),
1943+
);
1944+
true
19011945
}
19021946

19031947
fn is_recursive_obligation(
@@ -1948,6 +1992,7 @@ impl<'v> Visitor<'v> for FindTypeParam {
19481992
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
19491993
{
19501994
if !self.nested {
1995+
debug!("FindTypeParam::visit_ty: ty={:?}", ty);
19511996
self.invalid_spans.push(ty.span);
19521997
}
19531998
}

0 commit comments

Comments
 (0)