Rollup merge of #93556 - dtolnay:trailingcomma, r=cjgillot

Change struct expr pretty printing to match rustfmt style

This PR backports trailing comma support from https://github.com/dtolnay/prettyplease into rustc_ast_pretty and uses it to improve the formatting of struct expressions.

Example:

```rust
macro_rules! stringify_expr {
    ($expr:expr) => {
        stringify!($expr)
    };
}

fn main() {
    println!("{}", stringify_expr!(Struct {
        a: Struct { b, c },
    }));
    println!("{}", stringify_expr!(Struct {
        aaaaaaaaaa: AAAAAAAAAA,
        bbbbbbbbbb: Struct {
            cccccccccc: CCCCCCCCCC,
            dddddddddd: DDDDDDDDDD,
            eeeeeeeeee: EEEEEEEEEE,
        },
    }));
}
```

🤮 Before:

```console
Struct{a: Struct{b, c,},}
Struct{aaaaaaaaaa: AAAAAAAAAA,
    bbbbbbbbbb:
        Struct{cccccccccc: CCCCCCCCCC,
            dddddddddd: DDDDDDDDDD,
            eeeeeeeeee: EEEEEEEEEE,},}
```

After:

```console
Struct { a: Struct { b, c } }
Struct {
    aaaaaaaaaa: AAAAAAAAAA,
    bbbbbbbbbb: Struct {
        cccccccccc: CCCCCCCCCC,
        dddddddddd: DDDDDDDDDD,
        eeeeeeeeee: EEEEEEEEEE,
    },
}
```
This commit is contained in:
Matthias Krüger 2022-02-06 04:13:32 +01:00 committed by GitHub
commit 59baf4db0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 149 additions and 71 deletions

View file

@ -148,7 +148,7 @@ pub enum Breaks {
Inconsistent,
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
enum IndentStyle {
/// Vertically aligned under whatever column this block begins at.
///
@ -164,19 +164,20 @@ enum IndentStyle {
Block { offset: isize },
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Default, PartialEq)]
pub struct BreakToken {
offset: isize,
blank_space: isize,
pre_break: Option<char>,
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub struct BeginToken {
indent: IndentStyle,
breaks: Breaks,
}
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub enum Token {
// In practice a string token contains either a `&'static str` or a
// `String`. `Cow` is overkill for this because we never modify the data,
@ -313,6 +314,12 @@ impl Printer {
}
}
pub fn offset(&mut self, offset: isize) {
if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() {
token.offset += offset;
}
}
fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
@ -391,7 +398,9 @@ impl Printer {
if size > self.space {
self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks });
self.indent = match token.indent {
IndentStyle::Block { offset } => (self.indent as isize + offset) as usize,
IndentStyle::Block { offset } => {
usize::try_from(self.indent as isize + offset).unwrap()
}
IndentStyle::Visual => (MARGIN - self.space) as usize,
};
} else {
@ -415,6 +424,9 @@ impl Printer {
self.pending_indentation += token.blank_space;
self.space -= token.blank_space;
} else {
if let Some(pre_break) = token.pre_break {
self.out.push(pre_break);
}
self.out.push('\n');
let indent = self.indent as isize + token.offset;
self.pending_indentation = indent;

View file

@ -3,20 +3,17 @@ use std::borrow::Cow;
impl Printer {
/// "raw box"
pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
self.scan_begin(BeginToken {
indent: IndentStyle::Block { offset: indent as isize },
breaks,
})
pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks })
}
/// Inconsistent breaking box
pub fn ibox(&mut self, indent: usize) {
pub fn ibox(&mut self, indent: isize) {
self.rbox(indent, Breaks::Inconsistent)
}
/// Consistent breaking box
pub fn cbox(&mut self, indent: usize) {
pub fn cbox(&mut self, indent: isize) {
self.rbox(indent, Breaks::Consistent)
}
@ -25,7 +22,11 @@ impl Printer {
}
pub fn break_offset(&mut self, n: usize, off: isize) {
self.scan_break(BreakToken { offset: off, blank_space: n as isize })
self.scan_break(BreakToken {
offset: off,
blank_space: n as isize,
..BreakToken::default()
});
}
pub fn end(&mut self) {
@ -66,12 +67,24 @@ impl Printer {
}
pub fn hardbreak_tok_offset(off: isize) -> Token {
Token::Break(BreakToken { offset: off, blank_space: SIZE_INFINITY })
Token::Break(BreakToken {
offset: off,
blank_space: SIZE_INFINITY,
..BreakToken::default()
})
}
pub fn trailing_comma(&mut self) {
self.scan_break(BreakToken {
blank_space: 1,
pre_break: Some(','),
..BreakToken::default()
});
}
}
impl Token {
pub fn is_hardbreak_tok(&self) -> bool {
matches!(self, Token::Break(BreakToken { offset: 0, blank_space: SIZE_INFINITY }))
*self == Printer::hardbreak_tok_offset(0)
}
}

View file

@ -1,3 +1,4 @@
mod delimited;
mod expr;
mod item;
@ -23,6 +24,8 @@ use rustc_span::{BytePos, FileName, Span};
use std::borrow::Cow;
pub use self::delimited::IterDelimited;
pub enum MacHeader<'a> {
Path(&'a ast::Path),
Keyword(&'static str),
@ -92,7 +95,7 @@ pub struct State<'a> {
ann: &'a (dyn PpAnn + 'a),
}
crate const INDENT_UNIT: usize = 4;
crate const INDENT_UNIT: isize = 4;
/// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments to copy forward.

View file

@ -0,0 +1,41 @@
use std::iter::Peekable;
use std::mem;
use std::ops::Deref;
pub struct Delimited<I: Iterator> {
is_first: bool,
iter: Peekable<I>,
}
pub trait IterDelimited: Iterator + Sized {
fn delimited(self) -> Delimited<Self> {
Delimited { is_first: true, iter: self.peekable() }
}
}
impl<I: Iterator> IterDelimited for I {}
pub struct IteratorItem<T> {
value: T,
pub is_first: bool,
pub is_last: bool,
}
impl<I: Iterator> Iterator for Delimited<I> {
type Item = IteratorItem<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
let value = self.iter.next()?;
let is_first = mem::replace(&mut self.is_first, false);
let is_last = self.iter.peek().is_none();
Some(IteratorItem { value, is_first, is_last })
}
}
impl<T> Deref for IteratorItem<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}

View file

@ -1,5 +1,5 @@
use crate::pp::Breaks::{Consistent, Inconsistent};
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
use crate::pp::Breaks::Inconsistent;
use crate::pprust::state::{AnnNode, IterDelimited, PrintState, State, INDENT_UNIT};
use rustc_ast::ptr::P;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
@ -117,38 +117,46 @@ impl<'a> State<'a> {
} else {
self.print_path(path, true, 0);
}
self.nbsp();
self.word("{");
self.commasep_cmnt(
Consistent,
fields,
|s, field| {
s.print_outer_attributes(&field.attrs);
s.ibox(INDENT_UNIT);
if !field.is_shorthand {
s.print_ident(field.ident);
s.word_space(":");
}
s.print_expr(&field.expr);
s.end();
},
|f| f.span,
);
match rest {
ast::StructRest::Base(_) | ast::StructRest::Rest(_) => {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.word(",");
self.space();
}
self.word("..");
if let ast::StructRest::Base(ref expr) = *rest {
self.print_expr(expr);
}
self.end();
}
ast::StructRest::None if !fields.is_empty() => self.word(","),
_ => {}
let has_rest = match rest {
ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true,
ast::StructRest::None => false,
};
if fields.is_empty() && !has_rest {
self.word("}");
return;
}
self.cbox(0);
for field in fields.iter().delimited() {
self.maybe_print_comment(field.span.hi());
self.print_outer_attributes(&field.attrs);
if field.is_first {
self.space_if_not_bol();
}
if !field.is_shorthand {
self.print_ident(field.ident);
self.word_nbsp(":");
}
self.print_expr(&field.expr);
if !field.is_last || has_rest {
self.word_space(",");
} else {
self.trailing_comma();
}
}
if has_rest {
if fields.is_empty() {
self.space();
}
self.word("..");
if let ast::StructRest::Base(expr) = rest {
self.print_expr(expr);
}
self.space();
}
self.offset(-INDENT_UNIT);
self.end();
self.word("}");
}

View file

@ -139,7 +139,7 @@ impl<'a> PrintState<'a> for State<'a> {
}
}
pub const INDENT_UNIT: usize = 4;
pub const INDENT_UNIT: isize = 4;
/// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments to copy forward.

View file

@ -119,9 +119,9 @@ fn syntax() {
let _ = #[attr] foo![#! [attr]];
let _ = #[attr] foo! {};
let _ = #[attr] foo! { #! [attr] };
let _ = #[attr] Foo{bar: baz,};
let _ = #[attr] Foo{..foo};
let _ = #[attr] Foo{bar: baz, ..foo};
let _ = #[attr] Foo { bar: baz };
let _ = #[attr] Foo { ..foo };
let _ = #[attr] Foo { bar: baz, ..foo };
let _ = #[attr] (0);
{

View file

@ -8,9 +8,10 @@ struct C {
#[allow()]
const C: C =
C{
C {
#[cfg(debug_assertions)]
field: 0,
#[cfg(not(debug_assertions))]
field: 1,};
field: 1,
};

View file

@ -90,9 +90,9 @@ struct Bar(());
fn _7() {
#[rustc_dummy]
Foo{data: (),};
Foo { data: () };
let _ = #[rustc_dummy] Foo{data: (),};
let _ = #[rustc_dummy] Foo { data: () };
}
fn _8() {
@ -209,7 +209,7 @@ fn _11() {
let mut x = 0;
let _ = #[rustc_dummy] x = 15;
let _ = #[rustc_dummy] x += 15;
let s = Foo{data: (),};
let s = Foo { data: () };
let _ = #[rustc_dummy] s.data;
let _ = (#[rustc_dummy] s).data;
let t = Bar(());
@ -235,9 +235,9 @@ fn _11() {
let _ = #[rustc_dummy] expr_mac!();
let _ = #[rustc_dummy] expr_mac![];
let _ = #[rustc_dummy] expr_mac! {};
let _ = #[rustc_dummy] Foo{data: (),};
let _ = #[rustc_dummy] Foo{..s};
let _ = #[rustc_dummy] Foo{data: (), ..s};
let _ = #[rustc_dummy] Foo { data: () };
let _ = #[rustc_dummy] Foo { ..s };
let _ = #[rustc_dummy] Foo { data: (), ..s };
let _ = #[rustc_dummy] (0);
}

View file

@ -315,17 +315,17 @@ fn test_expr() {
assert_eq!(stringify_expr!(mac! { ... }), "mac! { ... }");
// ExprKind::Struct
assert_eq!(stringify_expr!(Struct {}), "Struct{}"); // FIXME
assert_eq!(stringify_expr!(Struct {}), "Struct {}");
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151
assert_eq!(stringify_expr!(<Struct as Trait>::Type {}), "<Struct as Trait>::Type{}");
assert_eq!(stringify_expr!(Struct { .. }), "Struct{..}"); // FIXME
assert_eq!(stringify_expr!(Struct { ..base }), "Struct{..base}"); // FIXME
assert_eq!(stringify_expr!(Struct { x }), "Struct{x,}");
assert_eq!(stringify_expr!(Struct { x, .. }), "Struct{x, ..}");
assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct{x, ..base}");
assert_eq!(stringify_expr!(Struct { x: true }), "Struct{x: true,}");
assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct{x: true, ..}");
assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct{x: true, ..base}");
assert_eq!(stringify_expr!(<Struct as Trait>::Type {}), "<Struct as Trait>::Type {}");
assert_eq!(stringify_expr!(Struct { .. }), "Struct { .. }");
assert_eq!(stringify_expr!(Struct { ..base }), "Struct { ..base }");
assert_eq!(stringify_expr!(Struct { x }), "Struct { x }");
assert_eq!(stringify_expr!(Struct { x, .. }), "Struct { x, .. }");
assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct { x, ..base }");
assert_eq!(stringify_expr!(Struct { x: true }), "Struct { x: true }");
assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct { x: true, .. }");
assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct { x: true, ..base }");
// ExprKind::Repeat
assert_eq!(stringify_expr!([(); 0]), "[(); 0]");

View file

@ -1,6 +1,6 @@
[$DIR/dbg-macro-expected-behavior.rs:20] Unit = Unit
[$DIR/dbg-macro-expected-behavior.rs:21] a = Unit
[$DIR/dbg-macro-expected-behavior.rs:27] Point{x: 42, y: 24,} = Point {
[$DIR/dbg-macro-expected-behavior.rs:27] Point { x: 42, y: 24 } = Point {
x: 42,
y: 24,
}