Skip to content

Commit e848eca

Browse files
Handle empty where-clause better
1 parent 7fc9e95 commit e848eca

File tree

23 files changed

+78
-59
lines changed

23 files changed

+78
-59
lines changed

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13761376

13771377
let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> =
13781378
self.lower_generic_params_mut(&generics.params).collect();
1379-
let has_where_clause = !generics.where_clause.predicates.is_empty();
1379+
let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
1380+
let has_where_clause_token = generics.where_clause.has_where_token;
13801381
let where_clause_span = self.lower_span(generics.where_clause.span);
13811382
let span = self.lower_span(generics.span);
13821383
let res = f(self);
@@ -1394,7 +1395,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13941395
let lowered_generics = self.arena.alloc(hir::Generics {
13951396
params: self.arena.alloc_from_iter(params),
13961397
predicates: self.arena.alloc_from_iter(predicates),
1397-
has_where_clause,
1398+
has_where_clause_predicates,
1399+
has_where_clause_token,
13981400
where_clause_span,
13991401
span,
14001402
});

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13321332
generics: self.arena.alloc(hir::Generics {
13331333
params: lifetime_defs,
13341334
predicates: &[],
1335-
has_where_clause: false,
1335+
has_where_clause_predicates: false,
1336+
has_where_clause_token: false,
13361337
where_clause_span: lctx.lower_span(span),
13371338
span: lctx.lower_span(span),
13381339
}),
@@ -1654,7 +1655,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16541655
generics: this.arena.alloc(hir::Generics {
16551656
params: generic_params,
16561657
predicates: &[],
1657-
has_where_clause: false,
1658+
has_where_clause_predicates: false,
1659+
has_where_clause_token: false,
16581660
where_clause_span: this.lower_span(span),
16591661
span: this.lower_span(span),
16601662
}),

compiler/rustc_hir/src/hir.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,8 @@ pub struct GenericParamCount {
535535
pub struct Generics<'hir> {
536536
pub params: &'hir [GenericParam<'hir>],
537537
pub predicates: &'hir [WherePredicate<'hir>],
538-
pub has_where_clause: bool,
538+
pub has_where_clause_predicates: bool,
539+
pub has_where_clause_token: bool,
539540
pub where_clause_span: Span,
540541
pub span: Span,
541542
}
@@ -545,7 +546,8 @@ impl<'hir> Generics<'hir> {
545546
const NOPE: Generics<'_> = Generics {
546547
params: &[],
547548
predicates: &[],
548-
has_where_clause: false,
549+
has_where_clause_predicates: false,
550+
has_where_clause_token: false,
549551
where_clause_span: DUMMY_SP,
550552
span: DUMMY_SP,
551553
};
@@ -585,17 +587,11 @@ impl<'hir> Generics<'hir> {
585587
if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
586588
}
587589

588-
/// The `where_span` under normal circumstances points at either the predicates or the empty
589-
/// space where the `where` clause should be. Only of use for diagnostic suggestions.
590-
pub fn span_for_predicates_or_empty_place(&self) -> Span {
591-
self.where_clause_span
592-
}
593-
594590
/// `Span` where further predicates would be suggested, accounting for trailing commas, like
595591
/// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
596592
pub fn tail_span_for_predicate_suggestion(&self) -> Span {
597-
let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
598-
if self.has_where_clause {
593+
let end = self.where_clause_span.shrink_to_hi();
594+
if self.has_where_clause_predicates {
599595
self.predicates
600596
.iter()
601597
.filter(|p| p.in_where_clause())
@@ -608,6 +604,16 @@ impl<'hir> Generics<'hir> {
608604
}
609605
}
610606

607+
pub fn add_where_or_trailing_comma(&self) -> &'static str {
608+
if self.has_where_clause_predicates {
609+
","
610+
} else if self.has_where_clause_token {
611+
""
612+
} else {
613+
" where"
614+
}
615+
}
616+
611617
pub fn bounds_for_param(
612618
&self,
613619
param_def_id: LocalDefId,

compiler/rustc_infer/src/infer/error_reporting/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2547,7 +2547,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
25472547
let pred = format!("{}: {}", bound_kind, sub);
25482548
let suggestion = format!(
25492549
"{} {}",
2550-
if !generics.predicates.is_empty() { "," } else { " where" },
2550+
generics.add_where_or_trailing_comma(),
25512551
pred,
25522552
);
25532553
err.span_suggestion(

compiler/rustc_infer/src/infer/error_reporting/note.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -367,17 +367,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
367367
.collect();
368368

369369
if !clauses.is_empty() {
370-
let where_clause_span = self
371-
.tcx
372-
.hir()
373-
.get_generics(impl_item_def_id)
374-
.unwrap()
375-
.where_clause_span
376-
.shrink_to_hi();
370+
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
371+
let where_clause_span = generics.tail_span_for_predicate_suggestion();
377372

378373
let suggestion = format!(
379374
"{} {}",
380-
if !impl_predicates.is_empty() { "," } else { " where" },
375+
generics.add_where_or_trailing_comma(),
381376
clauses.join(", "),
382377
);
383378
err.span_suggestion(

compiler/rustc_lint/src/builtin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2264,7 +2264,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
22642264

22652265
// If all predicates are inferable, drop the entire clause
22662266
// (including the `where`)
2267-
if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
2267+
if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates
2268+
{
22682269
let where_span = hir_generics
22692270
.where_clause_span()
22702271
.expect("span of (nonempty) where clause should exist");

compiler/rustc_middle/src/ty/diagnostics.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,14 @@ pub fn suggest_arbitrary_trait_bound(
9292
_ => {}
9393
}
9494
// Suggest a where clause bound for a non-type parameter.
95-
let (action, prefix) = if generics.has_where_clause {
96-
("extending the", ", ")
97-
} else {
98-
("introducing a", " where ")
99-
};
10095
err.span_suggestion_verbose(
10196
generics.tail_span_for_predicate_suggestion(),
10297
&format!(
103-
"consider {} `where` bound, but there might be an alternative better way to express \
98+
"consider {} `where` clause, but there might be an alternative better way to express \
10499
this requirement",
105-
action,
100+
if generics.has_where_clause_token { "extending the" } else { "introducing a" },
106101
),
107-
format!("{}{}: {}", prefix, param_name, constraint),
102+
format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
108103
Applicability::MaybeIncorrect,
109104
);
110105
true
@@ -257,7 +252,7 @@ pub fn suggest_constraining_type_params<'a>(
257252
continue;
258253
}
259254

260-
if generics.has_where_clause {
255+
if generics.has_where_clause_predicates {
261256
// This part is a bit tricky, because using the `where` clause user can
262257
// provide zero, one or many bounds for the same type parameter, so we
263258
// have following cases to consider:

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ pub trait InferCtxtExt<'tcx> {
322322
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
323323
(
324324
generics.tail_span_for_predicate_suggestion(),
325-
format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
325+
format!("{} {}", generics.add_where_or_trailing_comma(), pred),
326326
)
327327
}
328328

@@ -337,15 +337,16 @@ fn suggest_restriction<'tcx>(
337337
fn_sig: Option<&hir::FnSig<'_>>,
338338
projection: Option<&ty::ProjectionTy<'_>>,
339339
trait_pred: ty::PolyTraitPredicate<'tcx>,
340-
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
341-
) {
342340
// When we are dealing with a trait, `super_traits` will be `Some`:
343341
// Given `trait T: A + B + C {}`
344342
// - ^^^^^^^^^ GenericBounds
345343
// |
346344
// &Ident
347-
let span = generics.span_for_predicates_or_empty_place();
348-
if span.from_expansion() || span.desugaring_kind().is_some() {
345+
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
346+
) {
347+
if generics.where_clause_span.from_expansion()
348+
|| generics.where_clause_span.desugaring_kind().is_some()
349+
{
349350
return;
350351
}
351352
// Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...

compiler/rustc_typeck/src/check/method/suggest.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
542542
};
543543
if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
544544
if let Some(g) = kind.generics() {
545-
let key = match g.predicates {
546-
[.., pred] => (pred.span().shrink_to_hi(), false),
547-
[] => (g.span_for_predicates_or_empty_place(), true),
548-
};
545+
let key = (
546+
g.tail_span_for_predicate_suggestion(),
547+
g.add_where_or_trailing_comma(),
548+
);
549549
type_params
550550
.entry(key)
551551
.or_insert_with(FxHashSet::default)
@@ -809,7 +809,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
809809
.enumerate()
810810
.collect::<Vec<(usize, String)>>();
811811

812-
for ((span, empty_where), obligations) in type_params.into_iter() {
812+
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
813813
restrict_type_params = true;
814814
// #74886: Sort here so that the output is always the same.
815815
let mut obligations = obligations.into_iter().collect::<Vec<_>>();
@@ -823,7 +823,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
823823
),
824824
format!(
825825
"{} {}",
826-
if empty_where { " where" } else { "," },
826+
add_where_or_comma,
827827
obligations.join(", ")
828828
),
829829
Applicability::MaybeIncorrect,

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
421421

422422
let suggestion = format!(
423423
"{} {}",
424-
if !gat_item_hir.generics.predicates.is_empty() { "," } else { " where" },
424+
gat_item_hir.generics.add_where_or_trailing_comma(),
425425
unsatisfied_bounds.join(", "),
426426
);
427427
err.span_suggestion(

src/test/ui/issues/issue-35668.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | a.iter().map(|a| a*a)
66
| |
77
| &T
88
|
9-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
9+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1010
|
1111
LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
1212
| +++++++++++++++++

src/test/ui/partialeq_help.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ fn foo<T: PartialEq>(a: &T, b: T) {
22
a == b; //~ ERROR E0277
33
}
44

5+
fn foo2<T: PartialEq>(a: &T, b: T) where {
6+
a == b; //~ ERROR E0277
7+
}
8+
59
fn main() {
610
foo(&1, 1);
11+
foo2(&1, 1);
712
}

src/test/ui/partialeq_help.stderr

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@ LL | a == b;
55
| ^^ no implementation for `&T == T`
66
|
77
= help: the trait `PartialEq<T>` is not implemented for `&T`
8-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
8+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
99
|
1010
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
1111
| ++++++++++++++++++++++
1212

13-
error: aborting due to previous error
13+
error[E0277]: can't compare `&T` with `T`
14+
--> $DIR/partialeq_help.rs:6:7
15+
|
16+
LL | a == b;
17+
| ^^ no implementation for `&T == T`
18+
|
19+
= help: the trait `PartialEq<T>` is not implemented for `&T`
20+
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
21+
|
22+
LL | fn foo2<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
23+
| ++++++++++++++++
24+
25+
error: aborting due to 2 previous errors
1426

1527
For more information about this error, try `rustc --explain E0277`.

src/test/ui/rfc-2632-const-trait-impl/assoc-type.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ note: required by a bound in `Foo::Bar`
1515
|
1616
LL | type Bar: ~const std::ops::Add;
1717
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::Bar`
18-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
18+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1919
|
2020
LL | impl const Foo for NonConstAdd where NonConstAdd: ~const Add {
2121
| +++++++++++++++++++++++++++++

src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ note: required by a bound in `foo`
1414
|
1515
LL | const fn foo<T>() where T: ~const Tr {}
1616
| ^^^^^^^^^ required by this bound in `foo`
17-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
17+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1818
|
1919
LL | pub trait Foo where (): ~const Tr {
2020
| +++++++++++++++++++

src/test/ui/specialization/default-associated-type-bound-2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ note: required by a bound in `X::U`
2020
|
2121
LL | type U: PartialEq<T>;
2222
| ^^^^^^^^^^^^ required by this bound in `X::U`
23-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
23+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
2424
|
2525
LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
2626
| ++++++++++++++++++++++++++++++

src/test/ui/suggestions/derive-macro-missing-bounds.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ help: consider annotating `a::Inner<T>` with `#[derive(Debug)]`
1313
|
1414
LL | #[derive(Debug)]
1515
|
16-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
16+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1717
|
1818
LL | struct Outer<T>(Inner<T>) where a::Inner<T>: Debug;
1919
| ++++++++++++++++++++++++

src/test/ui/suggestions/invalid-bin-op.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ help: consider annotating `S<T>` with `#[derive(PartialEq)]`
1515
|
1616
LL | #[derive(PartialEq)]
1717
|
18-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
18+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1919
|
2020
LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
2121
| +++++++++++++++++++++

src/test/ui/suggestions/suggest-change-mut.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::io::{BufRead, BufReader, Read, Write};
44

5-
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` bound
5+
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` clause
66
let initial_message = format!("Hello world");
77
let mut buffer: Vec<u8> = Vec::new();
88
let bytes_written = stream.write_all(initial_message.as_bytes());

src/test/ui/suggestions/suggest-change-mut.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ help: consider removing the leading `&`-reference
1616
LL - let mut stream_reader = BufReader::new(&stream);
1717
LL + let mut stream_reader = BufReader::new(stream);
1818
|
19-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
19+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
2020
|
2121
LL | fn issue_81421<T: Read + Write>(mut stream: T) where &T: std::io::Read {
2222
| +++++++++++++++++++++++

src/test/ui/traits/resolution-in-overloaded-op.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | a * b
66
| |
77
| &T
88
|
9-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
9+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1010
|
1111
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
1212
| ++++++++++++++++++

src/test/ui/traits/suggest-where-clause.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ error[E0277]: the trait bound `u64: From<T>` is not satisfied
4949
LL | <u64 as From<T>>::from;
5050
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `u64`
5151
|
52-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
52+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
5353
|
5454
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<T> {
5555
| ++++++++++++++++++
@@ -60,7 +60,7 @@ error[E0277]: the trait bound `u64: From<<T as Iterator>::Item>` is not satisfie
6060
LL | <u64 as From<<T as Iterator>::Item>>::from;
6161
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<<T as Iterator>::Item>` is not implemented for `u64`
6262
|
63-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
63+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
6464
|
6565
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<<T as Iterator>::Item> {
6666
| ++++++++++++++++++++++++++++++++++++++

src/test/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | (a, a)
55
| ^ the trait `From<&A>` is not implemented for `&'static B`
66
|
77
= note: required because of the requirements on the impl of `Into<&'static B>` for `&A`
8-
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
8+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
99
|
1010
LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) where &'static B: From<&A> {
1111
| ++++++++++++++++++++++++++

0 commit comments

Comments
 (0)