diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d5ed9aa380f..1a3229a0bda 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1377,7 +1377,8 @@ impl<'hir> LoweringContext<'_, 'hir> { 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 @@ impl<'hir> LoweringContext<'_, 'hir> { 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, }); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6d780b8448c..bb34d02f5bc 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1315,7 +1315,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { 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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { 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), }), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 9fce8fac56f..9d724a17cfc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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 @@ impl<'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 @@ impl<'hir> Generics<'hir> { 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) 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 @@ impl<'hir> Generics<'hir> { } } + 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, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 1181925dd96..52ee7e2983c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2511,7 +2511,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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( diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index cbdcf013522..67bbace39e3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -367,17 +367,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .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( diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 92cd8c2b611..cd6f8055a5c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2293,7 +2293,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // 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"); diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index e4e575542c2..6fbe6b8550c 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -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: diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 185f500808f..e58451cce82 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -324,7 +324,7 @@ pub trait InferCtxtExt<'tcx> { 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`... diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 4071c389266..b65b7458fc1 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -538,10 +538,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .enumerate() .collect::>(); - 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::>(); @@ -819,7 +819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), format!( "{} {}", - if empty_where { " where" } else { "," }, + add_where_or_comma, obligations.join(", ") ), Applicability::MaybeIncorrect, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index d506314eb93..362e034ba54 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -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( diff --git a/src/test/ui/issues/issue-35668.stderr b/src/test/ui/issues/issue-35668.stderr index 07409e9834a..84add5799ab 100644 --- a/src/test/ui/issues/issue-35668.stderr +++ b/src/test/ui/issues/issue-35668.stderr @@ -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 where &T: Mul<&T> { | +++++++++++++++++ diff --git a/src/test/ui/partialeq_help.rs b/src/test/ui/partialeq_help.rs index c3ba805405b..34b88b8a866 100644 --- a/src/test/ui/partialeq_help.rs +++ b/src/test/ui/partialeq_help.rs @@ -2,6 +2,11 @@ fn foo(a: &T, b: T) { a == b; //~ ERROR E0277 } +fn foo2(a: &T, b: T) where { + a == b; //~ ERROR E0277 +} + fn main() { foo(&1, 1); + foo2(&1, 1); } diff --git a/src/test/ui/partialeq_help.stderr b/src/test/ui/partialeq_help.stderr index 528306b22dd..fdff94f425c 100644 --- a/src/test/ui/partialeq_help.stderr +++ b/src/test/ui/partialeq_help.stderr @@ -5,11 +5,23 @@ LL | a == b; | ^^ no implementation for `&T == T` | = help: the trait `PartialEq` 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(a: &T, b: T) where &T: PartialEq { | ++++++++++++++++++++++ -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` 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(a: &T, b: T) where &T: PartialEq { + | ++++++++++++++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/assoc-type.stderr b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.stderr index 0788b17a1c0..64501c52374 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/assoc-type.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.stderr @@ -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 { | +++++++++++++++++++++++++++++ diff --git a/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr index 668e166c298..7542b81fe2a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr @@ -14,7 +14,7 @@ note: required by a bound in `foo` | LL | const fn foo() 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 { | +++++++++++++++++++ diff --git a/src/test/ui/specialization/default-associated-type-bound-2.stderr b/src/test/ui/specialization/default-associated-type-bound-2.stderr index 0fd1f65b0a2..91778ed0f4c 100644 --- a/src/test/ui/specialization/default-associated-type-bound-2.stderr +++ b/src/test/ui/specialization/default-associated-type-bound-2.stderr @@ -20,7 +20,7 @@ note: required by a bound in `X::U` | LL | type U: PartialEq; | ^^^^^^^^^^^^ 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 X for T where &'static B: PartialEq { | ++++++++++++++++++++++++++++++ diff --git a/src/test/ui/suggestions/derive-macro-missing-bounds.stderr b/src/test/ui/suggestions/derive-macro-missing-bounds.stderr index 75658f58c8a..501d083e2bc 100644 --- a/src/test/ui/suggestions/derive-macro-missing-bounds.stderr +++ b/src/test/ui/suggestions/derive-macro-missing-bounds.stderr @@ -13,7 +13,7 @@ help: consider annotating `a::Inner` 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(Inner) where a::Inner: Debug; | ++++++++++++++++++++++++ diff --git a/src/test/ui/suggestions/invalid-bin-op.stderr b/src/test/ui/suggestions/invalid-bin-op.stderr index fe5e2b5816f..94bd2d41565 100644 --- a/src/test/ui/suggestions/invalid-bin-op.stderr +++ b/src/test/ui/suggestions/invalid-bin-op.stderr @@ -15,7 +15,7 @@ help: consider annotating `S` 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(s: S, t: S) where S: PartialEq { | +++++++++++++++++++++ diff --git a/src/test/ui/suggestions/suggest-change-mut.rs b/src/test/ui/suggestions/suggest-change-mut.rs index a2bc6fd09b5..47dc7c343da 100644 --- a/src/test/ui/suggestions/suggest-change-mut.rs +++ b/src/test/ui/suggestions/suggest-change-mut.rs @@ -2,7 +2,7 @@ use std::io::{BufRead, BufReader, Read, Write}; -fn issue_81421(mut stream: T) { //~ HELP consider introducing a `where` bound +fn issue_81421(mut stream: T) { //~ HELP consider introducing a `where` clause let initial_message = format!("Hello world"); let mut buffer: Vec = Vec::new(); let bytes_written = stream.write_all(initial_message.as_bytes()); diff --git a/src/test/ui/suggestions/suggest-change-mut.stderr b/src/test/ui/suggestions/suggest-change-mut.stderr index 2fa69cd5a2c..be549239e36 100644 --- a/src/test/ui/suggestions/suggest-change-mut.stderr +++ b/src/test/ui/suggestions/suggest-change-mut.stderr @@ -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(mut stream: T) where &T: std::io::Read { | +++++++++++++++++++++++ diff --git a/src/test/ui/traits/resolution-in-overloaded-op.stderr b/src/test/ui/traits/resolution-in-overloaded-op.stderr index 3ae6bf130cc..34fae64e4d2 100644 --- a/src/test/ui/traits/resolution-in-overloaded-op.stderr +++ b/src/test/ui/traits/resolution-in-overloaded-op.stderr @@ -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>(a: &T, b: f64) -> f64 where &T: Mul { | ++++++++++++++++++ diff --git a/src/test/ui/traits/suggest-where-clause.stderr b/src/test/ui/traits/suggest-where-clause.stderr index e2cdd368888..520ee0b5ea7 100644 --- a/src/test/ui/traits/suggest-where-clause.stderr +++ b/src/test/ui/traits/suggest-where-clause.stderr @@ -49,7 +49,7 @@ error[E0277]: the trait bound `u64: From` is not satisfied LL | >::from; | ^^^^^^^^^^^^^^^^^^^^^^ the trait `From` 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() where u64: From { | ++++++++++++++++++ @@ -60,7 +60,7 @@ error[E0277]: the trait bound `u64: From<::Item>` is not satisfie LL | ::Item>>::from; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<::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() where u64: From<::Item> { | ++++++++++++++++++++++++++++++++++++++ diff --git a/src/test/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr b/src/test/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr index f4d8b4509d4..198f3e26393 100644 --- a/src/test/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr +++ b/src/test/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr @@ -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: &'static A, b: B) -> (X, X) where &'static B: From<&A> { | ++++++++++++++++++++++++++