Skip to content

Commit 5724462

Browse files
committed
Auto merge of #49326 - petrochenkov:nteq, r=eddyb
macros: Remove matching on "complex" nonterminals requiring AST comparisons So, you can actually use nonterminals from outer macros in left hand side of nested macros and invocations of nested macros will try to match passed arguments to them. ```rust macro outer($nt_item: item) { macro inner($nt_item) { struct S; } inner!($nt_item); // OK, `$nt_item` matches `$nt_item` } ``` Why this is bad: - We can't do this matching correctly. When two nonterminals are compared, the original tokens are lost and we have to compare AST fragments instead. Right now the comparison is done by `PartialEq` impls derived on AST structures. - On one hand, AST loses information compared to original tokens (e.g. trailing separators and other simplifications done during parsing to AST), so we can produce matches that are not actually correct. - On another hand derived `PartialEq` impls for AST structures don't make much sense in general and compare various auxiliary garbage like spans. For the argument nonterminal to match we should use literally the same token (possibly cloned) as was used in the macro LHS (as in the example above). So we can reject matches that are actually correct. - Support for nonterminal matching is the only thing that forces us to derive `PartialEq` for all (!) AST structures. As I mentioned these impls are also mostly nonsensical. This PR removes support for matching on all nonterminals except for "simple" ones like `ident`, `lifetime` and `tt` for which we have original tokens that can be compared. After this is done I'll submit another PR removing huge number of `PartialEq` impls from AST and HIR structures. This is an arcane feature and I don't personally know why would anyone use it, but the change should ideally go through crater. We'll be able to support this feature again in the future when all nonterminals have original token streams attached to them in addition to (or instead of) AST fragments.
2 parents e7252f6 + 7e1f73b commit 5724462

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

src/libsyntax/parse/token.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ impl Token {
565565
}
566566
}
567567

568-
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
568+
#[derive(Clone, RustcEncodable, RustcDecodable, Eq, Hash)]
569569
/// For interpolation during macro expansion.
570570
pub enum Nonterminal {
571571
NtItem(P<ast::Item>),
@@ -591,6 +591,22 @@ pub enum Nonterminal {
591591
NtArg(ast::Arg),
592592
}
593593

594+
impl PartialEq for Nonterminal {
595+
fn eq(&self, rhs: &Self) -> bool {
596+
match (self, rhs) {
597+
(NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) =>
598+
ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs,
599+
(NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs,
600+
(NtTT(tt_lhs), NtTT(tt_rhs)) => tt_lhs == tt_rhs,
601+
// FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them
602+
// correctly based on data from AST. This will prevent them from matching each other
603+
// in macros. The comparison will become possible only when each nonterminal has an
604+
// attached token stream from which it was parsed.
605+
_ => false,
606+
}
607+
}
608+
}
609+
594610
impl fmt::Debug for Nonterminal {
595611
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
596612
match *self {
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that we are refusing to match on complex nonterminals for which tokens are
12+
// unavailable and we'd have to go through AST comparisons.
13+
14+
#![feature(decl_macro, macro_lifetime_matcher)]
15+
16+
macro simple_nonterminal($nt_ident: ident, $nt_lifetime: lifetime, $nt_tt: tt) {
17+
macro n(a $nt_ident b $nt_lifetime c $nt_tt d) {
18+
struct S;
19+
}
20+
21+
n!(a $nt_ident b $nt_lifetime c $nt_tt d);
22+
}
23+
24+
macro complex_nonterminal($nt_item: item) {
25+
macro n(a $nt_item b) {
26+
struct S;
27+
}
28+
29+
n!(a $nt_item b); //~ ERROR no rules expected the token `enum E { }`
30+
}
31+
32+
simple_nonterminal!(a, 'a, (x, y, z)); // OK
33+
34+
complex_nonterminal!(enum E {});
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: no rules expected the token `enum E { }`
2+
--> $DIR/nonterminal-matching.rs:29:10
3+
|
4+
LL | n!(a $nt_item b); //~ ERROR no rules expected the token `enum E { }`
5+
| ^^^^^^^^
6+
...
7+
LL | complex_nonterminal!(enum E {});
8+
| -------------------------------- in this macro invocation
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)