Skip to content

Commit 63487fc

Browse files
authored
Rollup merge of rust-lang#72306 - Aaron1011:feature/turbo-spacing, r=petrochenkov
Break tokens before checking if they are 'probably equal' Fixes rust-lang#68489 Fixes rust-lang#70987 When checking two `TokenStreams` to see if they are 'probably equal', we ignore the `IsJoint` information associated with each `TokenTree`. However, the `IsJoint` information determines whether adjacent tokens will be 'glued' (if possible) when construction the `TokenStream` - e.g. `[Gt Gt]` can be 'glued' to `BinOp(Shr)`. Since we are ignoring the `IsJoint` information, 'glued' and 'unglued' tokens are equivalent for determining if two `TokenStreams` are 'probably equal'. Therefore, we need to 'unglue' all tokens in the stream to avoid false negatives (which cause us to throw out the cached tokens, losing span information).
2 parents 660dea1 + 633293f commit 63487fc

File tree

5 files changed

+113
-4
lines changed

5 files changed

+113
-4
lines changed

src/librustc_ast/tokenstream.rs

+67-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use rustc_macros::HashStable_Generic;
2121
use rustc_span::{Span, DUMMY_SP};
2222
use smallvec::{smallvec, SmallVec};
2323

24+
use log::debug;
25+
2426
use std::{iter, mem};
2527

2628
/// When the main rust parser encounters a syntax-extension invocation, it
@@ -338,8 +340,71 @@ impl TokenStream {
338340
true
339341
}
340342

341-
let mut t1 = self.trees().filter(semantic_tree);
342-
let mut t2 = other.trees().filter(semantic_tree);
343+
// When comparing two `TokenStream`s, we ignore the `IsJoint` information.
344+
//
345+
// However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will
346+
// use `Token.glue` on adjacent tokens with the proper `IsJoint`.
347+
// Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`)
348+
// and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent
349+
// when determining if two `TokenStream`s are 'probably equal'.
350+
//
351+
// Therefore, we use `break_two_token_op` to convert all tokens
352+
// to the 'unglued' form (if it exists). This ensures that two
353+
// `TokenStream`s which differ only in how their tokens are glued
354+
// will be considered 'probably equal', which allows us to keep spans.
355+
//
356+
// This is important when the original `TokenStream` contained
357+
// extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces
358+
// will be omitted when we pretty-print, which can cause the original
359+
// and reparsed `TokenStream`s to differ in the assignment of `IsJoint`,
360+
// leading to some tokens being 'glued' together in one stream but not
361+
// the other. See #68489 for more details.
362+
fn break_tokens(tree: TokenTree) -> impl Iterator<Item = TokenTree> {
363+
// In almost all cases, we should have either zero or one levels
364+
// of 'unglueing'. However, in some unusual cases, we may need
365+
// to iterate breaking tokens mutliple times. For example:
366+
// '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]'
367+
let mut token_trees: SmallVec<[_; 2]>;
368+
if let TokenTree::Token(token) = &tree {
369+
let mut out = SmallVec::<[_; 2]>::new();
370+
out.push(token.clone());
371+
// Iterate to fixpoint:
372+
// * We start off with 'out' containing our initial token, and `temp` empty
373+
// * If we are able to break any tokens in `out`, then `out` will have
374+
// at least one more element than 'temp', so we will try to break tokens
375+
// again.
376+
// * If we cannot break any tokens in 'out', we are done
377+
loop {
378+
let mut temp = SmallVec::<[_; 2]>::new();
379+
let mut changed = false;
380+
381+
for token in out.into_iter() {
382+
if let Some((first, second)) = token.kind.break_two_token_op() {
383+
temp.push(Token::new(first, DUMMY_SP));
384+
temp.push(Token::new(second, DUMMY_SP));
385+
changed = true;
386+
} else {
387+
temp.push(token);
388+
}
389+
}
390+
out = temp;
391+
if !changed {
392+
break;
393+
}
394+
}
395+
token_trees = out.into_iter().map(|t| TokenTree::Token(t)).collect();
396+
if token_trees.len() != 1 {
397+
debug!("break_tokens: broke {:?} to {:?}", tree, token_trees);
398+
}
399+
} else {
400+
token_trees = SmallVec::new();
401+
token_trees.push(tree);
402+
}
403+
token_trees.into_iter()
404+
}
405+
406+
let mut t1 = self.trees().filter(semantic_tree).flat_map(break_tokens);
407+
let mut t2 = other.trees().filter(semantic_tree).flat_map(break_tokens);
343408
for (t1, t2) in t1.by_ref().zip(t2.by_ref()) {
344409
if !t1.probably_equal_for_proc_macro(&t2) {
345410
return false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// aux-build:test-macros.rs
2+
// Regression test for issues #68489 and #70987
3+
// Tests that we properly break tokens in `probably_equal_for_proc_macro`
4+
// See #72306
5+
//
6+
// Note that the weird spacing in this example is critical
7+
// for testing the issue.
8+
9+
extern crate test_macros;
10+
11+
#[test_macros::recollect_attr]
12+
fn repro() {
13+
f :: < Vec < _ > > ( ) ; //~ ERROR cannot find
14+
let a: Option<Option<u8>>= true; //~ ERROR mismatched
15+
}
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0425]: cannot find function `f` in this scope
2+
--> $DIR/break-token-spans.rs:13:5
3+
|
4+
LL | f :: < Vec < _ > > ( ) ;
5+
| ^ not found in this scope
6+
7+
error[E0308]: mismatched types
8+
--> $DIR/break-token-spans.rs:14:32
9+
|
10+
LL | let a: Option<Option<u8>>= true;
11+
| ------------------ ^^^^ expected enum `std::option::Option`, found `bool`
12+
| |
13+
| expected due to this
14+
|
15+
= note: expected enum `std::option::Option<std::option::Option<u8>>`
16+
found type `bool`
17+
18+
error: aborting due to 2 previous errors
19+
20+
Some errors have detailed explanations: E0308, E0425.
21+
For more information about an error, try `rustc --explain E0308`.

src/test/ui/suggestions/issue-61963.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct Qux<T>(T);
1616

1717
#[dom_struct]
1818
pub struct Foo {
19+
//~^ ERROR trait objects without an explicit `dyn` are deprecated [bare_trait_objects]
1920
qux: Qux<Qux<Baz>>,
2021
bar: Box<Bar>,
2122
//~^ ERROR trait objects without an explicit `dyn` are deprecated [bare_trait_objects]

src/test/ui/suggestions/issue-61963.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: trait objects without an explicit `dyn` are deprecated
2-
--> $DIR/issue-61963.rs:20:14
2+
--> $DIR/issue-61963.rs:21:14
33
|
44
LL | bar: Box<Bar>,
55
| ^^^ help: use `dyn`: `dyn Bar`
@@ -10,5 +10,11 @@ note: the lint level is defined here
1010
LL | #![deny(bare_trait_objects)]
1111
| ^^^^^^^^^^^^^^^^^^
1212

13-
error: aborting due to previous error
13+
error: trait objects without an explicit `dyn` are deprecated
14+
--> $DIR/issue-61963.rs:18:1
15+
|
16+
LL | pub struct Foo {
17+
| ^^^ help: use `dyn`: `dyn pub`
18+
19+
error: aborting due to 2 previous errors
1420

0 commit comments

Comments
 (0)