]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander}; |
e74abb32 | 2 | use crate::base::{SyntaxExtension, SyntaxExtensionKind}; |
dfeec247 | 3 | use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind}; |
e74abb32 XL |
4 | use crate::mbe; |
5 | use crate::mbe::macro_check; | |
74b04a01 | 6 | use crate::mbe::macro_parser::parse_tt; |
ba9703b0 | 7 | use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success}; |
74b04a01 | 8 | use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq}; |
e74abb32 XL |
9 | use crate::mbe::transcribe::transcribe; |
10 | ||
3dfed10e XL |
11 | use rustc_ast as ast; |
12 | use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*}; | |
74b04a01 | 13 | use rustc_ast::tokenstream::{DelimSpan, TokenStream}; |
5869c6ff | 14 | use rustc_ast::NodeId; |
74b04a01 XL |
15 | use rustc_ast_pretty::pprust; |
16 | use rustc_attr::{self as attr, TransparencyError}; | |
dfeec247 XL |
17 | use rustc_data_structures::fx::FxHashMap; |
18 | use rustc_data_structures::sync::Lrc; | |
ba9703b0 | 19 | use rustc_errors::{Applicability, DiagnosticBuilder}; |
60c5eb7d | 20 | use rustc_feature::Features; |
5869c6ff | 21 | use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS; |
60c5eb7d | 22 | use rustc_parse::parser::Parser; |
74b04a01 | 23 | use rustc_session::parse::ParseSess; |
3dfed10e | 24 | use rustc_session::Session; |
dfeec247 XL |
25 | use rustc_span::edition::Edition; |
26 | use rustc_span::hygiene::Transparency; | |
3dfed10e | 27 | use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent}; |
dfeec247 | 28 | use rustc_span::Span; |
9fa01778 | 29 | |
94b46f34 | 30 | use std::borrow::Cow; |
7cac9316 | 31 | use std::collections::hash_map::Entry; |
60c5eb7d | 32 | use std::{mem, slice}; |
3dfed10e | 33 | use tracing::debug; |
1a4d82fc | 34 | |
e74abb32 | 35 | crate struct ParserAnyMacro<'a> { |
9e0c209e | 36 | parser: Parser<'a>, |
9346a6ac AL |
37 | |
38 | /// Span of the expansion site of the macro this parser is for | |
39 | site_span: Span, | |
40 | /// The ident of the macro we're parsing | |
f9f354fc | 41 | macro_ident: Ident, |
5869c6ff | 42 | lint_node_id: NodeId, |
a1dfa0c6 | 43 | arm_span: Span, |
1a4d82fc JJ |
44 | } |
45 | ||
e74abb32 XL |
46 | crate fn annotate_err_with_kind( |
47 | err: &mut DiagnosticBuilder<'_>, | |
48 | kind: AstFragmentKind, | |
49 | span: Span, | |
50 | ) { | |
416331ca XL |
51 | match kind { |
52 | AstFragmentKind::Ty => { | |
53 | err.span_label(span, "this macro call doesn't expand to a type"); | |
54 | } | |
55 | AstFragmentKind::Pat => { | |
56 | err.span_label(span, "this macro call doesn't expand to a pattern"); | |
57 | } | |
58 | _ => {} | |
59 | }; | |
60 | } | |
61 | ||
ba9703b0 XL |
62 | fn emit_frag_parse_err( |
63 | mut e: DiagnosticBuilder<'_>, | |
64 | parser: &Parser<'_>, | |
65 | orig_parser: &mut Parser<'_>, | |
66 | site_span: Span, | |
ba9703b0 XL |
67 | arm_span: Span, |
68 | kind: AstFragmentKind, | |
69 | ) { | |
70 | if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") { | |
71 | if !e.span.is_dummy() { | |
72 | // early end of macro arm (#52866) | |
73 | e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); | |
74 | } | |
75 | let msg = &e.message[0]; | |
76 | e.message[0] = ( | |
77 | format!( | |
78 | "macro expansion ends with an incomplete expression: {}", | |
79 | msg.0.replace(", found `<eof>`", ""), | |
80 | ), | |
81 | msg.1, | |
82 | ); | |
83 | } | |
84 | if e.span.is_dummy() { | |
85 | // Get around lack of span in error (#30128) | |
86 | e.replace_span_with(site_span); | |
87 | if !parser.sess.source_map().is_imported(arm_span) { | |
88 | e.span_label(arm_span, "in this macro arm"); | |
89 | } | |
90 | } else if parser.sess.source_map().is_imported(parser.token.span) { | |
91 | e.span_label(site_span, "in this macro invocation"); | |
92 | } | |
93 | match kind { | |
ba9703b0 XL |
94 | // Try a statement if an expression is wanted but failed and suggest adding `;` to call. |
95 | AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) { | |
96 | Err(mut err) => err.cancel(), | |
97 | Ok(_) => { | |
98 | e.note( | |
99 | "the macro call doesn't expand to an expression, but it can expand to a statement", | |
100 | ); | |
101 | e.span_suggestion_verbose( | |
102 | site_span.shrink_to_hi(), | |
103 | "add `;` to interpret the expansion as a statement", | |
104 | ";".to_string(), | |
105 | Applicability::MaybeIncorrect, | |
106 | ); | |
107 | } | |
108 | }, | |
109 | _ => annotate_err_with_kind(&mut e, kind, site_span), | |
110 | }; | |
111 | e.emit(); | |
112 | } | |
113 | ||
1a4d82fc | 114 | impl<'a> ParserAnyMacro<'a> { |
e74abb32 | 115 | crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment { |
5869c6ff XL |
116 | let ParserAnyMacro { site_span, macro_ident, ref mut parser, lint_node_id, arm_span } = |
117 | *self; | |
ba9703b0 XL |
118 | let snapshot = &mut parser.clone(); |
119 | let fragment = match parse_ast_fragment(parser, kind) { | |
120 | Ok(f) => f, | |
121 | Err(err) => { | |
5869c6ff | 122 | emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind); |
ba9703b0 | 123 | return kind.dummy(site_span); |
a1dfa0c6 | 124 | } |
ba9703b0 | 125 | }; |
9e0c209e | 126 | |
0731742a | 127 | // We allow semicolons at the end of expressions -- e.g., the semicolon in |
9e0c209e | 128 | // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`, |
0731742a | 129 | // but `m!()` is allowed in expression positions (cf. issue #34706). |
8faf50e0 | 130 | if kind == AstFragmentKind::Expr && parser.token == token::Semi { |
5869c6ff XL |
131 | parser.sess.buffer_lint( |
132 | SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, | |
133 | parser.token.span, | |
134 | lint_node_id, | |
135 | "trailing semicolon in macro used in expression position", | |
136 | ); | |
9cc50fc6 | 137 | parser.bump(); |
1a4d82fc | 138 | } |
e9174d1e | 139 | |
9e0c209e | 140 | // Make sure we don't have any tokens left to parse so we don't silently drop anything. |
83c7162d | 141 | let path = ast::Path::from_ident(macro_ident.with_span_pos(site_span)); |
e74abb32 | 142 | ensure_complete_parse(parser, &path, kind.name(), site_span); |
8faf50e0 | 143 | fragment |
e9174d1e | 144 | } |
1a4d82fc JJ |
145 | } |
146 | ||
147 | struct MacroRulesMacroExpander { | |
f9f354fc | 148 | name: Ident, |
416331ca | 149 | span: Span, |
e1599b0c | 150 | transparency: Transparency, |
e74abb32 XL |
151 | lhses: Vec<mbe::TokenTree>, |
152 | rhses: Vec<mbe::TokenTree>, | |
92a42be0 | 153 | valid: bool, |
1a4d82fc JJ |
154 | } |
155 | ||
156 | impl TTMacroExpander for MacroRulesMacroExpander { | |
a1dfa0c6 XL |
157 | fn expand<'cx>( |
158 | &self, | |
9fa01778 | 159 | cx: &'cx mut ExtCtxt<'_>, |
a1dfa0c6 XL |
160 | sp: Span, |
161 | input: TokenStream, | |
dc9dc135 | 162 | ) -> Box<dyn MacResult + 'cx> { |
92a42be0 SL |
163 | if !self.valid { |
164 | return DummyResult::any(sp); | |
165 | } | |
e1599b0c | 166 | generic_extension( |
dfeec247 XL |
167 | cx, |
168 | sp, | |
169 | self.span, | |
170 | self.name, | |
171 | self.transparency, | |
172 | input, | |
173 | &self.lhses, | |
174 | &self.rhses, | |
e1599b0c | 175 | ) |
1a4d82fc JJ |
176 | } |
177 | } | |
178 | ||
ba9703b0 XL |
179 | fn macro_rules_dummy_expander<'cx>( |
180 | _: &'cx mut ExtCtxt<'_>, | |
181 | span: Span, | |
182 | _: TokenStream, | |
183 | ) -> Box<dyn MacResult + 'cx> { | |
184 | DummyResult::any(span) | |
185 | } | |
186 | ||
74b04a01 | 187 | fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) { |
5869c6ff | 188 | let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site); |
74b04a01 | 189 | cx_expansions.entry(sp).or_default().push(message); |
7cac9316 XL |
190 | } |
191 | ||
1a4d82fc | 192 | /// Given `lhses` and `rhses`, this is the new macro we create |
dc9dc135 XL |
193 | fn generic_extension<'cx>( |
194 | cx: &'cx mut ExtCtxt<'_>, | |
195 | sp: Span, | |
416331ca | 196 | def_span: Span, |
f9f354fc | 197 | name: Ident, |
e1599b0c | 198 | transparency: Transparency, |
dc9dc135 | 199 | arg: TokenStream, |
e74abb32 XL |
200 | lhses: &[mbe::TokenTree], |
201 | rhses: &[mbe::TokenTree], | |
dc9dc135 | 202 | ) -> Box<dyn MacResult + 'cx> { |
3dfed10e | 203 | let sess = &cx.sess.parse_sess; |
ba9703b0 | 204 | |
1a4d82fc | 205 | if cx.trace_macros() { |
f035d41b | 206 | let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); |
74b04a01 | 207 | trace_macros_note(&mut cx.expansions, sp, msg); |
1a4d82fc JJ |
208 | } |
209 | ||
210 | // Which arm's failure should we report? (the one furthest along) | |
dc9dc135 | 211 | let mut best_failure: Option<(Token, &str)> = None; |
74b04a01 XL |
212 | |
213 | // We create a base parser that can be used for the "black box" parts. | |
214 | // Every iteration needs a fresh copy of that parser. However, the parser | |
215 | // is not mutated on many of the iterations, particularly when dealing with | |
216 | // macros like this: | |
217 | // | |
218 | // macro_rules! foo { | |
219 | // ("a") => (A); | |
220 | // ("b") => (B); | |
221 | // ("c") => (C); | |
222 | // // ... etc. (maybe hundreds more) | |
223 | // } | |
224 | // | |
225 | // as seen in the `html5ever` benchmark. We use a `Cow` so that the base | |
226 | // parser is only cloned when necessary (upon mutation). Furthermore, we | |
227 | // reinitialize the `Cow` with the base parser at the start of every | |
228 | // iteration, so that any mutated parsers are not reused. This is all quite | |
229 | // hacky, but speeds up the `html5ever` benchmark significantly. (Issue | |
230 | // 68836 suggests a more comprehensive but more complex change to deal with | |
231 | // this situation.) | |
ba9703b0 | 232 | let parser = parser_from_cx(sess, arg.clone()); |
74b04a01 | 233 | |
dc9dc135 XL |
234 | for (i, lhs) in lhses.iter().enumerate() { |
235 | // try each arm's matchers | |
92a42be0 | 236 | let lhs_tt = match *lhs { |
e74abb32 | 237 | mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], |
dc9dc135 | 238 | _ => cx.span_bug(sp, "malformed macro lhs"), |
92a42be0 SL |
239 | }; |
240 | ||
60c5eb7d XL |
241 | // Take a snapshot of the state of pre-expansion gating at this point. |
242 | // This is used so that if a matcher is not `Success(..)`ful, | |
243 | // then the spans which became gated when parsing the unsuccessful matcher | |
244 | // are not recorded. On the first `Success(..)`ful matcher, the spans are merged. | |
ba9703b0 | 245 | let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut()); |
60c5eb7d | 246 | |
74b04a01 | 247 | match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) { |
92a42be0 | 248 | Success(named_matches) => { |
60c5eb7d XL |
249 | // The matcher was `Success(..)`ful. |
250 | // Merge the gated spans from parsing the matcher with the pre-existing ones. | |
ba9703b0 | 251 | sess.gated_spans.merge(gated_spans_snapshot); |
60c5eb7d | 252 | |
92a42be0 SL |
253 | let rhs = match rhses[i] { |
254 | // ignore delimiters | |
e74abb32 | 255 | mbe::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), |
3157f602 | 256 | _ => cx.span_bug(sp, "malformed macro rhs"), |
1a4d82fc | 257 | }; |
a1dfa0c6 | 258 | let arm_span = rhses[i].span(); |
3b2f2976 XL |
259 | |
260 | let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>(); | |
1a4d82fc | 261 | // rhs has holes ( `$id` and `$(...)` that need filled) |
ba9703b0 XL |
262 | let mut tts = match transcribe(cx, &named_matches, rhs, transparency) { |
263 | Ok(tts) => tts, | |
264 | Err(mut err) => { | |
265 | err.emit(); | |
266 | return DummyResult::any(arm_span); | |
267 | } | |
268 | }; | |
3b2f2976 XL |
269 | |
270 | // Replace all the tokens for the corresponding positions in the macro, to maintain | |
271 | // proper positions in error reporting, while maintaining the macro_backtrace. | |
272 | if rhs_spans.len() == tts.len() { | |
29967ef6 XL |
273 | tts = tts.map_enumerated(|i, tt| { |
274 | let mut tt = tt.clone(); | |
3b2f2976 | 275 | let mut sp = rhs_spans[i]; |
ea8adc8c | 276 | sp = sp.with_ctxt(tt.span().ctxt()); |
3b2f2976 XL |
277 | tt.set_span(sp); |
278 | tt | |
279 | }); | |
280 | } | |
7cac9316 XL |
281 | |
282 | if cx.trace_macros() { | |
f035d41b | 283 | let msg = format!("to `{}`", pprust::tts_to_string(&tts)); |
74b04a01 | 284 | trace_macros_note(&mut cx.expansions, sp, msg); |
7cac9316 XL |
285 | } |
286 | ||
ba9703b0 | 287 | let mut p = Parser::new(sess, tts, false, None); |
416331ca | 288 | p.last_type_ascription = cx.current_expansion.prior_type_ascription; |
5869c6ff | 289 | let lint_node_id = cx.resolver.lint_node_id(cx.current_expansion.id); |
476ff2be | 290 | |
1a4d82fc JJ |
291 | // Let the context choose how to interpret the result. |
292 | // Weird, but useful for X-macros. | |
d9579d0f | 293 | return Box::new(ParserAnyMacro { |
9e0c209e | 294 | parser: p, |
9346a6ac AL |
295 | |
296 | // Pass along the original expansion site and the name of the macro | |
297 | // so we can print a useful error message if the parse of the expanded | |
298 | // macro leaves unparsed tokens. | |
299 | site_span: sp, | |
a1dfa0c6 | 300 | macro_ident: name, |
5869c6ff | 301 | lint_node_id, |
a1dfa0c6 | 302 | arm_span, |
dc9dc135 | 303 | }); |
92a42be0 | 304 | } |
dc9dc135 XL |
305 | Failure(token, msg) => match best_failure { |
306 | Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {} | |
307 | _ => best_failure = Some((token, msg)), | |
92a42be0 | 308 | }, |
ba9703b0 XL |
309 | Error(err_sp, ref msg) => { |
310 | let span = err_sp.substitute_dummy(sp); | |
311 | cx.struct_span_err(span, &msg).emit(); | |
312 | return DummyResult::any(span); | |
313 | } | |
314 | ErrorReported => return DummyResult::any(sp), | |
1a4d82fc | 315 | } |
60c5eb7d XL |
316 | |
317 | // The matcher was not `Success(..)`ful. | |
318 | // Restore to the state before snapshotting and maybe try again. | |
ba9703b0 | 319 | mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut()); |
1a4d82fc | 320 | } |
74b04a01 | 321 | drop(parser); |
e9174d1e | 322 | |
dc9dc135 XL |
323 | let (token, label) = best_failure.expect("ran no matchers"); |
324 | let span = token.span.substitute_dummy(sp); | |
325 | let mut err = cx.struct_span_err(span, &parse_failure_msg(&token)); | |
326 | err.span_label(span, label); | |
ba9703b0 XL |
327 | if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { |
328 | err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); | |
a1dfa0c6 | 329 | } |
b7449926 XL |
330 | |
331 | // Check whether there's a missing comma in this macro call, like `println!("{}" a);` | |
332 | if let Some((arg, comma_span)) = arg.add_comma() { | |
dc9dc135 XL |
333 | for lhs in lhses { |
334 | // try each arm's matchers | |
b7449926 | 335 | let lhs_tt = match *lhs { |
e74abb32 | 336 | mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], |
b7449926 XL |
337 | _ => continue, |
338 | }; | |
ba9703b0 XL |
339 | if let Success(_) = |
340 | parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt) | |
341 | { | |
342 | if comma_span.is_dummy() { | |
343 | err.note("you might be missing a comma"); | |
344 | } else { | |
345 | err.span_suggestion_short( | |
346 | comma_span, | |
347 | "missing comma here", | |
348 | ", ".to_string(), | |
349 | Applicability::MachineApplicable, | |
350 | ); | |
b7449926 | 351 | } |
b7449926 XL |
352 | } |
353 | } | |
354 | } | |
355 | err.emit(); | |
ea8adc8c XL |
356 | cx.trace_macros_diag(); |
357 | DummyResult::any(sp) | |
1a4d82fc JJ |
358 | } |
359 | ||
360 | // Note that macro-by-example's input is also matched against a token tree: | |
361 | // $( $lhs:tt => $rhs:tt );+ | |
362 | // | |
363 | // Holy self-referential! | |
364 | ||
e74abb32 XL |
365 | /// Converts a macro item into a syntax extension. |
366 | pub fn compile_declarative_macro( | |
3dfed10e | 367 | sess: &Session, |
9fa01778 XL |
368 | features: &Features, |
369 | def: &ast::Item, | |
dc9dc135 | 370 | edition: Edition, |
9fa01778 | 371 | ) -> SyntaxExtension { |
f035d41b | 372 | debug!("compile_declarative_macro: {:?}", def); |
ba9703b0 XL |
373 | let mk_syn_ext = |expander| { |
374 | SyntaxExtension::new( | |
375 | sess, | |
376 | SyntaxExtensionKind::LegacyBang(expander), | |
377 | def.span, | |
378 | Vec::new(), | |
379 | edition, | |
380 | def.ident.name, | |
381 | &def.attrs, | |
382 | ) | |
383 | }; | |
384 | ||
3dfed10e | 385 | let diag = &sess.parse_sess.span_diagnostic; |
f9f354fc XL |
386 | let lhs_nm = Ident::new(sym::lhs, def.span); |
387 | let rhs_nm = Ident::new(sym::rhs, def.span); | |
b9856134 | 388 | let tt_spec = Some(NonterminalKind::TT); |
223e47cc | 389 | |
7cac9316 | 390 | // Parse the macro_rules! invocation |
ba9703b0 XL |
391 | let (macro_rules, body) = match &def.kind { |
392 | ast::ItemKind::MacroDef(def) => (def.macro_rules, def.body.inner_tokens()), | |
7cac9316 XL |
393 | _ => unreachable!(), |
394 | }; | |
395 | ||
1a4d82fc | 396 | // The pattern that macro_rules matches. |
223e47cc | 397 | // The grammar for macro_rules! is: |
1a4d82fc | 398 | // $( $lhs:tt => $rhs:tt );+ |
223e47cc | 399 | // ...quasiquoting this would be nice. |
1a4d82fc | 400 | // These spans won't matter, anyways |
3157f602 | 401 | let argument_gram = vec![ |
e74abb32 | 402 | mbe::TokenTree::Sequence( |
dc9dc135 | 403 | DelimSpan::dummy(), |
e74abb32 | 404 | Lrc::new(mbe::SequenceRepetition { |
dc9dc135 | 405 | tts: vec![ |
e74abb32 XL |
406 | mbe::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec), |
407 | mbe::TokenTree::token(token::FatArrow, def.span), | |
408 | mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec), | |
dc9dc135 XL |
409 | ], |
410 | separator: Some(Token::new( | |
ba9703b0 | 411 | if macro_rules { token::Semi } else { token::Comma }, |
dc9dc135 XL |
412 | def.span, |
413 | )), | |
e74abb32 | 414 | kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span), |
dc9dc135 XL |
415 | num_captures: 2, |
416 | }), | |
417 | ), | |
3157f602 | 418 | // to phase into semicolon-termination instead of semicolon-separation |
e74abb32 | 419 | mbe::TokenTree::Sequence( |
dc9dc135 | 420 | DelimSpan::dummy(), |
e74abb32 XL |
421 | Lrc::new(mbe::SequenceRepetition { |
422 | tts: vec![mbe::TokenTree::token( | |
ba9703b0 | 423 | if macro_rules { token::Semi } else { token::Comma }, |
416331ca XL |
424 | def.span, |
425 | )], | |
dc9dc135 | 426 | separator: None, |
e74abb32 | 427 | kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span), |
dc9dc135 XL |
428 | num_captures: 0, |
429 | }), | |
430 | ), | |
3157f602 | 431 | ]; |
223e47cc | 432 | |
3dfed10e | 433 | let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS); |
74b04a01 | 434 | let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) { |
e9174d1e | 435 | Success(m) => m, |
dc9dc135 XL |
436 | Failure(token, msg) => { |
437 | let s = parse_failure_msg(&token); | |
438 | let sp = token.span.substitute_dummy(def.span); | |
3dfed10e | 439 | sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); |
ba9703b0 | 440 | return mk_syn_ext(Box::new(macro_rules_dummy_expander)); |
c30ab7b3 | 441 | } |
ba9703b0 | 442 | Error(sp, msg) => { |
3dfed10e XL |
443 | sess.parse_sess |
444 | .span_diagnostic | |
445 | .struct_span_err(sp.substitute_dummy(def.span), &msg) | |
446 | .emit(); | |
ba9703b0 XL |
447 | return mk_syn_ext(Box::new(macro_rules_dummy_expander)); |
448 | } | |
449 | ErrorReported => { | |
450 | return mk_syn_ext(Box::new(macro_rules_dummy_expander)); | |
e9174d1e SL |
451 | } |
452 | }; | |
223e47cc | 453 | |
92a42be0 SL |
454 | let mut valid = true; |
455 | ||
223e47cc | 456 | // Extract the arguments: |
ba9703b0 | 457 | let lhses = match argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] { |
60c5eb7d | 458 | MatchedSeq(ref s) => s |
dc9dc135 XL |
459 | .iter() |
460 | .map(|m| { | |
041b39d2 | 461 | if let MatchedNonterminal(ref nt) = *m { |
c30ab7b3 | 462 | if let NtTT(ref tt) = **nt { |
5869c6ff XL |
463 | let tt = mbe::quoted::parse( |
464 | tt.clone().into(), | |
465 | true, | |
466 | &sess.parse_sess, | |
467 | def.id, | |
468 | features, | |
469 | ) | |
470 | .pop() | |
471 | .unwrap(); | |
3dfed10e | 472 | valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt); |
8bb4bdeb | 473 | return tt; |
c30ab7b3 | 474 | } |
3157f602 | 475 | } |
3dfed10e | 476 | sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") |
dc9dc135 | 477 | }) |
e74abb32 | 478 | .collect::<Vec<mbe::TokenTree>>(), |
3dfed10e | 479 | _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"), |
223e47cc LB |
480 | }; |
481 | ||
ba9703b0 | 482 | let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] { |
60c5eb7d | 483 | MatchedSeq(ref s) => s |
dc9dc135 XL |
484 | .iter() |
485 | .map(|m| { | |
041b39d2 | 486 | if let MatchedNonterminal(ref nt) = *m { |
c30ab7b3 | 487 | if let NtTT(ref tt) = **nt { |
3dfed10e XL |
488 | return mbe::quoted::parse( |
489 | tt.clone().into(), | |
490 | false, | |
491 | &sess.parse_sess, | |
492 | def.id, | |
5869c6ff | 493 | features, |
3dfed10e XL |
494 | ) |
495 | .pop() | |
496 | .unwrap(); | |
c30ab7b3 SL |
497 | } |
498 | } | |
3dfed10e | 499 | sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") |
dc9dc135 | 500 | }) |
e74abb32 | 501 | .collect::<Vec<mbe::TokenTree>>(), |
3dfed10e | 502 | _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"), |
223e47cc LB |
503 | }; |
504 | ||
92a42be0 | 505 | for rhs in &rhses { |
3dfed10e | 506 | valid &= check_rhs(&sess.parse_sess, rhs); |
9e0c209e SL |
507 | } |
508 | ||
509 | // don't abort iteration early, so that errors for multiple lhses can be reported | |
510 | for lhs in &lhses { | |
3dfed10e | 511 | valid &= check_lhs_no_empty_seq(&sess.parse_sess, slice::from_ref(lhs)); |
92a42be0 SL |
512 | } |
513 | ||
3dfed10e | 514 | valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses); |
dc9dc135 | 515 | |
3dfed10e | 516 | let (transparency, transparency_error) = attr::find_transparency(sess, &def.attrs, macro_rules); |
416331ca | 517 | match transparency_error { |
dfeec247 XL |
518 | Some(TransparencyError::UnknownTransparency(value, span)) => { |
519 | diag.span_err(span, &format!("unknown macro transparency: `{}`", value)) | |
520 | } | |
521 | Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => { | |
522 | diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes") | |
523 | } | |
416331ca XL |
524 | None => {} |
525 | } | |
dc9dc135 | 526 | |
ba9703b0 | 527 | mk_syn_ext(Box::new(MacroRulesMacroExpander { |
dfeec247 XL |
528 | name: def.ident, |
529 | span: def.span, | |
530 | transparency, | |
531 | lhses, | |
532 | rhses, | |
533 | valid, | |
ba9703b0 | 534 | })) |
1a4d82fc JJ |
535 | } |
536 | ||
dc9dc135 XL |
537 | fn check_lhs_nt_follows( |
538 | sess: &ParseSess, | |
539 | features: &Features, | |
540 | attrs: &[ast::Attribute], | |
e74abb32 | 541 | lhs: &mbe::TokenTree, |
dc9dc135 | 542 | ) -> bool { |
92a42be0 SL |
543 | // lhs is going to be like TokenTree::Delimited(...), where the |
544 | // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. | |
e74abb32 | 545 | if let mbe::TokenTree::Delimited(_, ref tts) = *lhs { |
041b39d2 | 546 | check_matcher(sess, features, attrs, &tts.tts) |
7cac9316 XL |
547 | } else { |
548 | let msg = "invalid macro matcher; matchers must be contained in balanced delimiters"; | |
549 | sess.span_diagnostic.span_err(lhs.span(), msg); | |
550 | false | |
3157f602 | 551 | } |
1a4d82fc JJ |
552 | // we don't abort on errors on rejection, the driver will do that for us |
553 | // after parsing/expansion. we can report every error in every macro this way. | |
554 | } | |
555 | ||
9fa01778 | 556 | /// Checks that the lhs contains no repetition which could match an empty token |
9e0c209e | 557 | /// tree, because then the matcher would hang indefinitely. |
e74abb32 XL |
558 | fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { |
559 | use mbe::TokenTree; | |
9e0c209e SL |
560 | for tt in tts { |
561 | match *tt { | |
041b39d2 | 562 | TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (), |
dc9dc135 XL |
563 | TokenTree::Delimited(_, ref del) => { |
564 | if !check_lhs_no_empty_seq(sess, &del.tts) { | |
565 | return false; | |
566 | } | |
567 | } | |
9e0c209e | 568 | TokenTree::Sequence(span, ref seq) => { |
dc9dc135 XL |
569 | if seq.separator.is_none() |
570 | && seq.tts.iter().all(|seq_tt| match *seq_tt { | |
b9856134 | 571 | TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, |
dc9dc135 | 572 | TokenTree::Sequence(_, ref sub_seq) => { |
e74abb32 XL |
573 | sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore |
574 | || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne | |
dc9dc135 | 575 | } |
7cac9316 | 576 | _ => false, |
dc9dc135 XL |
577 | }) |
578 | { | |
b7449926 XL |
579 | let sp = span.entire(); |
580 | sess.span_diagnostic.span_err(sp, "repetition matches empty token tree"); | |
7cac9316 | 581 | return false; |
9e0c209e SL |
582 | } |
583 | if !check_lhs_no_empty_seq(sess, &seq.tts) { | |
584 | return false; | |
585 | } | |
586 | } | |
587 | } | |
588 | } | |
589 | ||
590 | true | |
591 | } | |
592 | ||
e74abb32 | 593 | fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool { |
92a42be0 | 594 | match *rhs { |
e74abb32 | 595 | mbe::TokenTree::Delimited(..) => return true, |
dc9dc135 | 596 | _ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited"), |
92a42be0 SL |
597 | } |
598 | false | |
599 | } | |
600 | ||
dc9dc135 XL |
601 | fn check_matcher( |
602 | sess: &ParseSess, | |
603 | features: &Features, | |
604 | attrs: &[ast::Attribute], | |
e74abb32 | 605 | matcher: &[mbe::TokenTree], |
dc9dc135 | 606 | ) -> bool { |
9cc50fc6 SL |
607 | let first_sets = FirstSets::new(matcher); |
608 | let empty_suffix = TokenSet::empty(); | |
9e0c209e | 609 | let err = sess.span_diagnostic.err_count(); |
041b39d2 | 610 | check_matcher_core(sess, features, attrs, &first_sets, matcher, &empty_suffix); |
9e0c209e | 611 | err == sess.span_diagnostic.err_count() |
9cc50fc6 SL |
612 | } |
613 | ||
0731742a | 614 | // `The FirstSets` for a matcher is a mapping from subsequences in the |
9cc50fc6 SL |
615 | // matcher to the FIRST set for that subsequence. |
616 | // | |
617 | // This mapping is partially precomputed via a backwards scan over the | |
618 | // token trees of the matcher, which provides a mapping from each | |
0731742a | 619 | // repetition sequence to its *first* set. |
9cc50fc6 | 620 | // |
0731742a XL |
621 | // (Hypothetically, sequences should be uniquely identifiable via their |
622 | // spans, though perhaps that is false, e.g., for macro-generated macros | |
9cc50fc6 SL |
623 | // that do not try to inject artificial span information. My plan is |
624 | // to try to catch such cases ahead of time and not include them in | |
625 | // the precomputed mapping.) | |
626 | struct FirstSets { | |
627 | // this maps each TokenTree::Sequence `$(tt ...) SEP OP` that is uniquely identified by its | |
628 | // span in the original matcher to the First set for the inner sequence `tt ...`. | |
629 | // | |
630 | // If two sequences have the same span in a matcher, then map that | |
631 | // span to None (invalidating the mapping here and forcing the code to | |
632 | // use a slow path). | |
b7449926 | 633 | first: FxHashMap<Span, Option<TokenSet>>, |
9cc50fc6 SL |
634 | } |
635 | ||
636 | impl FirstSets { | |
e74abb32 XL |
637 | fn new(tts: &[mbe::TokenTree]) -> FirstSets { |
638 | use mbe::TokenTree; | |
8bb4bdeb | 639 | |
b7449926 | 640 | let mut sets = FirstSets { first: FxHashMap::default() }; |
9cc50fc6 SL |
641 | build_recur(&mut sets, tts); |
642 | return sets; | |
643 | ||
644 | // walks backward over `tts`, returning the FIRST for `tts` | |
645 | // and updating `sets` at the same time for all sequence | |
646 | // substructure we find within `tts`. | |
647 | fn build_recur(sets: &mut FirstSets, tts: &[TokenTree]) -> TokenSet { | |
648 | let mut first = TokenSet::empty(); | |
649 | for tt in tts.iter().rev() { | |
650 | match *tt { | |
041b39d2 | 651 | TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { |
8bb4bdeb | 652 | first.replace_with(tt.clone()); |
9cc50fc6 | 653 | } |
32a655c1 | 654 | TokenTree::Delimited(span, ref delimited) => { |
9cc50fc6 | 655 | build_recur(sets, &delimited.tts[..]); |
60c5eb7d | 656 | first.replace_with(delimited.open_tt(span)); |
9cc50fc6 SL |
657 | } |
658 | TokenTree::Sequence(sp, ref seq_rep) => { | |
659 | let subfirst = build_recur(sets, &seq_rep.tts[..]); | |
660 | ||
b7449926 | 661 | match sets.first.entry(sp.entire()) { |
9cc50fc6 SL |
662 | Entry::Vacant(vac) => { |
663 | vac.insert(Some(subfirst.clone())); | |
664 | } | |
665 | Entry::Occupied(mut occ) => { | |
666 | // if there is already an entry, then a span must have collided. | |
667 | // This should not happen with typical macro_rules macros, | |
668 | // but syntax extensions need not maintain distinct spans, | |
669 | // so distinct syntax trees can be assigned the same span. | |
670 | // In such a case, the map cannot be trusted; so mark this | |
671 | // entry as unusable. | |
672 | occ.insert(None); | |
673 | } | |
674 | } | |
675 | ||
676 | // If the sequence contents can be empty, then the first | |
677 | // token could be the separator token itself. | |
678 | ||
dc9dc135 XL |
679 | if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) { |
680 | first.add_one_maybe(TokenTree::Token(sep.clone())); | |
9cc50fc6 SL |
681 | } |
682 | ||
683 | // Reverse scan: Sequence comes before `first`. | |
9fa01778 | 684 | if subfirst.maybe_empty |
e74abb32 XL |
685 | || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore |
686 | || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne | |
9fa01778 | 687 | { |
9cc50fc6 SL |
688 | // If sequence is potentially empty, then |
689 | // union them (preserving first emptiness). | |
690 | first.add_all(&TokenSet { maybe_empty: true, ..subfirst }); | |
691 | } else { | |
692 | // Otherwise, sequence guaranteed | |
693 | // non-empty; replace first. | |
694 | first = subfirst; | |
695 | } | |
696 | } | |
697 | } | |
698 | } | |
699 | ||
7cac9316 | 700 | first |
9cc50fc6 SL |
701 | } |
702 | } | |
703 | ||
704 | // walks forward over `tts` until all potential FIRST tokens are | |
705 | // identified. | |
e74abb32 XL |
706 | fn first(&self, tts: &[mbe::TokenTree]) -> TokenSet { |
707 | use mbe::TokenTree; | |
8bb4bdeb | 708 | |
9cc50fc6 SL |
709 | let mut first = TokenSet::empty(); |
710 | for tt in tts.iter() { | |
711 | assert!(first.maybe_empty); | |
712 | match *tt { | |
041b39d2 | 713 | TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { |
8bb4bdeb | 714 | first.add_one(tt.clone()); |
9cc50fc6 SL |
715 | return first; |
716 | } | |
32a655c1 | 717 | TokenTree::Delimited(span, ref delimited) => { |
60c5eb7d | 718 | first.add_one(delimited.open_tt(span)); |
9cc50fc6 SL |
719 | return first; |
720 | } | |
721 | TokenTree::Sequence(sp, ref seq_rep) => { | |
416331ca XL |
722 | let subfirst_owned; |
723 | let subfirst = match self.first.get(&sp.entire()) { | |
724 | Some(&Some(ref subfirst)) => subfirst, | |
9cc50fc6 | 725 | Some(&None) => { |
416331ca XL |
726 | subfirst_owned = self.first(&seq_rep.tts[..]); |
727 | &subfirst_owned | |
9cc50fc6 | 728 | } |
9cc50fc6 SL |
729 | None => { |
730 | panic!("We missed a sequence during FirstSets construction"); | |
731 | } | |
416331ca XL |
732 | }; |
733 | ||
734 | // If the sequence contents can be empty, then the first | |
735 | // token could be the separator token itself. | |
736 | if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) { | |
737 | first.add_one_maybe(TokenTree::Token(sep.clone())); | |
738 | } | |
739 | ||
740 | assert!(first.maybe_empty); | |
741 | first.add_all(subfirst); | |
742 | if subfirst.maybe_empty | |
e74abb32 XL |
743 | || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore |
744 | || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne | |
416331ca XL |
745 | { |
746 | // Continue scanning for more first | |
747 | // tokens, but also make sure we | |
748 | // restore empty-tracking state. | |
749 | first.maybe_empty = true; | |
750 | continue; | |
751 | } else { | |
752 | return first; | |
9cc50fc6 SL |
753 | } |
754 | } | |
755 | } | |
756 | } | |
757 | ||
758 | // we only exit the loop if `tts` was empty or if every | |
759 | // element of `tts` matches the empty sequence. | |
760 | assert!(first.maybe_empty); | |
7cac9316 | 761 | first |
9cc50fc6 SL |
762 | } |
763 | } | |
764 | ||
e74abb32 | 765 | // A set of `mbe::TokenTree`s, which may include `TokenTree::Match`s |
8bb4bdeb | 766 | // (for macro-by-example syntactic variables). It also carries the |
9cc50fc6 SL |
767 | // `maybe_empty` flag; that is true if and only if the matcher can |
768 | // match an empty token sequence. | |
769 | // | |
770 | // The First set is computed on submatchers like `$($a:expr b),* $(c)* d`, | |
771 | // which has corresponding FIRST = {$a:expr, c, d}. | |
772 | // Likewise, `$($a:expr b),* $(c)+ d` has FIRST = {$a:expr, c}. | |
773 | // | |
774 | // (Notably, we must allow for *-op to occur zero times.) | |
775 | #[derive(Clone, Debug)] | |
776 | struct TokenSet { | |
e74abb32 | 777 | tokens: Vec<mbe::TokenTree>, |
9cc50fc6 SL |
778 | maybe_empty: bool, |
779 | } | |
780 | ||
781 | impl TokenSet { | |
782 | // Returns a set for the empty sequence. | |
dc9dc135 XL |
783 | fn empty() -> Self { |
784 | TokenSet { tokens: Vec::new(), maybe_empty: true } | |
785 | } | |
9cc50fc6 SL |
786 | |
787 | // Returns the set `{ tok }` for the single-token (and thus | |
788 | // non-empty) sequence [tok]. | |
e74abb32 | 789 | fn singleton(tok: mbe::TokenTree) -> Self { |
9cc50fc6 SL |
790 | TokenSet { tokens: vec![tok], maybe_empty: false } |
791 | } | |
792 | ||
793 | // Changes self to be the set `{ tok }`. | |
794 | // Since `tok` is always present, marks self as non-empty. | |
e74abb32 | 795 | fn replace_with(&mut self, tok: mbe::TokenTree) { |
9cc50fc6 SL |
796 | self.tokens.clear(); |
797 | self.tokens.push(tok); | |
798 | self.maybe_empty = false; | |
799 | } | |
800 | ||
801 | // Changes self to be the empty set `{}`; meant for use when | |
802 | // the particular token does not matter, but we want to | |
803 | // record that it occurs. | |
804 | fn replace_with_irrelevant(&mut self) { | |
805 | self.tokens.clear(); | |
806 | self.maybe_empty = false; | |
807 | } | |
808 | ||
809 | // Adds `tok` to the set for `self`, marking sequence as non-empy. | |
e74abb32 | 810 | fn add_one(&mut self, tok: mbe::TokenTree) { |
9cc50fc6 SL |
811 | if !self.tokens.contains(&tok) { |
812 | self.tokens.push(tok); | |
813 | } | |
814 | self.maybe_empty = false; | |
815 | } | |
816 | ||
817 | // Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.) | |
e74abb32 | 818 | fn add_one_maybe(&mut self, tok: mbe::TokenTree) { |
9cc50fc6 SL |
819 | if !self.tokens.contains(&tok) { |
820 | self.tokens.push(tok); | |
821 | } | |
822 | } | |
823 | ||
824 | // Adds all elements of `other` to this. | |
825 | // | |
826 | // (Since this is a set, we filter out duplicates.) | |
827 | // | |
828 | // If `other` is potentially empty, then preserves the previous | |
829 | // setting of the empty flag of `self`. If `other` is guaranteed | |
830 | // non-empty, then `self` is marked non-empty. | |
831 | fn add_all(&mut self, other: &Self) { | |
832 | for tok in &other.tokens { | |
833 | if !self.tokens.contains(tok) { | |
834 | self.tokens.push(tok.clone()); | |
835 | } | |
836 | } | |
837 | if !other.maybe_empty { | |
838 | self.maybe_empty = false; | |
839 | } | |
840 | } | |
841 | } | |
842 | ||
843 | // Checks that `matcher` is internally consistent and that it | |
416331ca | 844 | // can legally be followed by a token `N`, for all `N` in `follow`. |
9cc50fc6 SL |
845 | // (If `follow` is empty, then it imposes no constraint on |
846 | // the `matcher`.) | |
847 | // | |
848 | // Returns the set of NT tokens that could possibly come last in | |
849 | // `matcher`. (If `matcher` matches the empty sequence, then | |
850 | // `maybe_empty` will be set to true.) | |
851 | // | |
852 | // Requires that `first_sets` is pre-computed for `matcher`; | |
853 | // see `FirstSets::new`. | |
dc9dc135 XL |
854 | fn check_matcher_core( |
855 | sess: &ParseSess, | |
856 | features: &Features, | |
857 | attrs: &[ast::Attribute], | |
858 | first_sets: &FirstSets, | |
e74abb32 | 859 | matcher: &[mbe::TokenTree], |
dc9dc135 XL |
860 | follow: &TokenSet, |
861 | ) -> TokenSet { | |
e74abb32 | 862 | use mbe::TokenTree; |
9cc50fc6 SL |
863 | |
864 | let mut last = TokenSet::empty(); | |
865 | ||
866 | // 2. For each token and suffix [T, SUFFIX] in M: | |
867 | // ensure that T can be followed by SUFFIX, and if SUFFIX may be empty, | |
868 | // then ensure T can also be followed by any element of FOLLOW. | |
869 | 'each_token: for i in 0..matcher.len() { | |
870 | let token = &matcher[i]; | |
dc9dc135 | 871 | let suffix = &matcher[i + 1..]; |
9cc50fc6 SL |
872 | |
873 | let build_suffix_first = || { | |
874 | let mut s = first_sets.first(suffix); | |
dc9dc135 XL |
875 | if s.maybe_empty { |
876 | s.add_all(follow); | |
877 | } | |
7cac9316 | 878 | s |
9cc50fc6 SL |
879 | }; |
880 | ||
881 | // (we build `suffix_first` on demand below; you can tell | |
882 | // which cases are supposed to fall through by looking for the | |
883 | // initialization of this variable.) | |
884 | let suffix_first; | |
885 | ||
886 | // First, update `last` so that it corresponds to the set | |
887 | // of NT tokens that might end the sequence `... token`. | |
888 | match *token { | |
041b39d2 | 889 | TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { |
3dfed10e | 890 | if token_can_be_followed_by_any(token) { |
9cc50fc6 SL |
891 | // don't need to track tokens that work with any, |
892 | last.replace_with_irrelevant(); | |
893 | // ... and don't need to check tokens that can be | |
894 | // followed by anything against SUFFIX. | |
895 | continue 'each_token; | |
896 | } else { | |
8bb4bdeb | 897 | last.replace_with(token.clone()); |
9cc50fc6 SL |
898 | suffix_first = build_suffix_first(); |
899 | } | |
900 | } | |
32a655c1 | 901 | TokenTree::Delimited(span, ref d) => { |
60c5eb7d | 902 | let my_suffix = TokenSet::singleton(d.close_tt(span)); |
041b39d2 | 903 | check_matcher_core(sess, features, attrs, first_sets, &d.tts, &my_suffix); |
9cc50fc6 SL |
904 | // don't track non NT tokens |
905 | last.replace_with_irrelevant(); | |
906 | ||
907 | // also, we don't need to check delimited sequences | |
908 | // against SUFFIX | |
909 | continue 'each_token; | |
910 | } | |
dc9dc135 | 911 | TokenTree::Sequence(_, ref seq_rep) => { |
9cc50fc6 SL |
912 | suffix_first = build_suffix_first(); |
913 | // The trick here: when we check the interior, we want | |
914 | // to include the separator (if any) as a potential | |
915 | // (but not guaranteed) element of FOLLOW. So in that | |
916 | // case, we make a temp copy of suffix and stuff | |
917 | // delimiter in there. | |
918 | // | |
919 | // FIXME: Should I first scan suffix_first to see if | |
920 | // delimiter is already in it before I go through the | |
921 | // work of cloning it? But then again, this way I may | |
922 | // get a "tighter" span? | |
923 | let mut new; | |
dc9dc135 | 924 | let my_suffix = if let Some(sep) = &seq_rep.separator { |
9cc50fc6 | 925 | new = suffix_first.clone(); |
dc9dc135 | 926 | new.add_one_maybe(TokenTree::Token(sep.clone())); |
9cc50fc6 SL |
927 | &new |
928 | } else { | |
929 | &suffix_first | |
930 | }; | |
931 | ||
932 | // At this point, `suffix_first` is built, and | |
933 | // `my_suffix` is some TokenSet that we can use | |
934 | // for checking the interior of `seq_rep`. | |
dc9dc135 XL |
935 | let next = |
936 | check_matcher_core(sess, features, attrs, first_sets, &seq_rep.tts, my_suffix); | |
9cc50fc6 SL |
937 | if next.maybe_empty { |
938 | last.add_all(&next); | |
939 | } else { | |
940 | last = next; | |
941 | } | |
942 | ||
943 | // the recursive call to check_matcher_core already ran the 'each_last | |
944 | // check below, so we can just keep going forward here. | |
945 | continue 'each_token; | |
946 | } | |
947 | } | |
948 | ||
949 | // (`suffix_first` guaranteed initialized once reaching here.) | |
950 | ||
951 | // Now `last` holds the complete set of NT tokens that could | |
952 | // end the sequence before SUFFIX. Check that every one works with `suffix`. | |
3dfed10e | 953 | for token in &last.tokens { |
b9856134 | 954 | if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token { |
8bb4bdeb | 955 | for next_token in &suffix_first.tokens { |
3dfed10e | 956 | match is_in_follow(next_token, kind) { |
a1dfa0c6 | 957 | IsInFollow::Yes => {} |
dc9dc135 XL |
958 | IsInFollow::No(possible) => { |
959 | let may_be = if last.tokens.len() == 1 && suffix_first.tokens.len() == 1 | |
9cc50fc6 SL |
960 | { |
961 | "is" | |
962 | } else { | |
963 | "may be" | |
964 | }; | |
965 | ||
a1dfa0c6 XL |
966 | let sp = next_token.span(); |
967 | let mut err = sess.span_diagnostic.struct_span_err( | |
968 | sp, | |
dc9dc135 XL |
969 | &format!( |
970 | "`${name}:{frag}` {may_be} followed by `{next}`, which \ | |
971 | is not allowed for `{frag}` fragments", | |
972 | name = name, | |
3dfed10e | 973 | frag = kind, |
dc9dc135 XL |
974 | next = quoted_tt_to_string(next_token), |
975 | may_be = may_be | |
976 | ), | |
3157f602 | 977 | ); |
3dfed10e | 978 | err.span_label(sp, format!("not allowed after `{}` fragments", kind)); |
a1dfa0c6 | 979 | let msg = "allowed there are: "; |
dc9dc135 | 980 | match possible { |
a1dfa0c6 XL |
981 | &[] => {} |
982 | &[t] => { | |
983 | err.note(&format!( | |
984 | "only {} is allowed after `{}` fragments", | |
3dfed10e | 985 | t, kind, |
a1dfa0c6 XL |
986 | )); |
987 | } | |
988 | ts => { | |
989 | err.note(&format!( | |
990 | "{}{} or {}", | |
991 | msg, | |
dc9dc135 XL |
992 | ts[..ts.len() - 1] |
993 | .iter() | |
74b04a01 | 994 | .copied() |
dc9dc135 XL |
995 | .collect::<Vec<_>>() |
996 | .join(", "), | |
a1dfa0c6 XL |
997 | ts[ts.len() - 1], |
998 | )); | |
999 | } | |
1000 | } | |
1001 | err.emit(); | |
9cc50fc6 SL |
1002 | } |
1003 | } | |
1004 | } | |
1005 | } | |
1006 | } | |
1007 | } | |
1008 | last | |
1009 | } | |
1010 | ||
e74abb32 | 1011 | fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { |
b9856134 | 1012 | if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok { |
3dfed10e | 1013 | frag_can_be_followed_by_any(kind) |
9cc50fc6 | 1014 | } else { |
74b04a01 | 1015 | // (Non NT's can always be followed by anything in matchers.) |
9cc50fc6 SL |
1016 | true |
1017 | } | |
1018 | } | |
1019 | ||
9fa01778 XL |
1020 | /// Returns `true` if a fragment of type `frag` can be followed by any sort of |
1021 | /// token. We use this (among other things) as a useful approximation | |
9cc50fc6 SL |
1022 | /// for when `frag` can be followed by a repetition like `$(...)*` or |
1023 | /// `$(...)+`. In general, these can be a bit tricky to reason about, | |
1024 | /// so we adopt a conservative position that says that any fragment | |
1025 | /// specifier which consumes at most one token tree can be followed by | |
1026 | /// a fragment specifier (indeed, these fragments can be followed by | |
1027 | /// ANYTHING without fear of future compatibility hazards). | |
3dfed10e | 1028 | fn frag_can_be_followed_by_any(kind: NonterminalKind) -> bool { |
29967ef6 XL |
1029 | matches!( |
1030 | kind, | |
3dfed10e XL |
1031 | NonterminalKind::Item // always terminated by `}` or `;` |
1032 | | NonterminalKind::Block // exactly one token tree | |
1033 | | NonterminalKind::Ident // exactly one token tree | |
1034 | | NonterminalKind::Literal // exactly one token tree | |
1035 | | NonterminalKind::Meta // exactly one token tree | |
1036 | | NonterminalKind::Lifetime // exactly one token tree | |
29967ef6 XL |
1037 | | NonterminalKind::TT // exactly one token tree |
1038 | ) | |
62682a34 SL |
1039 | } |
1040 | ||
a1dfa0c6 XL |
1041 | enum IsInFollow { |
1042 | Yes, | |
dc9dc135 | 1043 | No(&'static [&'static str]), |
a1dfa0c6 XL |
1044 | } |
1045 | ||
9fa01778 | 1046 | /// Returns `true` if `frag` can legally be followed by the token `tok`. For |
9cc50fc6 | 1047 | /// fragments that can consume an unbounded number of tokens, `tok` |
62682a34 SL |
1048 | /// must be within a well-defined follow set. This is intended to |
1049 | /// guarantee future compatibility: for example, without this rule, if | |
1050 | /// we expanded `expr` to include a new binary operator, we might | |
1051 | /// break macros that were relying on that binary operator as a | |
1052 | /// separator. | |
9cc50fc6 | 1053 | // when changing this do not forget to update doc/book/macros.md! |
3dfed10e | 1054 | fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { |
e74abb32 | 1055 | use mbe::TokenTree; |
8bb4bdeb | 1056 | |
dc9dc135 | 1057 | if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { |
62682a34 SL |
1058 | // closing a token tree can never be matched by any fragment; |
1059 | // iow, we always require that `(` and `)` match, etc. | |
a1dfa0c6 | 1060 | IsInFollow::Yes |
85aaf69f | 1061 | } else { |
3dfed10e XL |
1062 | match kind { |
1063 | NonterminalKind::Item => { | |
85aaf69f SL |
1064 | // since items *must* be followed by either a `;` or a `}`, we can |
1065 | // accept anything after them | |
a1dfa0c6 | 1066 | IsInFollow::Yes |
dc9dc135 | 1067 | } |
3dfed10e | 1068 | NonterminalKind::Block => { |
b039eaaf | 1069 | // anything can follow block, the braces provide an easy boundary to |
85aaf69f | 1070 | // maintain |
a1dfa0c6 | 1071 | IsInFollow::Yes |
dc9dc135 | 1072 | } |
3dfed10e | 1073 | NonterminalKind::Stmt | NonterminalKind::Expr => { |
dc9dc135 XL |
1074 | const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; |
1075 | match tok { | |
1076 | TokenTree::Token(token) => match token.kind { | |
a1dfa0c6 | 1077 | FatArrow | Comma | Semi => IsInFollow::Yes, |
dc9dc135 | 1078 | _ => IsInFollow::No(TOKENS), |
a1dfa0c6 | 1079 | }, |
dc9dc135 | 1080 | _ => IsInFollow::No(TOKENS), |
a1dfa0c6 | 1081 | } |
dc9dc135 | 1082 | } |
5869c6ff | 1083 | NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { |
dc9dc135 XL |
1084 | const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; |
1085 | match tok { | |
1086 | TokenTree::Token(token) => match token.kind { | |
a1dfa0c6 | 1087 | FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes, |
dc9dc135 XL |
1088 | Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes, |
1089 | _ => IsInFollow::No(TOKENS), | |
a1dfa0c6 | 1090 | }, |
dc9dc135 | 1091 | _ => IsInFollow::No(TOKENS), |
a1dfa0c6 | 1092 | } |
dc9dc135 | 1093 | } |
3dfed10e | 1094 | NonterminalKind::Path | NonterminalKind::Ty => { |
dc9dc135 XL |
1095 | const TOKENS: &[&str] = &[ |
1096 | "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`", | |
a1dfa0c6 XL |
1097 | "`where`", |
1098 | ]; | |
dc9dc135 XL |
1099 | match tok { |
1100 | TokenTree::Token(token) => match token.kind { | |
1101 | OpenDelim(token::DelimToken::Brace) | |
1102 | | OpenDelim(token::DelimToken::Bracket) | |
1103 | | Comma | |
1104 | | FatArrow | |
1105 | | Colon | |
1106 | | Eq | |
1107 | | Gt | |
1108 | | BinOp(token::Shr) | |
1109 | | Semi | |
1110 | | BinOp(token::Or) => IsInFollow::Yes, | |
1111 | Ident(name, false) if name == kw::As || name == kw::Where => { | |
1112 | IsInFollow::Yes | |
1113 | } | |
1114 | _ => IsInFollow::No(TOKENS), | |
a1dfa0c6 | 1115 | }, |
b9856134 | 1116 | TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes, |
dc9dc135 | 1117 | _ => IsInFollow::No(TOKENS), |
a1dfa0c6 | 1118 | } |
dc9dc135 | 1119 | } |
3dfed10e | 1120 | NonterminalKind::Ident | NonterminalKind::Lifetime => { |
ff7c6d11 | 1121 | // being a single token, idents and lifetimes are harmless |
a1dfa0c6 | 1122 | IsInFollow::Yes |
dc9dc135 | 1123 | } |
3dfed10e | 1124 | NonterminalKind::Literal => { |
94b46f34 | 1125 | // literals may be of a single token, or two tokens (negative numbers) |
a1dfa0c6 | 1126 | IsInFollow::Yes |
dc9dc135 | 1127 | } |
3dfed10e | 1128 | NonterminalKind::Meta | NonterminalKind::TT => { |
85aaf69f SL |
1129 | // being either a single token or a delimited sequence, tt is |
1130 | // harmless | |
a1dfa0c6 | 1131 | IsInFollow::Yes |
dc9dc135 | 1132 | } |
3dfed10e | 1133 | NonterminalKind::Vis => { |
cc61c64b | 1134 | // Explicitly disallow `priv`, on the off chance it comes back. |
dc9dc135 XL |
1135 | const TOKENS: &[&str] = &["`,`", "an ident", "a type"]; |
1136 | match tok { | |
1137 | TokenTree::Token(token) => match token.kind { | |
a1dfa0c6 | 1138 | Comma => IsInFollow::Yes, |
dc9dc135 XL |
1139 | Ident(name, is_raw) if is_raw || name != kw::Priv => IsInFollow::Yes, |
1140 | _ => { | |
1141 | if token.can_begin_type() { | |
1142 | IsInFollow::Yes | |
1143 | } else { | |
1144 | IsInFollow::No(TOKENS) | |
1145 | } | |
a1dfa0c6 | 1146 | } |
cc61c64b | 1147 | }, |
3dfed10e XL |
1148 | TokenTree::MetaVarDecl( |
1149 | _, | |
1150 | _, | |
b9856134 | 1151 | Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path), |
3dfed10e | 1152 | ) => IsInFollow::Yes, |
dc9dc135 | 1153 | _ => IsInFollow::No(TOKENS), |
cc61c64b | 1154 | } |
dc9dc135 | 1155 | } |
85aaf69f | 1156 | } |
1a4d82fc | 1157 | } |
223e47cc | 1158 | } |
9cc50fc6 | 1159 | |
e74abb32 | 1160 | fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { |
8bb4bdeb | 1161 | match *tt { |
e74abb32 XL |
1162 | mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token), |
1163 | mbe::TokenTree::MetaVar(_, name) => format!("${}", name), | |
b9856134 XL |
1164 | mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind), |
1165 | mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name), | |
dc9dc135 | 1166 | _ => panic!( |
fc512014 XL |
1167 | "{}", |
1168 | "unexpected mbe::TokenTree::{Sequence or Delimited} \ | |
dc9dc135 XL |
1169 | in follow set checker" |
1170 | ), | |
8bb4bdeb XL |
1171 | } |
1172 | } | |
e74abb32 | 1173 | |
ba9703b0 XL |
1174 | fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> { |
1175 | Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS) | |
e74abb32 XL |
1176 | } |
1177 | ||
1178 | /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For | |
1179 | /// other tokens, this is "unexpected token...". | |
1180 | fn parse_failure_msg(tok: &Token) -> String { | |
1181 | match tok.kind { | |
1182 | token::Eof => "unexpected end of macro invocation".to_string(), | |
dfeec247 | 1183 | _ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),), |
e74abb32 XL |
1184 | } |
1185 | } |