Skip to content

Commit 1773f60

Browse files
committed
Auto merge of #78712 - petrochenkov:visitok, r=Aaron1011
rustc_ast: Visit tokens stored in AST nodes in mutable visitor After #77271 token visiting is enabled only for one visitor in `rustc_expand\src\mbe\transcribe.rs` which applies hygiene marks to tokens produced by declarative macros (`macro_rules` or `macro`), so this change doesn't affect anything else. When a macro has some interpolated token from an outer macro in its output ```rust macro inner() { $interpolated } ``` we can use the usual interpretation of interpolated tokens in token-based model - a None-delimited group - to write this macro in an equivalent form ```rust macro inner() { ⟪ a b c d ⟫ } ``` When we are expanding the macro `inner` we need to apply hygiene marks to all tokens produced by it, including the tokens inside the group. Before this PR we did this by visiting the AST piece inside the interpolated token and applying marks to all spans in it. I'm not sure this is 100% correct (ideally we should apply the marks to tokens and then re-parse the AST from tokens), but it's a very good approximation at least. We didn't however apply the marks to actual tokens stored in the nonterminal, so if we used the nonterminal as a token rather than as an AST piece (e.g. passed it to a proc macro), then we got hygiene bugs. This PR applies the marks to tokens in addition to the AST pieces thus fixing the issue. r? `@Aaron1011`
2 parents b1277d0 + 8def2fc commit 1773f60

File tree

3 files changed

+156
-16
lines changed

3 files changed

+156
-16
lines changed

compiler/rustc_ast/src/mut_visit.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>(
461461
}
462462

463463
pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
464-
let Ty { id, kind, span, tokens: _ } = ty.deref_mut();
464+
let Ty { id, kind, span, tokens } = ty.deref_mut();
465465
vis.visit_id(id);
466466
match kind {
467467
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {}
@@ -497,6 +497,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
497497
TyKind::MacCall(mac) => vis.visit_mac(mac),
498498
}
499499
vis.visit_span(span);
500+
visit_lazy_tts(tokens, vis);
500501
}
501502

502503
pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
@@ -523,13 +524,14 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis:
523524
vis.visit_span(span);
524525
}
525526

526-
pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) {
527+
pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens }: &mut Path, vis: &mut T) {
527528
vis.visit_span(span);
528529
for PathSegment { ident, id, args } in segments {
529530
vis.visit_ident(ident);
530531
vis.visit_id(id);
531532
visit_opt(args, |args| vis.visit_generic_args(args));
532533
}
534+
visit_lazy_tts(tokens, vis);
533535
}
534536

535537
pub fn noop_visit_qself<T: MutVisitor>(qself: &mut Option<QSelf>, vis: &mut T) {
@@ -587,15 +589,17 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
587589
}
588590

589591
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
590-
let Attribute { kind, id: _, style: _, span, tokens: _ } = attr;
592+
let Attribute { kind, id: _, style: _, span, tokens } = attr;
591593
match kind {
592-
AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
594+
AttrKind::Normal(AttrItem { path, args, tokens }) => {
593595
vis.visit_path(path);
594596
visit_mac_args(args, vis);
597+
visit_lazy_tts(tokens, vis);
595598
}
596599
AttrKind::DocComment(..) => {}
597600
}
598601
vis.visit_span(span);
602+
visit_lazy_tts(tokens, vis);
599603
}
600604

601605
pub fn noop_visit_mac<T: MutVisitor>(mac: &mut MacCall, vis: &mut T) {
@@ -652,12 +656,22 @@ pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
652656

653657
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
654658
pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
655-
if vis.token_visiting_enabled() {
659+
if vis.token_visiting_enabled() && !tts.is_empty() {
656660
let tts = Lrc::make_mut(tts);
657661
visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis));
658662
}
659663
}
660664

665+
pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyTokenStream>, vis: &mut T) {
666+
if vis.token_visiting_enabled() {
667+
visit_opt(lazy_tts, |lazy_tts| {
668+
let mut tts = lazy_tts.create_token_stream();
669+
visit_tts(&mut tts, vis);
670+
*lazy_tts = LazyTokenStream::new(tts);
671+
})
672+
}
673+
}
674+
661675
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
662676
// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
663677
// In practice the ident part is not actually used by specific visitors right now,
@@ -725,9 +739,10 @@ pub fn visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut
725739
token::NtLifetime(ident) => vis.visit_ident(ident),
726740
token::NtLiteral(expr) => vis.visit_expr(expr),
727741
token::NtMeta(item) => {
728-
let AttrItem { path, args, tokens: _ } = item.deref_mut();
742+
let AttrItem { path, args, tokens } = item.deref_mut();
729743
vis.visit_path(path);
730744
visit_mac_args(args, vis);
745+
visit_lazy_tts(tokens, vis);
731746
}
732747
token::NtPath(path) => vis.visit_path(path),
733748
token::NtTT(tt) => visit_tt(tt, vis),
@@ -887,10 +902,11 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu
887902
}
888903

889904
pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
890-
let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
905+
let Block { id, stmts, rules: _, span, tokens } = block.deref_mut();
891906
vis.visit_id(id);
892907
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
893908
vis.visit_span(span);
909+
visit_lazy_tts(tokens, vis);
894910
}
895911

896912
pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
@@ -955,7 +971,7 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
955971
mut item: P<AssocItem>,
956972
visitor: &mut T,
957973
) -> SmallVec<[P<AssocItem>; 1]> {
958-
let Item { id, ident, vis, attrs, kind, span, tokens: _ } = item.deref_mut();
974+
let Item { id, ident, vis, attrs, kind, span, tokens } = item.deref_mut();
959975
visitor.visit_id(id);
960976
visitor.visit_ident(ident);
961977
visitor.visit_vis(vis);
@@ -978,6 +994,7 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
978994
AssocItemKind::MacCall(mac) => visitor.visit_mac(mac),
979995
}
980996
visitor.visit_span(span);
997+
visit_lazy_tts(tokens, visitor);
981998
smallvec![item]
982999
}
9831000

@@ -1028,16 +1045,14 @@ pub fn noop_flat_map_item<T: MutVisitor>(
10281045
mut item: P<Item>,
10291046
visitor: &mut T,
10301047
) -> SmallVec<[P<Item>; 1]> {
1031-
let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut();
1048+
let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
10321049
visitor.visit_ident(ident);
10331050
visit_attrs(attrs, visitor);
10341051
visitor.visit_id(id);
10351052
visitor.visit_item_kind(kind);
10361053
visitor.visit_vis(vis);
10371054
visitor.visit_span(span);
1038-
1039-
// FIXME: if `tokens` is modified with a call to `vis.visit_tts` it causes
1040-
// an ICE during resolve... odd!
1055+
visit_lazy_tts(tokens, visitor);
10411056

10421057
smallvec![item]
10431058
}
@@ -1046,7 +1061,7 @@ pub fn noop_flat_map_foreign_item<T: MutVisitor>(
10461061
mut item: P<ForeignItem>,
10471062
visitor: &mut T,
10481063
) -> SmallVec<[P<ForeignItem>; 1]> {
1049-
let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut();
1064+
let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
10501065
visitor.visit_id(id);
10511066
visitor.visit_ident(ident);
10521067
visitor.visit_vis(vis);
@@ -1069,11 +1084,12 @@ pub fn noop_flat_map_foreign_item<T: MutVisitor>(
10691084
ForeignItemKind::MacCall(mac) => visitor.visit_mac(mac),
10701085
}
10711086
visitor.visit_span(span);
1087+
visit_lazy_tts(tokens, visitor);
10721088
smallvec![item]
10731089
}
10741090

10751091
pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
1076-
let Pat { id, kind, span, tokens: _ } = pat.deref_mut();
1092+
let Pat { id, kind, span, tokens } = pat.deref_mut();
10771093
vis.visit_id(id);
10781094
match kind {
10791095
PatKind::Wild | PatKind::Rest => {}
@@ -1108,6 +1124,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
11081124
PatKind::MacCall(mac) => vis.visit_mac(mac),
11091125
}
11101126
vis.visit_span(span);
1127+
visit_lazy_tts(tokens, vis);
11111128
}
11121129

11131130
pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonConst, vis: &mut T) {
@@ -1116,7 +1133,7 @@ pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonCo
11161133
}
11171134

11181135
pub fn noop_visit_expr<T: MutVisitor>(
1119-
Expr { kind, id, span, attrs, tokens: _ }: &mut Expr,
1136+
Expr { kind, id, span, attrs, tokens }: &mut Expr,
11201137
vis: &mut T,
11211138
) {
11221139
match kind {
@@ -1295,6 +1312,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
12951312
vis.visit_id(id);
12961313
vis.visit_span(span);
12971314
visit_thin_attrs(attrs, vis);
1315+
visit_lazy_tts(tokens, vis);
12981316
}
12991317

13001318
pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Option<P<Expr>> {
@@ -1305,11 +1323,12 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio
13051323
}
13061324

13071325
pub fn noop_flat_map_stmt<T: MutVisitor>(
1308-
Stmt { kind, mut span, mut id, tokens }: Stmt,
1326+
Stmt { kind, mut span, mut id, mut tokens }: Stmt,
13091327
vis: &mut T,
13101328
) -> SmallVec<[Stmt; 1]> {
13111329
vis.visit_id(&mut id);
13121330
vis.visit_span(&mut span);
1331+
visit_lazy_tts(&mut tokens, vis);
13131332
noop_flat_map_stmt_kind(kind, vis)
13141333
.into_iter()
13151334
.map(|kind| Stmt { id, kind, span, tokens: tokens.clone() })
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Make sure that marks from declarative macros are applied to tokens in nonterminal.
2+
3+
// check-pass
4+
// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene
5+
// compile-flags: -Z trim-diagnostic-paths=no
6+
// normalize-stdout-test "\d+#" -> "0#"
7+
// aux-build:test-macros.rs
8+
9+
#![feature(decl_macro)]
10+
11+
#![no_std] // Don't load unnecessary hygiene information from std
12+
extern crate std;
13+
14+
#[macro_use]
15+
extern crate test_macros;
16+
17+
macro_rules! outer {
18+
($item:item) => {
19+
macro inner() {
20+
print_bang! { $item }
21+
}
22+
23+
inner!();
24+
};
25+
}
26+
27+
struct S;
28+
29+
outer! {
30+
struct S; // OK, not a duplicate definition of `S`
31+
}
32+
33+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
PRINT-BANG INPUT (DISPLAY): struct S;
2+
PRINT-BANG RE-COLLECTED (DISPLAY): struct S ;
3+
PRINT-BANG INPUT (DEBUG): TokenStream [
4+
Group {
5+
delimiter: None,
6+
stream: TokenStream [
7+
Ident {
8+
ident: "struct",
9+
span: $DIR/nonterminal-token-hygiene.rs:30:5: 30:11 (#5),
10+
},
11+
Ident {
12+
ident: "S",
13+
span: $DIR/nonterminal-token-hygiene.rs:30:12: 30:13 (#5),
14+
},
15+
Punct {
16+
ch: ';',
17+
spacing: Alone,
18+
span: $DIR/nonterminal-token-hygiene.rs:30:13: 30:14 (#5),
19+
},
20+
],
21+
span: $DIR/nonterminal-token-hygiene.rs:20:27: 20:32 (#6),
22+
},
23+
]
24+
#![feature /* 0#0 */(prelude_import)]
25+
#![no_std /* 0#0 */]
26+
// Make sure that marks from declarative macros are applied to tokens in nonterminal.
27+
28+
// check-pass
29+
// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene
30+
// compile-flags: -Z trim-diagnostic-paths=no
31+
// normalize-stdout-test "\d+#" -> "0#"
32+
// aux-build:test-macros.rs
33+
34+
#![feature /* 0#0 */(decl_macro)]
35+
36+
#![no_std /* 0#0 */]
37+
#[prelude_import /* 0#1 */]
38+
use ::core /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*;
39+
#[macro_use /* 0#1 */]
40+
extern crate core /* 0#2 */;
41+
#[macro_use /* 0#1 */]
42+
extern crate compiler_builtins /* 0#2 */;
43+
// Don't load unnecessary hygiene information from std
44+
extern crate std /* 0#0 */;
45+
46+
#[macro_use /* 0#0 */]
47+
extern crate test_macros /* 0#0 */;
48+
49+
macro_rules! outer
50+
/*
51+
0#0
52+
*/ {
53+
($ item : item) =>
54+
{
55+
macro inner() { print_bang ! { $ item } } inner ! () ;
56+
57+
} ;
58+
}
59+
60+
struct S /* 0#0 */;
61+
macro inner /* 0#4 */ { () => { print_bang ! { struct S; } } }
62+
63+
struct S /* 0#5 */;
64+
// OK, not a duplicate definition of `S`
65+
66+
fn main /* 0#0 */() { }
67+
68+
/*
69+
Expansions:
70+
0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root
71+
1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports)
72+
2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "outer")
73+
3: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports)
74+
4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #4, kind: Macro(Bang, "inner")
75+
5: parent: ExpnId(4), call_site_ctxt: #6, def_site_ctxt: #0, kind: Macro(Bang, "print_bang")
76+
77+
SyntaxContexts:
78+
#0: parent: #0, outer_mark: (ExpnId(0), Opaque)
79+
#1: parent: #0, outer_mark: (ExpnId(1), Opaque)
80+
#2: parent: #0, outer_mark: (ExpnId(1), Transparent)
81+
#3: parent: #0, outer_mark: (ExpnId(3), Opaque)
82+
#4: parent: #0, outer_mark: (ExpnId(2), SemiTransparent)
83+
#5: parent: #0, outer_mark: (ExpnId(4), Opaque)
84+
#6: parent: #4, outer_mark: (ExpnId(4), Opaque)
85+
#7: parent: #0, outer_mark: (ExpnId(5), Opaque)
86+
#8: parent: #6, outer_mark: (ExpnId(5), Transparent)
87+
#9: parent: #5, outer_mark: (ExpnId(5), SemiTransparent)
88+
*/

0 commit comments

Comments
 (0)