Skip to content

Commit 9b2b8a5

Browse files
committed
Break tokens before checking if they are 'probably equal'
Fixes #68489 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).
1 parent 3a7dfda commit 9b2b8a5

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

src/librustc_ast/tokenstream.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,38 @@ impl TokenStream {
338338
true
339339
}
340340

341-
let mut t1 = self.trees().filter(semantic_tree);
342-
let mut t2 = other.trees().filter(semantic_tree);
341+
// When comparing two `TokenStream`s, we ignore the `IsJoint` information.
342+
//
343+
// However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will
344+
// use `Token.glue` on adjacent tokens with the proper `IsJoint`.
345+
// Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`)
346+
// and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent
347+
// when determining if two `TokenStream`s are 'probably equal'.
348+
//
349+
// Therefore, we use `break_two_token_op` to convert all tokens
350+
// to the 'unglued' form (if it exists). This ensures that two
351+
// `TokenStream`s which differ only in how their tokens are glued
352+
// will be considered 'probably equal', which allows us to keep spans.
353+
//
354+
// This is important when the original `TokenStream` contained
355+
// extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces
356+
// will be omitted when we pretty-print, which can cause the original
357+
// and reparsed `TokenStream`s to differ in the assignment of `IsJoint`,
358+
// leading to some tokens being 'glued' together in one stream but not
359+
// the other. See #68489 for more details.
360+
fn break_tokens(tree: TokenTree) -> impl Iterator<Item = TokenTree> {
361+
if let TokenTree::Token(token) = &tree {
362+
if let Some((first, second)) = token.kind.break_two_token_op() {
363+
return SmallVec::from_buf([TokenTree::Token(Token::new(first, DUMMY_SP)), TokenTree::Token(Token::new(second, DUMMY_SP))]).into_iter()
364+
}
365+
}
366+
let mut vec = SmallVec::<[_; 2]>::new();
367+
vec.push(tree);
368+
vec.into_iter()
369+
}
370+
371+
let mut t1 = self.trees().filter(semantic_tree).flat_map(break_tokens);
372+
let mut t2 = other.trees().filter(semantic_tree).flat_map(break_tokens);
343373
for (t1, t2) in t1.by_ref().zip(t2.by_ref()) {
344374
if !t1.probably_equal_for_proc_macro(&t2) {
345375
return false;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// aux-build:test-macros.rs
2+
3+
extern crate test_macros;
4+
5+
#[test_macros::recollect_attr]
6+
fn repro() {
7+
f :: < Vec < _ > > ( ) ; //~ ERROR cannot find
8+
}
9+
fn main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0425]: cannot find function `f` in this scope
2+
--> $DIR/turbo-proc-macro.rs:7:5
3+
|
4+
LL | f :: < Vec < _ > > ( ) ;
5+
| ^ not found in this scope
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)