Skip to content

Commit d85c017

Browse files
committed
Cleanup and followup to PR #17830: parsing changes
Prevents breaking down `$name` tokens into separate `$` and `name`. Reports unknown macro variables. Fixes #18775 Fixes #18839 Fixes #15640
1 parent 8efd990 commit d85c017

File tree

12 files changed

+170
-103
lines changed

12 files changed

+170
-103
lines changed

src/doc/reference.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -690,10 +690,9 @@ balanced, but they are otherwise not special.
690690

691691
In the matcher, `$` _name_ `:` _designator_ matches the nonterminal in the Rust
692692
syntax named by _designator_. Valid designators are `item`, `block`, `stmt`,
693-
`pat`, `expr`, `ty` (type), `ident`, `path`, `matchers` (lhs of the `=>` in
694-
macro rules), `tt` (rhs of the `=>` in macro rules). In the transcriber, the
695-
designator is already known, and so only the name of a matched nonterminal
696-
comes after the dollar sign.
693+
`pat`, `expr`, `ty` (type), `ident`, `path`, `tt` (either side of the `=>`
694+
in macro rules). In the transcriber, the designator is already known, and so
695+
only the name of a matched nonterminal comes after the dollar sign.
697696

698697
In both the matcher and transcriber, the Kleene star-like operator indicates
699698
repetition. The Kleene star operator consists of `$` and parens, optionally

src/libstd/io/mod.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -1780,9 +1780,11 @@ pub struct UnstableFileStat {
17801780
pub gen: u64,
17811781
}
17821782

1783+
1784+
// NOTE(stage0): change this one last #[doc=..] to /// after the next snapshot
17831785
bitflags! {
1784-
#[doc = "A set of permissions for a file or directory is represented"]
1785-
#[doc = "by a set of flags which are or'd together."]
1786+
#[doc = "A set of permissions for a file or directory is represented by a set of"]
1787+
/// flags which are or'd together.
17861788
flags FilePermission: u32 {
17871789
const USER_READ = 0o400,
17881790
const USER_WRITE = 0o200,
@@ -1798,20 +1800,20 @@ bitflags! {
17981800
const GROUP_RWX = GROUP_READ.bits | GROUP_WRITE.bits | GROUP_EXECUTE.bits,
17991801
const OTHER_RWX = OTHER_READ.bits | OTHER_WRITE.bits | OTHER_EXECUTE.bits,
18001802

1801-
#[doc = "Permissions for user owned files, equivalent to 0644 on"]
1802-
#[doc = "unix-like systems."]
1803+
/// Permissions for user owned files, equivalent to 0644 on unix-like
1804+
/// systems.
18031805
const USER_FILE = USER_READ.bits | USER_WRITE.bits | GROUP_READ.bits | OTHER_READ.bits,
18041806

1805-
#[doc = "Permissions for user owned directories, equivalent to 0755 on"]
1806-
#[doc = "unix-like systems."]
1807+
/// Permissions for user owned directories, equivalent to 0755 on
1808+
/// unix-like systems.
18071809
const USER_DIR = USER_RWX.bits | GROUP_READ.bits | GROUP_EXECUTE.bits |
18081810
OTHER_READ.bits | OTHER_EXECUTE.bits,
18091811

1810-
#[doc = "Permissions for user owned executables, equivalent to 0755"]
1811-
#[doc = "on unix-like systems."]
1812+
/// Permissions for user owned executables, equivalent to 0755
1813+
/// on unix-like systems.
18121814
const USER_EXEC = USER_DIR.bits,
18131815

1814-
#[doc = "All possible permissions enabled."]
1816+
/// All possible permissions enabled.
18151817
const ALL_PERMISSIONS = USER_RWX.bits | GROUP_RWX.bits | OTHER_RWX.bits,
18161818
}
18171819
}

src/libsyntax/ast.rs

-6
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,6 @@ impl TokenTree {
883883
pub fn len(&self) -> uint {
884884
match *self {
885885
TtToken(_, token::DocComment(_)) => 2,
886-
TtToken(_, token::SubstNt(..)) => 2,
887886
TtToken(_, token::SpecialVarNt(..)) => 2,
888887
TtToken(_, token::MatchNt(..)) => 3,
889888
TtDelimited(_, ref delimed) => {
@@ -921,11 +920,6 @@ impl TokenTree {
921920
}
922921
delimed.tts[index - 1].clone()
923922
}
924-
(&TtToken(sp, token::SubstNt(name, name_st)), _) => {
925-
let v = [TtToken(sp, token::Dollar),
926-
TtToken(sp, token::Ident(name, name_st))];
927-
v[index]
928-
}
929923
(&TtToken(sp, token::SpecialVarNt(var)), _) => {
930924
let v = [TtToken(sp, token::Dollar),
931925
TtToken(sp, token::Ident(token::str_to_ident(var.as_str()),

src/libsyntax/ext/tt/macro_parser.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,17 @@ pub fn parse(sess: &ParseSess,
506506
}
507507

508508
pub fn parse_nt(p: &mut Parser, name: &str) -> Nonterminal {
509+
match name {
510+
"tt" => {
511+
p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
512+
let res = token::NtTT(P(p.parse_token_tree()));
513+
p.quote_depth -= 1u;
514+
return res;
515+
}
516+
_ => {}
517+
}
518+
// check at the beginning and the parser checks after each bump
519+
p.check_unknown_macro_variable();
509520
match name {
510521
"item" => match p.parse_item(Vec::new()) {
511522
Some(i) => token::NtItem(i),
@@ -529,12 +540,6 @@ pub fn parse_nt(p: &mut Parser, name: &str) -> Nonterminal {
529540
token::NtPath(box p.parse_path(LifetimeAndTypesWithoutColons))
530541
}
531542
"meta" => token::NtMeta(p.parse_meta_item()),
532-
"tt" => {
533-
p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
534-
let res = token::NtTT(P(p.parse_token_tree()));
535-
p.quote_depth -= 1u;
536-
res
537-
}
538543
_ => {
539544
p.fatal(format!("unsupported builtin nonterminal parser: {}", name)[])
540545
}

src/libsyntax/ext/tt/macro_rules.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use ext::base::{NormalTT, TTMacroExpander};
1616
use ext::tt::macro_parser::{Success, Error, Failure};
1717
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
1818
use ext::tt::macro_parser::{parse, parse_or_else};
19-
use parse::lexer::new_tt_reader;
19+
use parse::lexer::{new_tt_reader, new_tt_reader_with_doc_flag};
2020
use parse::parser::Parser;
2121
use parse::attr::ParserAttr;
2222
use parse::token::{special_idents, gensym_ident};
@@ -158,13 +158,13 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
158158
_ => cx.span_fatal(sp, "malformed macro lhs")
159159
};
160160
// `None` is because we're not interpolating
161-
let mut arg_rdr = new_tt_reader(&cx.parse_sess().span_diagnostic,
162-
None,
163-
None,
164-
arg.iter()
165-
.map(|x| (*x).clone())
166-
.collect());
167-
arg_rdr.desugar_doc_comments = true;
161+
let arg_rdr = new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
162+
None,
163+
None,
164+
arg.iter()
165+
.map(|x| (*x).clone())
166+
.collect(),
167+
true);
168168
match parse(cx.parse_sess(), cx.cfg(), arg_rdr, lhs_tt) {
169169
Success(named_matches) => {
170170
let rhs = match *rhses[i] {
@@ -183,7 +183,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
183183
Some(named_matches),
184184
imported_from,
185185
rhs);
186-
let p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
186+
let mut p = Parser::new(cx.parse_sess(), cx.cfg(), box trncbr);
187+
p.check_unknown_macro_variable();
187188
// Let the context choose how to interpret the result.
188189
// Weird, but useful for X-macros.
189190
return box ParserAnyMacro {

src/libsyntax/ext/tt/transcribe.rs

+23-11
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,28 @@ pub struct TtReader<'a> {
5353
}
5454

5555
/// This can do Macro-By-Example transcription. On the other hand, if
56-
/// `src` contains no `TtSequence`s and `TtNonterminal`s, `interp` can (and
57-
/// should) be none.
56+
/// `src` contains no `TtSequence`s, `MatchNt`s or `SubstNt`s, `interp` can
57+
/// (and should) be None.
5858
pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
5959
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
6060
imported_from: Option<Ident>,
61-
src: Vec<ast::TokenTree> )
61+
src: Vec<ast::TokenTree>)
6262
-> TtReader<'a> {
63+
new_tt_reader_with_doc_flag(sp_diag, interp, imported_from, src, false)
64+
}
65+
66+
/// The extra `desugar_doc_comments` flag enables reading doc comments
67+
/// like any other attribute which consists of `meta` and surrounding #[ ] tokens.
68+
///
69+
/// This can do Macro-By-Example transcription. On the other hand, if
70+
/// `src` contains no `TtSequence`s, `MatchNt`s or `SubstNt`s, `interp` can
71+
/// (and should) be None.
72+
pub fn new_tt_reader_with_doc_flag<'a>(sp_diag: &'a SpanHandler,
73+
interp: Option<HashMap<Ident, Rc<NamedMatch>>>,
74+
imported_from: Option<Ident>,
75+
src: Vec<ast::TokenTree>,
76+
desugar_doc_comments: bool)
77+
-> TtReader<'a> {
6378
let mut r = TtReader {
6479
sp_diag: sp_diag,
6580
stack: vec!(TtFrame {
@@ -80,7 +95,7 @@ pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
8095
crate_name_next: None,
8196
repeat_idx: Vec::new(),
8297
repeat_len: Vec::new(),
83-
desugar_doc_comments: false,
98+
desugar_doc_comments: desugar_doc_comments,
8499
/* dummy values, never read: */
85100
cur_tok: token::Eof,
86101
cur_span: DUMMY_SP,
@@ -266,18 +281,15 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
266281
}
267282
// FIXME #2887: think about span stuff here
268283
TtToken(sp, SubstNt(ident, namep)) => {
284+
r.stack.last_mut().unwrap().idx += 1;
269285
match lookup_cur_matched(r, ident) {
270286
None => {
271-
r.stack.push(TtFrame {
272-
forest: TtToken(sp, SubstNt(ident, namep)),
273-
idx: 0,
274-
dotdotdoted: false,
275-
sep: None
276-
});
287+
r.cur_span = sp;
288+
r.cur_tok = SubstNt(ident, namep);
289+
return ret_val;
277290
// this can't be 0 length, just like TtDelimited
278291
}
279292
Some(cur_matched) => {
280-
r.stack.last_mut().unwrap().idx += 1;
281293
match *cur_matched {
282294
// sidestep the interpolation tricks for ident because
283295
// (a) idents can be in lots of places, so it'd be a pain

src/libsyntax/parse/lexer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::rc::Rc;
2525
use std::str;
2626
use std::string::CowString;
2727

28-
pub use ext::tt::transcribe::{TtReader, new_tt_reader};
28+
pub use ext::tt::transcribe::{TtReader, new_tt_reader, new_tt_reader_with_doc_flag};
2929

3030
pub mod comments;
3131

src/libsyntax/parse/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,9 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess,
297297
tts: Vec<ast::TokenTree>,
298298
cfg: ast::CrateConfig) -> Parser<'a> {
299299
let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
300-
Parser::new(sess, cfg, box trdr)
300+
let mut p = Parser::new(sess, cfg, box trdr);
301+
p.check_unknown_macro_variable();
302+
p
301303
}
302304

303305
// FIXME (Issue #16472): The `with_hygiene` mod should go away after

src/libsyntax/parse/parser.rs

+83-55
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,8 @@ impl<'a> Parser<'a> {
946946
self.token = next.tok;
947947
self.tokens_consumed += 1u;
948948
self.expected_tokens.clear();
949+
// check after each token
950+
self.check_unknown_macro_variable();
949951
}
950952

951953
/// Advance the parser by one token and return the bumped token.
@@ -2655,6 +2657,70 @@ impl<'a> Parser<'a> {
26552657
return e;
26562658
}
26572659

2660+
// Parse unquoted tokens after a `$` in a token tree
2661+
fn parse_unquoted(&mut self) -> TokenTree {
2662+
let mut sp = self.span;
2663+
let (name, namep) = match self.token {
2664+
token::Dollar => {
2665+
self.bump();
2666+
2667+
if self.token == token::OpenDelim(token::Paren) {
2668+
let Spanned { node: seq, span: seq_span } = self.parse_seq(
2669+
&token::OpenDelim(token::Paren),
2670+
&token::CloseDelim(token::Paren),
2671+
seq_sep_none(),
2672+
|p| p.parse_token_tree()
2673+
);
2674+
let (sep, repeat) = self.parse_sep_and_kleene_op();
2675+
let name_num = macro_parser::count_names(seq[]);
2676+
return TtSequence(mk_sp(sp.lo, seq_span.hi),
2677+
Rc::new(SequenceRepetition {
2678+
tts: seq,
2679+
separator: sep,
2680+
op: repeat,
2681+
num_captures: name_num
2682+
}));
2683+
} else if self.token.is_keyword_allow_following_colon(keywords::Crate) {
2684+
self.bump();
2685+
return TtToken(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar));
2686+
} else {
2687+
sp = mk_sp(sp.lo, self.span.hi);
2688+
let namep = match self.token { token::Ident(_, p) => p, _ => token::Plain };
2689+
let name = self.parse_ident();
2690+
(name, namep)
2691+
}
2692+
}
2693+
token::SubstNt(name, namep) => {
2694+
self.bump();
2695+
(name, namep)
2696+
}
2697+
_ => unreachable!()
2698+
};
2699+
// continue by trying to parse the `:ident` after `$name`
2700+
if self.token == token::Colon && self.look_ahead(1, |t| t.is_ident() &&
2701+
!t.is_strict_keyword() &&
2702+
!t.is_reserved_keyword()) {
2703+
self.bump();
2704+
sp = mk_sp(sp.lo, self.span.hi);
2705+
let kindp = match self.token { token::Ident(_, p) => p, _ => token::Plain };
2706+
let nt_kind = self.parse_ident();
2707+
TtToken(sp, MatchNt(name, nt_kind, namep, kindp))
2708+
} else {
2709+
TtToken(sp, SubstNt(name, namep))
2710+
}
2711+
}
2712+
2713+
pub fn check_unknown_macro_variable(&mut self) {
2714+
if self.quote_depth == 0u {
2715+
match self.token {
2716+
token::SubstNt(name, _) =>
2717+
self.fatal(format!("unknown macro variable `{}`",
2718+
token::get_ident(name))[]),
2719+
_ => {}
2720+
}
2721+
}
2722+
}
2723+
26582724
/// Parse an optional separator followed by a Kleene-style
26592725
/// repetition token (+ or *).
26602726
pub fn parse_sep_and_kleene_op(&mut self) -> (Option<token::Token>, ast::KleeneOp) {
@@ -2701,63 +2767,25 @@ impl<'a> Parser<'a> {
27012767
fn parse_non_delim_tt_tok(p: &mut Parser) -> TokenTree {
27022768
maybe_whole!(deref p, NtTT);
27032769
match p.token {
2704-
token::CloseDelim(_) => {
2705-
// This is a conservative error: only report the last unclosed delimiter. The
2706-
// previous unclosed delimiters could actually be closed! The parser just hasn't
2707-
// gotten to them yet.
2708-
match p.open_braces.last() {
2709-
None => {}
2710-
Some(&sp) => p.span_note(sp, "unclosed delimiter"),
2711-
};
2712-
let token_str = p.this_token_to_string();
2713-
p.fatal(format!("incorrect close delimiter: `{}`",
2714-
token_str)[])
2715-
},
2716-
/* we ought to allow different depths of unquotation */
2717-
token::Dollar if p.quote_depth > 0u => {
2718-
p.bump();
2719-
let sp = p.span;
2720-
2721-
if p.token == token::OpenDelim(token::Paren) {
2722-
let seq = p.parse_seq(
2723-
&token::OpenDelim(token::Paren),
2724-
&token::CloseDelim(token::Paren),
2725-
seq_sep_none(),
2726-
|p| p.parse_token_tree()
2727-
);
2728-
let (sep, repeat) = p.parse_sep_and_kleene_op();
2729-
let seq = match seq {
2730-
Spanned { node, .. } => node,
2770+
token::CloseDelim(_) => {
2771+
// This is a conservative error: only report the last unclosed delimiter. The
2772+
// previous unclosed delimiters could actually be closed! The parser just hasn't
2773+
// gotten to them yet.
2774+
match p.open_braces.last() {
2775+
None => {}
2776+
Some(&sp) => p.span_note(sp, "unclosed delimiter"),
27312777
};
2732-
let name_num = macro_parser::count_names(seq[]);
2733-
TtSequence(mk_sp(sp.lo, p.span.hi),
2734-
Rc::new(SequenceRepetition {
2735-
tts: seq,
2736-
separator: sep,
2737-
op: repeat,
2738-
num_captures: name_num
2739-
}))
2740-
} else if p.token.is_keyword_allow_following_colon(keywords::Crate) {
2741-
p.bump();
2742-
TtToken(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar))
2743-
} else {
2744-
// A nonterminal that matches or not
2745-
let namep = match p.token { token::Ident(_, p) => p, _ => token::Plain };
2746-
let name = p.parse_ident();
2747-
if p.token == token::Colon && p.look_ahead(1, |t| t.is_ident()) {
2748-
p.bump();
2749-
let kindp = match p.token { token::Ident(_, p) => p, _ => token::Plain };
2750-
let nt_kind = p.parse_ident();
2751-
let m = TtToken(sp, MatchNt(name, nt_kind, namep, kindp));
2752-
m
2753-
} else {
2754-
TtToken(sp, SubstNt(name, namep))
2755-
}
2778+
let token_str = p.this_token_to_string();
2779+
p.fatal(format!("incorrect close delimiter: `{}`",
2780+
token_str)[])
2781+
},
2782+
/* we ought to allow different depths of unquotation */
2783+
token::Dollar | token::SubstNt(..) if p.quote_depth > 0u => {
2784+
p.parse_unquoted()
2785+
}
2786+
_ => {
2787+
TtToken(p.span, p.bump_and_get())
27562788
}
2757-
}
2758-
_ => {
2759-
TtToken(p.span, p.bump_and_get())
2760-
}
27612789
}
27622790
}
27632791

0 commit comments

Comments
 (0)