Skip to content

Commit 6008004

Browse files
committed
Only match a fragment specifier the if it starts with certain tokens.
Fixes #24189. Fixes #26444. Fixes #27832. Fixes #34030. Fixes #35650. Fixes #39964. Fixes the 4th comment in #40569. Fixes the issue blocking #40984.
1 parent b80b659 commit 6008004

File tree

4 files changed

+317
-6
lines changed

4 files changed

+317
-6
lines changed

src/libsyntax/ext/tt/macro_parser.rs

+70-4
Original file line numberDiff line numberDiff line change
@@ -386,12 +386,11 @@ fn inner_parse_loop(sess: &ParseSess,
386386
return Error(span, "missing fragment specifier".to_string());
387387
}
388388
}
389-
TokenTree::MetaVarDecl(..) => {
389+
TokenTree::MetaVarDecl(_, _, id) => {
390390
// Built-in nonterminals never start with these tokens,
391391
// so we can eliminate them from consideration.
392-
match *token {
393-
token::CloseDelim(_) => {},
394-
_ => bb_eis.push(ei),
392+
if may_begin_with(&*id.name.as_str(), token) {
393+
bb_eis.push(ei);
395394
}
396395
}
397396
seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..)) => {
@@ -493,6 +492,73 @@ pub fn parse(sess: &ParseSess,
493492
}
494493
}
495494

495+
/// Checks whether a non-terminal may begin with a particular token.
496+
///
497+
/// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
498+
/// token. Be conservative (return true) if not sure.
499+
fn may_begin_with(name: &str, token: &Token) -> bool {
500+
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
501+
fn may_be_ident(nt: &token::Nonterminal) -> bool {
502+
match *nt {
503+
token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) => false,
504+
_ => true,
505+
}
506+
}
507+
508+
match name {
509+
"expr" => token.can_begin_expr(),
510+
"ty" => token.can_begin_type(),
511+
"ident" => token.is_ident(),
512+
"vis" => match *token { // The follow-set of :vis + "priv" keyword + interpolated
513+
Token::Comma | Token::Ident(_) | Token::Interpolated(_) => true,
514+
_ => token.can_begin_type(),
515+
},
516+
"block" => match *token {
517+
Token::OpenDelim(token::Brace) => true,
518+
Token::Interpolated(ref nt) => match nt.0 {
519+
token::NtItem(_) |
520+
token::NtPat(_) |
521+
token::NtTy(_) |
522+
token::NtIdent(_) |
523+
token::NtMeta(_) |
524+
token::NtPath(_) |
525+
token::NtVis(_) => false, // none of these may start with '{'.
526+
_ => true,
527+
},
528+
_ => false,
529+
},
530+
"path" | "meta" => match *token {
531+
Token::ModSep | Token::Ident(_) => true,
532+
Token::Interpolated(ref nt) => match nt.0 {
533+
token::NtPath(_) | token::NtMeta(_) => true,
534+
_ => may_be_ident(&nt.0),
535+
},
536+
_ => false,
537+
},
538+
"pat" => match *token {
539+
Token::Ident(_) | // box, ref, mut, and other identifiers (can stricten)
540+
Token::OpenDelim(token::Paren) | // tuple pattern
541+
Token::OpenDelim(token::Bracket) | // slice pattern
542+
Token::BinOp(token::And) | // reference
543+
Token::BinOp(token::Minus) | // negative literal
544+
Token::AndAnd | // double reference
545+
Token::Literal(..) | // literal
546+
Token::DotDot | // range pattern (future compat)
547+
Token::DotDotDot | // range pattern (future compat)
548+
Token::ModSep | // path
549+
Token::Lt | // path (UFCS constant)
550+
Token::BinOp(token::Shl) | // path (double UFCS)
551+
Token::Underscore => true, // placeholder
552+
Token::Interpolated(ref nt) => may_be_ident(&nt.0),
553+
_ => false,
554+
},
555+
_ => match *token {
556+
token::CloseDelim(_) => false,
557+
_ => true,
558+
},
559+
}
560+
}
561+
496562
fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
497563
if name == "tt" {
498564
return token::NtTT(p.parse_token_tree());

src/test/compile-fail/fail-simple.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
panic!(@); //~ ERROR expected expression, found `@`
12+
panic!(@); //~ ERROR no rules expected the token `@`
1313
}

src/test/compile-fail/vec-macro-with-comma-only.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
// except according to those terms.
1010

1111
pub fn main() {
12-
vec![,]; //~ ERROR expected expression, found `,`
12+
vec![,]; //~ ERROR no rules expected the token `,`
1313
}

src/test/run-pass/macro-first-set.rs

+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// Copyright 2017 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+
#![feature(macro_vis_matcher)]
12+
13+
//{{{ issue 40569 ==============================================================
14+
15+
macro_rules! my_struct {
16+
($(#[$meta:meta])* $ident:ident) => {
17+
$(#[$meta])* struct $ident;
18+
}
19+
}
20+
21+
my_struct!(#[derive(Debug, PartialEq)] Foo40569);
22+
23+
fn test_40569() {
24+
assert_eq!(Foo40569, Foo40569);
25+
}
26+
27+
//}}}
28+
29+
//{{{ issue 26444 ==============================================================
30+
31+
macro_rules! foo_26444 {
32+
($($beginning:ident),*; $middle:ident; $($end:ident),*) => {
33+
stringify!($($beginning,)* $middle $(,$end)*)
34+
}
35+
}
36+
37+
fn test_26444() {
38+
assert_eq!("a , b , c , d , e", foo_26444!(a, b; c; d, e));
39+
assert_eq!("f", foo_26444!(; f ;));
40+
}
41+
42+
macro_rules! pat_26444 {
43+
($fname:ident $($arg:pat)* =) => {}
44+
}
45+
46+
pat_26444!(foo 1 2 5...7 =);
47+
pat_26444!(bar Some(ref x) Ok(ref mut y) &(w, z) =);
48+
49+
//}}}
50+
51+
//{{{ issue 40984 ==============================================================
52+
53+
macro_rules! thread_local_40984 {
54+
() => {};
55+
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => {
56+
thread_local_40984!($($rest)*);
57+
};
58+
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => {};
59+
}
60+
61+
thread_local_40984! {
62+
// no docs
63+
#[allow(unused)]
64+
static FOO: i32 = 42;
65+
/// docs
66+
pub static BAR: String = String::from("bar");
67+
68+
// look at these restrictions!!
69+
pub(crate) static BAZ: usize = 0;
70+
pub(in foo) static QUUX: usize = 0;
71+
}
72+
73+
//}}}
74+
75+
//{{{ issue 35650 ==============================================================
76+
77+
macro_rules! size {
78+
($ty:ty) => {
79+
std::mem::size_of::<$ty>()
80+
};
81+
($size:tt) => {
82+
$size
83+
};
84+
}
85+
86+
fn test_35650() {
87+
assert_eq!(size!(u64), 8);
88+
assert_eq!(size!(5), 5);
89+
}
90+
91+
//}}}
92+
93+
//{{{ issue 27832 ==============================================================
94+
95+
macro_rules! m {
96+
( $i:ident ) => ();
97+
( $t:tt $j:tt ) => ();
98+
}
99+
100+
m!(c);
101+
m!(t 9);
102+
m!(0 9);
103+
m!(struct);
104+
m!(struct Foo);
105+
106+
macro_rules! m2 {
107+
( $b:expr ) => ();
108+
( $t:tt $u:tt ) => ();
109+
}
110+
111+
m2!(3);
112+
m2!(1 2);
113+
m2!(_ 1);
114+
m2!(enum Foo);
115+
116+
//}}}
117+
118+
//{{{ issue 39964 ==============================================================
119+
120+
macro_rules! foo_39964 {
121+
($a:ident) => {};
122+
(_) => {};
123+
}
124+
125+
foo_39964!(_);
126+
127+
//}}}
128+
129+
//{{{ issue 34030 ==============================================================
130+
131+
macro_rules! foo_34030 {
132+
($($t:ident),* /) => {};
133+
}
134+
135+
foo_34030!(a, b/);
136+
foo_34030!(a/);
137+
foo_34030!(/);
138+
139+
//}}}
140+
141+
//{{{ issue 24189 ==============================================================
142+
143+
macro_rules! foo_24189 {
144+
(
145+
pub enum $name:ident {
146+
$( #[$attr:meta] )* $var:ident
147+
}
148+
) => {
149+
pub enum $name {
150+
$( #[$attr] )* $var
151+
}
152+
};
153+
}
154+
155+
foo_24189! {
156+
pub enum Foo24189 {
157+
#[doc = "Bar"] Baz
158+
}
159+
}
160+
161+
macro_rules! serializable {
162+
(
163+
$(#[$struct_meta:meta])*
164+
pub struct $name:ident {
165+
$(
166+
$(#[$field_meta:meta])*
167+
$field:ident: $type_:ty
168+
),* ,
169+
}
170+
) => {
171+
$(#[$struct_meta])*
172+
pub struct $name {
173+
$(
174+
$(#[$field_meta])*
175+
$field: $type_
176+
),* ,
177+
}
178+
}
179+
}
180+
181+
serializable! {
182+
#[allow(dead_code)]
183+
/// This is a test
184+
pub struct Tester {
185+
#[allow(dead_code)]
186+
name: String,
187+
}
188+
}
189+
190+
macro_rules! foo_24189_c {
191+
( $( > )* $x:ident ) => { };
192+
}
193+
foo_24189_c!( > a );
194+
195+
fn test_24189() {
196+
let _ = Foo24189::Baz;
197+
let _ = Tester { name: "".to_owned() };
198+
}
199+
200+
//}}}
201+
202+
//{{{ some more tests ==========================================================
203+
204+
macro_rules! test_block {
205+
(< $($b:block)* >) => {}
206+
}
207+
208+
test_block!(<>);
209+
test_block!(<{}>);
210+
test_block!(<{1}{2}>);
211+
212+
macro_rules! test_ty {
213+
($($t:ty),* $(,)*) => {}
214+
}
215+
216+
test_ty!();
217+
test_ty!(,);
218+
test_ty!(u8);
219+
test_ty!(u8,);
220+
221+
macro_rules! test_path {
222+
($($t:path),* $(,)*) => {}
223+
}
224+
225+
test_path!();
226+
test_path!(,);
227+
test_path!(::std);
228+
test_path!(std::u8,);
229+
test_path!(any, super, super::super::self::path, X<Y>::Z<'a, T=U>);
230+
231+
macro_rules! test_meta_block {
232+
($($m:meta)* $b:block) => {};
233+
}
234+
235+
test_meta_block!(windows {});
236+
237+
//}}}
238+
239+
fn main() {
240+
test_26444();
241+
test_40569();
242+
test_35650();
243+
test_24189();
244+
}
245+

0 commit comments

Comments
 (0)