]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/ast/rust-ast-dump.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / ast / rust-ast-dump.cc
1 // Copyright (C) 2020-2024 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 #include "rust-ast-dump.h"
20
21 namespace Rust {
22 namespace AST {
23
24 Indent::Indent () : tabs (0) {}
25
26 std::ostream &
27 operator<< (std::ostream &stream, const Indent &indent)
28 {
29 return stream << std::string (indent.tabs, '\t');
30 }
31
32 void
33 Indent::increment ()
34 {
35 tabs++;
36 }
37
38 void
39 Indent::decrement ()
40 {
41 rust_assert (tabs != 0);
42 tabs--;
43 }
44
45 Dump::Dump (std::ostream &stream) : stream (stream), indentation (Indent ()) {}
46
47 void
48 Dump::go (AST::Crate &crate)
49 {
50 visit_items_as_lines (crate.items, "");
51 }
52
53 void
54 Dump::go (AST::Item &item)
55 {
56 item.accept_vis (*this);
57 }
58
59 template <typename T>
60 void
61 Dump::visit (std::unique_ptr<T> &node)
62 {
63 node->accept_vis (*this);
64 }
65
66 template <typename T>
67 void
68 Dump::visit (T &node)
69 {
70 node.accept_vis (*this);
71 }
72
73 template <typename T>
74 void
75 Dump::visit_items_joined_by_separator (T &collection,
76 const std::string &separator,
77 size_t start_offset, size_t end_offset)
78 {
79 if (collection.size () > start_offset)
80 {
81 visit (collection.at (start_offset));
82 auto size = collection.size () - end_offset;
83 for (size_t i = start_offset + 1; i < size; i++)
84 {
85 stream << separator;
86 visit (collection.at (i));
87 }
88 }
89 }
90
91 template <typename T>
92 void
93 Dump::visit_as_line (T &item, const std::string &trailing)
94 {
95 stream << indentation;
96 visit (item);
97 stream << trailing << '\n';
98 }
99
100 template <typename T>
101 void
102 Dump::visit_items_as_lines (T &collection, const std::string &trailing)
103 {
104 for (auto &item : collection)
105 visit_as_line (item, trailing);
106 }
107
108 template <typename T>
109 void
110 Dump::visit_items_as_block (T &collection, const std::string &line_trailing,
111 char left_brace, char right_brace)
112 {
113 if (collection.empty ())
114 {
115 stream << left_brace << right_brace << '\n';
116 }
117 else
118 {
119 stream << left_brace << '\n';
120
121 indentation.increment ();
122 visit_items_as_lines (collection, line_trailing);
123 indentation.decrement ();
124
125 stream << indentation << right_brace << '\n';
126 }
127 }
128
129 void
130 Dump::visit (FunctionParam &param)
131 {
132 visit (param.get_pattern ());
133 stream << ": ";
134 visit (param.get_type ());
135 }
136
137 void
138 Dump::visit (Attribute &attrib)
139 {
140 stream << "#[";
141 visit_items_joined_by_separator (attrib.get_path ().get_segments (), "::");
142
143 if (attrib.has_attr_input ())
144 {
145 stream << " = ";
146
147 bool is_literal = attrib.get_attr_input ().get_attr_input_type ()
148 == AST::AttrInput::AttrInputType::LITERAL;
149 if (is_literal)
150 {
151 auto &literal
152 = static_cast<AST::AttrInputLiteral &> (attrib.get_attr_input ());
153 const auto &value = literal.get_literal ().as_string ();
154
155 stream << "\"" << value << "\"";
156 }
157 else
158 {
159 stream << "FIXME";
160 }
161 }
162
163 stream << "]";
164 }
165
166 void
167 Dump::visit (SimplePathSegment &segment)
168 {
169 stream << segment.get_segment_name ();
170 }
171
172 void
173 Dump::visit (Visibility &vis)
174 {
175 switch (vis.get_vis_type ())
176 {
177 case Visibility::PUB:
178 stream << "pub ";
179 break;
180 case Visibility::PUB_CRATE:
181 stream << "pub(crate) ";
182 break;
183 case Visibility::PUB_SELF:
184 stream << "pub(self) ";
185 break;
186 case Visibility::PUB_SUPER:
187 stream << "pub(super) ";
188 break;
189 case Visibility::PUB_IN_PATH:
190 stream << "pub(in " << vis.get_path ().as_string () << ") ";
191 break;
192 case Visibility::PRIV:
193 break;
194 }
195 }
196
197 void
198 Dump::visit (NamedFunctionParam &param)
199 {
200 stream << param.get_name () << ": ";
201 visit (param.get_type ());
202 }
203
204 void
205 Dump::visit (std::vector<std::unique_ptr<GenericParam>> &params)
206 {
207 stream << "<";
208 visit_items_joined_by_separator (params, ", ");
209 stream << ">";
210 }
211
212 void
213 Dump::visit (TupleField &field)
214 {
215 // TODO: do we need to emit outer attrs here?
216 visit (field.get_visibility ());
217 visit (field.get_field_type ());
218 }
219
220 void
221 Dump::visit (StructField &field)
222 {
223 // TODO: do we need to emit outer attrs here?
224 visit (field.get_visibility ());
225 stream << field.get_field_name () << ": ";
226 visit (field.get_field_type ());
227 }
228
229 // TODO is this unique by type?
230 void
231 Dump::visit (std::vector<LifetimeParam> &for_lifetimes)
232 {
233 // ForLifetimes :
234 // for GenericParams
235 //
236 // GenericParams :
237 // < >
238 // | < (GenericParam ,)* GenericParam ,? >
239 //
240 // GenericParam :
241 // OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )
242 //
243 // LifetimeParam :
244 // LIFETIME_OR_LABEL ( : LifetimeBounds )?
245
246 stream << "for <";
247 visit_items_joined_by_separator (for_lifetimes, " + ");
248 stream << "> ";
249 }
250
251 void
252 Dump::visit (FunctionQualifiers &qualifiers)
253 {
254 // Syntax:
255 // `const`? `async`? `unsafe`? (`extern` Abi?)?
256 // unsafe? (extern Abi?)?
257
258 switch (qualifiers.get_const_status ())
259 {
260 case NONE:
261 break;
262 case CONST_FN:
263 stream << "const ";
264 break;
265 case ASYNC_FN:
266 stream << "async ";
267 break;
268 }
269
270 if (qualifiers.is_unsafe ())
271 stream << "unsafe ";
272 if (qualifiers.is_extern ())
273 {
274 stream << "extern ";
275 if (qualifiers.has_abi ())
276 stream << "\"" << qualifiers.get_extern_abi () << "\" ";
277 }
278 } // namespace AST
279
280 void
281 Dump::visit (MaybeNamedParam &param)
282 {
283 // Syntax:
284 // OuterAttribute* ( ( IDENTIFIER | _ ) : )? Type
285
286 visit_items_joined_by_separator (param.get_outer_attrs (), " ");
287 switch (param.get_param_kind ())
288 {
289 case MaybeNamedParam::UNNAMED:
290 break;
291 case MaybeNamedParam::IDENTIFIER:
292 stream << " " << param.get_name () << ": ";
293 break;
294 case MaybeNamedParam::WILDCARD:
295 stream << " _: ";
296 break;
297 }
298 visit (param.get_type ());
299 }
300
301 void
302 Dump::visit (Token &tok)
303 {
304 stream << tok.as_string ();
305 }
306
307 void
308 Dump::visit (DelimTokenTree &delim_tok_tree)
309 {
310 indentation.increment ();
311 stream << '\n' << indentation;
312
313 auto tokens = delim_tok_tree.to_token_stream ();
314 visit_items_joined_by_separator (tokens, " ");
315
316 indentation.decrement ();
317 stream << '\n' << indentation;
318 }
319
320 void
321 Dump::visit (AttrInputMetaItemContainer &)
322 {}
323
324 void
325 Dump::visit (IdentifierExpr &ident_expr)
326 {
327 stream << ident_expr.get_ident ();
328 }
329
330 void
331 Dump::visit (Lifetime &lifetime)
332 {
333 // Syntax:
334 // Lifetime :
335 // LIFETIME_OR_LABEL
336 // | 'static
337 // | '_
338 stream << lifetime.as_string ();
339 }
340
341 void
342 Dump::visit (LifetimeParam &lifetime_param)
343 {
344 // Syntax:
345 // LIFETIME_OR_LABEL ( : LifetimeBounds )?
346 // LifetimeBounds :
347 // ( Lifetime + )* Lifetime?
348
349 // TODO what to do with outer attr? They are not mentioned in the reference.
350
351 auto lifetime = lifetime_param.get_lifetime ();
352 visit (lifetime);
353
354 if (lifetime_param.has_lifetime_bounds ())
355 {
356 stream << ": ";
357 visit_items_joined_by_separator (lifetime_param.get_lifetime_bounds (),
358 " + ");
359 }
360 }
361
362 void
363 Dump::visit (ConstGenericParam &)
364 {}
365
366 // rust-path.h
367 void
368 Dump::visit (PathInExpression &path)
369 {
370 stream << path.as_string ();
371 }
372
373 void
374 Dump::visit (TypePathSegment &segment)
375 {
376 // Syntax:
377 // PathIdentSegment
378
379 stream << segment.get_ident_segment ().as_string ();
380 }
381
382 void
383 Dump::visit (TypePathSegmentGeneric &segment)
384 {
385 // Syntax:
386 // PathIdentSegment `::`? (GenericArgs)?
387 // GenericArgs :
388 // `<` `>`
389 // | `<` ( GenericArg `,` )* GenericArg `,`? `>`
390
391 stream << segment.get_ident_segment ().as_string ();
392
393 if (segment.get_separating_scope_resolution ())
394 stream << "::";
395
396 stream << "<";
397
398 {
399 // Here we join 3 lists (each possibly empty) with a separator.
400
401 auto &lifetime_args = segment.get_generic_args ().get_lifetime_args ();
402 auto &generic_args = segment.get_generic_args ().get_generic_args ();
403 auto &binding_args = segment.get_generic_args ().get_binding_args ();
404
405 visit_items_joined_by_separator (lifetime_args, ", ");
406 if (!lifetime_args.empty ()
407 && (!generic_args.empty () || !binding_args.empty ()))
408 {
409 // Insert separator if some items have been already emitted and some
410 // more are to be emitted from any of the following collections.
411 stream << ", ";
412 }
413 visit_items_joined_by_separator (generic_args, ", ");
414 if (!generic_args.empty () && !binding_args.empty ())
415 {
416 // Insert separator if some item vas emitted from the previous
417 // collection and more are to be emitted from the last.
418 stream << ", ";
419 }
420 visit_items_joined_by_separator (binding_args, ", ");
421 }
422
423 stream << ">";
424 }
425
426 void
427 Dump::visit (GenericArgsBinding &binding)
428 {
429 // Syntax:
430 // IDENTIFIER `=` Type
431
432 stream << binding.get_identifier () << " << ";
433 visit (binding.get_type ());
434 }
435
436 void
437 Dump::visit (GenericArg &arg)
438 {
439 // `GenericArg` implements `accept_vis` but it is not useful for this case as
440 // it ignores unresolved cases (`Kind::Either`).
441
442 switch (arg.get_kind ())
443 {
444 case GenericArg::Kind::Const:
445 visit (arg.get_expression ());
446 break;
447 case GenericArg::Kind::Type:
448 visit (arg.get_type ());
449 break;
450 case GenericArg::Kind::Either:
451 stream << arg.get_path ();
452 break;
453 case GenericArg::Kind::Error:
454 gcc_unreachable ();
455 }
456 } // namespace AST
457
458 void
459 Dump::visit (TypePathSegmentFunction &segment)
460 {
461 // Syntax:
462 // PathIdentSegment `::`? (TypePathFn)?
463
464 stream << segment.get_ident_segment ().as_string ();
465
466 if (segment.get_separating_scope_resolution ())
467 stream << "::";
468
469 if (!segment.is_ident_only ())
470 visit (segment.get_type_path_function ());
471 }
472
473 void
474 Dump::visit (TypePathFunction &type_path_fn)
475 {
476 // Syntax:
477 // `(` TypePathFnInputs? `)` (`->` Type)?
478 // TypePathFnInputs :
479 // Type (`,` Type)* `,`?
480
481 stream << '(';
482 if (type_path_fn.has_inputs ())
483 visit_items_joined_by_separator (type_path_fn.get_params (), ", ");
484 stream << ')';
485
486 if (type_path_fn.has_return_type ())
487 {
488 stream << "->";
489 visit (type_path_fn.get_return_type ());
490 }
491 }
492
493 void
494 Dump::visit (TypePath &path)
495 {
496 // Syntax:
497 // `::`? TypePathSegment (`::` TypePathSegment)*
498
499 if (path.has_opening_scope_resolution_op ())
500 stream << "::";
501 visit_items_joined_by_separator (path.get_segments (), "::");
502 }
503
504 void
505 Dump::visit (QualifiedPathInExpression &path)
506 {
507 stream << path.as_string ();
508 }
509
510 void
511 Dump::visit (QualifiedPathInType &)
512 {}
513
514 // rust-expr.h
515 void
516 Dump::visit (LiteralExpr &expr)
517 {
518 stream << expr.as_string ();
519 }
520
521 void
522 Dump::visit (AttrInputLiteral &)
523 {}
524
525 void
526 Dump::visit (MetaItemLitExpr &)
527 {}
528
529 void
530 Dump::visit (MetaItemPathLit &)
531 {}
532
533 void
534 Dump::visit (BorrowExpr &expr)
535 {
536 stream << '&';
537 if (expr.get_is_double_borrow ())
538 stream << '&';
539 if (expr.get_is_mut ())
540 stream << "mut ";
541
542 visit (expr.get_borrowed_expr ());
543 }
544
545 void
546 Dump::visit (DereferenceExpr &expr)
547 {
548 stream << '*';
549 visit (expr.get_dereferenced_expr ());
550 }
551
552 void
553 Dump::visit (ErrorPropagationExpr &expr)
554 {
555 visit (expr.get_propagating_expr ());
556 stream << '?';
557 }
558
559 void
560 Dump::visit (NegationExpr &expr)
561 {
562 switch (expr.get_expr_type ())
563 {
564 case NegationOperator::NEGATE:
565 stream << '-';
566 break;
567 case NegationOperator::NOT:
568 stream << '!';
569 break;
570 }
571 visit (expr.get_negated_expr ());
572 }
573
574 void
575 Dump::visit (ArithmeticOrLogicalExpr &expr)
576 {
577 auto op = "";
578 switch (expr.get_expr_type ())
579 {
580 case ArithmeticOrLogicalOperator::ADD:
581 op = "+";
582 break;
583
584 case ArithmeticOrLogicalOperator::SUBTRACT:
585 op = "-";
586 break;
587
588 case ArithmeticOrLogicalOperator::MULTIPLY:
589 op = "*";
590 break;
591
592 case ArithmeticOrLogicalOperator::DIVIDE:
593 op = "/";
594 break;
595
596 case ArithmeticOrLogicalOperator::MODULUS:
597 op = "%";
598 break;
599
600 case ArithmeticOrLogicalOperator::BITWISE_AND:
601 op = "&";
602 break;
603
604 case ArithmeticOrLogicalOperator::BITWISE_OR:
605 op = "|";
606 break;
607
608 case ArithmeticOrLogicalOperator::BITWISE_XOR:
609 op = "^";
610 break;
611
612 case ArithmeticOrLogicalOperator::LEFT_SHIFT:
613 op = "<<";
614 break;
615
616 case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
617 op = ">>";
618 break;
619 }
620
621 visit (expr.get_left_expr ());
622 stream << " " << op << " ";
623 visit (expr.get_right_expr ());
624 }
625
626 void
627 Dump::visit (ComparisonExpr &expr)
628 {
629 auto op = "";
630 switch (expr.get_expr_type ())
631 {
632 case ComparisonOperator::EQUAL:
633 op = "==";
634 break;
635 case ComparisonOperator::NOT_EQUAL:
636 op = "!=";
637 break;
638
639 case ComparisonOperator::GREATER_THAN:
640 op = ">";
641 break;
642
643 case ComparisonOperator::LESS_THAN:
644 op = "<";
645 break;
646
647 case ComparisonOperator::GREATER_OR_EQUAL:
648 op = ">=";
649 break;
650
651 case ComparisonOperator::LESS_OR_EQUAL:
652 op = "<=";
653 break;
654 }
655
656 visit (expr.get_left_expr ());
657 stream << " " << op << " ";
658 visit (expr.get_right_expr ());
659 }
660
661 void
662 Dump::visit (LazyBooleanExpr &expr)
663 {
664 auto op = "";
665 switch (expr.get_expr_type ())
666 {
667 case LazyBooleanOperator::LOGICAL_AND:
668 op = "&&";
669 break;
670 case LazyBooleanOperator::LOGICAL_OR:
671 op = "||";
672 break;
673 }
674
675 visit (expr.get_left_expr ());
676 stream << " " << op << " ";
677 visit (expr.get_right_expr ());
678 }
679
680 void
681 Dump::visit (TypeCastExpr &expr)
682 {
683 visit (expr.get_casted_expr ());
684 stream << " as ";
685 visit (expr.get_type_to_cast_to ());
686 }
687
688 void
689 Dump::visit (AssignmentExpr &expr)
690 {
691 expr.visit_lhs (*this);
692 stream << " = ";
693 expr.visit_rhs (*this);
694 }
695
696 void
697 Dump::visit (CompoundAssignmentExpr &expr)
698 {
699 auto op = "";
700 switch (expr.get_expr_type ())
701 {
702 case CompoundAssignmentOperator::ADD:
703 op = "+";
704 break;
705
706 case CompoundAssignmentOperator::SUBTRACT:
707 op = "-";
708 break;
709
710 case CompoundAssignmentOperator::MULTIPLY:
711 op = "*";
712 break;
713
714 case CompoundAssignmentOperator::DIVIDE:
715 op = "/";
716 break;
717
718 case CompoundAssignmentOperator::MODULUS:
719 op = "%";
720 break;
721
722 case CompoundAssignmentOperator::BITWISE_AND:
723 op = "&";
724 break;
725
726 case CompoundAssignmentOperator::BITWISE_OR:
727 op = "|";
728 break;
729
730 case CompoundAssignmentOperator::BITWISE_XOR:
731 op = "^";
732 break;
733
734 case CompoundAssignmentOperator::LEFT_SHIFT:
735 op = "<<";
736 break;
737
738 case CompoundAssignmentOperator::RIGHT_SHIFT:
739 op = ">>";
740 break;
741 }
742
743 visit (expr.get_left_expr ());
744 stream << " " << op << "= ";
745 visit (expr.get_right_expr ());
746 }
747
748 void
749 Dump::visit (GroupedExpr &expr)
750 {
751 stream << '(';
752 visit (expr.get_expr_in_parens ());
753 stream << ')';
754 }
755
756 void
757 Dump::visit (ArrayElemsValues &elems)
758 {
759 visit_items_joined_by_separator (elems.get_values (), ", ");
760 }
761
762 void
763 Dump::visit (ArrayElemsCopied &elems)
764 {
765 visit (elems.get_elem_to_copy ());
766 stream << "; ";
767 visit (elems.get_num_copies ());
768 }
769
770 void
771 Dump::visit (ArrayExpr &expr)
772 {
773 stream << '[';
774 visit (expr.get_array_elems ());
775 stream << ']';
776 }
777
778 void
779 Dump::visit (ArrayIndexExpr &expr)
780 {
781 visit (expr.get_array_expr ());
782 stream << '[';
783 visit (expr.get_index_expr ());
784 stream << ']';
785 }
786
787 void
788 Dump::visit (TupleExpr &)
789 {}
790
791 void
792 Dump::visit (TupleIndexExpr &)
793 {}
794
795 void
796 Dump::visit (StructExprStruct &)
797 {}
798
799 void
800 Dump::visit (StructExprFieldIdentifier &)
801 {}
802
803 void
804 Dump::visit (StructExprFieldIdentifierValue &)
805 {}
806
807 void
808 Dump::visit (StructExprFieldIndexValue &)
809 {}
810
811 void
812 Dump::visit (StructExprStructFields &)
813 {}
814
815 void
816 Dump::visit (StructExprStructBase &)
817 {}
818
819 void
820 Dump::visit (CallExpr &expr)
821 {
822 visit (expr.get_function_expr ());
823
824 stream << '(' << '\n';
825 indentation.increment ();
826
827 visit_items_as_lines (expr.get_params (), ",");
828
829 indentation.decrement ();
830 stream << indentation << ')';
831 }
832
833 void
834 Dump::visit (MethodCallExpr &)
835 {}
836
837 void
838 Dump::visit (FieldAccessExpr &)
839 {}
840
841 void
842 Dump::visit (ClosureExprInner &)
843 {}
844
845 void
846 Dump::visit (BlockExpr &expr)
847 {
848 stream << "{\n";
849 indentation.increment ();
850
851 visit_items_as_lines (expr.get_statements (), ";");
852
853 if (expr.has_tail_expr ())
854 visit_as_line (expr.get_tail_expr (), " /* tail expr */\n");
855
856 indentation.decrement ();
857 stream << indentation << "}\n";
858 }
859
860 void
861 Dump::visit (ClosureExprInnerTyped &)
862 {}
863
864 void
865 Dump::visit (ContinueExpr &)
866 {}
867
868 void
869 Dump::visit (BreakExpr &)
870 {}
871
872 void
873 Dump::visit (RangeFromToExpr &expr)
874 {
875 visit (expr.get_from_expr ());
876 stream << "..";
877 visit (expr.get_to_expr ());
878 }
879
880 void
881 Dump::visit (RangeFromExpr &expr)
882 {
883 visit (expr.get_from_expr ());
884 stream << "..";
885 }
886
887 void
888 Dump::visit (RangeToExpr &expr)
889 {
890 stream << "..";
891 visit (expr.get_to_expr ());
892 }
893
894 void
895 Dump::visit (RangeFullExpr &)
896 {
897 stream << "..";
898 }
899
900 void
901 Dump::visit (RangeFromToInclExpr &expr)
902 {
903 visit (expr.get_from_expr ());
904 stream << "..=";
905 visit (expr.get_to_expr ());
906 }
907
908 void
909 Dump::visit (RangeToInclExpr &expr)
910 {
911 stream << "..=";
912 visit (expr.get_to_expr ());
913 }
914
915 void
916 Dump::visit (ReturnExpr &)
917 {}
918
919 void
920 Dump::visit (UnsafeBlockExpr &)
921 {}
922
923 void
924 Dump::visit (LoopExpr &)
925 {}
926
927 void
928 Dump::visit (WhileLoopExpr &)
929 {}
930
931 void
932 Dump::visit (WhileLetLoopExpr &)
933 {}
934
935 void
936 Dump::visit (ForLoopExpr &)
937 {}
938
939 void
940 Dump::visit (IfExpr &expr)
941 {
942 stream << "if ";
943 visit (expr.get_condition_expr ());
944 stream << " ";
945 visit (expr.get_if_block ());
946 }
947
948 void
949 Dump::visit (IfExprConseqElse &expr)
950 {
951 stream << "if ";
952 visit (expr.get_condition_expr ());
953 stream << " ";
954 visit (expr.get_if_block ());
955 stream << indentation << "else ";
956 visit (expr.get_else_block ());
957 }
958
959 void
960 Dump::visit (IfExprConseqIf &expr)
961 {
962 stream << "if ";
963 visit (expr.get_condition_expr ());
964 stream << " ";
965 visit (expr.get_if_block ());
966 stream << indentation << "else ";
967 // The "if" part of the "else if" is printed by the next visitor
968 visit (expr.get_conseq_if_expr ());
969 }
970
971 void
972 Dump::visit (IfExprConseqIfLet &)
973 {}
974
975 void
976 Dump::visit (IfLetExpr &)
977 {}
978
979 void
980 Dump::visit (IfLetExprConseqElse &)
981 {}
982
983 void
984 Dump::visit (IfLetExprConseqIf &)
985 {}
986
987 void
988 Dump::visit (IfLetExprConseqIfLet &)
989 {}
990
991 void
992 Dump::visit (MatchExpr &)
993 {}
994
995 void
996 Dump::visit (AwaitExpr &)
997 {}
998
999 void
1000 Dump::visit (AsyncBlockExpr &)
1001 {}
1002
1003 // rust-item.h
1004 void
1005 Dump::visit (TypeParam &param)
1006 {
1007 // Syntax:
1008 // IDENTIFIER( : TypeParamBounds? )? ( = Type )?
1009 // TypeParamBounds :
1010 // TypeParamBound ( + TypeParamBound )* +?
1011
1012 stream << param.get_type_representation ();
1013 if (param.has_type_param_bounds ())
1014 {
1015 stream << ": ";
1016 visit_items_joined_by_separator (param.get_type_param_bounds (), " + ");
1017 }
1018 if (param.has_type ())
1019 {
1020 stream << " = ";
1021 visit (param.get_type ());
1022 }
1023 }
1024
1025 void
1026 Dump::visit (WhereClause &rule)
1027 {
1028 // Syntax:
1029 // where ( WhereClauseItem , )* WhereClauseItem ?
1030 // WhereClauseItem :
1031 // LifetimeWhereClauseItem
1032 // | TypeBoundWhereClauseItem
1033
1034 stream << " where\n";
1035 indentation.increment ();
1036 visit_items_as_lines (rule.get_items (), ",");
1037 indentation.decrement ();
1038 }
1039
1040 void
1041 Dump::visit (LifetimeWhereClauseItem &item)
1042 {
1043 // Syntax:
1044 // Lifetime : LifetimeBounds
1045 // LifetimeBounds :
1046 // ( Lifetime + )* Lifetime?
1047
1048 visit (item.get_lifetime ());
1049 stream << ": ";
1050 visit_items_joined_by_separator (item.get_lifetime_bounds (), " + ");
1051 }
1052
1053 void
1054 Dump::visit (TypeBoundWhereClauseItem &item)
1055 {
1056 // Syntax:
1057 // ForLifetimes? Type : TypeParamBounds?
1058 // TypeParamBounds :
1059 // TypeParamBound ( + TypeParamBound )* +?
1060 // TypeParamBound :
1061 // Lifetime | TraitBound
1062
1063 if (item.has_for_lifetimes ())
1064 visit (item.get_for_lifetimes ());
1065
1066 visit (item.get_type ());
1067 stream << ": ";
1068
1069 visit_items_joined_by_separator (item.get_type_param_bounds (), " + ");
1070 }
1071
1072 void
1073 Dump::visit (Method &method)
1074 {
1075 visit (method.get_visibility ());
1076 stream << "fn " << method.get_method_name () << '(';
1077
1078 stream << method.get_self_param ().as_string ();
1079 if (!method.get_function_params ().empty ())
1080 {
1081 stream << ", ";
1082 visit_items_joined_by_separator (method.get_function_params (), ", ");
1083 }
1084
1085 stream << ") ";
1086
1087 if (method.has_return_type ())
1088 {
1089 stream << "-> ";
1090 visit (method.get_return_type ());
1091 stream << " ";
1092 }
1093
1094 auto &block = method.get_definition ();
1095 if (!block)
1096 stream << ';';
1097 else
1098 visit (block);
1099
1100 stream << '\n';
1101 }
1102
1103 void
1104 Dump::visit (Module &module)
1105 {
1106 // Syntax:
1107 // mod IDENTIFIER ;
1108 // | mod IDENTIFIER {
1109 // InnerAttribute*
1110 // Item*
1111 // }
1112
1113 visit (module.get_visibility ());
1114 stream << "mod " << module.get_name ();
1115
1116 if (module.get_kind () == Module::UNLOADED)
1117 {
1118 stream << ";\n";
1119 }
1120 else /* Module::LOADED */
1121 {
1122 stream << " {\n";
1123
1124 indentation.increment ();
1125
1126 visit_items_as_lines (module.get_inner_attrs ());
1127 visit_items_as_lines (module.get_items ());
1128
1129 indentation.decrement ();
1130
1131 stream << indentation << "}\n";
1132 }
1133 }
1134
1135 void
1136 Dump::visit (ExternCrate &)
1137 {}
1138
1139 void
1140 Dump::visit (UseTreeGlob &)
1141 {}
1142
1143 void
1144 Dump::visit (UseTreeList &)
1145 {}
1146
1147 void
1148 Dump::visit (UseTreeRebind &)
1149 {}
1150
1151 void
1152 Dump::visit (UseDeclaration &)
1153 {}
1154
1155 void
1156 Dump::visit (Function &function)
1157 {
1158 // Syntax:
1159 // FunctionQualifiers fn IDENTIFIER GenericParams?
1160 // ( FunctionParameters? )
1161 // FunctionReturnType? WhereClause?
1162 // ( BlockExpression | ; )
1163
1164 visit (function.get_visibility ());
1165
1166 stream << "fn " << function.get_function_name ();
1167 if (function.has_generics ())
1168 visit (function.get_generic_params ());
1169
1170 stream << '(';
1171 visit_items_joined_by_separator (function.get_function_params ());
1172 stream << ") ";
1173
1174 if (function.has_return_type ())
1175 {
1176 stream << "-> ";
1177 visit (function.get_return_type ());
1178 stream << " ";
1179 }
1180
1181 if (function.has_where_clause ())
1182 visit (function.get_where_clause ());
1183
1184 auto &block = function.get_definition ();
1185 if (!block)
1186 stream << ';';
1187 else
1188 visit (block);
1189
1190 stream << '\n';
1191 }
1192
1193 void
1194 Dump::visit (TypeAlias &type_alias)
1195 {
1196 // Syntax:
1197 // Visibility? type IDENTIFIER GenericParams? WhereClause? = Type;
1198
1199 // Note: Associated types are handled by `AST::TraitItemType`.
1200
1201 if (type_alias.has_visibility ())
1202 visit (type_alias.get_visibility ());
1203 stream << "type " << type_alias.get_new_type_name ();
1204 if (type_alias.has_generics ())
1205 visit (type_alias.get_generic_params ());
1206 if (type_alias.has_where_clause ())
1207 visit (type_alias.get_where_clause ());
1208 stream << " = ";
1209 visit (type_alias.get_type_aliased ());
1210 }
1211
1212 void
1213 Dump::visit (StructStruct &struct_item)
1214 {
1215 stream << "struct " << struct_item.get_identifier ();
1216 if (struct_item.has_generics ())
1217 visit (struct_item.get_generic_params ());
1218 if (struct_item.has_where_clause ())
1219 visit (struct_item.get_where_clause ());
1220 if (struct_item.is_unit_struct ())
1221 stream << ";\n";
1222 else
1223 visit_items_as_block (struct_item.get_fields (), ",");
1224 }
1225
1226 void
1227 Dump::visit (TupleStruct &tuple_struct)
1228 {
1229 stream << "struct " << tuple_struct.get_identifier ();
1230 if (tuple_struct.has_generics ())
1231 visit (tuple_struct.get_generic_params ());
1232 if (tuple_struct.has_where_clause ())
1233 visit (tuple_struct.get_where_clause ());
1234
1235 stream << '(';
1236 visit_items_joined_by_separator (tuple_struct.get_fields (), ", ");
1237 stream << ");\n";
1238 }
1239
1240 void
1241 Dump::visit (EnumItem &item)
1242 {
1243 stream << item.get_identifier ();
1244 }
1245
1246 void
1247 Dump::visit (EnumItemTuple &item)
1248 {
1249 stream << item.get_identifier () << '(';
1250 visit_items_joined_by_separator (item.get_tuple_fields (), ", ");
1251 stream << ')';
1252 }
1253
1254 void
1255 Dump::visit (EnumItemStruct &item)
1256 {
1257 stream << item.get_identifier ();
1258 visit_items_as_block (item.get_struct_fields (), ",");
1259 }
1260
1261 void
1262 Dump::visit (EnumItemDiscriminant &item)
1263 {
1264 stream << item.get_identifier () << " = ";
1265 visit (item.get_expr ());
1266 }
1267
1268 void
1269 Dump::visit (Enum &enum_item)
1270 {
1271 stream << "enum " << enum_item.get_identifier ();
1272 if (enum_item.has_generics ())
1273 visit (enum_item.get_generic_params ());
1274 if (enum_item.has_where_clause ())
1275 visit (enum_item.get_where_clause ());
1276
1277 visit_items_as_block (enum_item.get_variants (), ",");
1278 }
1279
1280 void
1281 Dump::visit (Union &union_item)
1282 {
1283 stream << "union " << union_item.get_identifier ();
1284 if (union_item.has_generics ())
1285 visit (union_item.get_generic_params ());
1286 if (union_item.has_where_clause ())
1287 visit (union_item.get_where_clause ());
1288
1289 visit_items_as_block (union_item.get_variants (), ",");
1290 }
1291
1292 void
1293 Dump::visit (ConstantItem &)
1294 {}
1295
1296 void
1297 Dump::visit (StaticItem &)
1298 {}
1299
1300 void
1301 Dump::visit_function_common (std::unique_ptr<Type> &return_type,
1302 std::unique_ptr<BlockExpr> &block)
1303 {
1304 // FIXME: This should format the `<vis> fn <name> ( [args] )` as well
1305 if (return_type)
1306 {
1307 stream << "-> ";
1308 visit (return_type);
1309 }
1310
1311 if (block)
1312 {
1313 if (return_type)
1314 {
1315 stream << ' ';
1316 visit (block);
1317 }
1318 }
1319 else
1320 stream << ";\n";
1321 }
1322
1323 void
1324 Dump::visit (TraitItemFunc &item)
1325 {
1326 auto func = item.get_trait_function_decl ();
1327 stream << "fn " << func.get_identifier () << '(';
1328
1329 visit_items_joined_by_separator (func.get_function_params ());
1330
1331 stream << ") ";
1332
1333 visit_function_common (func.get_return_type (), item.get_definition ());
1334 }
1335
1336 void
1337 Dump::visit (TraitItemMethod &item)
1338 {
1339 auto method = item.get_trait_method_decl ();
1340
1341 // FIXME: Can we have visibility here?
1342 // emit_visibility (method.get_visibility ());
1343 stream << "fn " << method.get_identifier () << '(';
1344
1345 stream << method.get_self_param ().as_string ();
1346
1347 if (!method.get_function_params ().empty ())
1348 {
1349 stream << ", ";
1350 visit_items_joined_by_separator (method.get_function_params (), ", ");
1351 }
1352
1353 stream << ") ";
1354
1355 visit_function_common (method.get_return_type (), item.get_definition ());
1356 }
1357
1358 void
1359 Dump::visit (TraitItemConst &item)
1360 {
1361 stream << indentation << "const " << item.get_identifier () << ": ";
1362 visit (item.get_type ());
1363 stream << ";\n";
1364 }
1365
1366 void
1367 Dump::visit (TraitItemType &item)
1368 {
1369 stream << indentation << "type " << item.get_identifier () << ";\n";
1370 }
1371
1372 void
1373 Dump::visit (Trait &trait)
1374 {
1375 for (auto &attr : trait.get_outer_attrs ())
1376 {
1377 visit (attr);
1378 stream << "\n" << indentation;
1379 }
1380
1381 visit (trait.get_visibility ());
1382
1383 stream << "trait " << trait.get_identifier ();
1384
1385 // Traits actually have an implicit Self thrown at the start, so we must
1386 // expect the number of generic params to be > 1
1387 if (trait.get_generic_params ().size () > 1)
1388 {
1389 stream << "<";
1390 visit_items_joined_by_separator (trait.get_generic_params (), ", ", 1);
1391 stream << ">";
1392 }
1393
1394 visit_items_as_block (trait.get_trait_items (), "");
1395 }
1396
1397 void
1398 Dump::visit (InherentImpl &impl)
1399 {
1400 stream << "impl ";
1401
1402 // FIXME: Handle generics
1403
1404 visit (impl.get_type ());
1405
1406 if (impl.has_where_clause ())
1407 visit (impl.get_where_clause ());
1408
1409 // FIXME: Handle inner attributes
1410
1411 visit_items_as_block (impl.get_impl_items (), "");
1412 }
1413
1414 void
1415 Dump::visit (TraitImpl &impl)
1416 {
1417 stream << "impl ";
1418 visit (impl.get_trait_path ());
1419 stream << " for ";
1420 visit (impl.get_type ());
1421 stream << " {\n";
1422
1423 indentation.increment ();
1424
1425 for (auto &item : impl.get_impl_items ())
1426 {
1427 stream << indentation;
1428 visit (item);
1429 }
1430
1431 indentation.decrement ();
1432 stream << "\n}\n";
1433 }
1434
1435 void
1436 Dump::visit (ExternalStaticItem &)
1437 {}
1438
1439 void
1440 Dump::visit (ExternalFunctionItem &function)
1441 {
1442 visit (function.get_visibility ());
1443
1444 stream << "fn " << function.get_identifier () << '(';
1445
1446 visit_items_joined_by_separator (function.get_function_params ());
1447
1448 stream << ')';
1449 if (function.has_return_type ())
1450 {
1451 stream << "-> ";
1452 visit (function.get_return_type ());
1453 }
1454 }
1455
1456 void
1457 Dump::visit (ExternBlock &block)
1458 {
1459 stream << "extern ";
1460
1461 if (block.has_abi ())
1462 stream << "\"" << block.get_abi () << "\" ";
1463
1464 visit_items_as_block (block.get_extern_items (), ";");
1465 }
1466
1467 static std::pair<char, char>
1468 get_delimiters (DelimType delim)
1469 {
1470 auto start_delim = '\0';
1471 auto end_delim = '\0';
1472
1473 switch (delim)
1474 {
1475 case PARENS:
1476 start_delim = '(';
1477 end_delim = ')';
1478 break;
1479 case SQUARE:
1480 start_delim = '[';
1481 end_delim = ']';
1482 break;
1483 case CURLY:
1484 start_delim = '{';
1485 end_delim = '}';
1486 break;
1487 }
1488
1489 return {start_delim, end_delim};
1490 }
1491
1492 void
1493 Dump::visit (MacroMatchFragment &match)
1494 {
1495 stream << '$' << match.get_ident () << ':'
1496 << match.get_frag_spec ().as_string ();
1497 }
1498
1499 void
1500 Dump::visit (MacroMatchRepetition &repetition)
1501 {
1502 stream << "$(";
1503
1504 visit_items_joined_by_separator (repetition.get_matches (), " ");
1505
1506 auto op_char = '\0';
1507 switch (repetition.get_op ())
1508 {
1509 case MacroMatchRepetition::ANY:
1510 op_char = '*';
1511 break;
1512 case MacroMatchRepetition::ONE_OR_MORE:
1513 op_char = '+';
1514 break;
1515 case MacroMatchRepetition::ZERO_OR_ONE:
1516 op_char = '?';
1517 break;
1518 case MacroMatchRepetition::NONE:
1519 break;
1520 }
1521
1522 stream << ')';
1523
1524 if (repetition.has_sep ())
1525 stream << repetition.get_sep ()->as_string ();
1526
1527 stream << op_char;
1528 }
1529
1530 void
1531 Dump::visit (MacroMatcher &matcher)
1532 {
1533 auto delimiters = get_delimiters (matcher.get_delim_type ());
1534
1535 stream << delimiters.first;
1536
1537 visit_items_joined_by_separator (matcher.get_matches (), " ");
1538
1539 stream << delimiters.second;
1540 }
1541
1542 void
1543 Dump::visit (MacroRule &rule)
1544 {
1545 visit (rule.get_matcher ());
1546 stream << " => ";
1547 visit (rule.get_transcriber ().get_token_tree ());
1548 stream << ";";
1549 }
1550
1551 void
1552 Dump::visit (MacroRulesDefinition &rules_def)
1553 {
1554 for (auto &outer_attr : rules_def.get_outer_attrs ())
1555 visit (outer_attr);
1556
1557 stream << "macro_rules! " << rules_def.get_rule_name ();
1558
1559 visit_items_as_block (rules_def.get_rules (), ";");
1560 }
1561
1562 void
1563 Dump::visit (MacroInvocation &)
1564 {}
1565
1566 void
1567 Dump::visit (MetaItemPath &)
1568 {}
1569
1570 void
1571 Dump::visit (MetaItemSeq &)
1572 {}
1573
1574 void
1575 Dump::visit (MetaWord &)
1576 {}
1577
1578 void
1579 Dump::visit (MetaNameValueStr &)
1580 {}
1581
1582 void
1583 Dump::visit (MetaListPaths &)
1584 {}
1585
1586 void
1587 Dump::visit (MetaListNameValueStr &)
1588 {}
1589
1590 // rust-pattern.h
1591 void
1592 Dump::visit (LiteralPattern &)
1593 {}
1594
1595 void
1596 Dump::visit (IdentifierPattern &pattern)
1597 {
1598 stream << pattern.get_ident ();
1599 }
1600
1601 void
1602 Dump::visit (WildcardPattern &)
1603 {}
1604
1605 // void Dump::visit(RangePatternBound& ){}
1606
1607 void
1608 Dump::visit (RangePatternBoundLiteral &)
1609 {}
1610
1611 void
1612 Dump::visit (RangePatternBoundPath &)
1613 {}
1614
1615 void
1616 Dump::visit (RangePatternBoundQualPath &)
1617 {}
1618
1619 void
1620 Dump::visit (RangePattern &)
1621 {}
1622
1623 void
1624 Dump::visit (ReferencePattern &)
1625 {}
1626
1627 // void Dump::visit(StructPatternField& ){}
1628
1629 void
1630 Dump::visit (StructPatternFieldTuplePat &)
1631 {}
1632
1633 void
1634 Dump::visit (StructPatternFieldIdentPat &)
1635 {}
1636
1637 void
1638 Dump::visit (StructPatternFieldIdent &)
1639 {}
1640
1641 void
1642 Dump::visit (StructPattern &)
1643 {}
1644
1645 // void Dump::visit(TupleStructItems& ){}
1646
1647 void
1648 Dump::visit (TupleStructItemsNoRange &)
1649 {}
1650
1651 void
1652 Dump::visit (TupleStructItemsRange &)
1653 {}
1654
1655 void
1656 Dump::visit (TupleStructPattern &)
1657 {}
1658
1659 // void Dump::visit(TuplePatternItems& ){}
1660
1661 void
1662 Dump::visit (TuplePatternItemsMultiple &)
1663 {}
1664
1665 void
1666 Dump::visit (TuplePatternItemsRanged &)
1667 {}
1668
1669 void
1670 Dump::visit (TuplePattern &)
1671 {}
1672
1673 void
1674 Dump::visit (GroupedPattern &)
1675 {}
1676
1677 void
1678 Dump::visit (SlicePattern &)
1679 {}
1680
1681 void
1682 Dump::visit (AltPattern &)
1683 {}
1684
1685 // rust-stmt.h
1686 void
1687 Dump::visit (EmptyStmt &)
1688 {}
1689
1690 void
1691 Dump::visit (LetStmt &stmt)
1692 {
1693 stream << "let ";
1694 auto &pattern = stmt.get_pattern ();
1695 if (pattern)
1696 visit (pattern);
1697
1698 if (stmt.has_type ())
1699 {
1700 stream << ": ";
1701 visit (stmt.get_type ());
1702 }
1703
1704 if (stmt.has_init_expr ())
1705 {
1706 stream << " = ";
1707 visit (stmt.get_init_expr ());
1708 }
1709 }
1710
1711 void
1712 Dump::visit (ExprStmtWithoutBlock &stmt)
1713 {
1714 visit (stmt.get_expr ());
1715 }
1716
1717 void
1718 Dump::visit (ExprStmtWithBlock &stmt)
1719 {
1720 visit (stmt.get_expr ());
1721 }
1722
1723 // rust-type.h
1724 void
1725 Dump::visit (TraitBound &bound)
1726 {
1727 // Syntax:
1728 // ?? ForLifetimes? TypePath
1729 // | ( ?? ForLifetimes? TypePath )
1730
1731 if (bound.has_opening_question_mark ())
1732 stream << "? ";
1733
1734 if (bound.has_for_lifetimes ())
1735 visit (bound.get_for_lifetimes ());
1736
1737 visit (bound.get_type_path ());
1738 }
1739
1740 void
1741 Dump::visit (ImplTraitType &type)
1742 {
1743 // Syntax:
1744 // impl TypeParamBounds
1745 // TypeParamBounds :
1746 // TypeParamBound ( + TypeParamBound )* +?
1747
1748 stream << "impl ";
1749 visit_items_joined_by_separator (type.get_type_param_bounds (), " + ");
1750 }
1751
1752 void
1753 Dump::visit (TraitObjectType &type)
1754 {
1755 // Syntax:
1756 // dyn? TypeParamBounds
1757 // TypeParamBounds :
1758 // TypeParamBound ( + TypeParamBound )* +?
1759
1760 if (type.is_dyn ())
1761 stream << "dyn ";
1762 visit_items_joined_by_separator (type.get_type_param_bounds (), " + ");
1763 }
1764
1765 void
1766 Dump::visit (ParenthesisedType &type)
1767 {
1768 // Syntax:
1769 // ( Type )
1770
1771 stream << "(";
1772 visit (type.get_type_in_parens ());
1773 stream << ")";
1774 }
1775
1776 void
1777 Dump::visit (ImplTraitTypeOneBound &type)
1778 {
1779 // Syntax:
1780 // impl TraitBound
1781
1782 stream << "impl ";
1783 visit (type.get_trait_bound ());
1784 }
1785
1786 void
1787 Dump::visit (TraitObjectTypeOneBound &type)
1788 {
1789 // Syntax:
1790 // dyn? TraitBound
1791
1792 if (type.is_dyn ())
1793 stream << "dyn ";
1794 visit (type.get_trait_bound ());
1795 }
1796
1797 void
1798 Dump::visit (TupleType &type)
1799 {
1800 // Syntax:
1801 // ( )
1802 // | ( ( Type , )+ Type? )
1803
1804 stream << '(';
1805 visit_items_joined_by_separator (type.get_elems (), ", ");
1806 stream << ')';
1807 }
1808
1809 void
1810 Dump::visit (NeverType &)
1811 {
1812 // Syntax:
1813 // !
1814
1815 stream << '!';
1816 }
1817
1818 void
1819 Dump::visit (RawPointerType &type)
1820 {
1821 // Syntax:
1822 // * ( mut | const ) TypeNoBounds
1823
1824 if (type.get_pointer_type () == RawPointerType::MUT)
1825 stream << "*mut ";
1826 else /* RawPointerType::CONST */
1827 stream << "*const ";
1828
1829 visit (type.get_type_pointed_to ());
1830 }
1831
1832 void
1833 Dump::visit (ReferenceType &type)
1834 {
1835 // Syntax:
1836 // & Lifetime? mut? TypeNoBounds
1837
1838 stream << '&';
1839
1840 if (type.has_lifetime ())
1841 {
1842 visit (type.get_lifetime ());
1843 stream << ' ';
1844 }
1845
1846 if (type.get_has_mut ())
1847 stream << "mut ";
1848
1849 visit (type.get_type_referenced ());
1850 }
1851
1852 void
1853 Dump::visit (ArrayType &type)
1854 {
1855 // Syntax:
1856 // [ Type ; Expression ]
1857
1858 stream << '[';
1859 visit (type.get_elem_type ());
1860 stream << "; ";
1861 visit (type.get_size_expr ());
1862 stream << ']';
1863 }
1864
1865 void
1866 Dump::visit (SliceType &type)
1867 {
1868 // Syntax:
1869 // [ Type ]
1870
1871 stream << '[';
1872 visit (type.get_elem_type ());
1873 stream << ']';
1874 }
1875
1876 void
1877 Dump::visit (InferredType &)
1878 {
1879 // Syntax:
1880 // _
1881
1882 stream << "_";
1883 }
1884
1885 void
1886 Dump::visit (BareFunctionType &type)
1887 {
1888 // Syntax:
1889 // ForLifetimes? FunctionTypeQualifiers fn
1890 // ( FunctionParametersMaybeNamedVariadic? ) BareFunctionReturnType?
1891 //
1892 // BareFunctionReturnType:
1893 // -> TypeNoBounds
1894 //
1895 // FunctionParametersMaybeNamedVariadic :
1896 // MaybeNamedFunctionParameters | MaybeNamedFunctionParametersVariadic
1897 //
1898 // MaybeNamedFunctionParameters :
1899 // MaybeNamedParam ( , MaybeNamedParam )* ,?
1900 //
1901 // MaybeNamedFunctionParametersVariadic :
1902 // ( MaybeNamedParam , )* MaybeNamedParam , OuterAttribute* ...
1903
1904 if (type.has_for_lifetimes ())
1905 visit (type.get_for_lifetimes ());
1906
1907 visit (type.get_function_qualifiers ());
1908
1909 stream << "fn (";
1910
1911 visit_items_joined_by_separator (type.get_function_params (), ", ");
1912
1913 if (type.is_variadic ())
1914 {
1915 stream << ", ";
1916 visit_items_joined_by_separator (type.get_variadic_attr (), " ");
1917 stream << "...";
1918 }
1919
1920 stream << ')';
1921
1922 if (type.has_return_type ())
1923 {
1924 stream << " -> ";
1925 visit (type.get_return_type ());
1926 }
1927 }
1928
1929 } // namespace AST
1930 } // namespace Rust