Skip to content

Commit 6475e44

Browse files
committed
struct_field_align with shorthand and rest/base
This works with the new target files, but breaks in surprising previously working places. test with cargo +nightly-2025-01-02 test system_tests -- --nocapture
1 parent 6f7aeed commit 6475e44

File tree

8 files changed

+153
-20
lines changed

8 files changed

+153
-20
lines changed

src/expr.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -1620,10 +1620,6 @@ fn rewrite_index(
16201620
}
16211621
}
16221622

1623-
fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool {
1624-
!has_base && fields.iter().all(|field| !field.is_shorthand)
1625-
}
1626-
16271623
fn rewrite_struct_lit<'a>(
16281624
context: &RewriteContext<'_>,
16291625
path: &ast::Path,
@@ -1660,15 +1656,14 @@ fn rewrite_struct_lit<'a>(
16601656

16611657
let one_line_width = h_shape.map_or(0, |shape| shape.width);
16621658
let body_lo = context.snippet_provider.span_after(span, "{");
1663-
let fields_str = if struct_lit_can_be_aligned(fields, has_base_or_rest)
1664-
&& context.config.struct_field_align_threshold() > 0
1665-
{
1659+
let fields_str = if context.config.struct_field_align_threshold() > 0 {
16661660
rewrite_with_alignment(
16671661
fields,
16681662
context,
16691663
v_shape,
16701664
mk_sp(body_lo, span.hi()),
16711665
one_line_width,
1666+
Some(struct_rest),
16721667
)
16731668
.unknown_error()?
16741669
} else {
@@ -1720,10 +1715,10 @@ fn rewrite_struct_lit<'a>(
17201715
body_lo,
17211716
span.hi(),
17221717
false,
1723-
);
1724-
let item_vec = items.collect::<Vec<_>>();
1718+
)
1719+
.collect::<Vec<_>>();
17251720

1726-
let tactic = struct_lit_tactic(h_shape, context, &item_vec);
1721+
let tactic = struct_lit_tactic(h_shape, context, &items);
17271722
let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
17281723

17291724
let ends_with_comma = span_ends_with_comma(context, span);
@@ -1736,7 +1731,7 @@ fn rewrite_struct_lit<'a>(
17361731
force_no_trailing_comma || has_base_or_rest || !context.use_block_indent(),
17371732
);
17381733

1739-
write_list(&item_vec, &fmt)?
1734+
write_list(&items, &fmt)?
17401735
};
17411736

17421737
let fields_str =

src/items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,7 @@ pub(crate) fn format_struct_struct(
15031503
Shape::indented(offset.block_indent(context.config), context.config).sub_width_opt(1)?,
15041504
mk_sp(body_lo, span.hi()),
15051505
one_line_budget,
1506+
None
15061507
)?;
15071508

15081509
if !items_str.contains('\n')

src/test/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,13 @@ fn system_tests() {
181181
init_log();
182182
run_test_with(&TestSetting::default(), || {
183183
// Get all files in the tests/source directory.
184-
let files = get_test_files(Path::new("tests/source"), true);
184+
// let files = get_test_files(Path::new("tests/source"), true);
185+
let files = vec![
186+
// PathBuf::from("tests/source/issue_6096.rs"),
187+
// PathBuf::from("tests/source/issue_6080.rs")
188+
PathBuf::from("tests/source/configs/struct_field_align_threshold/20.rs")
189+
];
190+
// dbg!(&files);
185191
let (_reports, count, fails) = check_files(files, &None);
186192

187193
// Display results.

src/vertical.rs

+73-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use itertools::Itertools;
66
use rustc_ast::ast;
77
use rustc_span::{BytePos, Span};
88

9-
use crate::comment::combine_strs_with_missing_comments;
9+
use crate::comment::{FindUncommented, combine_strs_with_missing_comments};
1010
use crate::config::lists::*;
1111
use crate::expr::rewrite_field;
1212
use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
@@ -108,12 +108,13 @@ impl AlignedItem for ast::ExprField {
108108
}
109109
}
110110

111-
pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
111+
pub(crate) fn rewrite_with_alignment<T: AlignedItem + std::fmt::Debug>(
112112
fields: &[T],
113113
context: &RewriteContext<'_>,
114114
shape: Shape,
115115
span: Span,
116116
one_line_width: usize,
117+
struct_rest: Option<&ast::StructRest>,
117118
) -> Option<String> {
118119
let (spaces, group_index) = if context.config.struct_field_align_threshold() > 0 {
119120
group_aligned_items(context, fields)
@@ -170,12 +171,15 @@ pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
170171
shape.indent,
171172
one_line_width,
172173
force_separator,
174+
struct_rest,
175+
shape,
173176
)?;
174177
if rest.is_empty() {
175178
Some(result + spaces)
176179
} else {
177180
let rest_span = mk_sp(init_last_pos, span.hi());
178-
let rest_str = rewrite_with_alignment(rest, context, shape, rest_span, one_line_width)?;
181+
let rest_str =
182+
rewrite_with_alignment(rest, context, shape, rest_span, one_line_width, struct_rest)?;
179183
Some(format!(
180184
"{}{}\n{}{}",
181185
result,
@@ -211,6 +215,8 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
211215
offset: Indent,
212216
one_line_width: usize,
213217
force_trailing_separator: bool,
218+
struct_rest: Option<&ast::StructRest>,
219+
shape: Shape,
214220
) -> Option<String> {
215221
// 1 = ","
216222
let item_shape = Shape::indented(offset, context.config).sub_width_opt(1)?;
@@ -221,14 +227,71 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
221227
field_prefix_max_width = 0;
222228
}
223229

230+
enum StructLitField<'a, T> {
231+
Regular(&'a T),
232+
Base(&'a ast::Expr),
233+
Rest(Span),
234+
}
235+
236+
let has_base_or_rest = match struct_rest {
237+
Some(rest) => match rest {
238+
// ast::StructRest::None if fields.is_empty() => return format!("{path_str} {{}}"),
239+
// ast::StructRest::Rest(_) if fields.is_empty() => {
240+
// return Ok(format!("{path_str} {{ .. }}"));
241+
// }
242+
ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true,
243+
_ => false,
244+
},
245+
None => false,
246+
};
247+
248+
let field_iter = fields.iter().map(StructLitField::Regular).chain(
249+
struct_rest
250+
.and_then(|rest| match rest {
251+
ast::StructRest::Base(expr) => Some(StructLitField::Base(&**expr)),
252+
ast::StructRest::Rest(span) => Some(StructLitField::Rest(*span)),
253+
ast::StructRest::None => None,
254+
})
255+
.into_iter(),
256+
);
257+
258+
let span_lo = |item: &StructLitField<'_, T>| match *item {
259+
StructLitField::Regular(field) => field.get_span().lo(),
260+
StructLitField::Base(expr) => {
261+
let last_field_hi = fields
262+
.last()
263+
.map_or(span.lo(), |field| field.get_span().hi());
264+
let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo()));
265+
let pos = snippet.find_uncommented("..").unwrap();
266+
last_field_hi + BytePos(pos as u32)
267+
}
268+
StructLitField::Rest(span) => span.lo(),
269+
};
270+
let span_hi = |item: &StructLitField<'_, T>| match *item {
271+
StructLitField::Regular(field) => field.get_span().hi(),
272+
StructLitField::Base(expr) => expr.span.hi(),
273+
StructLitField::Rest(span) => span.hi(),
274+
};
275+
let rewrite = |item: &StructLitField<'_, T>| match *item {
276+
StructLitField::Regular(field) => {
277+
field.rewrite_aligned_item(context, item_shape, field_prefix_max_width)
278+
}
279+
StructLitField::Base(expr) => {
280+
// 2 = ..
281+
expr.rewrite_result(context, shape.offset_left(2, span)?)
282+
.map(|s| format!("..{}", s))
283+
}
284+
StructLitField::Rest(_) => Ok("..".to_owned()),
285+
};
286+
224287
let mut items = itemize_list(
225288
context.snippet_provider,
226-
fields.iter(),
289+
field_iter,
227290
"}",
228291
",",
229-
|field| field.get_span().lo(),
230-
|field| field.get_span().hi(),
231-
|field| field.rewrite_aligned_item(context, item_shape, field_prefix_max_width),
292+
span_lo,
293+
span_hi,
294+
rewrite,
232295
span.lo(),
233296
span.hi(),
234297
false,
@@ -258,6 +321,8 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
258321

259322
let separator_tactic = if force_trailing_separator {
260323
SeparatorTactic::Always
324+
} else if has_base_or_rest {
325+
SeparatorTactic::Never
261326
} else {
262327
context.config.trailing_comma()
263328
};
@@ -277,7 +342,7 @@ fn group_aligned_items<T: AlignedItem>(
277342
fields: &[T],
278343
) -> (&'static str, usize) {
279344
let mut index = 0;
280-
for i in 0..fields.len() - 1 {
345+
for i in 0..fields.len().saturating_sub(1) {
281346
if fields[i].skip() {
282347
return ("", index);
283348
}

tests/source/issue_6080.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// rustfmt-struct_field_align_threshold: 30
2+
3+
#[derive(Default)]
4+
struct Foo {
5+
id: u8,
6+
age: u8,
7+
name: String,
8+
}
9+
10+
fn main() {
11+
foo = Foo {
12+
id: 5,
13+
name: "John".into(),
14+
..Default::default()
15+
};
16+
}

tests/source/issue_6096.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// rustfmt-struct_field_align_threshold: 30
2+
3+
struct Foo {
4+
id: u8,
5+
name: String,
6+
x: u8,
7+
}
8+
9+
fn main() {
10+
let x = 5;
11+
12+
foo = Foo {
13+
id: 3,
14+
x,
15+
name: "John".into(),
16+
};
17+
}

tests/target/issue_6080.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// rustfmt-struct_field_align_threshold: 30
2+
3+
#[derive(Default)]
4+
struct Foo {
5+
id: u8,
6+
age: u8,
7+
name: String,
8+
}
9+
10+
fn main() {
11+
foo = Foo {
12+
id: 5,
13+
name: "John".into(),
14+
..Default::default()
15+
};
16+
}

tests/target/issue_6096.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// rustfmt-struct_field_align_threshold: 30
2+
3+
struct Foo {
4+
id: u8,
5+
name: String,
6+
x: u8,
7+
}
8+
9+
fn main() {
10+
let x = 5;
11+
12+
foo = Foo {
13+
id: 3,
14+
x,
15+
name: "John".into(),
16+
};
17+
}

0 commit comments

Comments
 (0)