@@ -146,37 +146,144 @@ pub fn print_crate<'a>(
146
146
s. s . eof ( )
147
147
}
148
148
149
- /// This makes printed token streams look slightly nicer,
150
- /// and also addresses some specific regressions described in #63896 and #73345.
151
- fn space_between ( prev : & TokenTree , curr : & TokenTree ) -> bool {
152
- if let TokenTree :: Token ( token, _) = prev {
153
- // No space after these tokens, e.g. `x.y`, `$e`
154
- // (The carets point to `prev`.) ^ ^
155
- if matches ! ( token. kind, token:: Dot | token:: Dollar ) {
156
- return false ;
157
- }
158
- if let token:: DocComment ( comment_kind, ..) = token. kind {
159
- return comment_kind != CommentKind :: Line ;
160
- }
161
- }
162
- match curr {
163
- // No space before these tokens, e.g. `foo,`, `println!`, `x.y`
164
- // (The carets point to `curr`.) ^ ^ ^
165
- //
166
- // FIXME: having `Not` here works well for macro invocations like
167
- // `println!()`, but is bad when `!` means "logical not" or "the never
168
- // type", where the lack of space causes ugliness like this:
169
- // `Fn() ->!`, `x =! y`, `if! x { f(); }`.
170
- TokenTree :: Token ( token, _) => !matches ! ( token. kind, token:: Comma | token:: Not | token:: Dot ) ,
171
- // No space before parentheses if preceded by these tokens, e.g. `foo(...)`
172
- TokenTree :: Delimited ( _, Delimiter :: Parenthesis , _) => {
173
- !matches ! ( prev, TokenTree :: Token ( Token { kind: token:: Ident ( ..) , .. } , _) )
174
- }
175
- // No space before brackets if preceded by these tokens, e.g. `#[...]`
176
- TokenTree :: Delimited ( _, Delimiter :: Bracket , _) => {
177
- !matches ! ( prev, TokenTree :: Token ( Token { kind: token:: Pound , .. } , _) )
178
- }
179
- TokenTree :: Delimited ( ..) => true ,
149
+ fn is_punct ( tt : & TokenTree ) -> bool {
150
+ matches ! ( tt, TokenTree :: Token ( tok, _) if tok. is_punct( ) )
151
+ }
152
+
153
+ /// Should two consecutive token trees be printed with a space between them?
154
+ ///
155
+ /// NOTE: should always be false if both token trees are punctuation, so that
156
+ /// any old proc macro that parses pretty-printed code won't glue together
157
+ /// tokens that shouldn't be glued.
158
+ ///
159
+ /// Note: some old proc macros parse pretty-printed output, so changes here can
160
+ /// break old code. For example:
161
+ /// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
162
+ /// - #73345: `#[allow(unused)] must be printed rather than `# [allow(unused)]
163
+ ///
164
+ fn space_between ( prev : Option < & TokenTree > , tt1 : & TokenTree , tt2 : & TokenTree ) -> bool {
165
+ use token:: * ;
166
+ use Delimiter :: * ;
167
+ use TokenTree :: Delimited as Del ;
168
+ use TokenTree :: Token as Tok ;
169
+
170
+ // Each match arm has one or more examples in comments.
171
+ match ( tt1, tt2) {
172
+ // No space after line doc comments.
173
+ ( Tok ( Token { kind : DocComment ( CommentKind :: Line , ..) , .. } , _) , _) => false ,
174
+
175
+ // `.` + NON-PUNCT: `x.y`, `tup.0`
176
+ // `$` + NON-PUNCT: `$e`
177
+ ( Tok ( Token { kind : Dot | Dollar , .. } , _) , tt2) if !is_punct ( tt2) => false ,
178
+
179
+ // NON-PUNCT + `,`: `foo,`
180
+ // NON-PUNCT + `;`: `x = 3;`, `[T; 3]`
181
+ // NON-PUNCT + `.`: `x.y`, `tup.0`
182
+ // NON-PUNCT + `:`: `'a: loop { ... }`, `x: u8`, `where T: U`,
183
+ // `<Self as T>::x`, `Trait<'a>: Sized`, `X<Y<Z>>: Send`,
184
+ // `let (a, b): (u32, u32);`
185
+ ( tt1, Tok ( Token { kind : Comma | Semi | Dot | Colon , .. } , _) ) if !is_punct ( tt1) => false ,
186
+
187
+ // ANYTHING-BUT-`,`|`:`|`mut`|`<` + `[`: `<expr>[1]`, `vec![]`, `#[attr]`,
188
+ // `#![attr]`, but not `data: [T; 0]`, `f(a, [])`, `&mut [T]`,
189
+ // `NonNull< [T] >`
190
+ ( Tok ( Token { kind : Comma | Colon | Lt , .. } , _) , Del ( _, Bracket , _) ) => true ,
191
+ ( Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) , Del ( _, Bracket , _) )
192
+ if * sym == kw:: Mut && !is_raw =>
193
+ {
194
+ true
195
+ }
196
+ ( Tok ( _, _) , Del ( _, Bracket , _) ) => false ,
197
+
198
+ // IDENT|`fn`|`Self`|`pub` + `(`: `f(3)`, `fn(x: u8)`, `Self()`, `pub(crate)`,
199
+ // but `let (a, b) = (1, 2)` needs a space after the `let`
200
+ ( Tok ( Token { kind : Ident ( sym, is_raw) , span } , _) , Del ( _, Parenthesis , _) )
201
+ if !Ident :: new ( * sym, * span) . is_reserved ( )
202
+ || * sym == kw:: Fn
203
+ || * sym == kw:: SelfUpper
204
+ || * sym == kw:: Pub
205
+ || * is_raw =>
206
+ {
207
+ false
208
+ }
209
+
210
+ // IDENT|`self`|`Self`|`$crate`|`crate`|`super` + `::`: `x::y`,
211
+ // `Self::a`, `$crate::x`, `crate::x`, `super::x`, but
212
+ // `if ::a::b() { ... }` needs a space after the `if`.
213
+ ( Tok ( Token { kind : Ident ( sym, is_raw) , span } , _) , Tok ( Token { kind : ModSep , .. } , _) )
214
+ if !Ident :: new ( * sym, * span) . is_reserved ( )
215
+ || sym. is_path_segment_keyword ( )
216
+ || * is_raw =>
217
+ {
218
+ false
219
+ }
220
+
221
+ // `::` + IDENT: `foo::bar`
222
+ // `::` + `{`: `use a::{b, c}`
223
+ (
224
+ Tok ( Token { kind : ModSep , .. } , _) ,
225
+ Tok ( Token { kind : Ident ( ..) , .. } , _) | Del ( _, Brace , _) ,
226
+ ) => false ,
227
+
228
+ // `impl` + `<`: `impl<T> Foo<T> { ... }`
229
+ // `for` + `<`: `for<'a> fn()`
230
+ ( Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) , Tok ( Token { kind : Lt , .. } , _) )
231
+ if ( * sym == kw:: Impl || * sym == kw:: For ) && !is_raw =>
232
+ {
233
+ false
234
+ }
235
+
236
+ // `fn` + IDENT + `<`: `fn f<T>(t: T) { ... }`
237
+ ( Tok ( Token { kind : Ident ( ..) , .. } , _) , Tok ( Token { kind : Lt , .. } , _) )
238
+ if let Some ( prev) = prev
239
+ && let Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) = prev
240
+ && * sym == kw:: Fn
241
+ && !is_raw =>
242
+ {
243
+ false
244
+ }
245
+
246
+ // `>` + `(`: `f::<u8>()`
247
+ // `>>` + `(`: `collect::<Vec<_>>()`
248
+ ( Tok ( Token { kind : Gt | BinOp ( Shr ) , .. } , _) , Del ( _, Parenthesis , _) ) => false ,
249
+
250
+ // IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
251
+ ( Tok ( Token { kind : Ident ( sym, is_raw) , span } , _) , Tok ( Token { kind : Not , .. } , _) )
252
+ if !Ident :: new ( * sym, * span) . is_reserved ( ) || * is_raw =>
253
+ {
254
+ false
255
+ }
256
+
257
+ // ANYTHING-BUT-`macro_rules` + `!` + NON-PUNCT-OR-BRACE: `foo!()`, `vec![]`,
258
+ // `if !cond { ... }`, but not `macro_rules! m { ... }`
259
+ ( Tok ( Token { kind : Not , .. } , _) , tt2) if is_punct ( tt2) => true ,
260
+ ( Tok ( Token { kind : Not , .. } , _) , Del ( _, Brace , _) ) => true ,
261
+ ( Tok ( Token { kind : Not , .. } , _) , _) =>
262
+ if let Some ( prev) = prev
263
+ && let Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) = prev
264
+ && * sym == sym:: macro_rules
265
+ && !is_raw
266
+ {
267
+ true
268
+ } else {
269
+ false
270
+ }
271
+
272
+ // `~` + `const`: `impl ~const Clone`
273
+ ( Tok ( Token { kind : Tilde , .. } , _) , Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) )
274
+ if * sym == kw:: Const && !is_raw =>
275
+ {
276
+ false
277
+ }
278
+
279
+ // `?` + `Sized`: `dyn ?Sized`
280
+ ( Tok ( Token { kind : Question , .. } , _) , Tok ( Token { kind : Ident ( sym, is_raw) , .. } , _) )
281
+ if * sym == sym:: Sized && !is_raw =>
282
+ {
283
+ false
284
+ }
285
+
286
+ _ => true ,
180
287
}
181
288
}
182
289
@@ -571,14 +678,19 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
571
678
}
572
679
573
680
fn print_tts ( & mut self , tts : & TokenStream , convert_dollar_crate : bool ) {
681
+ let mut prev = None ;
574
682
let mut iter = tts. trees ( ) . peekable ( ) ;
575
683
while let Some ( tt) = iter. next ( ) {
576
684
self . print_tt ( tt, convert_dollar_crate) ;
577
685
if let Some ( next) = iter. peek ( ) {
578
- if space_between ( tt, next) {
686
+ if space_between ( prev , tt, next) {
579
687
self . space ( ) ;
688
+ } else {
689
+ // There must be a space between two punctuation tokens.
690
+ assert ! ( !is_punct( tt) || !is_punct( next) ) ;
580
691
}
581
692
}
693
+ prev = Some ( tt) ;
582
694
}
583
695
}
584
696
0 commit comments