diff --git a/check_diff/src/lib.rs b/check_diff/src/lib.rs index d6bb7678ea0..50b3bb76671 100644 --- a/check_diff/src/lib.rs +++ b/check_diff/src/lib.rs @@ -21,6 +21,8 @@ pub enum Edition { Edition2021, /// rust edition 2024 Edition2024, + /// rust edition 2027 + Edition2027, } impl Edition { @@ -30,6 +32,7 @@ impl Edition { Edition::Edition2018 => "2018", Edition::Edition2021 => "2021", Edition::Edition2024 => "2024", + Edition::Edition2027 => "2027", } } } @@ -43,6 +46,7 @@ impl FromStr for Edition { "2018" => Ok(Edition::Edition2018), "2021" => Ok(Edition::Edition2021), "2024" => Ok(Edition::Edition2024), + "2027" => Ok(Edition::Edition2027), _ => Err(format!("Invalid rust language edition {s}")), } } @@ -54,6 +58,8 @@ pub enum StyleEdition { Edition2021, // rustfmt style_edition 2024 Edition2024, + // rustfmt style_edition 2027 + Edition2027, } impl StyleEdition { @@ -61,6 +67,7 @@ impl StyleEdition { match self { StyleEdition::Edition2021 => "2021", StyleEdition::Edition2024 => "2024", + StyleEdition::Edition2027 => "2027", } } } @@ -74,6 +81,7 @@ impl FromStr for StyleEdition { "2018" => Ok(StyleEdition::Edition2021), "2021" => Ok(StyleEdition::Edition2021), "2024" => Ok(StyleEdition::Edition2024), + "2027" => Ok(StyleEdition::Edition2027), _ => Err(format!("Invalid rustfmt style edition {s}")), } } diff --git a/src/bin/main.rs b/src/bin/main.rs index 54b267bb445..c06d4901965 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -139,7 +139,7 @@ fn make_opts() -> Options { "", "style-edition", "The edition of the Style Guide (unstable).", - "[2015|2018|2021|2024]", + "[2015|2018|2021|2024|2027]", ); opts.optopt( "", diff --git a/src/config/options.rs b/src/config/options.rs index 00f9c3f7ec1..4ceef4455ae 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -458,6 +458,10 @@ pub enum Edition { #[doc_hint = "2024"] /// Edition 2024. Edition2024, + #[value = "2027"] + #[doc_hint = "2027"] + /// Edition 2027. + Edition2027, } impl Default for Edition { @@ -473,6 +477,8 @@ impl From for rustc_span::edition::Edition { Edition::Edition2018 => Self::Edition2018, Edition::Edition2021 => Self::Edition2021, Edition::Edition2024 => Self::Edition2024, + // FIXME: rustc doesn't have an edition 2027 yet. + Edition::Edition2027 => Self::Edition2024, } } } @@ -484,6 +490,7 @@ impl From for StyleEdition { Edition::Edition2018 => StyleEdition::Edition2018, Edition::Edition2021 => StyleEdition::Edition2021, Edition::Edition2024 => StyleEdition::Edition2024, + Edition::Edition2027 => StyleEdition::Edition2027, } } } diff --git a/src/lists.rs b/src/lists.rs index 9d811e5d9b5..0a10b4b34d2 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -198,7 +198,7 @@ impl ListItem { } fn empty_result(s: &RewriteResult) -> bool { - !matches!(*s, Ok(ref s) if !s.is_empty()) + !matches!(*s, Ok(ref s) | Err(RewriteError::InlineComment(ref s)) if !s.is_empty()) } !(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment)) @@ -285,7 +285,18 @@ where let indent_str = &formatting.shape.indent.to_string(formatting.config); while let Some((i, item)) = iter.next() { let item = item.as_ref(); - let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?; + let inner_item = match item.item.as_ref() { + Ok(item) => item, + Err(RewriteError::InlineComment(item)) => { + // Keep the original contents and recover, allowing the subsequent items to be + // formatted while keeping the original formatting of this item. + // This is currently only relevant to or-patterns with comments in between patterns. + // We gate this under future edition 2027 because the amount of code that needs to + // be reformatted in the wild is quite high. + item + } + Err(err) => return Err(err.clone()), + }; let first = i == 0; let last = iter.peek().is_none(); let mut separate = match sep_place { diff --git a/src/matches.rs b/src/matches.rs index 4741abbe465..a50a674733e 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -6,7 +6,9 @@ use rustc_ast::{MatchKind, ast}; use rustc_span::{BytePos, Span}; use tracing::debug; -use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment}; +use crate::comment::{ + FindUncommented, combine_strs_with_missing_comments, recover_comment_removed, rewrite_comment, +}; use crate::config::lists::*; use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition}; use crate::expr::{ @@ -293,7 +295,13 @@ fn rewrite_match_arm( .offset_left(pipe_offset, arm.span)? } }; - let pats_str = arm.pat.rewrite_result(context, pat_shape)?; + let pats_str_rewritten = arm.pat.rewrite_result(context, pat_shape)?; + // FIXME: if the original pattern span is multiline, then we should at least try to reindent + // it, but for now we leave it as it was before. + let pats_str = recover_comment_removed(pats_str_rewritten.clone(), arm.pat.span, context); + // We will continue formatting all other elements of of the `match`, but will still signal the + // error. + let inline_comment = pats_str != pats_str_rewritten; // Guard let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); @@ -319,7 +327,7 @@ fn rewrite_match_arm( arm.pat.span.hi(), arm.body.as_ref().unknown_error()?.span().lo(), ); - rewrite_match_body( + let body = rewrite_match_body( context, arm.body.as_ref().unknown_error()?, &lhs_str, @@ -327,7 +335,11 @@ fn rewrite_match_arm( guard_str.contains('\n'), arrow_span, is_last, - ) + )?; + if inline_comment && context.config.style_edition() >= StyleEdition::Edition2027 { + return Err(RewriteError::InlineComment(body)); + } + Ok(body) } fn stmt_is_expr_mac(stmt: &ast::Stmt) -> bool { diff --git a/src/rewrite.rs b/src/rewrite.rs index 96dc4d449c9..267f1ab1498 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -57,6 +57,13 @@ pub(crate) enum RewriteError { #[error("Failed to format given macro{} at: {span:?}", kind)] MacroFailure { kind: MacroErrorKind, span: Span }, + /// This failure will normally happen when an or-pattern has comments in between its elements. + /// + /// We will keep the original code, report an error, but recover and format code that comes + /// later properly. + #[error("Formatting was skipped due to a comment that would have been removed.")] + InlineComment(String), + /// Format failure that does not fit to above categories. #[error("An unknown error occurred during formatting.")] Unknown, diff --git a/tests/source/match.rs b/tests/source/match.rs index d1d8d7f2c36..3205b14dac6 100644 --- a/tests/source/match.rs +++ b/tests/source/match.rs @@ -1,3 +1,4 @@ +// rustfmt-edition: 2027 // rustfmt-normalize_comments: true // Match expressions. @@ -590,3 +591,16 @@ unsafe {} } } } + +fn issue_4119() { + match () { +()=> {} +() // Comment +| () => { +{ +println!("Foo"); +} +} +()=> {} +} +} diff --git a/tests/target/match.rs b/tests/target/match.rs index 0e7815a814d..499bdf90fb9 100644 --- a/tests/target/match.rs +++ b/tests/target/match.rs @@ -1,3 +1,4 @@ +// rustfmt-edition: 2027 // rustfmt-normalize_comments: true // Match expressions. @@ -635,3 +636,14 @@ fn issue_4109() { } } } + +fn issue_4119() { + match () { + () => {} + () // Comment +| () => { + println!("Foo"); + } + () => {} + } +}