Skip to content

Commit 3fb61ce

Browse files
authored
Rollup merge of #119204 - petrochenkov:dialoc2, r=WaffleLapkin
macro_rules: Less hacky heuristic for using `tt` metavariable spans See the big comment on `fn maybe_use_metavar_location` for a more detailed description.
2 parents 13840b3 + e1d12c8 commit 3fb61ce

11 files changed

+85
-92
lines changed

compiler/rustc_ast/src/tokenstream.rs

+1-22
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_span::{sym, Span, Symbol, DUMMY_SP};
2626
use smallvec::{smallvec, SmallVec};
2727

2828
use std::borrow::Cow;
29-
use std::{cmp, fmt, iter, mem};
29+
use std::{cmp, fmt, iter};
3030

3131
/// When the main Rust parser encounters a syntax-extension invocation, it
3232
/// parses the arguments to the invocation as a token tree. This is a very
@@ -81,14 +81,6 @@ impl TokenTree {
8181
}
8282
}
8383

84-
/// Modify the `TokenTree`'s span in-place.
85-
pub fn set_span(&mut self, span: Span) {
86-
match self {
87-
TokenTree::Token(token, _) => token.span = span,
88-
TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span),
89-
}
90-
}
91-
9284
/// Create a `TokenTree::Token` with alone spacing.
9385
pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree {
9486
TokenTree::Token(Token::new(kind, span), Spacing::Alone)
@@ -461,19 +453,6 @@ impl TokenStream {
461453
t1.next().is_none() && t2.next().is_none()
462454
}
463455

464-
/// Applies the supplied function to each `TokenTree` and its index in `self`, returning a new `TokenStream`
465-
///
466-
/// It is equivalent to `TokenStream::new(self.trees().cloned().enumerate().map(|(i, tt)| f(i, tt)).collect())`.
467-
pub fn map_enumerated_owned(
468-
mut self,
469-
mut f: impl FnMut(usize, TokenTree) -> TokenTree,
470-
) -> TokenStream {
471-
let owned = Lrc::make_mut(&mut self.0); // clone if necessary
472-
// rely on vec's in-place optimizations to avoid another allocation
473-
*owned = mem::take(owned).into_iter().enumerate().map(|(i, tree)| f(i, tree)).collect();
474-
self
475-
}
476-
477456
/// Create a token stream containing a single token with alone spacing. The
478457
/// spacing used for the final token in a constructed stream doesn't matter
479458
/// because it's never used. In practice we arbitrarily use

compiler/rustc_expand/src/mbe/macro_rules.rs

+2-33
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::mbe::transcribe::transcribe;
1010

1111
use rustc_ast as ast;
1212
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
13-
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
13+
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
1414
use rustc_ast::{NodeId, DUMMY_NODE_ID};
1515
use rustc_ast_pretty::pprust;
1616
use rustc_attr::{self as attr, TransparencyError};
@@ -213,45 +213,14 @@ fn expand_macro<'cx>(
213213
let arm_span = rhses[i].span();
214214

215215
// rhs has holes ( `$id` and `$(...)` that need filled)
216-
let mut tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
216+
let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
217217
Ok(tts) => tts,
218218
Err(mut err) => {
219219
err.emit();
220220
return DummyResult::any(arm_span);
221221
}
222222
};
223223

224-
// Replace all the tokens for the corresponding positions in the macro, to maintain
225-
// proper positions in error reporting, while maintaining the macro_backtrace.
226-
if tts.len() == rhs.tts.len() {
227-
tts = tts.map_enumerated_owned(|i, mut tt| {
228-
let rhs_tt = &rhs.tts[i];
229-
let ctxt = tt.span().ctxt();
230-
match (&mut tt, rhs_tt) {
231-
// preserve the delim spans if able
232-
(
233-
TokenTree::Delimited(target_sp, ..),
234-
mbe::TokenTree::Delimited(source_sp, ..),
235-
) => {
236-
target_sp.open = source_sp.open.with_ctxt(ctxt);
237-
target_sp.close = source_sp.close.with_ctxt(ctxt);
238-
}
239-
(
240-
TokenTree::Delimited(target_sp, ..),
241-
mbe::TokenTree::MetaVar(source_sp, ..),
242-
) => {
243-
target_sp.open = source_sp.with_ctxt(ctxt);
244-
target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi();
245-
}
246-
_ => {
247-
let sp = rhs_tt.span().with_ctxt(ctxt);
248-
tt.set_span(sp);
249-
}
250-
}
251-
tt
252-
});
253-
}
254-
255224
if cx.trace_macros() {
256225
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
257226
trace_macros_note(&mut cx.expansions, sp, msg);

compiler/rustc_expand/src/mbe/transcribe.rs

+61-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::errors::{
44
NoSyntaxVarsExprRepeat, VarStillRepeating,
55
};
66
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
7-
use crate::mbe::{self, MetaVarExpr};
7+
use crate::mbe::{self, KleeneOp, MetaVarExpr};
88
use rustc_ast::mut_visit::{self, MutVisitor};
99
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
1010
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
@@ -42,6 +42,7 @@ enum Frame<'a> {
4242
tts: &'a [mbe::TokenTree],
4343
idx: usize,
4444
sep: Option<Token>,
45+
kleene_op: KleeneOp,
4546
},
4647
}
4748

@@ -207,7 +208,7 @@ pub(super) fn transcribe<'a>(
207208

208209
// Is the repetition empty?
209210
if len == 0 {
210-
if seq.kleene.op == mbe::KleeneOp::OneOrMore {
211+
if seq.kleene.op == KleeneOp::OneOrMore {
211212
// FIXME: this really ought to be caught at macro definition
212213
// time... It happens when the Kleene operator in the matcher and
213214
// the body for the same meta-variable do not match.
@@ -227,6 +228,7 @@ pub(super) fn transcribe<'a>(
227228
idx: 0,
228229
sep: seq.separator.clone(),
229230
tts: &delimited.tts,
231+
kleene_op: seq.kleene.op,
230232
});
231233
}
232234
}
@@ -243,7 +245,7 @@ pub(super) fn transcribe<'a>(
243245
MatchedTokenTree(tt) => {
244246
// `tt`s are emitted into the output stream directly as "raw tokens",
245247
// without wrapping them into groups.
246-
result.push(tt.clone());
248+
result.push(maybe_use_metavar_location(cx, &stack, sp, tt));
247249
}
248250
MatchedNonterminal(nt) => {
249251
// Other variables are emitted into the output stream as groups with
@@ -308,6 +310,62 @@ pub(super) fn transcribe<'a>(
308310
}
309311
}
310312

313+
/// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
314+
/// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
315+
/// case however, and there's no place for keeping a second span. So we try to give the single
316+
/// produced span a location that would be most useful in practice (the hygiene part of the span
317+
/// must not be changed).
318+
///
319+
/// Different locations are useful for different purposes:
320+
/// - The original location is useful when we need to report a diagnostic for the original token in
321+
/// isolation, without combining it with any surrounding tokens. This case occurs, but it is not
322+
/// very common in practice.
323+
/// - The metavariable location is useful when we need to somehow combine the token span with spans
324+
/// of its surrounding tokens. This is the most common way to use token spans.
325+
///
326+
/// So this function replaces the original location with the metavariable location in all cases
327+
/// except these two:
328+
/// - The metavariable is an element of undelimited sequence `$($tt)*`.
329+
/// These are typically used for passing larger amounts of code, and tokens in that code usually
330+
/// combine with each other and not with tokens outside of the sequence.
331+
/// - The metavariable span comes from a different crate, then we prefer the more local span.
332+
///
333+
/// FIXME: Find a way to keep both original and metavariable spans for all tokens without
334+
/// regressing compilation time too much. Several experiments for adding such spans were made in
335+
/// the past (PR #95580, #118517, #118671) and all showed some regressions.
336+
fn maybe_use_metavar_location(
337+
cx: &ExtCtxt<'_>,
338+
stack: &[Frame<'_>],
339+
metavar_span: Span,
340+
orig_tt: &TokenTree,
341+
) -> TokenTree {
342+
let undelimited_seq = matches!(
343+
stack.last(),
344+
Some(Frame::Sequence {
345+
tts: [_],
346+
sep: None,
347+
kleene_op: KleeneOp::ZeroOrMore | KleeneOp::OneOrMore,
348+
..
349+
})
350+
);
351+
if undelimited_seq || cx.source_map().is_imported(metavar_span) {
352+
return orig_tt.clone();
353+
}
354+
355+
match orig_tt {
356+
TokenTree::Token(Token { kind, span }, spacing) => {
357+
let span = metavar_span.with_ctxt(span.ctxt());
358+
TokenTree::Token(Token { kind: kind.clone(), span }, *spacing)
359+
}
360+
TokenTree::Delimited(dspan, dspacing, delimiter, tts) => {
361+
let open = metavar_span.shrink_to_lo().with_ctxt(dspan.open.ctxt());
362+
let close = metavar_span.shrink_to_hi().with_ctxt(dspan.close.ctxt());
363+
let dspan = DelimSpan::from_pair(open, close);
364+
TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone())
365+
}
366+
}
367+
}
368+
311369
/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
312370
/// the set of matches `interpolations`.
313371
///

tests/coverage/macro_name_span.cov-map

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
Function name: macro_name_span::affected_function
2-
Raw bytes (9): 0x[01, 01, 00, 01, 01, 06, 1b, 00, 20]
2+
Raw bytes (9): 0x[01, 01, 00, 01, 01, 16, 1c, 02, 06]
33
Number of files: 1
44
- file 0 => global file 1
55
Number of expressions: 0
66
Number of file 0 mappings: 1
7-
- Code(Counter(0)) at (prev + 6, 27) to (start + 0, 32)
7+
- Code(Counter(0)) at (prev + 22, 28) to (start + 2, 6)
88

99
Function name: macro_name_span::main
10-
Raw bytes (9): 0x[01, 02, 00, 01, 01, 0b, 01, 02, 02]
10+
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0b, 01, 02, 02]
1111
Number of files: 1
12-
- file 0 => global file 2
12+
- file 0 => global file 1
1313
Number of expressions: 0
1414
Number of file 0 mappings: 1
1515
- Code(Counter(0)) at (prev + 11, 1) to (start + 2, 2)
+3-16
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
$DIR/auxiliary/macro_name_span_helper.rs:
2-
LL| |// edition: 2021
3-
LL| |
4-
LL| |#[macro_export]
5-
LL| |macro_rules! macro_that_defines_a_function {
6-
LL| | (fn $name:ident () $body:tt) => {
7-
LL| 1| fn $name () -> () $body
8-
LL| | }
9-
LL| |}
10-
LL| |
11-
LL| |// Non-executable comment.
12-
13-
$DIR/macro_name_span.rs:
141
LL| |// edition: 2021
152
LL| |
163
LL| |// Regression test for <https://github.com/rust-lang/rust/issues/117788>.
@@ -32,8 +19,8 @@ $DIR/macro_name_span.rs:
3219
LL| |}
3320
LL| |
3421
LL| |macro_name_span_helper::macro_that_defines_a_function! {
35-
LL| | fn affected_function() {
36-
LL| | macro_with_an_unreasonably_and_egregiously_long_name!();
37-
LL| | }
22+
LL| 1| fn affected_function() {
23+
LL| 1| macro_with_an_unreasonably_and_egregiously_long_name!();
24+
LL| 1| }
3825
LL| |}
3926

tests/ui/macros/issue-118786.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ LL | macro_rules! $macro_name {
66
|
77
help: change the delimiters to curly braces
88
|
9-
LL | macro_rules! {} {
10-
| ~ +
9+
LL | macro_rules! {$macro_name} {
10+
| + +
1111
help: add a semicolon
1212
|
1313
LL | macro_rules! $macro_name; {
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
error: missing condition for `if` expression
2-
--> $DIR/issue-68091-unicode-ident-after-if.rs:3:14
2+
--> $DIR/issue-68091-unicode-ident-after-if.rs:3:13
33
|
44
LL | $($c)ö* {}
5-
| ^ - if this block is the condition of the `if` expression, then it must be followed by another block
6-
| |
7-
| expected condition here
5+
| ^ - if this block is the condition of the `if` expression, then it must be followed by another block
6+
| |
7+
| expected condition here
88

99
error: aborting due to 1 previous error
1010

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: macro expansion ends with an incomplete expression: expected expression
2-
--> $DIR/issue-68092-unicode-ident-after-incomplete-expr.rs:3:14
2+
--> $DIR/issue-68092-unicode-ident-after-incomplete-expr.rs:3:13
33
|
44
LL | $($c)ö*
5-
| ^ expected expression
5+
| ^ expected expression
66

77
error: aborting due to 1 previous error
88

tests/ui/proc-macro/capture-macro-rules-invoke.stdout

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
271271
span: $DIR/capture-macro-rules-invoke.rs:47:19: 47:20 (#0),
272272
},
273273
],
274-
span: $DIR/capture-macro-rules-invoke.rs:47:13: 47:22 (#0),
274+
span: $DIR/capture-macro-rules-invoke.rs:15:60: 15:63 (#0),
275275
},
276276
Punct {
277277
ch: ',',

tests/ui/proc-macro/expand-expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ expand_expr_is!("hello", stringify!(hello));
3737
expand_expr_is!("10 + 20", stringify!(10 + 20));
3838

3939
macro_rules! echo_tts {
40-
($($t:tt)*) => { $($t)* }; //~ ERROR: expected expression, found `$`
40+
($($t:tt)*) => { $($t)* };
4141
}
4242

4343
macro_rules! echo_lit {
@@ -109,7 +109,7 @@ expand_expr_fail!("string"; hello); //~ ERROR: expected one of `.`, `?`, or an o
109109

110110
// Invalid expressions produce errors in addition to returning `Err(())`.
111111
expand_expr_fail!($); //~ ERROR: expected expression, found `$`
112-
expand_expr_fail!(echo_tts!($));
112+
expand_expr_fail!(echo_tts!($)); //~ ERROR: expected expression, found `$`
113113
expand_expr_fail!(echo_pm!($)); //~ ERROR: expected expression, found `$`
114114

115115
// We get errors reported and recover during macro expansion if the macro

tests/ui/proc-macro/expand-expr.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ LL | expand_expr_fail!($);
1111
| ^ expected expression
1212

1313
error: expected expression, found `$`
14-
--> $DIR/expand-expr.rs:40:23
14+
--> $DIR/expand-expr.rs:112:29
1515
|
16-
LL | ($($t:tt)*) => { $($t)* };
17-
| ^^^^ expected expression
16+
LL | expand_expr_fail!(echo_tts!($));
17+
| ^ expected expression
1818

1919
error: expected expression, found `$`
2020
--> $DIR/expand-expr.rs:113:28

0 commit comments

Comments
 (0)