@@ -146,47 +146,142 @@ pub fn print_crate<'a>(
146
146
s. s . eof ( )
147
147
}
148
148
149
- /// Should two consecutive tokens be printed with a space between them?
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.
150
158
///
151
159
/// Note: some old proc macros parse pretty-printed output, so changes here can
152
160
/// break old code. For example:
153
161
/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
154
162
/// - #73345: `#[allow(unused)] must be printed rather than `# [allow(unused)]
155
163
///
156
- fn space_between ( tt1 : & TokenTree , tt2 : & TokenTree ) -> bool {
164
+ fn space_between ( prev : Option < & TokenTree > , tt1 : & TokenTree , tt2 : & TokenTree ) -> bool {
157
165
use token:: * ;
158
166
use Delimiter :: * ;
159
167
use TokenTree :: Delimited as Del ;
160
168
use TokenTree :: Token as Tok ;
161
169
162
- // Each match arm has one or more examples in comments. The default is to
163
- // insert space between adjacent tokens, except for the cases listed in
164
- // this match.
170
+ // Each match arm has one or more examples in comments.
165
171
match ( tt1, tt2) {
166
172
// No space after line doc comments.
167
173
( Tok ( Token { kind : DocComment ( CommentKind :: Line , ..) , .. } , _) , _) => false ,
168
174
169
- // `.` + ANYTHING: `x.y`, `tup.0`
170
- // `$` + ANYTHING: `$e`
171
- ( Tok ( Token { kind : Dot | Dollar , .. } , _) , _) => false ,
172
-
173
- // ANYTHING + `,`: `foo,`
174
- // ANYTHING + `.`: `x.y`, `tup.0`
175
- // ANYTHING + `!`: `foo! { ... }`
176
- //
177
- // FIXME: Incorrect cases:
178
- // - Logical not: `x =! y`, `if! x { f(); }`
179
- // - Never type: `Fn() ->!`
180
- ( _, Tok ( Token { kind : Comma | Dot | Not , .. } , _) ) => false ,
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
+ }
181
271
182
- // IDENT + `(`: `f(3)`
183
- //
184
- // FIXME: Incorrect cases:
185
- // - Let: `let(a, b) = (1, 2)`
186
- ( Tok ( Token { kind : Ident ( ..) , .. } , _) , Del ( _, Parenthesis , _) ) => false ,
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
+ }
187
278
188
- // `#` + `[`: `#[attr]`
189
- ( Tok ( Token { kind : Pound , .. } , _) , Del ( _, Bracket , _) ) => false ,
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
+ }
190
285
191
286
_ => true ,
192
287
}
@@ -583,14 +678,19 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
583
678
}
584
679
585
680
fn print_tts ( & mut self , tts : & TokenStream , convert_dollar_crate : bool ) {
681
+ let mut prev = None ;
586
682
let mut iter = tts. trees ( ) . peekable ( ) ;
587
683
while let Some ( tt) = iter. next ( ) {
588
684
self . print_tt ( tt, convert_dollar_crate) ;
589
685
if let Some ( next) = iter. peek ( ) {
590
- if space_between ( tt, next) {
686
+ if space_between ( prev , tt, next) {
591
687
self . space ( ) ;
688
+ } else {
689
+ // There must be a space between two punctuation tokens.
690
+ assert ! ( !is_punct( tt) || !is_punct( next) ) ;
592
691
}
593
692
}
693
+ prev = Some ( tt) ;
594
694
}
595
695
}
596
696
0 commit comments