Skip to main content

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}