zerodds_lang_cst/cpp/mod.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The C++ concrete syntax tree. Spec §1.
4//!
5//! C++ has namespaces, templates and access sections; the tree has them
6//! as nodes rather than as flags. An access section is where it appears
7//! in the class, because that is what the output shows.
8
9use crate::c::Include;
10use crate::common::{Doc, Ident, Literal, Path};
11use alloc::boxed::Box;
12use alloc::string::{String, ToString};
13use alloc::vec::Vec;
14
15/// A C++ header or source file.
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
17pub struct File {
18 /// The leading comment.
19 pub doc: Doc,
20 /// Whether the file opens with `#pragma once`.
21 pub pragma_once: bool,
22 /// `#include` lines, in written order.
23 pub includes: Vec<Include>,
24 /// Declarations at file scope, in written order.
25 pub decls: Vec<Decl>,
26}
27
28/// One declaration.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum Decl {
31 /// `namespace name { ... }`
32 Namespace(Namespace),
33 /// `class Name { ... };` or `struct Name { ... };`
34 Class(Class),
35 /// `enum class Name : T { ... };`
36 Enum(Enum),
37 /// `using Name = ...;`
38 Alias(Alias),
39 /// A function at namespace scope.
40 Function(Function),
41 /// A variable at namespace scope.
42 Variable(Variable),
43 /// `template <..> <decl>`
44 Template(Template),
45 /// A comment standing on its own.
46 Comment(Doc),
47 /// `@verbatim` text, copied into the output unchanged.
48 ///
49 /// OMG IDL 4.2 §8.3.5.1: the annotation's `text` "shall be copied
50 /// verbatim" into the generated artifact — raw lines, never a
51 /// comment. One node per directive, lines split on `\n`.
52 Verbatim(String),
53 /// A forward declaration: `class Name;`
54 Forward {
55 /// Whether it was written `struct`.
56 is_struct: bool,
57 /// The name.
58 name: Ident,
59 },
60}
61
62/// `namespace name { ... }`
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct Namespace {
65 /// Documentation.
66 pub doc: Doc,
67 /// Name, empty for an anonymous namespace.
68 pub name: Ident,
69 /// What is inside.
70 pub decls: Vec<Decl>,
71}
72
73/// `class Name : bases { sections };`
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct Class {
76 /// Documentation.
77 pub doc: Doc,
78 /// Whether it is written `struct` rather than `class`.
79 pub is_struct: bool,
80 /// Name.
81 pub name: Ident,
82 /// Base classes, in written order.
83 pub bases: Vec<Base>,
84 /// Access sections, in the order they appear.
85 ///
86 /// A section rather than a per-member access word: the output has
87 /// `public:` once above a run of members, and a tree that stored
88 /// access per member would leave the grouping to the renderer.
89 pub sections: Vec<Section>,
90}
91
92/// One base class.
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct Base {
95 /// How it is inherited.
96 pub access: Access,
97 /// Whether it is `virtual`.
98 pub virtual_: bool,
99 /// Its name.
100 pub path: Path,
101}
102
103/// One access section of a class.
104#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct Section {
106 /// Which access it opens.
107 pub access: Access,
108 /// Its members, in written order.
109 pub members: Vec<ClassMember>,
110}
111
112/// Access specifiers.
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
114pub enum Access {
115 /// `public`
116 Public,
117 /// `protected`
118 Protected,
119 /// `private`
120 Private,
121}
122
123impl Access {
124 /// Every access specifier.
125 pub const ALL: [Self; 3] = [Self::Public, Self::Protected, Self::Private];
126
127 /// How it is written.
128 #[must_use]
129 pub const fn as_str(self) -> &'static str {
130 match self {
131 Self::Public => "public",
132 Self::Protected => "protected",
133 Self::Private => "private",
134 }
135 }
136}
137
138/// One member of a class.
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub enum ClassMember {
141 /// A data member.
142 Field(Field),
143 /// A member function.
144 Method(Method),
145 /// A constructor.
146 Constructor(Constructor),
147 /// A destructor.
148 Destructor(Destructor),
149 /// A nested declaration.
150 Nested(Box<Decl>),
151 /// A `using` inside a class.
152 Alias(Alias),
153 /// A comment on its own line.
154 Comment(Doc),
155 /// `@verbatim` text inside the class body, copied unchanged.
156 ///
157 /// OMG IDL 4.2 §8.3.5.1 `begin-declaration` / `end-declaration`:
158 /// the text lands inside the annotated declaration's scope.
159 Verbatim(String),
160}
161
162/// A data member.
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct Field {
165 /// Documentation.
166 pub doc: Doc,
167 /// Type.
168 pub type_: Type,
169 /// Name.
170 pub name: Ident,
171 /// Array dimensions, outermost first.
172 pub dims: Vec<Expr>,
173 /// A default member initialiser.
174 pub default: Option<Expr>,
175 /// `static`.
176 pub is_static: bool,
177 /// `constexpr`.
178 pub is_constexpr: bool,
179 /// A bitfield width.
180 pub bits: Option<u16>,
181}
182
183/// A member function.
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct Method {
186 /// Documentation.
187 pub doc: Doc,
188 /// Return type.
189 pub returns: Type,
190 /// Name.
191 pub name: Ident,
192 /// Parameters.
193 pub params: Vec<Param>,
194 /// `const` on the method.
195 pub is_const: bool,
196 /// `static`.
197 pub is_static: bool,
198 /// `virtual`.
199 pub is_virtual: bool,
200 /// `= 0`.
201 pub is_pure: bool,
202 /// `constexpr`.
203 pub is_constexpr: bool,
204 /// `noexcept`.
205 pub is_noexcept: bool,
206 /// `override`.
207 pub is_override: bool,
208 /// The body, absent for a declaration.
209 pub body: Option<Block>,
210}
211
212/// A constructor.
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct Constructor {
215 /// Documentation.
216 pub doc: Doc,
217 /// The class's name.
218 pub name: Ident,
219 /// Parameters.
220 pub params: Vec<Param>,
221 /// The member initialiser list, in written order.
222 pub initialisers: Vec<(Ident, Expr)>,
223 /// `explicit`.
224 pub is_explicit: bool,
225 /// `constexpr`.
226 pub is_constexpr: bool,
227 /// `noexcept`.
228 pub is_noexcept: bool,
229 /// The body, or the defaulted or deleted form.
230 pub body: FunctionBody,
231}
232
233/// A destructor.
234#[derive(Debug, Clone, PartialEq, Eq)]
235pub struct Destructor {
236 /// Documentation.
237 pub doc: Doc,
238 /// The class's name.
239 pub name: Ident,
240 /// `virtual`.
241 pub is_virtual: bool,
242 /// The body, or the defaulted form.
243 pub body: FunctionBody,
244}
245
246/// What follows a function's signature.
247#[derive(Debug, Clone, PartialEq, Eq)]
248pub enum FunctionBody {
249 /// `{ ... }`
250 Block(Block),
251 /// `= default;`
252 Defaulted,
253 /// `= delete;`
254 Deleted,
255 /// `;` — a declaration only.
256 None,
257}
258
259/// `enum class Name : T { ... };`
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct Enum {
262 /// Documentation.
263 pub doc: Doc,
264 /// Whether it is `enum class` rather than a plain `enum`.
265 pub is_scoped: bool,
266 /// Name.
267 pub name: Ident,
268 /// The underlying type, when written.
269 pub underlying: Option<Type>,
270 /// Enumerators, in written order.
271 pub enumerators: Vec<Enumerator>,
272}
273
274/// One enumerator.
275#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct Enumerator {
277 /// Documentation.
278 pub doc: Doc,
279 /// Name.
280 pub name: Ident,
281 /// `= value`, when written.
282 pub value: Option<Expr>,
283}
284
285/// `using Name = Type;`
286#[derive(Debug, Clone, PartialEq, Eq)]
287pub struct Alias {
288 /// Documentation.
289 pub doc: Doc,
290 /// Name.
291 pub name: Ident,
292 /// What it aliases.
293 pub type_: Type,
294}
295
296/// A function at namespace scope.
297#[derive(Debug, Clone, PartialEq, Eq)]
298pub struct Function {
299 /// Documentation.
300 pub doc: Doc,
301 /// Return type.
302 pub returns: Type,
303 /// Name.
304 pub name: Ident,
305 /// Parameters.
306 pub params: Vec<Param>,
307 /// `inline`.
308 pub is_inline: bool,
309 /// `static`.
310 pub is_static: bool,
311 /// `constexpr`.
312 pub is_constexpr: bool,
313 /// `noexcept`.
314 pub is_noexcept: bool,
315 /// `[[maybe_unused]]` (C++17 §9.12.8): a function that may
316 /// legitimately have no caller, so `-Wunused-function` stays quiet
317 /// about it and only about it.
318 pub is_maybe_unused: bool,
319 /// The body.
320 pub body: FunctionBody,
321}
322
323/// A variable at namespace scope.
324#[derive(Debug, Clone, PartialEq, Eq)]
325pub struct Variable {
326 /// Documentation.
327 pub doc: Doc,
328 /// Type.
329 pub type_: Type,
330 /// Name.
331 pub name: Ident,
332 /// Initialiser.
333 pub value: Option<Expr>,
334 /// `static`.
335 pub is_static: bool,
336 /// `constexpr`.
337 pub is_constexpr: bool,
338 /// `inline`.
339 pub is_inline: bool,
340}
341
342/// One parameter.
343#[derive(Debug, Clone, PartialEq, Eq)]
344pub struct Param {
345 /// Type.
346 pub type_: Type,
347 /// Name, absent for an unused parameter.
348 pub name: Option<Ident>,
349 /// A default argument.
350 pub default: Option<Expr>,
351}
352
353/// `template <..> <decl>`
354#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct Template {
356 /// Documentation.
357 pub doc: Doc,
358 /// The parameters, empty for an explicit specialisation.
359 pub params: Vec<TemplateParam>,
360 /// What is templated.
361 pub decl: Box<Decl>,
362 /// The specialisation arguments, for `template <> struct S<T>`.
363 pub specialisation: Vec<Type>,
364}
365
366/// One template parameter.
367#[derive(Debug, Clone, PartialEq, Eq)]
368pub struct TemplateParam {
369 /// The kind of parameter.
370 pub kind: TemplateParamKind,
371 /// Its name.
372 pub name: Ident,
373 /// A default argument.
374 pub default: Option<Type>,
375}
376
377/// Which kind of template parameter.
378#[derive(Debug, Clone, PartialEq, Eq)]
379pub enum TemplateParamKind {
380 /// `typename T`
381 TypeName,
382 /// `class T`
383 Class,
384 /// A non-type parameter of the given type.
385 NonType(Box<Type>),
386}
387
388/// A C++ type as it will be written.
389#[derive(Debug, Clone, PartialEq, Eq)]
390pub enum Type {
391 /// A built-in.
392 Primitive(Primitive),
393 /// A named type with any template arguments.
394 Named {
395 /// Its path.
396 path: Path,
397 /// Its template arguments, in written order.
398 args: Vec<Type>,
399 },
400 /// `T *`
401 Pointer(Box<Type>),
402 /// `T &`
403 Ref(Box<Type>),
404 /// `T &&`
405 RvalueRef(Box<Type>),
406 /// `const T`
407 Const(Box<Type>),
408 /// `T[N]`
409 Array {
410 /// Element type.
411 element: Box<Type>,
412 /// Length.
413 length: Box<Expr>,
414 },
415 /// `void`
416 Void,
417 /// `auto`
418 Auto,
419}
420
421impl Type {
422 /// A named type with no template arguments.
423 #[must_use]
424 pub fn named(path: Path) -> Self {
425 Self::Named {
426 path,
427 args: Vec::new(),
428 }
429 }
430
431 /// A named type with template arguments.
432 #[must_use]
433 pub fn template(path: Path, args: Vec<Self>) -> Self {
434 Self::Named { path, args }
435 }
436}
437
438/// A C++ built-in type.
439#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
440pub enum Primitive {
441 /// `bool`
442 Bool,
443 /// `char`
444 Char,
445 /// `char16_t`
446 Char16,
447 /// `char32_t`
448 Char32,
449 /// `wchar_t`
450 WChar,
451 /// `int8_t`
452 Int8,
453 /// `int16_t`
454 Int16,
455 /// `int32_t`
456 Int32,
457 /// `int64_t`
458 Int64,
459 /// `uint8_t`
460 UInt8,
461 /// `uint16_t`
462 UInt16,
463 /// `uint32_t`
464 UInt32,
465 /// `uint64_t`
466 UInt64,
467 /// `float`
468 Float,
469 /// `double`
470 Double,
471 /// `long double`
472 LongDouble,
473 /// `size_t`
474 Size,
475}
476
477impl Primitive {
478 /// Every built-in the tree can hold.
479 pub const ALL: [Self; 17] = [
480 Self::Bool,
481 Self::Char,
482 Self::Char16,
483 Self::Char32,
484 Self::WChar,
485 Self::Int8,
486 Self::Int16,
487 Self::Int32,
488 Self::Int64,
489 Self::UInt8,
490 Self::UInt16,
491 Self::UInt32,
492 Self::UInt64,
493 Self::Float,
494 Self::Double,
495 Self::LongDouble,
496 Self::Size,
497 ];
498
499 /// How it is written.
500 #[must_use]
501 pub const fn as_str(self) -> &'static str {
502 match self {
503 Self::Bool => "bool",
504 Self::Char => "char",
505 Self::Char16 => "char16_t",
506 Self::Char32 => "char32_t",
507 Self::WChar => "wchar_t",
508 Self::Int8 => "int8_t",
509 Self::Int16 => "int16_t",
510 Self::Int32 => "int32_t",
511 Self::Int64 => "int64_t",
512 Self::UInt8 => "uint8_t",
513 Self::UInt16 => "uint16_t",
514 Self::UInt32 => "uint32_t",
515 Self::UInt64 => "uint64_t",
516 Self::Float => "float",
517 Self::Double => "double",
518 Self::LongDouble => "long double",
519 Self::Size => "size_t",
520 }
521 }
522
523 /// The header it comes from, when it needs one.
524 #[must_use]
525 pub const fn header(self) -> Option<&'static str> {
526 match self {
527 Self::Int8
528 | Self::Int16
529 | Self::Int32
530 | Self::Int64
531 | Self::UInt8
532 | Self::UInt16
533 | Self::UInt32
534 | Self::UInt64 => Some("cstdint"),
535 Self::Size => Some("cstddef"),
536 Self::Bool
537 | Self::Char
538 | Self::Char16
539 | Self::Char32
540 | Self::WChar
541 | Self::Float
542 | Self::Double
543 | Self::LongDouble => None,
544 }
545 }
546}
547
548/// A block of statements.
549#[derive(Debug, Clone, Default, PartialEq, Eq)]
550pub struct Block {
551 /// The statements, in order.
552 pub statements: Vec<Statement>,
553}
554
555/// One statement.
556#[derive(Debug, Clone, PartialEq, Eq)]
557pub enum Statement {
558 /// A declaration with an optional initialiser.
559 Declare {
560 /// Type.
561 type_: Type,
562 /// Name.
563 name: Ident,
564 /// Initialiser.
565 value: Option<Expr>,
566 },
567 /// An expression followed by `;`.
568 Expr(Expr),
569 /// `return expr;`
570 Return(Option<Expr>),
571 /// `if (cond) { .. } else { .. }`
572 If {
573 /// Condition.
574 condition: Expr,
575 /// Then-block.
576 then: Block,
577 /// Else-block.
578 otherwise: Option<Block>,
579 },
580 /// `for (init; cond; step) { .. }`
581 ///
582 /// Not [`Statement::ForEach`]. A generated codec walks a collection
583 /// by index because the index is what the wire needs — an element's
584 /// position decides its alignment and, in a `map` written in
585 /// ascending key order, which entry is written at all.
586 For {
587 /// Initialiser.
588 init: Box<Statement>,
589 /// Condition.
590 condition: Expr,
591 /// Step.
592 step: Expr,
593 /// Body.
594 body: Block,
595 },
596 /// `while (cond) { .. }`
597 ///
598 /// Not a `for` with the two clauses left empty. A decoder walks a
599 /// `@mutable` aggregate's headers until the delimiter is used up and
600 /// a PL_CDR1 list until its sentinel; neither has a counter.
601 While {
602 /// Condition.
603 condition: Expr,
604 /// Body.
605 body: Block,
606 },
607 /// `break;`
608 Break,
609 /// `for (auto& name : range) { .. }`
610 ForEach {
611 /// The binding.
612 binding: Ident,
613 /// What is iterated.
614 range: Expr,
615 /// The body.
616 body: Block,
617 },
618 /// `switch (value) { case ..: }`
619 Switch {
620 /// What is switched on.
621 value: Expr,
622 /// The cases, in written order.
623 cases: Vec<SwitchCase>,
624 },
625 /// `throw expr;`
626 ///
627 /// The Java tree has had one from the start and this one had not,
628 /// which held for as long as the only C++ output was data
629 /// declarations. A CORBA skeleton's default arm is
630 /// `throw CORBA::BAD_OPERATION();` — §8.3 — and a call expression
631 /// spelled to look like one would compile and then fall through.
632 Throw(Expr),
633 /// `try { .. } catch (T& name) { .. } catch (...) { .. }`
634 ///
635 /// A generated DDS-RPC service turns what an operation throws into the
636 /// case of the reply union that carries it (DDS-RPC 1.0 §7.5.1.1.5)
637 /// or into a remote exception code (§7.5.2); that needs a handler.
638 Try {
639 /// The guarded block.
640 body: Block,
641 /// The handlers, in written order.
642 handlers: Vec<CatchClause>,
643 },
644 /// A comment on its own line.
645 Comment(Doc),
646}
647
648/// One handler of a [`Statement::Try`].
649#[derive(Debug, Clone, PartialEq, Eq)]
650pub struct CatchClause {
651 /// The type caught, taken by const reference; `None` is `catch (...)`.
652 pub type_: Option<Type>,
653 /// The name the exception is bound to, absent when it is not used.
654 pub binding: Option<Ident>,
655 /// The handler's body.
656 pub body: Block,
657}
658
659/// One case of a switch.
660#[derive(Debug, Clone, PartialEq, Eq)]
661pub struct SwitchCase {
662 /// The labels; empty means `default:`.
663 pub labels: Vec<Expr>,
664 /// The body.
665 pub body: Block,
666 /// Whether it ends in `break;`.
667 pub breaks: bool,
668}
669
670/// An expression.
671#[derive(Debug, Clone, PartialEq, Eq)]
672pub enum Expr {
673 /// A literal.
674 Literal(Literal),
675 /// A name or qualified name.
676 Path(Path),
677 /// `f(args)`
678 Call {
679 /// What is called.
680 callee: Box<Expr>,
681 /// Arguments.
682 args: Vec<Expr>,
683 },
684 /// `value.name(args)`
685 MethodCall {
686 /// The receiver.
687 receiver: Box<Expr>,
688 /// The method.
689 name: Ident,
690 /// Arguments.
691 args: Vec<Expr>,
692 },
693 /// `a.b`
694 Member {
695 /// The value.
696 value: Box<Expr>,
697 /// The member.
698 name: Ident,
699 },
700 /// `a->b`
701 Arrow {
702 /// The pointer.
703 value: Box<Expr>,
704 /// The member.
705 name: Ident,
706 },
707 /// `a[i]`
708 Index {
709 /// The value.
710 value: Box<Expr>,
711 /// The index.
712 index: Box<Expr>,
713 },
714 /// `T{args}` or `T(args)`.
715 Construct {
716 /// The type.
717 type_: Box<Type>,
718 /// Arguments.
719 args: Vec<Expr>,
720 /// Whether braces are used.
721 braced: bool,
722 },
723 /// `op a`, written as given — `!a`, `-a`, `~a`.
724 ///
725 /// The tree carries no precedence, so a caller that needs the
726 /// operand bracketed writes [`Expr::Paren`] around it.
727 ///
728 /// Added because there was no form for it and the backend faked
729 /// one with `Binary { op, lhs: 0 }`. That reads as `0 - a` for a
730 /// negation, which is the same number, and as `0 ~ a` for a
731 /// complement, which is not an expression: 21 corpus cases stopped
732 /// compiling on `zd_bits & 0 ~ UINT64_C(32897)`.
733 Unary {
734 /// The operator, as written.
735 op: &'static str,
736 /// What it applies to.
737 value: Box<Expr>,
738 },
739 /// `a op b`
740 Binary {
741 /// The operator, as written.
742 op: &'static str,
743 /// Left.
744 lhs: Box<Expr>,
745 /// Right.
746 rhs: Box<Expr>,
747 },
748 /// `static_cast<T>(v)` and friends.
749 Cast {
750 /// Which cast.
751 kind: CastKind,
752 /// The target type.
753 type_: Box<Type>,
754 /// The value.
755 value: Box<Expr>,
756 },
757 /// `&a`
758 AddressOf(Box<Expr>),
759 /// `*a`
760 Deref(Box<Expr>),
761 /// `{ a, b }`
762 InitList(Vec<Expr>),
763 /// `sizeof(T)`
764 SizeOf(Box<Type>),
765 /// `(a)` — parentheses written on purpose.
766 ///
767 /// The same node the C tree carries and for the same reason: this
768 /// tree holds no operator precedence, so a renderer walking it
769 /// cannot know where a bracket is needed. A builder that needs one
770 /// says so. `Binary { op: "<<", lhs: Binary { op: "+", .. }, .. }`
771 /// renders as `a + b << c` without it, which is a different number
772 /// and compiles without a word — the C column met that shape in a
773 /// `#define` and the same composition is reachable here through a
774 /// `constexpr` initialiser or a case label.
775 Paren(Box<Expr>),
776}
777
778/// Which cast form.
779#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
780pub enum CastKind {
781 /// `static_cast`
782 Static,
783 /// `const_cast`
784 Const,
785 /// `reinterpret_cast`
786 Reinterpret,
787 /// `dynamic_cast`
788 Dynamic,
789}
790
791impl CastKind {
792 /// Every cast form.
793 pub const ALL: [Self; 4] = [Self::Static, Self::Const, Self::Reinterpret, Self::Dynamic];
794
795 /// How it is written.
796 #[must_use]
797 pub const fn as_str(self) -> &'static str {
798 match self {
799 Self::Static => "static_cast",
800 Self::Const => "const_cast",
801 Self::Reinterpret => "reinterpret_cast",
802 Self::Dynamic => "dynamic_cast",
803 }
804 }
805}
806
807/// The name a C++ keyword collision escapes to.
808#[must_use]
809pub fn escape_keyword(name: &str) -> String {
810 if RESERVED.contains(&name) {
811 let mut out = name.to_string();
812 out.push('_');
813 return out;
814 }
815 name.to_string()
816}
817
818/// The C++ keywords a generated identifier may collide with.
819///
820/// The C++17 set, which is what the emitters this replaces targeted.
821const RESERVED: &[&str] = &[
822 "alignas",
823 "alignof",
824 "and",
825 "asm",
826 "auto",
827 "bitand",
828 "bitor",
829 "bool",
830 "break",
831 "case",
832 "catch",
833 "char",
834 "class",
835 "compl",
836 "concept",
837 "const",
838 "consteval",
839 "constexpr",
840 "const_cast",
841 "continue",
842 "decltype",
843 "default",
844 "delete",
845 "do",
846 "double",
847 "dynamic_cast",
848 "else",
849 "enum",
850 "explicit",
851 "export",
852 "extern",
853 "false",
854 "float",
855 "for",
856 "friend",
857 "goto",
858 "if",
859 "inline",
860 "int",
861 "long",
862 "mutable",
863 "namespace",
864 "new",
865 "noexcept",
866 "not",
867 "nullptr",
868 "operator",
869 "or",
870 "private",
871 "protected",
872 "public",
873 "register",
874 "reinterpret_cast",
875 "requires",
876 "return",
877 "short",
878 "signed",
879 "sizeof",
880 "static",
881 "static_assert",
882 "static_cast",
883 "struct",
884 "switch",
885 "template",
886 "this",
887 "thread_local",
888 "throw",
889 "true",
890 "try",
891 "typedef",
892 "typeid",
893 "typename",
894 "union",
895 "unsigned",
896 "using",
897 "virtual",
898 "void",
899 "volatile",
900 "wchar_t",
901 "while",
902 "xor",
903];
904
905#[cfg(test)]
906mod tests {
907 #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
908 use super::*;
909
910 #[test]
911 fn every_primitive_writes_its_own_spelling() {
912 let mut seen = Vec::new();
913 for primitive in Primitive::ALL {
914 let text = primitive.as_str();
915 assert!(!seen.contains(&text), "{text} is used twice");
916 seen.push(text);
917 }
918 }
919
920 #[test]
921 fn access_is_a_section_rather_than_a_word_on_each_member() {
922 // The output writes `public:` once above a run; a tree storing
923 // access per member would leave the grouping to the renderer.
924 let class = Class {
925 doc: Doc::none(),
926 is_struct: false,
927 name: Ident::new("Point"),
928 bases: Vec::new(),
929 sections: alloc::vec![
930 Section {
931 access: Access::Public,
932 members: Vec::new(),
933 },
934 Section {
935 access: Access::Private,
936 members: Vec::new(),
937 },
938 ],
939 };
940 let order: Vec<Access> = class.sections.iter().map(|s| s.access).collect();
941 assert_eq!(order, alloc::vec![Access::Public, Access::Private]);
942 }
943
944 #[test]
945 fn a_keyword_escapes_with_a_trailing_underscore() {
946 assert_eq!(escape_keyword("class"), "class_");
947 assert_eq!(escape_keyword("operator"), "operator_");
948 assert_eq!(escape_keyword("value"), "value");
949 }
950
951 #[test]
952 fn a_defaulted_constructor_is_its_own_body_form() {
953 // `= default` is not an empty block: the two compile
954 // differently, and the tree has to keep them apart.
955 assert_ne!(
956 FunctionBody::Defaulted,
957 FunctionBody::Block(Block::default())
958 );
959 assert_ne!(FunctionBody::Deleted, FunctionBody::None);
960 }
961}