@@ -146,47 +146,142 @@ pub fn print_crate<'a>(
146146 s. s . eof ( )
147147}
148148
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.
150158///
151159/// Note: some old proc macros parse pretty-printed output, so changes here can
152160/// break old code. For example:
153161/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
154162/// - #73345: `#[allow(unused)] must be printed rather than `# [allow(unused)]
155163///
156- fn space_between ( tt1 : & TokenTree , tt2 : & TokenTree ) -> bool {
164+ fn space_between ( prev : Option < & TokenTree > , tt1 : & TokenTree , tt2 : & TokenTree ) -> bool {
157165 use token:: * ;
158166 use Delimiter :: * ;
159167 use TokenTree :: Delimited as Del ;
160168 use TokenTree :: Token as Tok ;
161169
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.
165171 match ( tt1, tt2) {
166172 // No space after line doc comments.
167173 ( Tok ( Token { kind : DocComment ( CommentKind :: Line , ..) , .. } , _) , _) => false ,
168174
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+ }
181271
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+ }
187278
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+ }
190285
191286 _ => true ,
192287 }
@@ -583,14 +678,19 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
583678 }
584679
585680 fn print_tts ( & mut self , tts : & TokenStream , convert_dollar_crate : bool ) {
681+ let mut prev = None ;
586682 let mut iter = tts. trees ( ) . peekable ( ) ;
587683 while let Some ( tt) = iter. next ( ) {
588684 self . print_tt ( tt, convert_dollar_crate) ;
589685 if let Some ( next) = iter. peek ( ) {
590- if space_between ( tt, next) {
686+ if space_between ( prev , tt, next) {
591687 self . space ( ) ;
688+ } else {
689+ // There must be a space between two punctuation tokens.
690+ assert ! ( !is_punct( tt) || !is_punct( next) ) ;
592691 }
593692 }
693+ prev = Some ( tt) ;
594694 }
595695 }
596696
0 commit comments