Skip to content

Commit d874ecc

Browse files
committed
Use correct edition when parsing :pat matchers
As described in issue #85708, we currently do not properly decode `SyntaxContext::root()` and `ExpnId::root()` from foreign crates. As a result, when we decode a span from a foreign crate with `SyntaxContext::root()`, we end up up considering it to have the edition of the *current* crate, instead of the foreign crate where it was originally created. A full fix for this issue will be a fairly significant undertaking. Fortunately, it's possible to implement a partial fix, which gives us the correct edition-dependent behavior for `:pat` matchers when the macro is loaded from another crate. Since we have the edition of the macro's defining crate available, we can 'recover' from seeing a `SyntaxContext::root()` and use the edition of the macro's defining crate. Any solution to issue #85708 must reproduce the behavior of this targeted fix - properly preserving a foreign `SyntaxContext::root()` means (among other things) preserving its edition, which by definition is the edition of the foreign crate itself. Therefore, this fix moves us closer to the correct overall solution, and does not expose any new incorrect behavior to macros.
1 parent ff2c947 commit d874ecc

File tree

5 files changed

+56
-5
lines changed

5 files changed

+56
-5
lines changed

compiler/rustc_expand/src/mbe/macro_rules.rs

+2
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ pub fn compile_declarative_macro(
467467
&sess.parse_sess,
468468
def.id,
469469
features,
470+
edition,
470471
)
471472
.pop()
472473
.unwrap();
@@ -492,6 +493,7 @@ pub fn compile_declarative_macro(
492493
&sess.parse_sess,
493494
def.id,
494495
features,
496+
edition,
495497
)
496498
.pop()
497499
.unwrap();

compiler/rustc_expand/src/mbe/quoted.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use rustc_feature::Features;
99
use rustc_session::parse::ParseSess;
1010
use rustc_span::symbol::{kw, Ident};
1111

12-
use rustc_span::Span;
12+
use rustc_span::edition::Edition;
13+
use rustc_span::{Span, SyntaxContext};
1314

1415
use rustc_data_structures::sync::Lrc;
1516

@@ -32,6 +33,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
3233
/// - `sess`: the parsing session. Any errors will be emitted to this session.
3334
/// - `node_id`: the NodeId of the macro we are parsing.
3435
/// - `features`: language features so we can do feature gating.
36+
/// - `edition`: the edition of the crate defining the macro
3537
///
3638
/// # Returns
3739
///
@@ -42,6 +44,7 @@ pub(super) fn parse(
4244
sess: &ParseSess,
4345
node_id: NodeId,
4446
features: &Features,
47+
edition: Edition,
4548
) -> Vec<TokenTree> {
4649
// Will contain the final collection of `self::TokenTree`
4750
let mut result = Vec::new();
@@ -52,7 +55,7 @@ pub(super) fn parse(
5255
while let Some(tree) = trees.next() {
5356
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually
5457
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
55-
let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features);
58+
let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features, edition);
5659
match tree {
5760
TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
5861
let span = match trees.next() {
@@ -64,7 +67,19 @@ pub(super) fn parse(
6467

6568
let kind =
6669
token::NonterminalKind::from_symbol(frag.name, || {
67-
span.edition()
70+
// FIXME(#85708) - once we properly decode a foreign
71+
// crate's `SyntaxContext::root`, then we can replace
72+
// this with just `span.edition()`. A
73+
// `SyntaxContext::root()` from the current crate will
74+
// have the edition of the current crate, and a
75+
// `SyntaxxContext::root()` from a foreign crate will
76+
// have the edition of that crate (which we manually
77+
// retrieve via the `edition` parameter).
78+
if span.ctxt() == SyntaxContext::root() {
79+
edition
80+
} else {
81+
span.edition()
82+
}
6883
})
6984
.unwrap_or_else(
7085
|| {
@@ -117,13 +132,15 @@ pub(super) fn parse(
117132
/// - `expect_matchers`: same as for `parse` (see above).
118133
/// - `sess`: the parsing session. Any errors will be emitted to this session.
119134
/// - `features`: language features so we can do feature gating.
135+
/// - `edition` - the edition of the crate defining the macro
120136
fn parse_tree(
121137
tree: tokenstream::TokenTree,
122138
outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
123139
expect_matchers: bool,
124140
sess: &ParseSess,
125141
node_id: NodeId,
126142
features: &Features,
143+
edition: Edition,
127144
) -> TokenTree {
128145
// Depending on what `tree` is, we could be parsing different parts of a macro
129146
match tree {
@@ -151,7 +168,7 @@ fn parse_tree(
151168
sess.span_diagnostic.span_err(span.entire(), &msg);
152169
}
153170
// Parse the contents of the sequence itself
154-
let sequence = parse(tts, expect_matchers, sess, node_id, features);
171+
let sequence = parse(tts, expect_matchers, sess, node_id, features, edition);
155172
// Get the Kleene operator and optional separator
156173
let (separator, kleene) =
157174
parse_sep_and_kleene_op(&mut trees, span.entire(), sess);
@@ -204,7 +221,7 @@ fn parse_tree(
204221
span,
205222
Lrc::new(Delimited {
206223
delim,
207-
tts: parse(tts, expect_matchers, sess, node_id, features),
224+
tts: parse(tts, expect_matchers, sess, node_id, features, edition),
208225
}),
209226
),
210227
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// edition:2018
2+
3+
#[macro_export]
4+
macro_rules! custom_matches {
5+
($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => {
6+
match $expression {
7+
$( $pattern )|+ $( if $guard )? => true,
8+
_ => false
9+
}
10+
}
11+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// edition:2021
2+
// check-pass
3+
// aux-build: foreign-crate-macro-pat.rs
4+
//
5+
// Tests that the edition of the foreign crate is used
6+
// when determining the behavior of the `:pat` matcher.
7+
8+
extern crate foreign_crate_macro_pat;
9+
10+
fn main() {
11+
let _b = foreign_crate_macro_pat::custom_matches!(b'3', b'0' ..= b'9');
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// edition:2021
2+
// check-pass
3+
//
4+
// Regression test for issue #84429
5+
// Tests that we can properly invoke `matches!` from a 2021-edition crate.
6+
7+
fn main() {
8+
let _b = matches!(b'3', b'0' ..= b'9');
9+
}

0 commit comments

Comments
 (0)