Line data Source code
1 : // Copyright (C) 2025-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : /* DO NOT INCLUDE ANYWHERE - this is automatically included
20 : * by rust-parse-impl.h
21 : * This is also the reason why there are no include guards. */
22 :
23 : #include "rust-parse.h"
24 :
25 : namespace Rust {
26 :
27 : // Parses a semi-coloned (except for full block) macro invocation item.
28 : template <typename ManagedTokenSource>
29 : std::unique_ptr<AST::MacroInvocation>
30 385 : Parser<ManagedTokenSource>::parse_macro_invocation_semi (
31 : AST::AttrVec outer_attrs)
32 : {
33 385 : location_t macro_locus = lexer.peek_token ()->get_locus ();
34 385 : auto path = parse_simple_path ();
35 385 : if (!path)
36 0 : return nullptr;
37 :
38 385 : if (!skip_token (EXCLAM))
39 : {
40 : // skip after somewhere?
41 0 : return nullptr;
42 : }
43 :
44 : // save delim type to ensure it is reused later
45 385 : AST::DelimType delim_type = AST::PARENS;
46 :
47 : // Map tokens to DelimType
48 385 : const_TokenPtr t = lexer.peek_token ();
49 385 : switch (t->get_id ())
50 : {
51 : case LEFT_PAREN:
52 : delim_type = AST::PARENS;
53 : break;
54 0 : case LEFT_SQUARE:
55 0 : delim_type = AST::SQUARE;
56 0 : break;
57 231 : case LEFT_CURLY:
58 231 : delim_type = AST::CURLY;
59 231 : break;
60 0 : default:
61 0 : add_error (Error (t->get_locus (),
62 : "unexpected token %qs - expecting delimiters (for a "
63 : "macro invocation semi body)",
64 : t->get_token_description ()));
65 :
66 0 : return nullptr;
67 : }
68 385 : location_t tok_tree_locus = t->get_locus ();
69 385 : lexer.skip_token ();
70 :
71 : // parse actual token trees
72 385 : std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
73 385 : auto delim_open
74 385 : = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
75 385 : token_trees.push_back (std::move (delim_open));
76 :
77 385 : t = lexer.peek_token ();
78 : // parse token trees until the initial delimiter token is found again
79 4098 : while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type)
80 4097 : && t->get_id () != END_OF_FILE)
81 : {
82 3713 : auto tree = parse_token_tree ();
83 3713 : if (!tree)
84 1 : return nullptr;
85 :
86 3712 : token_trees.push_back (std::move (tree.value ()));
87 :
88 3712 : t = lexer.peek_token ();
89 : }
90 384 : auto delim_close
91 384 : = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
92 384 : token_trees.push_back (std::move (delim_close));
93 :
94 384 : AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees),
95 : tok_tree_locus);
96 384 : AST::MacroInvocData invoc_data (std::move (path.value ()),
97 : std::move (delim_tok_tree));
98 :
99 : // parse end delimiters
100 384 : t = lexer.peek_token ();
101 384 : if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
102 : {
103 : // tokens match opening delimiter, so skip.
104 384 : lexer.skip_token ();
105 :
106 384 : if (delim_type != AST::CURLY)
107 : {
108 : // skip semicolon at end of non-curly macro invocation semis
109 153 : if (!skip_token (SEMICOLON))
110 : {
111 : // as this is the end, allow recovery (probably) - may change
112 :
113 0 : return AST::MacroInvocation::Regular (std::move (invoc_data),
114 : std::move (outer_attrs),
115 0 : macro_locus, true);
116 : }
117 : }
118 :
119 : // DEBUG:
120 768 : rust_debug ("skipped token is '%s', next token (current peek) is '%s'",
121 : t->get_token_description (),
122 : lexer.peek_token ()->get_token_description ());
123 :
124 768 : return AST::MacroInvocation::Regular (std::move (invoc_data),
125 : std::move (outer_attrs),
126 384 : macro_locus, true);
127 : }
128 : else
129 : {
130 : // tokens don't match opening delimiters, so produce error
131 0 : Error error (t->get_locus (),
132 : "unexpected token %qs - expecting closing delimiter %qs "
133 : "(for a macro invocation semi)",
134 : t->get_token_description (),
135 : (delim_type == AST::PARENS
136 : ? ")"
137 : : (delim_type == AST::SQUARE ? "]" : "}")));
138 0 : add_error (std::move (error));
139 :
140 : /* return empty macro invocation despite possibly parsing mostly valid one
141 : * - TODO is this a good idea? */
142 0 : return nullptr;
143 0 : }
144 770 : }
145 :
146 : // Parses a non-semicoloned macro invocation (i.e. as pattern or expression).
147 : template <typename ManagedTokenSource>
148 : std::unique_ptr<AST::MacroInvocation>
149 362 : Parser<ManagedTokenSource>::parse_macro_invocation (AST::AttrVec outer_attrs)
150 : {
151 : // parse macro path
152 362 : auto macro_path = parse_simple_path ();
153 362 : if (!macro_path)
154 : {
155 272 : Error error (lexer.peek_token ()->get_locus (),
156 : "failed to parse macro invocation path");
157 272 : add_error (std::move (error));
158 :
159 : // skip?
160 272 : return nullptr;
161 272 : }
162 :
163 90 : if (!skip_token (EXCLAM))
164 : {
165 : // skip after somewhere?
166 3 : return nullptr;
167 : }
168 :
169 : // parse internal delim token tree
170 87 : auto delim_tok_tree = parse_delim_token_tree ();
171 87 : if (!delim_tok_tree)
172 0 : return nullptr;
173 :
174 87 : location_t macro_locus = macro_path->get_locus ();
175 :
176 87 : return AST::MacroInvocation::Regular (
177 174 : AST::MacroInvocData (std::move (macro_path.value ()),
178 87 : std::move (delim_tok_tree.value ())),
179 87 : std::move (outer_attrs), macro_locus);
180 87 : }
181 :
182 : // Parses a macro rule definition - does not parse semicolons.
183 : template <typename ManagedTokenSource>
184 : AST::MacroRule
185 1113 : Parser<ManagedTokenSource>::parse_macro_rule ()
186 : {
187 1113 : location_t locus = lexer.peek_token ()->get_locus ();
188 :
189 : // parse macro matcher
190 1113 : AST::MacroMatcher matcher = parse_macro_matcher ();
191 :
192 1113 : if (matcher.is_error ())
193 13 : return AST::MacroRule::create_error (locus);
194 :
195 1100 : if (!skip_token (MATCH_ARROW))
196 : {
197 : // skip after somewhere?
198 0 : return AST::MacroRule::create_error (locus);
199 : }
200 :
201 : // parse transcriber (this is just a delim token tree)
202 1100 : location_t token_tree_loc = lexer.peek_token ()->get_locus ();
203 1100 : auto delim_token_tree = parse_delim_token_tree ();
204 1100 : if (!delim_token_tree)
205 0 : return AST::MacroRule::create_error (token_tree_loc);
206 :
207 1100 : AST::MacroTranscriber transcriber (delim_token_tree.value (), token_tree_loc);
208 :
209 1100 : return AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
210 1100 : }
211 :
212 : // Parses a macro matcher (part of a macro rule definition).
213 : template <typename ManagedTokenSource>
214 : AST::MacroMatcher
215 1201 : Parser<ManagedTokenSource>::parse_macro_matcher ()
216 : {
217 : // save delim type to ensure it is reused later
218 1201 : AST::DelimType delim_type = AST::PARENS;
219 :
220 : // DEBUG
221 1201 : rust_debug ("begun parsing macro matcher");
222 :
223 : // Map tokens to DelimType
224 1201 : const_TokenPtr t = lexer.peek_token ();
225 1201 : location_t locus = t->get_locus ();
226 1201 : switch (t->get_id ())
227 : {
228 : case LEFT_PAREN:
229 : delim_type = AST::PARENS;
230 : break;
231 39 : case LEFT_SQUARE:
232 39 : delim_type = AST::SQUARE;
233 39 : break;
234 16 : case LEFT_CURLY:
235 16 : delim_type = AST::CURLY;
236 16 : break;
237 1 : default:
238 1 : add_error (Error (
239 : t->get_locus (),
240 : "unexpected token %qs - expecting delimiters (for a macro matcher)",
241 : t->get_token_description ()));
242 :
243 1 : return AST::MacroMatcher::create_error (t->get_locus ());
244 : }
245 1200 : lexer.skip_token ();
246 :
247 : // parse actual macro matches
248 1200 : std::vector<std::unique_ptr<AST::MacroMatch>> matches;
249 : // Set of possible preceding macro matches to make sure follow-set
250 : // restrictions are respected.
251 : // TODO: Consider using std::reference_wrapper instead of raw pointers?
252 1200 : std::vector<const AST::MacroMatch *> last_matches;
253 :
254 1200 : t = lexer.peek_token ();
255 : // parse token trees until the initial delimiter token is found again
256 2634 : while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
257 : {
258 1434 : std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
259 :
260 1434 : if (match == nullptr)
261 : {
262 1 : Error error (
263 : t->get_locus (),
264 : "failed to parse macro match for macro matcher - found %qs",
265 : t->get_token_description ());
266 1 : add_error (std::move (error));
267 :
268 1 : return AST::MacroMatcher::create_error (t->get_locus ());
269 1 : }
270 :
271 1433 : if (matches.size () > 0)
272 : {
273 591 : const auto *last_match = matches.back ().get ();
274 :
275 : // We want to check if we are dealing with a zeroable repetition
276 591 : bool zeroable = false;
277 591 : if (last_match->get_macro_match_type ()
278 : == AST::MacroMatch::MacroMatchType::Repetition)
279 : {
280 23 : auto repetition
281 : = static_cast<const AST::MacroMatchRepetition *> (last_match);
282 :
283 23 : if (repetition->get_op ()
284 : != AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE)
285 : zeroable = true;
286 : }
287 :
288 : if (!zeroable)
289 591 : last_matches.clear ();
290 :
291 591 : last_matches.emplace_back (last_match);
292 :
293 1173 : for (auto last : last_matches)
294 594 : if (!is_match_compatible (*last, *match))
295 : return AST::MacroMatcher::create_error (
296 12 : match->get_match_locus ());
297 : }
298 :
299 1421 : matches.push_back (std::move (match));
300 :
301 : // DEBUG
302 1421 : rust_debug ("pushed back a match in macro matcher");
303 :
304 1421 : t = lexer.peek_token ();
305 : }
306 :
307 : // parse end delimiters
308 1187 : t = lexer.peek_token ();
309 1187 : if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
310 : {
311 : // tokens match opening delimiter, so skip.
312 1187 : lexer.skip_token ();
313 :
314 1187 : return AST::MacroMatcher (delim_type, std::move (matches), locus);
315 : }
316 : else
317 : {
318 : // tokens don't match opening delimiters, so produce error
319 0 : Error error (t->get_locus (),
320 : "unexpected token %qs - expecting closing delimiter %qs "
321 : "(for a macro matcher)",
322 : t->get_token_description (),
323 : (delim_type == AST::PARENS
324 : ? ")"
325 : : (delim_type == AST::SQUARE ? "]" : "}")));
326 0 : add_error (std::move (error));
327 :
328 : /* return error macro matcher despite possibly parsing mostly correct one?
329 : * TODO is this the best idea? */
330 0 : return AST::MacroMatcher::create_error (t->get_locus ());
331 0 : }
332 1200 : }
333 :
334 : // Parses a macro match (syntax match inside a matcher in a macro rule).
335 : template <typename ManagedTokenSource>
336 : std::unique_ptr<AST::MacroMatch>
337 2098 : Parser<ManagedTokenSource>::parse_macro_match ()
338 : {
339 : // branch based on token available
340 2098 : const_TokenPtr t = lexer.peek_token ();
341 2098 : switch (t->get_id ())
342 : {
343 68 : case LEFT_PAREN:
344 : case LEFT_SQUARE:
345 : case LEFT_CURLY:
346 : {
347 : // must be macro matcher as delimited
348 68 : AST::MacroMatcher matcher = parse_macro_matcher ();
349 68 : if (matcher.is_error ())
350 : {
351 1 : Error error (lexer.peek_token ()->get_locus (),
352 : "failed to parse macro matcher in macro match");
353 1 : add_error (std::move (error));
354 :
355 1 : return nullptr;
356 1 : }
357 67 : return std::unique_ptr<AST::MacroMatcher> (
358 67 : new AST::MacroMatcher (std::move (matcher)));
359 68 : }
360 1502 : case DOLLAR_SIGN:
361 : {
362 : // have to do more lookahead to determine if fragment or repetition
363 1502 : const_TokenPtr t2 = lexer.peek_token (1);
364 1502 : switch (t2->get_id ())
365 : {
366 978 : case IDENTIFIER:
367 : case UNDERSCORE:
368 : // macro fragment
369 978 : return parse_macro_match_fragment ();
370 523 : case LEFT_PAREN:
371 : // macro repetition
372 523 : return parse_macro_match_repetition ();
373 1 : default:
374 1 : if (token_id_is_keyword (t2->get_id ()) && t2->get_id () != CRATE)
375 : {
376 : // keyword as macro fragment
377 1 : return parse_macro_match_fragment ();
378 : }
379 : else
380 : {
381 : // error: unrecognised
382 0 : add_error (Error (
383 : t2->get_locus (),
384 : "unrecognised token combination %<$%s%> at start of "
385 : "macro match - did you mean %<$identifier%> or %<$(%>?",
386 : t2->get_token_description ()));
387 :
388 : // skip somewhere?
389 0 : return nullptr;
390 : }
391 : }
392 1502 : }
393 0 : case RIGHT_PAREN:
394 : case RIGHT_SQUARE:
395 : case RIGHT_CURLY:
396 : // not allowed
397 0 : add_error (Error (
398 : t->get_locus (),
399 : "closing delimiters like %qs are not allowed at the start of a macro "
400 : "match",
401 : t->get_token_description ()));
402 :
403 : // skip somewhere?
404 0 : return nullptr;
405 528 : default:
406 : // just the token
407 528 : lexer.skip_token ();
408 528 : return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
409 : }
410 2098 : }
411 :
412 : // Parses a fragment macro match.
413 : template <typename ManagedTokenSource>
414 : std::unique_ptr<AST::MacroMatchFragment>
415 979 : Parser<ManagedTokenSource>::parse_macro_match_fragment ()
416 : {
417 979 : location_t fragment_locus = lexer.peek_token ()->get_locus ();
418 979 : skip_token (DOLLAR_SIGN);
419 :
420 979 : Identifier ident;
421 979 : auto identifier = lexer.peek_token ();
422 979 : if (identifier->get_id () == UNDERSCORE)
423 4 : ident = {Values::Keywords::UNDERSCORE, identifier->get_locus ()};
424 : else
425 1954 : ident = {identifier};
426 :
427 979 : if (ident.empty ())
428 : {
429 0 : Error error (lexer.peek_token ()->get_locus (),
430 : "missing identifier in macro match fragment");
431 0 : add_error (std::move (error));
432 :
433 0 : return nullptr;
434 0 : }
435 979 : skip_token (identifier->get_id ());
436 :
437 979 : if (!skip_token (COLON))
438 : {
439 : // skip after somewhere?
440 0 : return nullptr;
441 : }
442 :
443 : // get MacroFragSpec for macro
444 979 : const_TokenPtr t = expect_token (IDENTIFIER);
445 979 : if (t == nullptr)
446 0 : return nullptr;
447 :
448 : AST::MacroFragSpec frag
449 979 : = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ());
450 979 : if (frag.is_error ())
451 : {
452 0 : Error error (t->get_locus (),
453 : "invalid fragment specifier %qs in fragment macro match",
454 0 : t->get_str ().c_str ());
455 0 : add_error (std::move (error));
456 :
457 0 : return nullptr;
458 0 : }
459 :
460 : return std::unique_ptr<AST::MacroMatchFragment> (
461 979 : new AST::MacroMatchFragment (std::move (ident), frag, fragment_locus));
462 1958 : }
463 :
464 : // Parses a repetition macro match.
465 : template <typename ManagedTokenSource>
466 : std::unique_ptr<AST::MacroMatchRepetition>
467 523 : Parser<ManagedTokenSource>::parse_macro_match_repetition ()
468 : {
469 523 : skip_token (DOLLAR_SIGN);
470 523 : skip_token (LEFT_PAREN);
471 :
472 523 : std::vector<std::unique_ptr<AST::MacroMatch>> matches;
473 :
474 : // parse required first macro match
475 523 : std::unique_ptr<AST::MacroMatch> initial_match = parse_macro_match ();
476 523 : if (initial_match == nullptr)
477 : {
478 0 : Error error (
479 0 : lexer.peek_token ()->get_locus (),
480 : "could not parse required first macro match in macro match repetition");
481 0 : add_error (std::move (error));
482 :
483 : // skip after somewhere?
484 0 : return nullptr;
485 0 : }
486 523 : matches.push_back (std::move (initial_match));
487 :
488 : // parse optional later macro matches
489 523 : const_TokenPtr t = lexer.peek_token ();
490 664 : while (t->get_id () != RIGHT_PAREN)
491 : {
492 141 : std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
493 :
494 141 : if (match == nullptr)
495 : {
496 0 : Error error (lexer.peek_token ()->get_locus (),
497 : "failed to parse macro match in macro match repetition");
498 0 : add_error (std::move (error));
499 :
500 0 : return nullptr;
501 0 : }
502 :
503 141 : matches.push_back (std::move (match));
504 :
505 141 : t = lexer.peek_token ();
506 : }
507 :
508 523 : if (!skip_token (RIGHT_PAREN))
509 : {
510 : // skip after somewhere?
511 0 : return nullptr;
512 : }
513 :
514 523 : t = lexer.peek_token ();
515 : // see if separator token exists
516 523 : std::unique_ptr<AST::Token> separator = nullptr;
517 523 : switch (t->get_id ())
518 : {
519 : // repetition operators
520 : case ASTERISK:
521 : case PLUS:
522 : case QUESTION_MARK:
523 : // delimiters
524 : case LEFT_PAREN:
525 : case LEFT_CURLY:
526 : case LEFT_SQUARE:
527 : case RIGHT_PAREN:
528 : case RIGHT_CURLY:
529 : case RIGHT_SQUARE:
530 : // separator does not exist, so still null and don't skip token
531 : break;
532 135 : default:
533 : // separator does exist
534 135 : separator = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
535 135 : lexer.skip_token ();
536 : break;
537 : }
538 :
539 : // parse repetition operator
540 523 : t = lexer.peek_token ();
541 523 : AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE;
542 523 : switch (t->get_id ())
543 : {
544 441 : case ASTERISK:
545 441 : op = AST::MacroMatchRepetition::ANY;
546 441 : lexer.skip_token ();
547 : break;
548 42 : case PLUS:
549 42 : op = AST::MacroMatchRepetition::ONE_OR_MORE;
550 42 : lexer.skip_token ();
551 : break;
552 40 : case QUESTION_MARK:
553 40 : op = AST::MacroMatchRepetition::ZERO_OR_ONE;
554 40 : lexer.skip_token ();
555 :
556 40 : if (separator != nullptr)
557 : {
558 1 : add_error (
559 2 : Error (separator->get_locus (),
560 : "the %<?%> macro repetition operator does not take a "
561 : "separator"));
562 1 : separator = nullptr;
563 : }
564 :
565 : break;
566 0 : default:
567 0 : add_error (
568 0 : Error (t->get_locus (),
569 : "expected macro repetition operator (%<*%>, %<+%>, or %<?%>) in "
570 : "macro match - found %qs",
571 : t->get_token_description ()));
572 :
573 : // skip after somewhere?
574 0 : return nullptr;
575 : }
576 :
577 : return std::unique_ptr<AST::MacroMatchRepetition> (
578 523 : new AST::MacroMatchRepetition (std::move (matches), op,
579 523 : std::move (separator), t->get_locus ()));
580 1046 : }
581 :
582 : /* Parses a macro invocation with a path in expression already parsed (but not
583 : * '!' token). */
584 : template <typename ManagedTokenSource>
585 : std::unique_ptr<AST::MacroInvocation>
586 2462 : Parser<ManagedTokenSource>::parse_macro_invocation_partial (
587 : AST::PathInExpression path, AST::AttrVec outer_attrs,
588 : ParseRestrictions restrictions)
589 : {
590 : // macro invocation
591 2462 : if (!skip_token (EXCLAM))
592 : {
593 0 : return nullptr;
594 : }
595 :
596 : // convert PathInExpression to SimplePath - if this isn't possible, error
597 2462 : AST::SimplePath converted_path = path.as_simple_path ();
598 2462 : if (converted_path.is_empty ())
599 : {
600 0 : Error error (lexer.peek_token ()->get_locus (),
601 : "failed to parse simple path in macro invocation");
602 0 : add_error (std::move (error));
603 :
604 0 : return nullptr;
605 0 : }
606 :
607 2462 : auto tok_tree = parse_delim_token_tree ();
608 2462 : if (!tok_tree)
609 3 : return nullptr;
610 :
611 2459 : rust_debug ("successfully parsed macro invocation (via partial)");
612 :
613 2459 : location_t macro_locus = converted_path.get_locus ();
614 :
615 2459 : return AST::MacroInvocation::Regular (
616 4918 : AST::MacroInvocData (std::move (converted_path),
617 2459 : std::move (tok_tree.value ())),
618 2459 : std::move (outer_attrs), macro_locus);
619 4924 : }
620 :
621 : } // namespace Rust
|