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