From 8ed7fca74cb2bb6d868b44344e46d0e6add1f03f Mon Sep 17 00:00:00 2001 From: William Venner Date: Sat, 11 Jun 2022 01:01:18 +0100 Subject: [PATCH 1/3] Add `String::replace_first` and `String::replace_last` Rebased and modified by zachs18. Co-authored-by: zachs18 <8355914+zachs18@users.noreply.github.com> --- library/alloc/src/lib.rs | 1 + library/alloc/src/string.rs | 61 ++++++++++++++++++++++++++++++ library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/string.rs | 34 +++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 87ad5b0ce30e6..fd54a375f3ea9 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -85,6 +85,7 @@ // // Library features: // tidy-alphabetical-start +#![cfg_attr(not(no_global_oom_handling), feature(string_replace_in_place))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_into_iter_constructors)] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ae30cabf5af5b..2e70871fcef87 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2090,6 +2090,67 @@ impl String { unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes()); } + /// Replaces the leftmost occurrence of a pattern with another string, in-place. + /// + /// This method can be preferred over [`string = string.replacen(..., 1);`][replacen], + /// as it can use the `String`'s existing capacity to prevent a reallocation if + /// sufficient space is available. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_replace_in_place)] + /// + /// let mut s = String::from("Test Results: ❌❌❌"); + /// + /// // Replace the leftmost ❌ with a ✅ + /// s.replace_first('❌', "✅"); + /// assert_eq!(s, "Test Results: ✅❌❌"); + /// ``` + /// + /// [replacen]: ../../std/primitive.str.html#method.replacen + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_replace_in_place", issue = "147949")] + pub fn replace_first(&mut self, from: P, to: &str) { + let range = match self.match_indices(from).next() { + Some((start, match_str)) => start..start + match_str.len(), + None => return, + }; + + self.replace_range(range, to); + } + + /// Replaces the rightmost occurrence of a pattern with another string, in-place. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_replace_in_place)] + /// + /// let mut s = String::from("Test Results: ❌❌❌"); + /// + /// // Replace the rightmost ❌ with a ✅ + /// s.replace_last('❌', "✅"); + /// assert_eq!(s, "Test Results: ❌❌✅"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_replace_in_place", issue = "147949")] + pub fn replace_last(&mut self, from: P, to: &str) + where + for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { + let range = match self.rmatch_indices(from).next() { + Some((start, match_str)) => start..start + match_str.len(), + None => return, + }; + + self.replace_range(range, to); + } + /// Converts this `String` into a [Box]<[str]>. /// /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`]. diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 49fb21ef5f3ac..f94f92397bb18 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -36,6 +36,7 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] +#![feature(string_replace_in_place)] #![feature(vec_deque_pop_if)] #![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] diff --git a/library/alloctests/tests/string.rs b/library/alloctests/tests/string.rs index d996c55f94660..ecc5b9dc82ed0 100644 --- a/library/alloctests/tests/string.rs +++ b/library/alloctests/tests/string.rs @@ -719,6 +719,40 @@ fn test_replace_range_evil_end_bound() { assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); } +#[test] +fn test_replace_first() { + let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~"); + s.replace_first("❌", "✅✅"); + assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~"); + s.replace_first("🦀", "😳"); + assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~"); + + let mut s = String::from("❌"); + s.replace_first('❌', "✅✅"); + assert_eq!(s, "✅✅"); + + let mut s = String::from(""); + s.replace_first('🌌', "❌"); + assert_eq!(s, ""); +} + +#[test] +fn test_replace_last() { + let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~"); + s.replace_last("❌", "✅✅"); + assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~"); + s.replace_last("🦀", "😳"); + assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~"); + + let mut s = String::from("❌"); + s.replace_last::('❌', "✅✅"); + assert_eq!(s, "✅✅"); + + let mut s = String::from(""); + s.replace_last::('🌌', "❌"); + assert_eq!(s, ""); +} + #[test] fn test_extend_ref() { let mut a = "foo".to_string(); From cb7fb35ad1b567f02d8f2226df173736fba86850 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 22 Oct 2025 20:27:46 -0400 Subject: [PATCH 2/3] Revert inference failure from AsRef constification --- library/alloc/src/borrow.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index cb32896161e5c..4659fb2a8426d 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -441,11 +441,13 @@ where } } +// FIXME(inference): const bounds removed due to inference regressions found by crater; +// see https://github.com/rust-lang/rust/issues/147964 +// #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_convert", issue = "143773")] -impl const AsRef for Cow<'_, T> -where - T::Owned: [const] Borrow, +impl AsRef for Cow<'_, T> +// where +// T::Owned: [const] Borrow, { fn as_ref(&self) -> &T { self From 15c91bf308acdaced24eecf73d3cfd3d8eaa2cb0 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 15 Oct 2025 04:04:36 +0000 Subject: [PATCH 3/3] Retire ast::TyAliasWhereClauses. --- compiler/rustc_ast/src/ast.rs | 53 +++++------------ compiler/rustc_ast/src/visit.rs | 2 - compiler/rustc_ast_lowering/src/item.rs | 37 +++++++----- .../rustc_ast_passes/src/ast_validation.rs | 39 ++++++------ .../rustc_ast_pretty/src/pprust/state/item.rs | 29 ++++----- .../src/deriving/generic/mod.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 25 +------- .../clippy/clippy_utils/src/ast_utils/mod.rs | 10 ++-- src/tools/rustfmt/src/items.rs | 59 ++++++++----------- .../where-clauses/cfg-attr-issue-138010-1.rs | 14 +++++ .../where-clauses/cfg-attr-issue-138010-2.rs | 27 +++++++++ .../cfg-attr-issue-138010-2.stderr | 31 ++++++++++ 12 files changed, 170 insertions(+), 158 deletions(-) create mode 100644 tests/ui/where-clauses/cfg-attr-issue-138010-1.rs create mode 100644 tests/ui/where-clauses/cfg-attr-issue-138010-2.rs create mode 100644 tests/ui/where-clauses/cfg-attr-issue-138010-2.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e21155002b0f2..94c15094f5251 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3636,49 +3636,26 @@ pub struct Trait { pub items: ThinVec>, } -/// The location of a where clause on a `TyAlias` (`Span`) and whether there was -/// a `where` keyword (`bool`). This is split out from `WhereClause`, since there -/// are two locations for where clause on type aliases, but their predicates -/// are concatenated together. -/// -/// Take this example: -/// ```ignore (only-for-syntax-highlight) -/// trait Foo { -/// type Assoc<'a, 'b> where Self: 'a, Self: 'b; -/// } -/// impl Foo for () { -/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b; -/// // ^^^^^^^^^^^^^^ first where clause -/// // ^^^^^^^^^^^^^^ second where clause -/// } -/// ``` -/// -/// If there is no where clause, then this is `false` with `DUMMY_SP`. -#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)] -pub struct TyAliasWhereClause { - pub has_where_token: bool, - pub span: Span, -} - -/// The span information for the two where clauses on a `TyAlias`. -#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)] -pub struct TyAliasWhereClauses { - /// Before the equals sign. - pub before: TyAliasWhereClause, - /// After the equals sign. - pub after: TyAliasWhereClause, - /// The index in `TyAlias.generics.where_clause.predicates` that would split - /// into predicates from the where clause before the equals sign and the ones - /// from the where clause after the equals sign. - pub split: usize, -} - #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct TyAlias { pub defaultness: Defaultness, pub ident: Ident, pub generics: Generics, - pub where_clauses: TyAliasWhereClauses, + /// There are two locations for where clause on type aliases. This represents the second + /// where clause, before the semicolon. The first where clause is stored inside `generics`. + /// + /// Take this example: + /// ```ignore (only-for-syntax-highlight) + /// trait Foo { + /// type Assoc<'a, 'b> where Self: 'a, Self: 'b; + /// } + /// impl Foo for () { + /// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b; + /// // ^^^^^^^^^^^^^^ before where clause + /// // ^^^^^^^^^^^^^^ after where clause + /// } + /// ``` + pub after_where_clause: WhereClause, #[visitable(extra = BoundKind::Bound)] pub bounds: GenericBounds, pub ty: Option>, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ebd5aa6e93d83..03e5a6edeece5 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -471,8 +471,6 @@ macro_rules! common_visitor_and_walkers { TraitBoundModifiers, TraitObjectSyntax, TyAlias, - TyAliasWhereClause, - TyAliasWhereClauses, TyKind, TyPatKind, UnOp, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index e4b23cba8aa13..8527108f70419 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -36,20 +36,18 @@ pub(super) struct ItemLowerer<'a, 'hir> { /// clause if it exists. fn add_ty_alias_where_clause( generics: &mut ast::Generics, - mut where_clauses: TyAliasWhereClauses, + after_where_clause: &ast::WhereClause, prefer_first: bool, ) { + generics.where_clause.predicates.extend_from_slice(&after_where_clause.predicates); + + let mut before = (generics.where_clause.has_where_token, generics.where_clause.span); + let mut after = (after_where_clause.has_where_token, after_where_clause.span); if !prefer_first { - (where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before); + (before, after) = (after, before); } - let where_clause = - if where_clauses.before.has_where_token || !where_clauses.after.has_where_token { - where_clauses.before - } else { - where_clauses.after - }; - generics.where_clause.has_where_token = where_clause.has_where_token; - generics.where_clause.span = where_clause.span; + (generics.where_clause.has_where_token, generics.where_clause.span) = + if before.0 || !after.0 { before } else { after }; } impl<'a, 'hir> ItemLowerer<'a, 'hir> { @@ -271,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm)))); hir::ItemKind::GlobalAsm { asm, fake_body } } - ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => { + ItemKind::TyAlias(box TyAlias { ident, generics, after_where_clause, ty, .. }) => { // We lower // // type Foo = impl Trait @@ -282,7 +280,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // opaque type Foo1: Trait let ident = self.lower_ident(*ident); let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, true); + add_ty_alias_where_clause(&mut generics, after_where_clause, true); let (generics, ty) = self.lower_generics( &generics, id, @@ -901,10 +899,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } AssocItemKind::Type(box TyAlias { - ident, generics, where_clauses, bounds, ty, .. + ident, + generics, + after_where_clause, + bounds, + ty, + .. }) => { let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, false); + add_ty_alias_where_clause(&mut generics, after_where_clause, false); let (generics, kind) = self.lower_generics( &generics, i.id, @@ -1070,9 +1073,11 @@ impl<'hir> LoweringContext<'_, 'hir> { (*ident, (generics, hir::ImplItemKind::Fn(sig, body_id))) } - AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => { + AssocItemKind::Type(box TyAlias { + ident, generics, after_where_clause, ty, .. + }) => { let mut generics = generics.clone(); - add_ty_alias_where_clause(&mut generics, *where_clauses, false); + add_ty_alias_where_clause(&mut generics, after_where_clause, false); ( *ident, self.lower_generics( diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f773b02058ef1..93f8b74ad561f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -145,25 +145,24 @@ impl<'a> AstValidator<'a> { &mut self, ty_alias: &TyAlias, ) -> Result<(), errors::WhereClauseBeforeTypeAlias> { - if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token { + if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token { return Ok(()); } - let (before_predicates, after_predicates) = - ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split); - let span = ty_alias.where_clauses.before.span; + let span = ty_alias.generics.where_clause.span; - let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token + let sugg = if !ty_alias.generics.where_clause.predicates.is_empty() + || !ty_alias.after_where_clause.has_where_token { let mut state = State::new(); - if !ty_alias.where_clauses.after.has_where_token { + if !ty_alias.after_where_clause.has_where_token { state.space(); state.word_space("where"); } - let mut first = after_predicates.is_empty(); - for p in before_predicates { + let mut first = ty_alias.after_where_clause.predicates.is_empty(); + for p in &ty_alias.generics.where_clause.predicates { if !first { state.word_space(","); } @@ -174,7 +173,7 @@ impl<'a> AstValidator<'a> { errors::WhereClauseBeforeTypeAliasSugg::Move { left: span, snippet: state.s.eof(), - right: ty_alias.where_clauses.after.span.shrink_to_hi(), + right: ty_alias.after_where_clause.span.shrink_to_hi(), } } else { errors::WhereClauseBeforeTypeAliasSugg::Remove { span } @@ -566,11 +565,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::BoundInContext { span, ctx }); } - fn check_foreign_ty_genericless( - &self, - generics: &Generics, - where_clauses: &TyAliasWhereClauses, - ) { + fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) { let cannot_have = |span, descr, remove_descr| { self.dcx().emit_err(errors::ExternTypesCannotHave { span, @@ -584,14 +579,14 @@ impl<'a> AstValidator<'a> { cannot_have(generics.span, "generic parameters", "generic parameters"); } - let check_where_clause = |where_clause: TyAliasWhereClause| { + let check_where_clause = |where_clause: &WhereClause| { if where_clause.has_where_token { cannot_have(where_clause.span, "`where` clauses", "`where` clause"); } }; - check_where_clause(where_clauses.before); - check_where_clause(where_clauses.after); + check_where_clause(&generics.where_clause); + check_where_clause(&after_where_clause); } fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option) { @@ -1261,7 +1256,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_item(self, item); } ItemKind::TyAlias( - ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, + ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, ) => { self.check_defaultness(item.span, *defaultness); if ty.is_none() { @@ -1276,9 +1271,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { self.dcx().emit_err(err); } - } else if where_clauses.after.has_where_token { + } else if after_where_clause.has_where_token { self.dcx().emit_err(errors::WhereClauseAfterTypeAlias { - span: where_clauses.after.span, + span: after_where_clause.span, help: self.sess.is_nightly_build(), }); } @@ -1308,7 +1303,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, .. @@ -1316,7 +1311,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_defaultness(fi.span, *defaultness); self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); - self.check_foreign_ty_genericless(generics, where_clauses); + self.check_foreign_ty_genericless(generics, after_where_clause); self.check_foreign_item_ascii_only(*ident); } ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index ab402cbb8dc12..3412660863407 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -59,14 +59,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), vis, @@ -127,14 +127,12 @@ impl<'a> State<'a> { &mut self, ident: Ident, generics: &ast::Generics, - where_clauses: ast::TyAliasWhereClauses, + after_where_clause: &ast::WhereClause, bounds: &ast::GenericBounds, ty: Option<&ast::Ty>, vis: &ast::Visibility, defaultness: ast::Defaultness, ) { - let (before_predicates, after_predicates) = - generics.where_clause.predicates.split_at(where_clauses.split); let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_defaultness(defaultness); @@ -145,13 +143,13 @@ impl<'a> State<'a> { self.word_nbsp(":"); self.print_type_bounds(bounds); } - self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates); + self.print_where_clause(&generics.where_clause); if let Some(ty) = ty { self.space(); self.word_space("="); self.print_type(ty); } - self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates); + self.print_where_clause(&after_where_clause); self.word(";"); self.end(ib); self.end(cb); @@ -283,14 +281,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), &item.vis, @@ -585,14 +583,14 @@ impl<'a> State<'a> { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }) => { self.print_associated_type( *ident, generics, - *where_clauses, + after_where_clause, bounds, ty.as_deref(), vis, @@ -759,14 +757,7 @@ impl<'a> State<'a> { } fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { - self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates); - } - - fn print_where_clause_parts( - &mut self, - has_where_token: bool, - predicates: &[ast::WherePredicate], - ) { + let ast::WhereClause { has_where_token, ref predicates, span: _ } = *where_clause; if predicates.is_empty() && !has_where_token { return; } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 36ce1b7204467..24a71ae943899 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -610,7 +610,7 @@ impl<'a> TraitDef<'a> { defaultness: ast::Defaultness::Final, ident, generics: Generics::default(), - where_clauses: ast::TyAliasWhereClauses::default(), + after_where_clause: ast::WhereClause::default(), bounds: Vec::new(), ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), })), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6f0e3b81cf2a6..11eae9e2d903e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1041,40 +1041,19 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let bounds = if self.eat(exp!(Colon)) { self.parse_generic_bounds()? } else { Vec::new() }; - let before_where_clause = self.parse_where_clause()?; + generics.where_clause = self.parse_where_clause()?; let ty = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None }; let after_where_clause = self.parse_where_clause()?; - let where_clauses = TyAliasWhereClauses { - before: TyAliasWhereClause { - has_where_token: before_where_clause.has_where_token, - span: before_where_clause.span, - }, - after: TyAliasWhereClause { - has_where_token: after_where_clause.has_where_token, - span: after_where_clause.span, - }, - split: before_where_clause.predicates.len(), - }; - let mut predicates = before_where_clause.predicates; - predicates.extend(after_where_clause.predicates); - let where_clause = WhereClause { - has_where_token: before_where_clause.has_where_token - || after_where_clause.has_where_token, - predicates, - span: DUMMY_SP, - }; - generics.where_clause = where_clause; - self.expect_semi()?; Ok(ItemKind::TyAlias(Box::new(TyAlias { defaultness, ident, generics, - where_clauses, + after_where_clause, bounds, ty, }))) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index b01e160e22972..22b74ab16d615 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -562,7 +562,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: ld, ident: li, generics: lg, - where_clauses: _, + after_where_clause: lw, bounds: lb, ty: lt, }), @@ -570,7 +570,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: rd, ident: ri, generics: rg, - where_clauses: _, + after_where_clause: rw, bounds: rb, ty: rt, }), @@ -578,6 +578,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_id(*li, *ri) && eq_generics(lg, rg) + && over(&lw.predicates, &rw.predicates, eq_where_predicate) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, @@ -645,7 +646,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: ld, ident: li, generics: lg, - where_clauses: _, + after_where_clause: lw, bounds: lb, ty: lt, }), @@ -653,7 +654,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: rd, ident: ri, generics: rg, - where_clauses: _, + after_where_clause: rw, bounds: rb, ty: rt, }), @@ -661,6 +662,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_id(*li, *ri) && eq_generics(lg, rg) + && over(&lw.predicates, &rw.predicates, eq_where_predicate) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 75e468b35256c..a2e89c10a4803 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -836,8 +836,7 @@ pub(crate) fn format_impl( let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); let where_clause_str = rewrite_where_clause( context, - &generics.where_clause.predicates, - generics.where_clause.span, + &generics.where_clause, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), false, @@ -1224,8 +1223,7 @@ pub(crate) fn format_trait( let option = WhereClauseOption::snuggled(&generics_str); let where_clause_str = rewrite_where_clause( context, - &generics.where_clause.predicates, - generics.where_clause.span, + &generics.where_clause, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), where_on_new_line, @@ -1350,8 +1348,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { let where_str = rewrite_where_clause( context, - &self.generics.where_clause.predicates, - self.generics.where_clause.span, + &self.generics.where_clause, context.config.brace_style(), shape, false, @@ -1621,8 +1618,7 @@ fn format_tuple_struct( let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); rewrite_where_clause( context, - &generics.where_clause.predicates, - generics.where_clause.span, + &generics.where_clause, context.config.brace_style(), Shape::legacy(where_budget, offset.block_only()), false, @@ -1691,7 +1687,7 @@ struct TyAliasRewriteInfo<'c, 'g>( &'c RewriteContext<'c>, Indent, &'g ast::Generics, - ast::TyAliasWhereClauses, + &'g ast::WhereClause, symbol::Ident, Span, ); @@ -1712,13 +1708,13 @@ pub(crate) fn rewrite_type_alias<'a>( ref generics, ref bounds, ref ty, - where_clauses, + ref after_where_clause, } = *ty_alias_kind; let ty_opt = ty.as_ref(); let rhs_hi = ty .as_ref() - .map_or(where_clauses.before.span.hi(), |ty| ty.span.hi()); - let rw_info = &TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span); + .map_or(generics.where_clause.span.hi(), |ty| ty.span.hi()); + let rw_info = &TyAliasRewriteInfo(context, indent, generics, after_where_clause, ident, span); let op_ty = opaque_ty(ty); // Type Aliases are formatted slightly differently depending on the context // in which they appear, whether they are opaque, and whether they are associated. @@ -1762,11 +1758,7 @@ fn rewrite_ty( vis: &ast::Visibility, ) -> RewriteResult { let mut result = String::with_capacity(128); - let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info; - let (before_where_predicates, after_where_predicates) = generics - .where_clause - .predicates - .split_at(where_clauses.split); + let TyAliasRewriteInfo(context, indent, generics, after_where_clause, ident, span) = *rw_info; result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1804,8 +1796,7 @@ fn rewrite_ty( } let before_where_clause_str = rewrite_where_clause( context, - before_where_predicates, - where_clauses.before.span, + &generics.where_clause, context.config.brace_style(), Shape::legacy(where_budget, indent), false, @@ -1820,9 +1811,9 @@ fn rewrite_ty( // If there are any where clauses, add a newline before the assignment. // If there is a before where clause, do not indent, but if there is // only an after where clause, additionally indent the type. - if !before_where_predicates.is_empty() { + if !generics.where_clause.predicates.is_empty() { result.push_str(&indent.to_string_with_newline(context.config)); - } else if !after_where_predicates.is_empty() { + } else if !after_where_clause.predicates.is_empty() { result.push_str( &indent .block_indent(context.config) @@ -1835,7 +1826,7 @@ fn rewrite_ty( let comment_span = context .snippet_provider .opt_span_before(span, "=") - .map(|op_lo| mk_sp(where_clauses.before.span.hi(), op_lo)); + .map(|op_lo| mk_sp(generics.where_clause.span.hi(), op_lo)); let lhs = match comment_span { Some(comment_span) @@ -1846,7 +1837,7 @@ fn rewrite_ty( .unknown_error()?, ) => { - let comment_shape = if !before_where_predicates.is_empty() { + let comment_shape = if !generics.where_clause.predicates.is_empty() { Shape::indented(indent, context.config) } else { let shape = Shape::indented(indent, context.config); @@ -1869,7 +1860,7 @@ fn rewrite_ty( // 1 = `;` unless there's a trailing where clause let shape = Shape::indented(indent, context.config); - let shape = if after_where_predicates.is_empty() { + let shape = if after_where_clause.predicates.is_empty() { Shape::indented(indent, context.config) .sub_width(1) .max_width_error(shape.width, span)? @@ -1881,12 +1872,11 @@ fn rewrite_ty( result }; - if !after_where_predicates.is_empty() { + if !after_where_clause.predicates.is_empty() { let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); let after_where_clause_str = rewrite_where_clause( context, - after_where_predicates, - where_clauses.after.span, + &after_where_clause, context.config.brace_style(), Shape::indented(indent, context.config), false, @@ -2728,8 +2718,7 @@ fn rewrite_fn_base( } let where_clause_str = rewrite_where_clause( context, - &where_clause.predicates, - where_clause.span, + &where_clause, context.config.brace_style(), Shape::indented(indent, context.config), true, @@ -3158,8 +3147,7 @@ fn rewrite_bounds_on_where_clause( fn rewrite_where_clause( context: &RewriteContext<'_>, - predicates: &[ast::WherePredicate], - where_span: Span, + where_clause: &ast::WhereClause, brace_style: BraceStyle, shape: Shape, on_new_line: bool, @@ -3168,6 +3156,12 @@ fn rewrite_where_clause( span_end_before_where: BytePos, where_clause_option: WhereClauseOption, ) -> RewriteResult { + let ast::WhereClause { + ref predicates, + span: where_span, + has_where_token: _, + } = *where_clause; + if predicates.is_empty() { return Ok(String::new()); } @@ -3354,8 +3348,7 @@ fn format_generics( } let where_clause_str = rewrite_where_clause( context, - &generics.where_clause.predicates, - generics.where_clause.span, + &generics.where_clause, brace_style, Shape::legacy(budget, offset.block_only()), true, diff --git a/tests/ui/where-clauses/cfg-attr-issue-138010-1.rs b/tests/ui/where-clauses/cfg-attr-issue-138010-1.rs new file mode 100644 index 0000000000000..5d13e6e253a4e --- /dev/null +++ b/tests/ui/where-clauses/cfg-attr-issue-138010-1.rs @@ -0,0 +1,14 @@ +//@ check-pass + +#![feature(associated_type_defaults, where_clause_attrs)] +#![allow(deprecated_where_clause_location)] + +trait A { + type F + where + #[cfg(false)] + T: TraitB, + = T; +} + +fn main() {} diff --git a/tests/ui/where-clauses/cfg-attr-issue-138010-2.rs b/tests/ui/where-clauses/cfg-attr-issue-138010-2.rs new file mode 100644 index 0000000000000..cf84bf3ee3559 --- /dev/null +++ b/tests/ui/where-clauses/cfg-attr-issue-138010-2.rs @@ -0,0 +1,27 @@ +#![feature(where_clause_attrs, lazy_type_alias)] +#![expect(incomplete_features)] + +struct Foo; +trait Trait {} + +impl Trait for Foo {} + +type MixedWhereBounds1 +where + //~^ ERROR: where clauses are not allowed before the type for type aliases + #[cfg(true)] + Foo: Trait, += Foo +where + (): Sized; + +type MixedWhereBounds2 +where + //~^ ERROR: where clauses are not allowed before the type for type aliases + #[cfg(false)] + Foo: Trait, += Foo +where + (): Sized; + +fn main() {} diff --git a/tests/ui/where-clauses/cfg-attr-issue-138010-2.stderr b/tests/ui/where-clauses/cfg-attr-issue-138010-2.stderr new file mode 100644 index 0000000000000..f59ce0fa1acaa --- /dev/null +++ b/tests/ui/where-clauses/cfg-attr-issue-138010-2.stderr @@ -0,0 +1,31 @@ +error: where clauses are not allowed before the type for type aliases + --> $DIR/cfg-attr-issue-138010-2.rs:10:1 + | +LL | / where +LL | | +LL | | #[cfg(true)] +LL | | Foo: Trait, + | |_______________^ + | + = note: see issue #89122 for more information +help: move it to the end of the type declaration + | +LL + +LL | = Foo +LL | where +LL ~ (): Sized, Foo: Trait; + | + +error: where clauses are not allowed before the type for type aliases + --> $DIR/cfg-attr-issue-138010-2.rs:19:1 + | +LL | / where +LL | | +LL | | #[cfg(false)] +LL | | Foo: Trait, + | |_______________^ help: remove this `where` + | + = note: see issue #89122 for more information + +error: aborting due to 2 previous errors +