Handle empty where-clause better

This commit is contained in:
Michael Goulet 2022-06-05 17:37:45 -07:00
parent 8506b7d4e0
commit 9c47afe9fa
23 changed files with 78 additions and 59 deletions

View file

@ -1377,7 +1377,8 @@ fn lower_generics<T>(
let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> =
self.lower_generic_params_mut(&generics.params).collect();
let has_where_clause = !generics.where_clause.predicates.is_empty();
let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
let has_where_clause_token = generics.where_clause.has_where_token;
let where_clause_span = self.lower_span(generics.where_clause.span);
let span = self.lower_span(generics.span);
let res = f(self);
@ -1395,7 +1396,8 @@ fn lower_generics<T>(
let lowered_generics = self.arena.alloc(hir::Generics {
params: self.arena.alloc_from_iter(params),
predicates: self.arena.alloc_from_iter(predicates),
has_where_clause,
has_where_clause_predicates,
has_where_clause_token,
where_clause_span,
span,
});

View file

@ -1315,7 +1315,8 @@ fn lower_opaque_impl_trait(
generics: self.arena.alloc(hir::Generics {
params: lifetime_defs,
predicates: &[],
has_where_clause: false,
has_where_clause_predicates: false,
has_where_clause_token: false,
where_clause_span: lctx.lower_span(span),
span: lctx.lower_span(span),
}),
@ -1637,7 +1638,8 @@ fn lower_async_fn_ret_ty(
generics: this.arena.alloc(hir::Generics {
params: generic_params,
predicates: &[],
has_where_clause: false,
has_where_clause_predicates: false,
has_where_clause_token: false,
where_clause_span: this.lower_span(span),
span: this.lower_span(span),
}),

View file

@ -535,7 +535,8 @@ pub struct GenericParamCount {
pub struct Generics<'hir> {
pub params: &'hir [GenericParam<'hir>],
pub predicates: &'hir [WherePredicate<'hir>],
pub has_where_clause: bool,
pub has_where_clause_predicates: bool,
pub has_where_clause_token: bool,
pub where_clause_span: Span,
pub span: Span,
}
@ -545,7 +546,8 @@ pub const fn empty() -> &'hir Generics<'hir> {
const NOPE: Generics<'_> = Generics {
params: &[],
predicates: &[],
has_where_clause: false,
has_where_clause_predicates: false,
has_where_clause_token: false,
where_clause_span: DUMMY_SP,
span: DUMMY_SP,
};
@ -585,17 +587,11 @@ pub fn where_clause_span(&self) -> Option<Span> {
if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
}
/// The `where_span` under normal circumstances points at either the predicates or the empty
/// space where the `where` clause should be. Only of use for diagnostic suggestions.
pub fn span_for_predicates_or_empty_place(&self) -> Span {
self.where_clause_span
}
/// `Span` where further predicates would be suggested, accounting for trailing commas, like
/// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
pub fn tail_span_for_predicate_suggestion(&self) -> Span {
let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
if self.has_where_clause {
let end = self.where_clause_span.shrink_to_hi();
if self.has_where_clause_predicates {
self.predicates
.iter()
.filter(|p| p.in_where_clause())
@ -608,6 +604,16 @@ pub fn tail_span_for_predicate_suggestion(&self) -> Span {
}
}
pub fn add_where_or_trailing_comma(&self) -> &'static str {
if self.has_where_clause_predicates {
","
} else if self.has_where_clause_token {
""
} else {
" where"
}
}
pub fn bounds_for_param(
&self,
param_def_id: LocalDefId,

View file

@ -2511,7 +2511,7 @@ enum SubOrigin<'hir> {
let pred = format!("{}: {}", bound_kind, sub);
let suggestion = format!(
"{} {}",
if !generics.predicates.is_empty() { "," } else { " where" },
generics.add_where_or_trailing_comma(),
pred,
);
err.span_suggestion(

View file

@ -367,17 +367,12 @@ pub(super) fn report_concrete_failure(
.collect();
if !clauses.is_empty() {
let where_clause_span = self
.tcx
.hir()
.get_generics(impl_item_def_id)
.unwrap()
.where_clause_span
.shrink_to_hi();
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
let where_clause_span = generics.tail_span_for_predicate_suggestion();
let suggestion = format!(
"{} {}",
if !impl_predicates.is_empty() { "," } else { " where" },
generics.add_where_or_trailing_comma(),
clauses.join(", "),
);
err.span_suggestion(

View file

@ -2293,7 +2293,8 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
// If all predicates are inferable, drop the entire clause
// (including the `where`)
if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates
{
let where_span = hir_generics
.where_clause_span()
.expect("span of (nonempty) where clause should exist");

View file

@ -92,19 +92,14 @@ pub fn suggest_arbitrary_trait_bound(
_ => {}
}
// Suggest a where clause bound for a non-type parameter.
let (action, prefix) = if generics.has_where_clause {
("extending the", ", ")
} else {
("introducing a", " where ")
};
err.span_suggestion_verbose(
generics.tail_span_for_predicate_suggestion(),
&format!(
"consider {} `where` bound, but there might be an alternative better way to express \
"consider {} `where` clause, but there might be an alternative better way to express \
this requirement",
action,
if generics.has_where_clause_token { "extending the" } else { "introducing a" },
),
format!("{}{}: {}", prefix, param_name, constraint),
format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
Applicability::MaybeIncorrect,
);
true
@ -257,7 +252,7 @@ pub fn suggest_constraining_type_params<'a>(
continue;
}
if generics.has_where_clause {
if generics.has_where_clause_predicates {
// This part is a bit tricky, because using the `where` clause user can
// provide zero, one or many bounds for the same type parameter, so we
// have following cases to consider:

View file

@ -324,7 +324,7 @@ fn suggest_derive(
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
(
generics.tail_span_for_predicate_suggestion(),
format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
format!("{} {}", generics.add_where_or_trailing_comma(), pred),
)
}
@ -339,15 +339,16 @@ fn suggest_restriction<'tcx>(
fn_sig: Option<&hir::FnSig<'_>>,
projection: Option<&ty::ProjectionTy<'_>>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
) {
// When we are dealing with a trait, `super_traits` will be `Some`:
// Given `trait T: A + B + C {}`
// - ^^^^^^^^^ GenericBounds
// |
// &Ident
let span = generics.span_for_predicates_or_empty_place();
if span.from_expansion() || span.desugaring_kind().is_some() {
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
) {
if generics.where_clause_span.from_expansion()
|| generics.where_clause_span.desugaring_kind().is_some()
{
return;
}
// Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...

View file

@ -538,10 +538,10 @@ pub fn report_method_error(
};
if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
if let Some(g) = kind.generics() {
let key = match g.predicates {
[.., pred] => (pred.span().shrink_to_hi(), false),
[] => (g.span_for_predicates_or_empty_place(), true),
};
let key = (
g.tail_span_for_predicate_suggestion(),
g.add_where_or_trailing_comma(),
);
type_params
.entry(key)
.or_insert_with(FxHashSet::default)
@ -805,7 +805,7 @@ pub fn report_method_error(
.enumerate()
.collect::<Vec<(usize, String)>>();
for ((span, empty_where), obligations) in type_params.into_iter() {
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
restrict_type_params = true;
// #74886: Sort here so that the output is always the same.
let mut obligations = obligations.into_iter().collect::<Vec<_>>();
@ -819,7 +819,7 @@ trait bound{s}",
),
format!(
"{} {}",
if empty_where { " where" } else { "," },
add_where_or_comma,
obligations.join(", ")
),
Applicability::MaybeIncorrect,

View file

@ -421,7 +421,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
let suggestion = format!(
"{} {}",
if !gat_item_hir.generics.predicates.is_empty() { "," } else { " where" },
gat_item_hir.generics.add_where_or_trailing_comma(),
unsatisfied_bounds.join(", "),
);
err.span_suggestion(

View file

@ -6,7 +6,7 @@ LL | a.iter().map(|a| a*a)
| |
| &T
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
| +++++++++++++++++

View file

@ -2,6 +2,11 @@ fn foo<T: PartialEq>(a: &T, b: T) {
a == b; //~ ERROR E0277
}
fn foo2<T: PartialEq>(a: &T, b: T) where {
a == b; //~ ERROR E0277
}
fn main() {
foo(&1, 1);
foo2(&1, 1);
}

View file

@ -5,11 +5,23 @@ LL | a == b;
| ^^ no implementation for `&T == T`
|
= help: the trait `PartialEq<T>` is not implemented for `&T`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
| ++++++++++++++++++++++
error: aborting due to previous error
error[E0277]: can't compare `&T` with `T`
--> $DIR/partialeq_help.rs:6:7
|
LL | a == b;
| ^^ no implementation for `&T == T`
|
= help: the trait `PartialEq<T>` is not implemented for `&T`
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn foo2<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
| ++++++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -15,7 +15,7 @@ note: required by a bound in `Foo::Bar`
|
LL | type Bar: ~const std::ops::Add;
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::Bar`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | impl const Foo for NonConstAdd where NonConstAdd: ~const Add {
| +++++++++++++++++++++++++++++

View file

@ -14,7 +14,7 @@ note: required by a bound in `foo`
|
LL | const fn foo<T>() where T: ~const Tr {}
| ^^^^^^^^^ required by this bound in `foo`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | pub trait Foo where (): ~const Tr {
| +++++++++++++++++++

View file

@ -20,7 +20,7 @@ note: required by a bound in `X::U`
|
LL | type U: PartialEq<T>;
| ^^^^^^^^^^^^ required by this bound in `X::U`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
| ++++++++++++++++++++++++++++++

View file

@ -13,7 +13,7 @@ help: consider annotating `a::Inner<T>` with `#[derive(Debug)]`
|
LL | #[derive(Debug)]
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | struct Outer<T>(Inner<T>) where a::Inner<T>: Debug;
| ++++++++++++++++++++++++

View file

@ -15,7 +15,7 @@ help: consider annotating `S<T>` with `#[derive(PartialEq)]`
|
LL | #[derive(PartialEq)]
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
| +++++++++++++++++++++

View file

@ -2,7 +2,7 @@
use std::io::{BufRead, BufReader, Read, Write};
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` bound
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` clause
let initial_message = format!("Hello world");
let mut buffer: Vec<u8> = Vec::new();
let bytes_written = stream.write_all(initial_message.as_bytes());

View file

@ -16,7 +16,7 @@ help: consider removing the leading `&`-reference
LL - let mut stream_reader = BufReader::new(&stream);
LL + let mut stream_reader = BufReader::new(stream);
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn issue_81421<T: Read + Write>(mut stream: T) where &T: std::io::Read {
| +++++++++++++++++++++++

View file

@ -6,7 +6,7 @@ LL | a * b
| |
| &T
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
| ++++++++++++++++++

View file

@ -49,7 +49,7 @@ error[E0277]: the trait bound `u64: From<T>` is not satisfied
LL | <u64 as From<T>>::from;
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `u64`
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<T> {
| ++++++++++++++++++
@ -60,7 +60,7 @@ error[E0277]: the trait bound `u64: From<<T as Iterator>::Item>` is not satisfie
LL | <u64 as From<<T as Iterator>::Item>>::from;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<<T as Iterator>::Item>` is not implemented for `u64`
|
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<<T as Iterator>::Item> {
| ++++++++++++++++++++++++++++++++++++++

View file

@ -5,7 +5,7 @@ LL | (a, a)
| ^ the trait `From<&A>` is not implemented for `&'static B`
|
= note: required because of the requirements on the impl of `Into<&'static B>` for `&A`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) where &'static B: From<&A> {
| ++++++++++++++++++++++++++