Skip to content

Commit 4c0c5e0

Browse files
committed
Auto merge of #77271 - petrochenkov:notokenexp, r=Aaron1011
Expand `NtExpr` tokens only in key-value attributes Implement the experiment described in #55414 (comment) This PR also removes some customization points and token visiting functionality from AST visitors. Read-only visitor no longer visits tokens, mutable visitor visits tokens only when specifically enabled, mutable token visiting is restricted to its single intended use case. I haven't changed the representation of `MacArgs::Eq` yet, but it potentially can use a `TokenTree` or a `Token` instead of `TokenStream`. It's hard to get rid of `Nonterminal::NtExpr` there (and e.g. replace it with `ast::Expr`) due to the dual nature of key-value attributes (the value is both an expression and a token stream, depending on context), and `Nonterminal` has all the machinery for maintaining both representations in sync. Fixes #55414 Fixes #43860
2 parents 338f939 + 19dbb02 commit 4c0c5e0

18 files changed

+402
-134
lines changed

compiler/rustc_ast/src/mut_visit.rs

+44-28
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ impl<A: Array> ExpectOne<A> for SmallVec<A> {
3434
}
3535

3636
pub trait MutVisitor: Sized {
37+
/// Mutable token visiting only exists for the `macro_rules` token marker and should not be
38+
/// used otherwise. Token visitor would be entirely separate from the regular visitor if
39+
/// the marker didn't have to visit AST fragments in nonterminal tokens.
40+
fn token_visiting_enabled(&self) -> bool {
41+
false
42+
}
43+
3744
// Methods in this trait have one of three forms:
3845
//
3946
// fn visit_t(&mut self, t: &mut T); // common
@@ -246,22 +253,6 @@ pub trait MutVisitor: Sized {
246253
noop_flat_map_generic_param(param, self)
247254
}
248255

249-
fn visit_tt(&mut self, tt: &mut TokenTree) {
250-
noop_visit_tt(tt, self);
251-
}
252-
253-
fn visit_tts(&mut self, tts: &mut TokenStream) {
254-
noop_visit_tts(tts, self);
255-
}
256-
257-
fn visit_token(&mut self, t: &mut Token) {
258-
noop_visit_token(t, self);
259-
}
260-
261-
fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) {
262-
noop_visit_interpolated(nt, self);
263-
}
264-
265256
fn visit_param_bound(&mut self, tpb: &mut GenericBound) {
266257
noop_visit_param_bound(tpb, self);
267258
}
@@ -375,11 +366,30 @@ pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
375366
MacArgs::Empty => {}
376367
MacArgs::Delimited(dspan, _delim, tokens) => {
377368
visit_delim_span(dspan, vis);
378-
vis.visit_tts(tokens);
369+
visit_tts(tokens, vis);
379370
}
380371
MacArgs::Eq(eq_span, tokens) => {
381372
vis.visit_span(eq_span);
382-
vis.visit_tts(tokens);
373+
visit_tts(tokens, vis);
374+
// The value in `#[key = VALUE]` must be visited as an expression for backward
375+
// compatibility, so that macros can be expanded in that position.
376+
if !vis.token_visiting_enabled() {
377+
if let Some(TokenTree::Token(token)) = tokens.trees_ref().next() {
378+
if let token::Interpolated(..) = token.kind {
379+
// ^^ Do not `make_mut` unless we have to.
380+
match Lrc::make_mut(&mut tokens.0).get_mut(0) {
381+
Some((TokenTree::Token(token), _spacing)) => match &mut token.kind {
382+
token::Interpolated(nt) => match Lrc::make_mut(nt) {
383+
token::NtExpr(expr) => vis.visit_expr(expr),
384+
t => panic!("unexpected token in key-value attribute: {:?}", t),
385+
},
386+
t => panic!("unexpected token in key-value attribute: {:?}", t),
387+
},
388+
t => panic!("unexpected token in key-value attribute: {:?}", t),
389+
}
390+
}
391+
}
392+
}
383393
}
384394
}
385395
}
@@ -626,28 +636,33 @@ pub fn noop_flat_map_param<T: MutVisitor>(mut param: Param, vis: &mut T) -> Smal
626636
smallvec![param]
627637
}
628638

629-
pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
639+
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
640+
pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
630641
match tt {
631642
TokenTree::Token(token) => {
632-
vis.visit_token(token);
643+
visit_token(token, vis);
633644
}
634645
TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
635646
vis.visit_span(open);
636647
vis.visit_span(close);
637-
vis.visit_tts(tts);
648+
visit_tts(tts, vis);
638649
}
639650
}
640651
}
641652

642-
pub fn noop_visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
643-
let tts = Lrc::make_mut(tts);
644-
visit_vec(tts, |(tree, _is_joint)| vis.visit_tt(tree));
653+
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
654+
pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
655+
if vis.token_visiting_enabled() {
656+
let tts = Lrc::make_mut(tts);
657+
visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis));
658+
}
645659
}
646660

661+
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
647662
// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
648663
// In practice the ident part is not actually used by specific visitors right now,
649664
// but there's a test below checking that it works.
650-
pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
665+
pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
651666
let Token { kind, span } = t;
652667
match kind {
653668
token::Ident(name, _) | token::Lifetime(name) => {
@@ -659,13 +674,14 @@ pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
659674
}
660675
token::Interpolated(nt) => {
661676
let mut nt = Lrc::make_mut(nt);
662-
vis.visit_interpolated(&mut nt);
677+
visit_interpolated(&mut nt, vis);
663678
}
664679
_ => {}
665680
}
666681
vis.visit_span(span);
667682
}
668683

684+
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
669685
/// Applies the visitor to elements of interpolated nodes.
670686
//
671687
// N.B., this can occur only when applying a visitor to partially expanded
@@ -689,7 +705,7 @@ pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
689705
// contain multiple items, but decided against it when I looked at
690706
// `parse_item_or_view_item` and tried to figure out what I would do with
691707
// multiple items there....
692-
pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
708+
pub fn visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
693709
match nt {
694710
token::NtItem(item) => visit_clobber(item, |item| {
695711
// This is probably okay, because the only visitors likely to
@@ -714,7 +730,7 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
714730
visit_mac_args(args, vis);
715731
}
716732
token::NtPath(path) => vis.visit_path(path),
717-
token::NtTT(tt) => vis.visit_tt(tt),
733+
token::NtTT(tt) => visit_tt(tt, vis),
718734
token::NtVis(visib) => vis.visit_vis(visib),
719735
}
720736
}

compiler/rustc_ast/src/tokenstream.rs

+34
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ impl TokenStream {
318318
}
319319
}
320320

321+
pub fn trees_ref(&self) -> CursorRef<'_> {
322+
CursorRef::new(self)
323+
}
324+
321325
pub fn trees(&self) -> Cursor {
322326
self.clone().into_trees()
323327
}
@@ -408,6 +412,36 @@ impl TokenStreamBuilder {
408412
}
409413
}
410414

415+
/// By-reference iterator over a `TokenStream`.
416+
#[derive(Clone)]
417+
pub struct CursorRef<'t> {
418+
stream: &'t TokenStream,
419+
index: usize,
420+
}
421+
422+
impl<'t> CursorRef<'t> {
423+
fn new(stream: &TokenStream) -> CursorRef<'_> {
424+
CursorRef { stream, index: 0 }
425+
}
426+
427+
fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> {
428+
self.stream.0.get(self.index).map(|tree| {
429+
self.index += 1;
430+
tree
431+
})
432+
}
433+
}
434+
435+
impl<'t> Iterator for CursorRef<'t> {
436+
type Item = &'t TokenTree;
437+
438+
fn next(&mut self) -> Option<&'t TokenTree> {
439+
self.next_with_spacing().map(|(tree, _)| tree)
440+
}
441+
}
442+
443+
/// Owning by-value iterator over a `TokenStream`.
444+
/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
411445
#[derive(Clone)]
412446
pub struct Cursor {
413447
pub stream: TokenStream,

compiler/rustc_ast/src/visit.rs

+16-25
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
//! those that are created by the expansion of a macro.
1515
1616
use crate::ast::*;
17-
use crate::token::Token;
18-
use crate::tokenstream::{TokenStream, TokenTree};
17+
use crate::token;
18+
use crate::tokenstream::TokenTree;
1919

2020
use rustc_span::symbol::{Ident, Symbol};
2121
use rustc_span::Span;
@@ -208,14 +208,6 @@ pub trait Visitor<'ast>: Sized {
208208
fn visit_attribute(&mut self, attr: &'ast Attribute) {
209209
walk_attribute(self, attr)
210210
}
211-
fn visit_tt(&mut self, tt: TokenTree) {
212-
walk_tt(self, tt)
213-
}
214-
fn visit_tts(&mut self, tts: TokenStream) {
215-
walk_tts(self, tts)
216-
}
217-
fn visit_token(&mut self, _t: Token) {}
218-
// FIXME: add `visit_interpolated` and `walk_interpolated`
219211
fn visit_vis(&mut self, vis: &'ast Visibility) {
220212
walk_vis(self, vis)
221213
}
@@ -902,20 +894,19 @@ pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute)
902894
pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
903895
match args {
904896
MacArgs::Empty => {}
905-
MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()),
906-
MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()),
907-
}
908-
}
909-
910-
pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {
911-
match tt {
912-
TokenTree::Token(token) => visitor.visit_token(token),
913-
TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts),
914-
}
915-
}
916-
917-
pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) {
918-
for tt in tts.trees() {
919-
visitor.visit_tt(tt);
897+
MacArgs::Delimited(_dspan, _delim, _tokens) => {}
898+
// The value in `#[key = VALUE]` must be visited as an expression for backward
899+
// compatibility, so that macros can be expanded in that position.
900+
MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() {
901+
Some(TokenTree::Token(token)) => match &token.kind {
902+
token::Interpolated(nt) => match &**nt {
903+
token::NtExpr(expr) => visitor.visit_expr(expr),
904+
t => panic!("unexpected token in key-value attribute: {:?}", t),
905+
},
906+
token::Literal(..) | token::Ident(..) => {}
907+
t => panic!("unexpected token in key-value attribute: {:?}", t),
908+
},
909+
t => panic!("unexpected token in key-value attribute: {:?}", t),
910+
},
920911
}
921912
}

compiler/rustc_expand/src/mbe/transcribe.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ use std::mem;
2020
struct Marker(ExpnId, Transparency);
2121

2222
impl MutVisitor for Marker {
23+
fn token_visiting_enabled(&self) -> bool {
24+
true
25+
}
26+
2327
fn visit_span(&mut self, span: &mut Span) {
2428
*span = span.apply_mark(self.0, self.1)
2529
}
@@ -277,7 +281,7 @@ pub(super) fn transcribe<'a>(
277281
// preserve syntax context.
278282
mbe::TokenTree::Token(token) => {
279283
let mut tt = TokenTree::Token(token);
280-
marker.visit_tt(&mut tt);
284+
mut_visit::visit_tt(&mut tt, &mut marker);
281285
result.push(tt.into());
282286
}
283287

compiler/rustc_expand/src/mut_visit/tests.rs

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) {
1515
struct ToZzIdentMutVisitor;
1616

1717
impl MutVisitor for ToZzIdentMutVisitor {
18+
fn token_visiting_enabled(&self) -> bool {
19+
true
20+
}
1821
fn visit_ident(&mut self, ident: &mut Ident) {
1922
*ident = Ident::from_str("zz");
2023
}

compiler/rustc_resolve/src/build_reduced_graph.rs

-11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use crate::{
1515
};
1616
use crate::{Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, Segment, ToNameBinding};
1717

18-
use rustc_ast::token::{self, Token};
1918
use rustc_ast::visit::{self, AssocCtxt, Visitor};
2019
use rustc_ast::{self as ast, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId};
2120
use rustc_ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind};
@@ -1395,16 +1394,6 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> {
13951394
visit::walk_assoc_item(self, item, ctxt);
13961395
}
13971396

1398-
fn visit_token(&mut self, t: Token) {
1399-
if let token::Interpolated(nt) = t.kind {
1400-
if let token::NtExpr(ref expr) = *nt {
1401-
if let ast::ExprKind::MacCall(..) = expr.kind {
1402-
self.visit_invoc(expr.id);
1403-
}
1404-
}
1405-
}
1406-
}
1407-
14081397
fn visit_attribute(&mut self, attr: &'b ast::Attribute) {
14091398
if !attr.is_doc_comment() && attr::is_builtin_attr(attr) {
14101399
self.r

compiler/rustc_resolve/src/def_collector.rs

-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::Resolver;
2-
use rustc_ast::token::{self, Token};
32
use rustc_ast::visit::{self, FnKind};
43
use rustc_ast::walk_list;
54
use rustc_ast::*;
@@ -256,16 +255,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> {
256255
}
257256
}
258257

259-
fn visit_token(&mut self, t: Token) {
260-
if let token::Interpolated(nt) = t.kind {
261-
if let token::NtExpr(ref expr) = *nt {
262-
if let ExprKind::MacCall(..) = expr.kind {
263-
self.visit_macro_invoc(expr.id);
264-
}
265-
}
266-
}
267-
}
268-
269258
fn visit_arm(&mut self, arm: &'a Arm) {
270259
if arm.is_placeholder { self.visit_macro_invoc(arm.id) } else { visit::walk_arm(self, arm) }
271260
}

0 commit comments

Comments
 (0)