diff --git a/src/bin/main.rs b/src/bin/main.rs index 1185454c8e7..59e0526b2ba 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -64,11 +64,17 @@ enum Operation { /// Print version information Version, /// Output default config to a file, or stdout if None - ConfigOutputDefault { path: Option }, + ConfigOutputDefault { + path: Option, + }, /// Output current config (as if formatting to a file) to stdout - ConfigOutputCurrent { path: Option }, + ConfigOutputCurrent { + path: Option, + }, /// No file specified, read from stdin - Stdin { input: String }, + Stdin { + input: String, + }, } /// Rustfmt operations errors. diff --git a/src/items.rs b/src/items.rs index 35591df0fd8..dac90ac8152 100644 --- a/src/items.rs +++ b/src/items.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cmp::{max, min, Ordering}; +use itertools::Itertools; use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; @@ -21,7 +22,9 @@ use crate::expr::{ rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, RhsAssignKind, RhsTactics, }; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ + definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, +}; use crate::macros::{rewrite_macro, MacroPosition}; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; @@ -617,7 +620,10 @@ impl<'a> FmtVisitor<'a> { .unwrap_or(&0); let itemize_list_with = |one_line_width: usize| { - itemize_list( + let mut has_multiline_variant = false; + let mut has_single_line_variant = false; + + let items: Vec<_> = itemize_list( self.snippet_provider, enum_def.variants.iter(), "}", @@ -631,22 +637,26 @@ impl<'a> FmtVisitor<'a> { }, |f| f.span.hi(), |f| { - self.format_variant(f, one_line_width, pad_discrim_ident_to) - .unknown_error() + let (result, is_multi_line) = + self.format_variant(f, one_line_width, pad_discrim_ident_to); + has_multiline_variant |= is_multi_line; + has_single_line_variant |= !is_multi_line; + result.unknown_error() }, body_lo, body_hi, false, ) - .collect() + .collect(); + + (items, has_multiline_variant, has_single_line_variant) }; - let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width()); - // If one of the variants use multiple lines, use multi-lined formatting for all variants. - let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n')); - let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n')); + let (mut items, has_multiline_variant, has_single_line_variant) = + itemize_list_with(self.config.struct_variant_width()); + if has_multiline_variant && has_single_line_variant { - items = itemize_list_with(0); + (items, _, _) = itemize_list_with(0); } let shape = self.shape().sub_width(2)?; @@ -667,23 +677,35 @@ impl<'a> FmtVisitor<'a> { field: &ast::Variant, one_line_width: usize, pad_discrim_ident_to: usize, - ) -> Option { + ) -> (Option, bool) { + // Makes the early return a little more ergonomic since we can't use `?` + macro_rules! unwrap_early_return { + ($option:expr, $is_multi_line:expr) => { + match $option { + Some(v) => v, + None => return (None, $is_multi_line), + } + }; + } + + let mut is_variant_multi_line = self.snippet(field.span).contains('\n'); if contains_skip(&field.attrs) { let lo = field.attrs[0].span.lo(); let span = mk_sp(lo, field.span.hi()); - return Some(self.snippet(span).to_owned()); + return (Some(self.snippet(span).to_owned()), is_variant_multi_line); } let context = self.get_context(); let shape = self.shape(); let attrs_str = if context.config.style_edition() >= StyleEdition::Edition2024 { - field.attrs.rewrite(&context, shape)? + unwrap_early_return!(field.attrs.rewrite(&context, shape), is_variant_multi_line) } else { // StyleEdition::Edition20{15|18|21} formatting that was off by 1. See issue #5801 - field.attrs.rewrite(&context, shape.sub_width(1)?)? + let shape = unwrap_early_return!(shape.sub_width(1), is_variant_multi_line); + unwrap_early_return!(field.attrs.rewrite(&context, shape), is_variant_multi_line) }; // sub_width(1) to take the trailing comma into account - let shape = shape.sub_width(1)?; + let shape = unwrap_early_return!(shape.sub_width(1), is_variant_multi_line); let lo = field .attrs @@ -692,32 +714,45 @@ impl<'a> FmtVisitor<'a> { let span = mk_sp(lo, field.span.lo()); let variant_body = match field.data { - ast::VariantData::Tuple(..) | ast::VariantData::Struct { .. } => format_struct( - &context, - &StructParts::from_variant(field, &context), - self.block_indent, - Some(one_line_width), - )?, + ast::VariantData::Tuple(..) | ast::VariantData::Struct { .. } => { + let rewrite = format_struct( + &context, + &StructParts::from_variant(field, &context), + self.block_indent, + Some(one_line_width), + ); + unwrap_early_return!(rewrite, is_variant_multi_line) + } ast::VariantData::Unit(..) => rewrite_ident(&context, field.ident).to_owned(), }; let variant_body = if let Some(ref expr) = field.disr_expr { let lhs = format!("{variant_body:pad_discrim_ident_to$} ="); let ex = &*expr.value; - rewrite_assign_rhs_with( + let rewrite = rewrite_assign_rhs_with( &context, lhs, ex, shape, &RhsAssignKind::Expr(&ex.kind, ex.span), RhsTactics::AllowOverflow, - )? + ); + unwrap_early_return!(rewrite, is_variant_multi_line) } else { variant_body }; - combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false) - .ok() + is_variant_multi_line = variant_body.contains('\n'); + let rewirte = combine_strs_with_missing_comments( + &context, + &attrs_str, + &variant_body, + span, + shape, + false, + ) + .ok(); + (rewirte, is_variant_multi_line) } fn visit_impl_items(&mut self, items: &[ptr::P]) { diff --git a/src/lists.rs b/src/lists.rs index f9e722130cd..18c7b293d4f 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -745,7 +745,7 @@ where I: Iterator, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> RewriteResult, + F3: FnMut(&T) -> RewriteResult, { type Item = ListItem; @@ -810,7 +810,7 @@ where I: Iterator, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> RewriteResult, + F3: FnMut(&T) -> RewriteResult, { ListItems { snippet_provider, diff --git a/src/modules.rs b/src/modules.rs index 0590f28ee05..fb08c4d3d30 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -77,10 +77,14 @@ pub struct ModuleResolutionError { pub(crate) enum ModuleResolutionErrorKind { /// Find a file that cannot be parsed. #[error("cannot parse {file}")] - ParseError { file: PathBuf }, + ParseError { + file: PathBuf, + }, /// File cannot be found. #[error("{file} does not exist")] - NotFound { file: PathBuf }, + NotFound { + file: PathBuf, + }, /// File a.rs and a/mod.rs both exist #[error("file for module found at both {default_path:?} and {secondary_path:?}")] MultipleCandidates { diff --git a/tests/target/attribute-in-enum/horizontal-no-doc.rs b/tests/target/attribute-in-enum/horizontal-no-doc.rs new file mode 100644 index 00000000000..56c378cea97 --- /dev/null +++ b/tests/target/attribute-in-enum/horizontal-no-doc.rs @@ -0,0 +1,7 @@ +// rustfmt-style_edition: 2024 +enum MyType { + A { field1: bool, field2: bool }, + B { field1: bool, field2: bool }, + C { field1: bool, field2: bool }, + D { field1: bool, field2: bool }, +} diff --git a/tests/target/attribute-in-enum/horizontal-with-doc.rs b/tests/target/attribute-in-enum/horizontal-with-doc.rs new file mode 100644 index 00000000000..75531edcea0 --- /dev/null +++ b/tests/target/attribute-in-enum/horizontal-with-doc.rs @@ -0,0 +1,9 @@ +// rustfmt-style_edition: 2024 +enum MyType { + A { field1: bool, field2: bool }, + B { field1: bool, field2: bool }, + /// One-line doc comment + C { field1: bool, field2: bool }, + /** Documentation block */ + D { field1: bool, field2: bool }, +} diff --git a/tests/target/attribute-in-enum/vertical-macro-multi-line.rs b/tests/target/attribute-in-enum/vertical-macro-multi-line.rs new file mode 100644 index 00000000000..57b020f8371 --- /dev/null +++ b/tests/target/attribute-in-enum/vertical-macro-multi-line.rs @@ -0,0 +1,44 @@ +// rustfmt-style_edition: 2024 +enum A { + B { + a: usize, + b: usize, + c: usize, + d: usize, + }, + + #[multiline_macro_attribute( + very_very_long_option1, + very_very_long_option2, + very_very_long_option3 + )] + C { + a: usize, + }, + + #[attr_with_expression1(x = ']')] + D1 { + a: usize, + }, + + #[attr_with_expression2(x = vec![])] + D2 { + a: usize, + }, + + #[attr_with_expression3(x = "]")] + D3 { + a: usize, + }, + + #[attr_with_expression4(x = "\"]")] + D4 { + a: usize, + }, + + #[attr1] + #[attr2] + D5 { + a: usize, + }, +} diff --git a/tests/target/attribute-in-enum/vertical-macro-one-line.rs b/tests/target/attribute-in-enum/vertical-macro-one-line.rs new file mode 100644 index 00000000000..e7cfe64a1db --- /dev/null +++ b/tests/target/attribute-in-enum/vertical-macro-one-line.rs @@ -0,0 +1,14 @@ +// rustfmt-style_edition: 2024 +enum A { + B { + a: usize, + b: usize, + c: usize, + d: usize, + }, + + #[attr] + C { + a: usize, + }, +} diff --git a/tests/target/attribute-in-enum/vertical-no-doc.rs b/tests/target/attribute-in-enum/vertical-no-doc.rs new file mode 100644 index 00000000000..2369bc368b7 --- /dev/null +++ b/tests/target/attribute-in-enum/vertical-no-doc.rs @@ -0,0 +1,13 @@ +// rustfmt-style_edition: 2024 +enum A { + B { + a: usize, + b: usize, + c: usize, + d: usize, + }, + + C { + a: usize, + }, +} diff --git a/tests/target/attribute-in-enum/vertical-with-doc.rs b/tests/target/attribute-in-enum/vertical-with-doc.rs new file mode 100644 index 00000000000..7b1d3315c2b --- /dev/null +++ b/tests/target/attribute-in-enum/vertical-with-doc.rs @@ -0,0 +1,14 @@ +// rustfmt-style_edition: 2024 +enum A { + B { + a: usize, + b: usize, + c: usize, + d: usize, + }, + + /// C + C { + a: usize, + }, +}