From fb285b7f8c257efc95696a88137885e44d657fc1 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 23 Jan 2026 11:05:17 -0500 Subject: [PATCH 1/6] Add test fixtures --- tests/source/issue-6769.rs | 37 +++++++++++++++++++++++++++++++++++++ tests/source/issue-6778.rs | 26 ++++++++++++++++++++++++++ tests/target/issue-6769.rs | 37 +++++++++++++++++++++++++++++++++++++ tests/target/issue-6778.rs | 26 ++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 tests/source/issue-6769.rs create mode 100644 tests/source/issue-6778.rs create mode 100644 tests/target/issue-6769.rs create mode 100644 tests/target/issue-6778.rs diff --git a/tests/source/issue-6769.rs b/tests/source/issue-6769.rs new file mode 100644 index 00000000000..eaf0cf94b4f --- /dev/null +++ b/tests/source/issue-6769.rs @@ -0,0 +1,37 @@ +fn test() { + let foo = [ + (b"Welcome to Ringboard!" as &[u8], &em), + ( + b"Ringboard is a fast, efficient, and composable clipboard manager for Linux." + , &em, + ), + ( + b"It supports both Wayland and X11 along with multiple clients to manage your \ + clipboard history." + , &em, + ), + ( + b"Clients include a standard GUI, an interactive TUI, and a CLI for all your scripting \ + needs." + , &em, + ), + ( + "Ringboard can copy arbitrary bytes—that includes images!".as_bytes(), + &em, + ), + ( + b"Plaintext and RegEx search are available for fast entry retrieval.", + &em, + ), + (b"Enjoy this image from our AI overlords:", &em), + ( + include_bytes!("../logo.jpeg") as &[u8], + &MimeType::from("image/jpeg").unwrap(), + ), + ( + b"Finally, it's worth mentioning that Ringboard is extremely efficient, performant, \ + and scalable." + , &em, + ), + ]; +} diff --git a/tests/source/issue-6778.rs b/tests/source/issue-6778.rs new file mode 100644 index 00000000000..94ea0c266c6 --- /dev/null +++ b/tests/source/issue-6778.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +struct Parameter { + required: bool, + description: &'static str, +} + +pub struct Test; + +impl Test { + fn parameters(&self) -> &'static [Parameter] { + &[ + Parameter { + required: true, + description: "Foo", + }, + Parameter { + required: false, + description: "Bar +This string is exactly 100 chars long. Delete one character to make it 99 chars long and it'll work!", + }, + ] + } +} + +fn main() {} diff --git a/tests/target/issue-6769.rs b/tests/target/issue-6769.rs new file mode 100644 index 00000000000..231faa6ffb9 --- /dev/null +++ b/tests/target/issue-6769.rs @@ -0,0 +1,37 @@ +fn test() { + let foo = [ + (b"Welcome to Ringboard!" as &[u8], &em), + ( + b"Ringboard is a fast, efficient, and composable clipboard manager for Linux.", + &em, + ), + ( + b"It supports both Wayland and X11 along with multiple clients to manage your \ + clipboard history.", + &em, + ), + ( + b"Clients include a standard GUI, an interactive TUI, and a CLI for all your scripting \ + needs.", + &em, + ), + ( + "Ringboard can copy arbitrary bytes—that includes images!".as_bytes(), + &em, + ), + ( + b"Plaintext and RegEx search are available for fast entry retrieval.", + &em, + ), + (b"Enjoy this image from our AI overlords:", &em), + ( + include_bytes!("../logo.jpeg") as &[u8], + &MimeType::from("image/jpeg").unwrap(), + ), + ( + b"Finally, it's worth mentioning that Ringboard is extremely efficient, performant, \ + and scalable.", + &em, + ), + ]; +} diff --git a/tests/target/issue-6778.rs b/tests/target/issue-6778.rs new file mode 100644 index 00000000000..36c267b8d42 --- /dev/null +++ b/tests/target/issue-6778.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +struct Parameter { + required: bool, + description: &'static str, +} + +pub struct Test; + +impl Test { + fn parameters(&self) -> &'static [Parameter] { + &[ + Parameter { + required: true, + description: "Foo", + }, + Parameter { + required: false, + description: "Bar +This string is exactly 100 chars long. Delete one character to make it 99 chars long and it'll work!", + }, + ] + } +} + +fn main() {} From 0979410986e41916c096875e980963271d274162 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 23 Jan 2026 11:05:35 -0500 Subject: [PATCH 2/6] Add code fixing specific tests and breaking others --- src/utils.rs | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index fcd475b1784..f2d3ec72802 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -395,27 +395,55 @@ pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option bool { + use crate::comment::{FullCodeCharKind, LineClasses}; + let snippet = &filter_normal_code(snippet); if !snippet.is_empty() { - // First line must fits with `shape.width`. - if first_line_width(snippet) > shape.width { - return false; + // Collect line classifications to check for string content. + let line_classes: Vec<_> = LineClasses::new(snippet).collect(); + + // First line must fit with `shape.width`, unless it's a string literal + // that starts on this line (StartString) - string content cannot be shortened. + let first_line_width = first_line_width(snippet); + if first_line_width > shape.width { + let first_is_string = line_classes + .first() + .is_some_and(|(kind, _)| kind.is_string() || *kind == FullCodeCharKind::StartString); + if !first_is_string { + return false; + } } + // If the snippet does not include newline, we are done. if is_single_line(snippet) { return true; } + // The other lines must fit within the maximum width. - if snippet - .lines() - .skip(1) - .any(|line| unicode_str_width(line) > max_width) - { - return false; + // Exception: lines that are inside or end a multi-line string literal + // may exceed max_width since string content cannot be reformatted. + let mut last_line_is_string = false; + for (i, (kind, line)) in line_classes.iter().enumerate() { + if i == 0 { + continue; // First line already checked above + } + // Track if the last line is string content + last_line_is_string = + *kind == FullCodeCharKind::InString || *kind == FullCodeCharKind::EndString; + + if unicode_str_width(line) > max_width { + // Allow lines that are string continuations (InString) or + // end a string (EndString) to exceed max_width. + if !last_line_is_string { + return false; + } + } } + // A special check for the last line, since the caller may - // place trailing characters on this line. - if last_line_width(snippet) > shape.used_width() + shape.width { + // place trailing characters on this line. Skip this check if the + // last line is string content (which cannot be reformatted). + if !last_line_is_string && last_line_width(snippet) > shape.used_width() + shape.width { return false; } } From fa5b1e463adca7a55a6c43d3d8e95460f9c76a47 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 23 Jan 2026 11:17:23 -0500 Subject: [PATCH 3/6] Add multiline string check --- src/utils.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index f2d3ec72802..3d2d4e0f223 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -402,14 +402,18 @@ pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) - // Collect line classifications to check for string content. let line_classes: Vec<_> = LineClasses::new(snippet).collect(); - // First line must fit with `shape.width`, unless it's a string literal - // that starts on this line (StartString) - string content cannot be shortened. + // First line must fit with `shape.width`, unless it's a multi-line string + // literal that starts on this line - string content cannot be shortened. let first_line_width = first_line_width(snippet); if first_line_width > shape.width { - let first_is_string = line_classes - .first() - .is_some_and(|(kind, _)| kind.is_string() || *kind == FullCodeCharKind::StartString); - if !first_is_string { + // Only allow the exception for multi-line strings (StartString indicates + // the string continues on subsequent lines). + let is_multiline = line_classes.len() > 1; + let first_is_multiline_string = is_multiline + && line_classes + .first() + .is_some_and(|(kind, _)| *kind == FullCodeCharKind::StartString); + if !first_is_multiline_string { return false; } } From 8a371f9d0a000a693226f25efd09ec7b99892b03 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 24 Feb 2026 22:29:02 -0500 Subject: [PATCH 4/6] Pass in style_edition into filtered_str_fits/wrap_str --- src/chains.rs | 24 ++++++++++++++++++++---- src/expr.rs | 22 +++++++++++++++++++--- src/macros.rs | 7 ++++++- src/pairs.rs | 7 ++++++- src/string.rs | 7 ++++++- src/utils.rs | 16 +++++++++++++--- 6 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/chains.rs b/src/chains.rs index 9ab20b355fb..77f3a9dc543 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -566,8 +566,13 @@ impl Rewrite for Chain { formatter.format_root(&self.parent, context, shape)?; if let Some(result) = formatter.pure_root() { - return wrap_str(result, context.config.max_width(), shape) - .max_width_error(shape.width, self.parent.span); + return wrap_str( + context.config.style_edition(), + result, + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, self.parent.span); } let first = self.children.first().unwrap_or(&self.parent); @@ -582,7 +587,13 @@ impl Rewrite for Chain { formatter.format_last_child(context, shape, child_shape)?; let result = formatter.join_rewrites(context, child_shape)?; - wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span) + wrap_str( + context.config.style_edition(), + result, + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, full_span) } } @@ -973,7 +984,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { .visual_indent(self.offset) .sub_width(self.offset, item.span)?; let rewrite = item.rewrite_result(context, child_shape)?; - if filtered_str_fits(&rewrite, context.config.max_width(), shape) { + if filtered_str_fits( + context.config.style_edition(), + &rewrite, + context.config.max_width(), + shape, + ) { root_rewrite.push_str(&rewrite); } else { // We couldn't fit in at the visual indent, try the last diff --git a/src/expr.rs b/src/expr.rs index 348cffc21ee..ddbfe788847 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -277,6 +277,7 @@ pub(crate) fn format_expr( ast::ExprKind::MacCall(ref mac) => { rewrite_macro(mac, context, shape, MacroPosition::Expression).or_else(|_| { wrap_str( + context.config.style_edition(), context.snippet(expr.span).to_owned(), context.config.max_width(), shape, @@ -1304,6 +1305,7 @@ pub(crate) fn rewrite_literal( token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), token::LitKind::Float => rewrite_float_lit(context, token_lit, span, shape), _ => wrap_str( + context.config.style_edition(), context.snippet(span).to_owned(), context.config.max_width(), shape, @@ -1324,8 +1326,13 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> { return Ok(string_lit.to_owned()); } else { - return wrap_str(string_lit.to_owned(), context.config.max_width(), shape) - .max_width_error(shape.width, span); + return wrap_str( + context.config.style_edition(), + string_lit.to_owned(), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, span); } } @@ -1360,6 +1367,7 @@ fn rewrite_int_lit( }; if let Some(hex_lit) = hex_lit { return wrap_str( + context.config.style_edition(), format!( "0x{}{}", hex_lit, @@ -1373,6 +1381,7 @@ fn rewrite_int_lit( } wrap_str( + context.config.style_edition(), context.snippet(span).to_owned(), context.config.max_width(), shape, @@ -1391,6 +1400,7 @@ fn rewrite_float_lit( FloatLiteralTrailingZero::Preserve ) { return wrap_str( + context.config.style_edition(), context.snippet(span).to_owned(), context.config.max_width(), shape, @@ -1432,6 +1442,7 @@ fn rewrite_float_lit( "" }; wrap_str( + context.config.style_edition(), format!( "{}{}{}{}{}", integer_part, @@ -2285,7 +2296,12 @@ fn choose_rhs( match (orig_rhs, new_rhs) { (Ok(ref orig_rhs), Ok(ref new_rhs)) - if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => + if !filtered_str_fits( + context.config.style_edition(), + &new_rhs, + context.config.max_width(), + new_shape, + ) => { Ok(format!("{before_space_str}{orig_rhs}")) } diff --git a/src/macros.rs b/src/macros.rs index 3b58d28bfb0..90c0ce90462 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1346,7 +1346,12 @@ impl MacroBranch { } }; - if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { + if !filtered_str_fits( + config.style_edition(), + &new_body_snippet.snippet, + config.max_width(), + shape, + ) { return Err(RewriteError::ExceedsMaxWidth { configured_width: shape.width, span: self.span, diff --git a/src/pairs.rs b/src/pairs.rs index 48948b88b3b..9d84d0d9628 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -102,7 +102,12 @@ fn rewrite_pairs_one_line( return None; } - wrap_str(result, context.config.max_width(), shape) + wrap_str( + context.config.style_edition(), + result, + context.config.max_width(), + shape, + ) } fn rewrite_pairs_multiline( diff --git a/src/string.rs b/src/string.rs index 3b971188cd5..69ef23eb3f0 100644 --- a/src/string.rs +++ b/src/string.rs @@ -150,7 +150,12 @@ pub(crate) fn rewrite_string<'a>( } result.push_str(fmt.closer); - wrap_str(result, fmt.config.max_width(), fmt.shape) + wrap_str( + fmt.config.style_edition(), + result, + fmt.config.max_width(), + fmt.shape, + ) } /// Returns the index to the end of the URL if the split at index of the given string includes a diff --git a/src/utils.rs b/src/utils.rs index 3d2d4e0f223..26bb4805eac 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -386,15 +386,25 @@ macro_rules! skip_out_of_file_lines_range_visitor { // Wraps String in an Option. Returns Some when the string adheres to the // Rewrite constraints defined for the Rewrite trait and None otherwise. -pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option { - if filtered_str_fits(&s, max_width, shape) { +pub(crate) fn wrap_str( + style_edition: StyleEdition, + s: String, + max_width: usize, + shape: Shape, +) -> Option { + if filtered_str_fits(style_edition, &s, max_width, shape) { Some(s) } else { None } } -pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool { +pub(crate) fn filtered_str_fits( + _style_edition: StyleEdition, + snippet: &str, + max_width: usize, + shape: Shape, +) -> bool { use crate::comment::{FullCodeCharKind, LineClasses}; let snippet = &filter_normal_code(snippet); From d46365d25325954cbc1c1475d840657f519c6818 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 24 Feb 2026 22:55:16 -0500 Subject: [PATCH 5/6] 2027 style-gate fixtures --- tests/source/issue-6769.rs | 2 ++ tests/source/issue-6778.rs | 2 ++ tests/target/issue-6769.rs | 2 ++ tests/target/issue-6778.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/tests/source/issue-6769.rs b/tests/source/issue-6769.rs index eaf0cf94b4f..3a825e87ce8 100644 --- a/tests/source/issue-6769.rs +++ b/tests/source/issue-6769.rs @@ -1,3 +1,5 @@ +// rustfmt-style_edition: 2027 + fn test() { let foo = [ (b"Welcome to Ringboard!" as &[u8], &em), diff --git a/tests/source/issue-6778.rs b/tests/source/issue-6778.rs index 94ea0c266c6..7b6290d4f7d 100644 --- a/tests/source/issue-6778.rs +++ b/tests/source/issue-6778.rs @@ -1,3 +1,5 @@ +// rustfmt-style_edition: 2027 + #![allow(dead_code)] struct Parameter { diff --git a/tests/target/issue-6769.rs b/tests/target/issue-6769.rs index 231faa6ffb9..517e2c4b381 100644 --- a/tests/target/issue-6769.rs +++ b/tests/target/issue-6769.rs @@ -1,3 +1,5 @@ +// rustfmt-style_edition: 2027 + fn test() { let foo = [ (b"Welcome to Ringboard!" as &[u8], &em), diff --git a/tests/target/issue-6778.rs b/tests/target/issue-6778.rs index 36c267b8d42..63188099633 100644 --- a/tests/target/issue-6778.rs +++ b/tests/target/issue-6778.rs @@ -1,3 +1,5 @@ +// rustfmt-style_edition: 2027 + #![allow(dead_code)] struct Parameter { From e45f3edb703659c278869dbc830c945203b087a4 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 24 Feb 2026 23:03:02 -0500 Subject: [PATCH 6/6] Feature gate formatting with 2027 edition --- src/utils.rs | 57 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 26bb4805eac..04ebe91becd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -400,7 +400,7 @@ pub(crate) fn wrap_str( } pub(crate) fn filtered_str_fits( - _style_edition: StyleEdition, + style_edition: StyleEdition, snippet: &str, max_width: usize, shape: Shape, @@ -409,24 +409,35 @@ pub(crate) fn filtered_str_fits( let snippet = &filter_normal_code(snippet); if !snippet.is_empty() { - // Collect line classifications to check for string content. - let line_classes: Vec<_> = LineClasses::new(snippet).collect(); - - // First line must fit with `shape.width`, unless it's a multi-line string - // literal that starts on this line - string content cannot be shortened. - let first_line_width = first_line_width(snippet); - if first_line_width > shape.width { - // Only allow the exception for multi-line strings (StartString indicates - // the string continues on subsequent lines). - let is_multiline = line_classes.len() > 1; - let first_is_multiline_string = is_multiline - && line_classes - .first() - .is_some_and(|(kind, _)| *kind == FullCodeCharKind::StartString); - if !first_is_multiline_string { - return false; + let line_classes = { + if style_edition >= StyleEdition::Edition2027 { + // Collect line classifications to check for string content. + let line_classes: Vec<_> = LineClasses::new(snippet).collect(); + + // First line must fit with `shape.width`, unless it's a multi-line string + // literal that starts on this line - string content cannot be shortened. + let first_line_width = first_line_width(snippet); + if first_line_width > shape.width { + // Only allow the exception for multi-line strings (StartString indicates + // the string continues on subsequent lines). + let is_multiline = line_classes.len() > 1; + let first_is_multiline_string = is_multiline + && line_classes + .first() + .is_some_and(|(kind, _)| *kind == FullCodeCharKind::StartString); + if !first_is_multiline_string { + return false; + } + } + line_classes + } else { + // First line must fits with `shape.width`. + if first_line_width(snippet) > shape.width { + return false; + } + vec![] } - } + }; // If the snippet does not include newline, we are done. if is_single_line(snippet) { @@ -434,6 +445,16 @@ pub(crate) fn filtered_str_fits( } // The other lines must fit within the maximum width. + if style_edition < StyleEdition::Edition2027 { + if snippet + .lines() + .skip(1) + .any(|line| unicode_str_width(line) > max_width) + { + return false; + } + } + // Exception: lines that are inside or end a multi-line string literal // may exceed max_width since string content cannot be reformatted. let mut last_line_is_string = false;