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 384 : Parser<ManagedTokenSource>::parse_macro_invocation_semi (
31 : AST::AttrVec outer_attrs)
32 : {
33 384 : location_t macro_locus = lexer.peek_token ()->get_locus ();
34 384 : auto path = parse_simple_path ();
35 384 : if (!path)
36 0 : return nullptr;
37 :
38 384 : 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 384 : AST::DelimType delim_type = AST::PARENS;
46 :
47 : // Map tokens to DelimType
48 384 : const_TokenPtr t = lexer.peek_token ();
49 384 : 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 384 : location_t tok_tree_locus = t->get_locus ();
69 384 : lexer.skip_token ();
70 :
71 : // parse actual token trees
72 384 : std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
73 384 : auto delim_open
74 384 : = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
75 384 : token_trees.push_back (std::move (delim_open));
76 :
77 384 : t = lexer.peek_token ();
78 : // parse token trees until the initial delimiter token is found again
79 4094 : while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type)
80 4093 : && t->get_id () != END_OF_FILE)
81 : {
82 3710 : auto tree = parse_token_tree ();
83 3710 : if (!tree)
84 1 : return nullptr;
85 :
86 3709 : token_trees.push_back (std::move (tree.value ()));
87 :
88 3709 : t = lexer.peek_token ();
89 : }
90 383 : auto delim_close
91 383 : = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
92 383 : token_trees.push_back (std::move (delim_close));
93 :
94 383 : AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees),
95 : tok_tree_locus);
96 383 : AST::MacroInvocData invoc_data (std::move (path.value ()),
97 : std::move (delim_tok_tree));
98 :
99 : // parse end delimiters
100 383 : t = lexer.peek_token ();
101 383 : if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
102 : {
103 : // tokens match opening delimiter, so skip.
104 383 : lexer.skip_token ();
105 :
106 383 : if (delim_type != AST::CURLY)
107 : {
108 : // skip semicolon at end of non-curly macro invocation semis
109 152 : 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 766 : 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 766 : return AST::MacroInvocation::Regular (std::move (invoc_data),
125 : std::move (outer_attrs),
126 383 : 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 768 : }
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 360 : Parser<ManagedTokenSource>::parse_macro_invocation (AST::AttrVec outer_attrs)
150 : {
151 : // parse macro path
152 360 : auto macro_path = parse_simple_path ();
153 360 : 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 88 : if (!skip_token (EXCLAM))
164 : {
165 : // skip after somewhere?
166 3 : return nullptr;
167 : }
168 :
169 : // parse internal delim token tree
170 85 : auto delim_tok_tree = parse_delim_token_tree ();
171 85 : if (!delim_tok_tree)
172 0 : return nullptr;
173 :
174 85 : location_t macro_locus = macro_path->get_locus ();
175 :
176 85 : return AST::MacroInvocation::Regular (
177 170 : AST::MacroInvocData (std::move (macro_path.value ()),
178 85 : std::move (delim_tok_tree.value ())),
179 85 : std::move (outer_attrs), macro_locus);
180 85 : }
181 :
182 : // Parses a macro rule definition - does not parse semicolons.
183 : template <typename ManagedTokenSource>
184 : AST::MacroRule
185 1105 : Parser<ManagedTokenSource>::parse_macro_rule ()
186 : {
187 1105 : location_t locus = lexer.peek_token ()->get_locus ();
188 :
189 : // parse macro matcher
190 1105 : AST::MacroMatcher matcher = parse_macro_matcher ();
191 :
192 1105 : if (matcher.is_error ())
193 13 : return AST::MacroRule::create_error (locus);
194 :
195 1092 : 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 1092 : location_t token_tree_loc = lexer.peek_token ()->get_locus ();
203 1092 : auto delim_token_tree = parse_delim_token_tree ();
204 1092 : if (!delim_token_tree)
205 0 : return AST::MacroRule::create_error (token_tree_loc);
206 :
207 1092 : AST::MacroTranscriber transcriber (delim_token_tree.value (), token_tree_loc);
208 :
209 1092 : return AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
210 1092 : }
211 :
212 : // Parses a macro matcher (part of a macro rule definition).
213 : template <typename ManagedTokenSource>
214 : AST::MacroMatcher
215 1192 : Parser<ManagedTokenSource>::parse_macro_matcher ()
216 : {
217 : // save delim type to ensure it is reused later
218 1192 : AST::DelimType delim_type = AST::PARENS;
219 :
220 : // DEBUG
221 1192 : rust_debug ("begun parsing macro matcher");
222 :
223 : // Map tokens to DelimType
224 1192 : const_TokenPtr t = lexer.peek_token ();
225 1192 : location_t locus = t->get_locus ();
226 1192 : 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 15 : case LEFT_CURLY:
235 15 : delim_type = AST::CURLY;
236 15 : 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 1191 : lexer.skip_token ();
246 :
247 : // parse actual macro matches
248 1191 : 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 1191 : std::vector<const AST::MacroMatch *> last_matches;
253 :
254 1191 : t = lexer.peek_token ();
255 : // parse token trees until the initial delimiter token is found again
256 2606 : while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
257 : {
258 1415 : std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
259 :
260 1415 : 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 1414 : if (matches.size () > 0)
272 : {
273 580 : const auto *last_match = matches.back ().get ();
274 :
275 : // We want to check if we are dealing with a zeroable repetition
276 580 : bool zeroable = false;
277 580 : 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 580 : last_matches.clear ();
290 :
291 580 : last_matches.emplace_back (last_match);
292 :
293 1151 : for (auto last : last_matches)
294 583 : if (!is_match_compatible (*last, *match))
295 : return AST::MacroMatcher::create_error (
296 12 : match->get_match_locus ());
297 : }
298 :
299 1402 : matches.push_back (std::move (match));
300 :
301 : // DEBUG
302 1402 : rust_debug ("pushed back a match in macro matcher");
303 :
304 1402 : t = lexer.peek_token ();
305 : }
306 :
307 : // parse end delimiters
308 1178 : t = lexer.peek_token ();
309 1178 : if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
310 : {
311 : // tokens match opening delimiter, so skip.
312 1178 : lexer.skip_token ();
313 :
314 1178 : 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 1191 : }
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 2071 : Parser<ManagedTokenSource>::parse_macro_match ()
338 : {
339 : // branch based on token available
340 2071 : const_TokenPtr t = lexer.peek_token ();
341 2071 : switch (t->get_id ())
342 : {
343 67 : case LEFT_PAREN:
344 : case LEFT_SQUARE:
345 : case LEFT_CURLY:
346 : {
347 : // must be macro matcher as delimited
348 67 : AST::MacroMatcher matcher = parse_macro_matcher ();
349 67 : 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 66 : return std::unique_ptr<AST::MacroMatcher> (
358 66 : new AST::MacroMatcher (std::move (matcher)));
359 67 : }
360 1484 : case DOLLAR_SIGN:
361 : {
362 : // have to do more lookahead to determine if fragment or repetition
363 1484 : const_TokenPtr t2 = lexer.peek_token (1);
364 1484 : switch (t2->get_id ())
365 : {
366 967 : case IDENTIFIER:
367 : case UNDERSCORE:
368 : // macro fragment
369 967 : return parse_macro_match_fragment ();
370 516 : case LEFT_PAREN:
371 : // macro repetition
372 516 : 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 1484 : }
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 520 : default:
406 : // just the token
407 520 : lexer.skip_token ();
408 520 : return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
409 : }
410 2071 : }
411 :
412 : // Parses a fragment macro match.
413 : template <typename ManagedTokenSource>
414 : std::unique_ptr<AST::MacroMatchFragment>
415 968 : Parser<ManagedTokenSource>::parse_macro_match_fragment ()
416 : {
417 968 : location_t fragment_locus = lexer.peek_token ()->get_locus ();
418 968 : skip_token (DOLLAR_SIGN);
419 :
420 968 : Identifier ident;
421 968 : auto identifier = lexer.peek_token ();
422 968 : if (identifier->get_id () == UNDERSCORE)
423 4 : ident = {Values::Keywords::UNDERSCORE, identifier->get_locus ()};
424 : else
425 1932 : ident = {identifier};
426 :
427 968 : 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 968 : skip_token (identifier->get_id ());
436 :
437 968 : if (!skip_token (COLON))
438 : {
439 : // skip after somewhere?
440 0 : return nullptr;
441 : }
442 :
443 : // get MacroFragSpec for macro
444 968 : const_TokenPtr t = expect_token (IDENTIFIER);
445 968 : if (t == nullptr)
446 0 : return nullptr;
447 :
448 : AST::MacroFragSpec frag
449 968 : = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ());
450 968 : 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 968 : new AST::MacroMatchFragment (std::move (ident), frag, fragment_locus));
462 1936 : }
463 :
464 : // Parses a repetition macro match.
465 : template <typename ManagedTokenSource>
466 : std::unique_ptr<AST::MacroMatchRepetition>
467 516 : Parser<ManagedTokenSource>::parse_macro_match_repetition ()
468 : {
469 516 : skip_token (DOLLAR_SIGN);
470 516 : skip_token (LEFT_PAREN);
471 :
472 516 : std::vector<std::unique_ptr<AST::MacroMatch>> matches;
473 :
474 : // parse required first macro match
475 516 : std::unique_ptr<AST::MacroMatch> initial_match = parse_macro_match ();
476 516 : 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 516 : matches.push_back (std::move (initial_match));
487 :
488 : // parse optional later macro matches
489 516 : const_TokenPtr t = lexer.peek_token ();
490 656 : while (t->get_id () != RIGHT_PAREN)
491 : {
492 140 : std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
493 :
494 140 : 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 140 : matches.push_back (std::move (match));
504 :
505 140 : t = lexer.peek_token ();
506 : }
507 :
508 516 : if (!skip_token (RIGHT_PAREN))
509 : {
510 : // skip after somewhere?
511 0 : return nullptr;
512 : }
513 :
514 516 : t = lexer.peek_token ();
515 : // see if separator token exists
516 516 : std::unique_ptr<AST::Token> separator = nullptr;
517 516 : 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 516 : t = lexer.peek_token ();
541 516 : AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE;
542 516 : switch (t->get_id ())
543 : {
544 440 : case ASTERISK:
545 440 : op = AST::MacroMatchRepetition::ANY;
546 440 : lexer.skip_token ();
547 : break;
548 39 : case PLUS:
549 39 : op = AST::MacroMatchRepetition::ONE_OR_MORE;
550 39 : lexer.skip_token ();
551 : break;
552 37 : case QUESTION_MARK:
553 37 : op = AST::MacroMatchRepetition::ZERO_OR_ONE;
554 37 : lexer.skip_token ();
555 :
556 37 : 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 516 : new AST::MacroMatchRepetition (std::move (matches), op,
579 516 : std::move (separator), t->get_locus ()));
580 1032 : }
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 2457 : Parser<ManagedTokenSource>::parse_macro_invocation_partial (
587 : AST::PathInExpression path, AST::AttrVec outer_attrs,
588 : ParseRestrictions restrictions)
589 : {
590 : // macro invocation
591 2457 : if (!skip_token (EXCLAM))
592 : {
593 0 : return nullptr;
594 : }
595 :
596 : // convert PathInExpression to SimplePath - if this isn't possible, error
597 2457 : AST::SimplePath converted_path = path.as_simple_path ();
598 2457 : 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 2457 : auto tok_tree = parse_delim_token_tree ();
608 2457 : if (!tok_tree)
609 2 : return nullptr;
610 :
611 2455 : rust_debug ("successfully parsed macro invocation (via partial)");
612 :
613 2455 : location_t macro_locus = converted_path.get_locus ();
614 :
615 2455 : return AST::MacroInvocation::Regular (
616 4910 : AST::MacroInvocData (std::move (converted_path),
617 2455 : std::move (tok_tree.value ())),
618 2455 : std::move (outer_attrs), macro_locus);
619 4914 : }
620 :
621 : } // namespace Rust
|