Skip to main content

zerodds_idl_lower/
lower.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The CST-to-model walk. Spec §4.
4//!
5//! Builds through the model's own edit surface, so a file that fails
6//! validation leaves nothing behind. Module reopening across files
7//! converges on one node through `Ast::ensure_module` — the frontend
8//! needs no dedup pass, which is what building the middle first bought.
9
10use crate::annotation::{Applied, UseSite};
11
12/// One argument of a parameterised type, with the annotations written
13/// immediately in front of it.
14///
15/// `sequence<@try_construct(TRIM) String5, 10>` puts both in the same
16/// child list, and the pairing is what tells the element apart from the
17/// bound.
18type TemplateArgument = (
19    alloc::vec::Vec<alloc::sync::Arc<SyntaxNode>>,
20    alloc::sync::Arc<SyntaxNode>,
21);
22use crate::scope::ScopeStack;
23use alloc::string::{String, ToString};
24use alloc::sync::Arc;
25use alloc::vec::Vec;
26use zerodds_ast::{
27    AnyId, Ast, BinaryOp, Bound, CharWidth, Completeness, ConstExpr, ConstExprNode, Facets,
28    InterfaceKind, Literal, MemberSpec, ParamDirection, PrimitiveKind, SourceFacet, SourceOrigin,
29    TypeKind, TypeNode, UnaryOp, ValuetypeKind,
30};
31use zerodds_ast_core::{Id, PendingRef, Ref, ScopedName};
32use zerodds_idl_cst::{CstKind, Span, SyntaxNode, SyntaxSpans, TokenKind};
33use zerodds_idl_parse::{Diagnostic, DiagnosticCode};
34
35/// What lowering produced.
36pub struct Lowered {
37    /// The model, built as far as the file allowed.
38    pub ast: Ast,
39    /// Everything wrong that the model itself did not already reject.
40    pub diagnostics: Vec<Diagnostic>,
41}
42
43/// What the caller may decide that the source does not.
44///
45/// Empty by default: everything the IDL did not say stays unsaid in the
46/// model, and whoever asks a question the source left open applies the
47/// rule then. An option here is the caller overruling that, which the
48/// model records as though the source had said it.
49#[derive(Debug, Clone, Copy)]
50pub struct Options {
51    /// What an aggregate of unspecified extensibility is taken to be.
52    ///
53    /// `None` leaves the facet absent, which keeps "the source said
54    /// appendable" apart from "the source said nothing" and lets
55    /// `Ast::effective_extensibility` and the layout key apply their
56    /// own default. `Some` writes it into the model, so every consumer
57    /// — the wire plan, the TypeObject, the generated code — sees one
58    /// answer.
59    ///
60    /// XTypes 1.3 §7.3.1.2.1.8 provides for exactly this:
61    ///
62    /// > In the event that the representation of a given type does not
63    /// > indicate the type's extensibility kind, the type shall be
64    /// > considered appendable. Implementations may provide a mechanism
65    /// > to override this default behavior; for example, IDL compilers
66    /// > may provide configuration options to allow users to specify
67    /// > whether types of unspecified extensibility are to be considered
68    /// > final, appendable, or mutable.
69    ///
70    /// What the source *did* say still wins: an `@final` annotation, an
71    /// `@extensibility(...)`, or a `#pragma` are all the source
72    /// speaking, and this option only fills the silence.
73    pub default_extensibility: Option<zerodds_ast::Extensibility>,
74    /// The `@default_nested` of the top-level modules that carry no
75    /// annotation of their own, and of types declared outside any module.
76    ///
77    /// DDS-XTypes 1.3 §7.3.1.2.1.7: "If a top-level module is not
78    /// annotated, the default is FALSE. […] IDL compilers shall provide
79    /// the means to change the default value for non-annotated top-level
80    /// modules." `None` leaves the specification's FALSE; `Some` writes
81    /// the answer onto the root module, where `Ast::effective_nested`
82    /// finds it for every type below that no module annotated.
83    pub default_nested: Option<bool>,
84    /// Whether a `@hashid` member advances the sequential counter the
85    /// next unannotated member is numbered from.
86    ///
87    /// `true` by default, and the default is a measurement rather than a
88    /// preference. DDS-XTypes 1.3 §7.3.1.2.1.1 states the rule twice and
89    /// not the same way: one sentence numbers a member from "the
90    /// most-recently specified ID (using the @id annotation …)", which
91    /// leaves a hashed id out of the count, and the next from "the
92    /// member ID of the previous member of the same Aggregated Type plus
93    /// one", which does not. Cyclone and Fast DDS take the second, RTI
94    /// the first — `crates/ast-layout/tests/fixtures/vendor_bytes.md`
95    /// §7. Three of the four against one.
96    ///
97    /// It is here and not in the layout key because member ids are fixed
98    /// in [`crate::complete`], before any plan exists.
99    /// `zerodds_ast_layout::Dialect::hashed_id_advances_the_counter` is
100    /// where the vendor fact lives; this is the switch it sets.
101    pub hashed_id_advances_the_counter: HashedIdCounter,
102    /// Which spelling of an escaped name a member-id hash reads.
103    ///
104    /// IDL 4.2 §7.2.3.2 makes the escape underscore "a purely lexical
105    /// convention" that is not part of the identifier, so the
106    /// specification's hash input for `_long` is `long` — the default.
107    /// RTI Connext hashes the spelling as written, keyword or not:
108    /// measured with rtiddsgen 7.7.0, whose TypeCode carries member id
109    /// 79659193 = hash("_long") and 230782087 = hash("_foo") where the
110    /// clause gives 56906255 and 186170796. Like the counter above it
111    /// is settled in [`crate::complete`], before any layout key exists;
112    /// `zerodds_ast_layout::Dialect` carries the vendor fact.
113    pub hashed_name_spelling: HashedNameSpelling,
114    /// Whether `@key` on a union discriminator is carried into the
115    /// model.
116    ///
117    /// A frontend axis in the shape [`HashedIdCounter`] has, and here
118    /// for the reason that one is here: the answer is settled while the
119    /// file is read, long before any plan exists, and two load paths
120    /// that answer it differently produce two different
121    /// `TypeIdentifier`s for one declaration.
122    ///
123    /// [`KeyOnUnionDiscriminator::Refused`] is the default and is what
124    /// every measured front end does — see that type for the runs.
125    pub key_on_union_discriminator: KeyOnUnionDiscriminator,
126    /// Whether a declaration may carry the name of the scope it sits in.
127    ///
128    /// A dialect axis, measured and not chosen: the corpus case
129    /// `resolve-module-named-as-type` — `module common { struct common
130    /// { long x; }; ... }` — is refused by Cyclone DDS and OpenDDS and
131    /// taken by Fast DDS and RTI Connext, and the ledger records four
132    /// octet rows on each side of that split. A reader that refuses the
133    /// file can never reproduce the octets the other two wrote, so the
134    /// refusal cannot live in the front end alone.
135    pub name_of_the_enclosing_scope: NameOfTheEnclosingScope,
136    /// Which vendors' extensions this lowering admits. Spec §3.5.
137    ///
138    /// Two `#pragma` families change the model rather than decorate it:
139    /// Cyclone's `#pragma keylist` and OpenDDS's `#pragma DCPS_DATA_TYPE`
140    /// / `DCPS_DATA_KEY` both make members keys. They are in no building
141    /// block of OMG IDL 4.2, so a file that needs one is a file another
142    /// vendor's front end reads differently — and under
143    /// `Dialect::STRICT` that is an error rather than a warning.
144    ///
145    /// Not part of the derived `Default`: see the `impl Default` below.
146    pub dialect: zerodds_idl_parse::Dialect,
147}
148
149/// The literal form a constant expression settles to.
150///
151/// Only what the lowering can decide from the expression itself. It is
152/// deliberately coarser than a type: the question here is IDL 4.2
153/// §7.4.1.4.3's "consistent with the type declared for the constant",
154/// not what the value's exact type is.
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156enum ValueForm {
157    /// `TRUE` / `FALSE`.
158    Boolean,
159    /// An integer literal.
160    Integer,
161    /// A floating-point literal.
162    Float,
163    /// A fixed-point literal.
164    Fixed,
165    /// A narrow character literal.
166    Char,
167    /// A wide character literal.
168    WChar,
169    /// A narrow string literal.
170    String,
171    /// A wide string literal.
172    WString,
173}
174
175impl ValueForm {
176    /// Whether arithmetic applies to it.
177    const fn is_numeric(self) -> bool {
178        matches!(self, Self::Integer | Self::Float | Self::Fixed)
179    }
180
181    /// How the diagnostic names it.
182    const fn describe(self) -> &'static str {
183        match self {
184            Self::Boolean => "a boolean",
185            Self::Integer => "an integer literal",
186            Self::Float => "a floating-point literal",
187            Self::Fixed => "a fixed-point literal",
188            Self::Char => "a character literal",
189            Self::WChar => "a wide character literal",
190            Self::String => "a string literal",
191            Self::WString => "a wide string literal",
192        }
193    }
194}
195
196/// `Options::default()` admits every vendor extension.
197///
198/// Written out rather than derived, because `Dialect::default()` is
199/// `STRICT` and the two defaults are deliberately opposite. A parser
200/// pointed at a file somebody else wrote has to read it before it judges
201/// it, so the lenient dialect is what a caller who did not think about
202/// the question gets; `--strict` is the caller who did.
203impl Default for Options {
204    fn default() -> Self {
205        Self {
206            default_extensibility: None,
207            default_nested: None,
208            hashed_id_advances_the_counter: HashedIdCounter::default(),
209            hashed_name_spelling: HashedNameSpelling::default(),
210            key_on_union_discriminator: KeyOnUnionDiscriminator::default(),
211            name_of_the_enclosing_scope: NameOfTheEnclosingScope::default(),
212            dialect: zerodds_idl_parse::Dialect::ALL_VENDORS,
213        }
214    }
215}
216
217/// Whether a declaration may reuse the name of its immediate scope.
218///
219/// A named type for the reason [`HashedNameSpelling`] is one: the
220/// default has to be the specification's reading rather than whatever
221/// `false` happens to mean.
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
223pub enum NameOfTheEnclosingScope {
224    /// Refused. IDL 4.2 §7.5.2: "The name of a module, structure,
225    /// union, map, interface, value type, event type, exception or home
226    /// may not be redefined within the immediate scope of" that same
227    /// construct. Cyclone DDS and OpenDDS agree, and this is what a
228    /// caller who did not ask for a vendor gets.
229    #[default]
230    Refused,
231    /// Taken. Fast DDS and RTI Connext accept the declaration and write
232    /// octets for it; a probe that reproduces either of them has to
233    /// reach the same model they did.
234    Admitted,
235}
236
237/// Which spelling of an escaped name a member-id hash reads.
238///
239/// A named type for the reason [`HashedIdCounter`] is one: `Options` is
240/// `Default`, and the default has to be the specification's reading, not
241/// whatever `false` happens to mean.
242#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
243pub enum HashedNameSpelling {
244    /// The identifier without its escape underscore — `long` for
245    /// `_long`. IDL 4.2 §7.2.3.2's reading.
246    #[default]
247    Stripped,
248    /// The spelling in the file — `_long`, `_foo`. RTI Connext's
249    /// measured reading.
250    AsWritten,
251}
252
253/// Whether a `@hashid` member advances the sequential counter.
254///
255/// A named type and not a bare `bool`, because `Options` is `Default`
256/// and a boolean field defaulting to `false` would silently take RTI's
257/// reading for every caller that did not think about it. This defaults
258/// to the reading three of the four implementations take.
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
260pub enum HashedIdCounter {
261    /// The next unannotated member is the hashed id plus one. Cyclone,
262    /// Fast DDS and the second of §7.3.1.2.1.1's two sentences.
263    #[default]
264    Advances,
265    /// The hashed id is skipped: the next unannotated member is numbered
266    /// from the last *declared* id, or from zero when there is none.
267    /// RTI Connext, and the first of the two sentences.
268    Skipped,
269}
270
271/// Whether `@key` on a union discriminator is carried into the model.
272///
273/// # The clause permits it
274///
275/// DDS-XTypes 1.3 Table 21 lists `@key` against "Structure Members,
276/// Union discriminator", and §7.2.2.4.4.4.8's own example is
277///
278/// ```idl
279/// union MyCommand switch (@key CommandKind) { /* … */ };
280/// ```
281///
282/// so the default is to carry it: a front end that refuses the
283/// declaration, or tells the author the annotation "has no meaning"
284/// there, states the opposite of the specification.
285///
286/// # What the implementations do
287///
288/// Measured on the four toolchains the Linux test host carries:
289///
290/// | | `union U switch (@key uint32)` |
291/// |---|---|
292/// | Cyclone `idlc` | refuses the file — "Using union type as part of the key is currently unsupported" |
293/// | `rtiddsgen` | refuses it at the discriminator — "@key is not supported" |
294/// | `fastddsgen` | generates no `PubSubType` for a union at all, so it has no keyed topic to compare against |
295/// | `opendds_idl` | not recorded in that run |
296///
297/// So under [`Self::Carried`] this project computes a KeyHash over a
298/// union discriminator that no measured peer computes. [`Self::Refused`]
299/// is the setting for a tool that has to stay within what those
300/// toolchains accept; it names the clause that permits the declaration
301/// in its diagnostic.
302///
303/// # Why the OMG suite needs it
304///
305/// `union_uint32_one_key` pairs `unions.xml`'s `union_uint32` against
306/// `unions_key_discriminator.xml`'s `union_uint32_key`, whose
307/// discriminator carries `key="true"`, and expects `INCONSISTENT_TOPIC`
308/// on both sides: §7.2.4.4.4.5 requires that "either the discriminators
309/// of both T1 and T2 are keys or neither are keys". Under
310/// [`Self::Refused`] the subscriber never creates the topic and the case
311/// reports `TOPIC_NOT_CREATED`; under [`Self::Carried`] the declaration
312/// loads, the discriminator's `IS_KEY` reaches its `TypeObject`, and the
313/// two types are found not assignable — which is the verdict the suite
314/// asks for.
315///
316/// A named type and not a `bool` for the reason [`HashedIdCounter`] is
317/// one: `Options` is `Default`, and a boolean defaulting to `false`
318/// would carry no statement about which reading that is.
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
320pub enum KeyOnUnionDiscriminator {
321    /// The annotation is reported and the declaration does not carry
322    /// it. What the measured toolchains do.
323    Refused,
324    /// The annotation is carried: the model records it, the
325    /// discriminator's `UnionDiscriminatorFlag` gets `IS_KEY`, the
326    /// union is a keyed topic type and its KeyHash is computed over the
327    /// discriminator alone. **The default**, as Table 21 and
328    /// §7.2.2.4.4.4.8 have it.
329    #[default]
330    Carried,
331}
332
333impl KeyOnUnionDiscriminator {
334    /// Both.
335    pub const ALL: [Self; 2] = [Self::Refused, Self::Carried];
336
337    /// Whether this is the default.
338    #[must_use]
339    pub const fn is_default(self) -> bool {
340        matches!(self, Self::Carried)
341    }
342
343    /// Short name, for configuration and diagnostics.
344    #[must_use]
345    pub const fn as_str(self) -> &'static str {
346        match self {
347            Self::Refused => "refused",
348            Self::Carried => "carried",
349        }
350    }
351
352    /// Parse a configuration value.
353    ///
354    /// An unknown value returns `None` rather than falling back to the
355    /// default: a typo here changes a type's `TypeIdentifier`.
356    #[must_use]
357    pub fn parse(value: &str) -> Option<Self> {
358        match value {
359            "refused" => Some(Self::Refused),
360            "carried" => Some(Self::Carried),
361            _ => None,
362        }
363    }
364}
365
366/// Lower a parsed file into a fresh model.
367#[must_use]
368pub fn lower(root: &Arc<SyntaxNode>, path: &str) -> Lowered {
369    lower_with(root, path, Options::default())
370}
371
372/// Lower a parsed file into a fresh model, deciding what the source left
373/// open.
374#[must_use]
375pub fn lower_with(root: &Arc<SyntaxNode>, path: &str, options: Options) -> Lowered {
376    let mut ast = Ast::new();
377    let diagnostics = lower_into_with(&mut ast, root, path, options);
378    Lowered { ast, diagnostics }
379}
380
381/// Lower a parsed file into an existing model.
382///
383/// The second file of a two-file build goes here, which is where module
384/// reopening has to converge rather than duplicate.
385pub fn lower_into(ast: &mut Ast, root: &Arc<SyntaxNode>, path: &str) -> Vec<Diagnostic> {
386    lower_into_with(ast, root, path, Options::default())
387}
388
389/// Lower a parsed file into an existing model, deciding what the source
390/// left open.
391pub fn lower_into_with(
392    ast: &mut Ast,
393    root: &Arc<SyntaxNode>,
394    path: &str,
395    options: Options,
396) -> Vec<Diagnostic> {
397    let mut ctx = Lowering {
398        ast,
399        scopes: ScopeStack::new_placeholder(),
400        diagnostics: Vec::new(),
401        path: path.to_string(),
402        syntax_spans: SyntaxSpans::of(root),
403        options,
404        pending_annotations: Vec::new(),
405        pragma_extensibility: None,
406        repository_directives: Vec::new(),
407        module_ranges: Vec::new(),
408        keylists: Vec::new(),
409        prefixes: Vec::new(),
410        pragma_prefixes: Vec::new(),
411        pragma_versions: Vec::new(),
412        open_dds_types: Vec::new(),
413        open_dds_keys: Vec::new(),
414        unrecognised_pragmas: Vec::new(),
415        xtopics_regions: Vec::new(),
416        templates: Vec::new(),
417    };
418    ctx.scopes = ScopeStack::new(ctx.ast.root_module());
419    ctx.file(root);
420    // Completion runs over the finished model: a rule that reads the
421    // previous member's id needs every member to be there first.
422    ctx.complete();
423    // Validation last: a rule about member ids needs the derived ones,
424    // and a rule about forward declarations needs the whole file.
425    ctx.validate();
426    // A diagnostic points at text, not at the whitespace before it. The
427    // ranges that came from a node still carry that node's leading
428    // trivia, so each one — the primary and every label — is narrowed
429    // here, at the one place every diagnostic of the walk passes
430    // through. A range holding no syntax at all keeps what it had.
431    let mut diagnostics = core::mem::take(&mut ctx.diagnostics);
432    for diagnostic in &mut diagnostics {
433        diagnostic.primary = ctx.syntax_spans.trim(diagnostic.primary);
434        for (span, _) in &mut diagnostic.labels {
435            *span = ctx.syntax_spans.trim(*span);
436        }
437    }
438    // One fact, one diagnostic: a collision the model refused and the
439    // validation pass found again is the same collision.
440    dedup(diagnostics)
441}
442
443/// The walk's state.
444///
445/// Shared with the sibling modules — expressions, annotations,
446/// completion and validation are separate files over one walk, not
447/// separate passes over the tree.
448pub(crate) struct Lowering<'a> {
449    pub(crate) ast: &'a mut Ast,
450    pub(crate) scopes: ScopeStack,
451    pub(crate) diagnostics: Vec<Diagnostic>,
452    pub(crate) path: String,
453    /// Where the file's syntax is, with the trivia left out.
454    ///
455    /// A range taken from the tree begins at the node's leading trivia,
456    /// because that is where the lexer attaches it — right for the round
457    /// trip, wrong for anything a person reads. Every range this walk
458    /// records on a node and every range it puts in a diagnostic is
459    /// narrowed through this index first.
460    pub(crate) syntax_spans: SyntaxSpans,
461    /// What the caller decided that the source does not say.
462    pub(crate) options: Options,
463    /// The extensibility an enclosing `#pragma` asked for, which a type
464    /// without its own annotation inherits. Spec §5.
465    pub(crate) pragma_extensibility: Option<zerodds_ast::Extensibility>,
466    /// Annotations read from an enclosing wrapper node, waiting for the
467    /// declaration they modify to exist.
468    ///
469    /// A stack rather than one slot: an annotated declaration may
470    /// contain another one, and the inner annotations must not be
471    /// applied to the outer node.
472    pub(crate) pending_annotations: Vec<Vec<crate::annotation::Applied>>,
473    /// `typeid` and `typeprefix` directives, applied once the whole file
474    /// is walked: both may name a declaration that comes later.
475    ///
476    /// The name is kept as every scope it could denote, innermost first
477    /// — §7.5's lookup — because a directive inside a module may name a
478    /// sibling, its own module, or anything further out.
479    pub(crate) repository_directives: Vec<(Vec<ScopedName>, String, bool)>,
480    /// Every range a module's body was written in, with the module's
481    /// scoped name — one entry per opening, since a module may be reopened.
482    /// `#pragma prefix` and the type name of a `#pragma keylist` are read
483    /// in the module around them, which only these ranges know: a module
484    /// node carries no source range of its own.
485    pub(crate) module_ranges: Vec<(Span, ScopedName)>,
486    /// `keylist` directives, applied for the same reason: the scopes the
487    /// type name could denote (innermost first), the member names or
488    /// paths, and where the directive stands.
489    pub(crate) keylists: Vec<(Vec<ScopedName>, Vec<String>, Span)>,
490    /// `typeprefix` scopes, longest match first when a repository id is
491    /// derived.
492    pub(crate) prefixes: Vec<(ScopedName, String)>,
493    /// `#pragma prefix` scopes, which do the same thing in the older
494    /// spelling.
495    pub(crate) pragma_prefixes: Vec<(ScopedName, String)>,
496    /// `#pragma version X 1.2`, which replaces the `1.0` in a derived
497    /// repository id.
498    pub(crate) pragma_versions: Vec<(ScopedName, String)>,
499    /// `#pragma DCPS_DATA_TYPE "X"`.
500    pub(crate) open_dds_types: Vec<String>,
501    /// `#pragma DCPS_DATA_KEY X f`.
502    pub(crate) open_dds_keys: Vec<(String, Vec<String>, Span)>,
503    /// Pragmas with no typed meaning, kept verbatim on the root module.
504    pub(crate) unrecognised_pragmas: Vec<(String, String)>,
505    /// The `#pragma dds_xtopics begin` declarations still open, with the
506    /// version each named, innermost last. DDS-XTypes 1.3 §7.3.1.1.2.
507    ///
508    /// A stack because the clause nests them: "In the event that such
509    /// pragma declarations are nested within one another, the innermost
510    /// version number specified, if any, shall be in effect." Nothing
511    /// closes it at the end of the file, because the same clause says a
512    /// `begin` without a matching `end` "shall be considered to continue
513    /// until the end of the IDL input" — which is a complete region and
514    /// not a defect.
515    pub(crate) xtopics_regions: Vec<(Span, String)>,
516    /// The body of every template module declared so far, by the scoped
517    /// name it was declared under.
518    ///
519    /// A template module is not a scope with types in it — §7.4.12: its
520    /// contents are definitions *of a template*, and a definition whose
521    /// type is a formal parameter has no type until an actual one is
522    /// given. Lowering the body in place is what left `module
523    /// M<typename T> { struct S { T a; }; };` with `S.a` pending for
524    /// ever, and the block reporting success on a model that was not
525    /// one. The body is kept here and lowered once per instantiation.
526    pub(crate) templates: Vec<(ScopedName, Arc<SyntaxNode>)>,
527}
528
529impl Lowering<'_> {
530    fn file(&mut self, node: &Arc<SyntaxNode>) {
531        self.identifiers_do_not_collide_with_keywords(node);
532        for child in node.children() {
533            self.definition(&child);
534        }
535        // Pragmas are read after the walk: `#pragma ID X` names a
536        // declaration that may come after it, and the scope a `#pragma
537        // prefix` applies to is the module whose range contains it.
538        self.collect_pragmas(node);
539    }
540
541    /// An identifier that differs from a keyword only in case.
542    ///
543    /// OMG IDL 4.2 §7.2.4, p. 20: "Keywords must be written exactly as
544    /// shown in the above list. Identifiers that collide with keywords
545    /// (see 7.2.3, Identifiers) are illegal." §7.2.3 supplies the
546    /// comparison — "upper- and lower-case letters are treated as the
547    /// same letter" — and its worked example is `typedef boolean
548    /// BOOLEAN;`.
549    ///
550    /// Two rules, and the lexer used to apply the first as though it
551    /// were the second: it matched keywords without regard to case, so
552    /// `enum E { CUSTOM };` parsed as though the author had written the
553    /// keyword `custom` there. The lexer now matches exactly, which
554    /// leaves `CUSTOM` an ordinary identifier and leaves this the place
555    /// the clause is enforced.
556    ///
557    /// Over the token stream and not over the model, because the rule is
558    /// about the spelling in the file: a name that collides is illegal
559    /// wherever it is written, whether it declares something, names a
560    /// type or stands for a value. Five of the seven foreign front ends
561    /// measured on the Linux test host answer the same way, three of
562    /// them naming the keyword — OpenDDS and TAO with "identifier
563    /// spellings differ only in case", JacORB with "Identifier CUSTOM
564    /// collides with keyword custom".
565    ///
566    /// The escaped form is exempt. §7.2.3.2: prefixing an underscore "is
567    /// a purely lexical convention that ONLY turns off keyword
568    /// checking", which is exactly the check this is.
569    fn identifiers_do_not_collide_with_keywords(&mut self, root: &Arc<SyntaxNode>) {
570        let mut found: Vec<(Span, String, &'static str)> = Vec::new();
571        for node in root.descendants() {
572            for (span, token) in node.tokens() {
573                if token.kind() != TokenKind::Ident {
574                    continue;
575                }
576                let text = token.text();
577                // `_AnIdentifier` is the name `AnIdentifier` with the
578                // keyword check turned off, so it is not asked at all.
579                if text.starts_with('_') {
580                    continue;
581                }
582                if let Some(keyword) = zerodds_idl_cst::keyword_collision(text) {
583                    found.push((span, text.to_string(), keyword));
584                }
585            }
586        }
587        for (span, name, keyword) in found {
588            let message = alloc::format!(
589                "`{name}` differs from the keyword `{keyword}` only in case, and IDL compares \
590                 identifiers without regard to case. Write the keyword's own spelling, or escape \
591                 the identifier as `_{name}`"
592            );
593            self.diagnose_at(
594                DiagnosticCode::KeywordCollision,
595                span,
596                &message,
597                "OMG IDL 4.2 §7.2.3, §7.2.4",
598            );
599        }
600    }
601
602    /// Every `#pragma` in the file, with where it was written.
603    ///
604    /// The preprocessor leaves them alone — a pragma carries declaration
605    /// metadata rather than affecting expansion — so they arrive here as
606    /// the directive trivia they were, still in the tree.
607    fn collect_pragmas(&mut self, root: &Arc<SyntaxNode>) {
608        let mut found: Vec<(Span, String)> = Vec::new();
609        for node in root.descendants() {
610            for (span, token) in node.tokens() {
611                if token.kind() != TokenKind::Directive {
612                    continue;
613                }
614                let line = token.text().trim();
615                let Some(rest) = line.strip_prefix('#') else {
616                    continue;
617                };
618                let rest = rest.trim_start();
619                let Some(body) = rest.strip_prefix("pragma") else {
620                    continue;
621                };
622                found.push((span, body.trim().to_string()));
623            }
624        }
625        // Source order, and not the order the walk happened to reach
626        // the tokens in: `#pragma dds_xtopics begin` and its `end` are
627        // a pair whose nesting DDS-XTypes 1.3 §7.3.1.1.2 reads off the
628        // order they were written in, and a `keylist` naming a type
629        // declared twice reaches the same reading only that way.
630        found.sort_by_key(|(span, _)| span.start);
631        for (span, body) in found {
632            self.pragma(span, &body);
633        }
634    }
635
636    /// One `#pragma`, routed by its first word.
637    fn pragma(&mut self, span: Span, body: &str) {
638        let mut words = body.split_whitespace();
639        let Some(directive) = words.next() else {
640            return;
641        };
642        let rest: Vec<&str> = words.collect();
643        match directive {
644            // CORBA 3.3 §10.7: the three that shape repository ids.
645            "prefix" => {
646                if let Some(text) = unquote(rest.first().copied().unwrap_or_default()) {
647                    let scope = self.module_containing(span);
648                    self.record_repository_directive("typeprefix", &text);
649                    self.pragma_prefixes.push((scope, text));
650                }
651            }
652            "version" => {
653                if let (Some(name), Some(version)) = (rest.first(), rest.get(1)) {
654                    self.pragma_versions
655                        .push((pragma_name(name), (*version).to_string()));
656                }
657            }
658            "ID" => {
659                if let (Some(name), Some(id)) = (rest.first(), rest.get(1).copied()) {
660                    if let Some(text) = unquote(id) {
661                        let candidates = self.pragma_candidates(span, name);
662                        self.record_repository_directive("typeid", &text);
663                        self.repository_directives.push((candidates, text, false));
664                    }
665                }
666            }
667            // Cyclone DDS says keys the way RTI says `keylist`.
668            "keylist" => {
669                self.diagnose_vendor(
670                    zerodds_idl_parse::VendorFlag::Cyclone,
671                    span,
672                    "`#pragma keylist` is a Cyclone DDS directive and is in no building block \
673                     of OMG IDL 4.2; it makes members keys, so a front end that ignores it \
674                     builds a different type from the same file",
675                );
676                // OpenSplice's `idlpp` and Cyclone's `idlc` both take
677                // `a, b` as well as `a b` (measured, 11.0.1 and 6.9).
678                if let Some((name, fields)) = rest.split_first() {
679                    let fields = fields
680                        .iter()
681                        .flat_map(|f| f.split(','))
682                        .filter(|f| !f.is_empty())
683                        .map(ToString::to_string)
684                        .collect();
685                    let candidates = self.pragma_candidates(span, name);
686                    self.keylists.push((candidates, fields, span));
687                }
688            }
689            "DCPS_DATA_TYPE" => {
690                self.diagnose_vendor(
691                    zerodds_idl_parse::VendorFlag::OpenDds,
692                    span,
693                    "`#pragma DCPS_DATA_TYPE` is an OpenDDS directive and is in no building \
694                     block of OMG IDL 4.2; it marks the type as a topic type, so a front end \
695                     that ignores it builds a different type from the same file. The same \
696                     spelling is also OMG DDS 1.2 §8's DLRL directive that marks a DLRL object \
697                     type — this front end reads it as OpenDDS's, and `zerodds-idlc dlrl` \
698                     reads it as DLRL's",
699                );
700                // Quoted as OpenDDS writes it, or bare; either names the type.
701                match rest.first().copied() {
702                    Some(word) => {
703                        self.open_dds_types
704                            .push(unquote(word).unwrap_or_else(|| word.to_string()));
705                    }
706                    None => self.diagnose_at(
707                        DiagnosticCode::Unsupported,
708                        span,
709                        "`#pragma DCPS_DATA_TYPE` names no type",
710                        "OMG DDS 1.4 §2.2.1.2.2",
711                    ),
712                }
713            }
714            "DCPS_DATA_KEY" => {
715                self.diagnose_vendor(
716                    zerodds_idl_parse::VendorFlag::OpenDds,
717                    span,
718                    "`#pragma DCPS_DATA_KEY` is an OpenDDS directive and is in no building \
719                     block of OMG IDL 4.2; it makes a member a key, so a front end that \
720                     ignores it builds a different type from the same file. The same spelling \
721                     is also OMG DDS 1.2 §8's DLRL directive that marks a DLRL object key — \
722                     this front end reads it as OpenDDS's, and `zerodds-idlc dlrl` reads it as \
723                     DLRL's",
724                );
725                // OpenDDS writes the type and the field in one string,
726                // `"Robot::Pose id"`; the unquoted form `Robot::Pose id` is
727                // read the same.
728                let joined = rest.join(" ");
729                let words: Vec<String> = unquote(&joined)
730                    .unwrap_or(joined)
731                    .split_whitespace()
732                    .map(String::from)
733                    .collect();
734                if let Some((name, fields)) = words.split_first() {
735                    self.open_dds_keys
736                        .push((name.clone(), fields.to_vec(), span));
737                } else {
738                    self.diagnose_at(
739                        DiagnosticCode::Unsupported,
740                        span,
741                        "`#pragma DCPS_DATA_KEY` names neither a type nor a member; \
742                         the directive keys nothing",
743                        "OMG DDS 1.4 §2.2.1.2.2",
744                    );
745                }
746            }
747            // OMG DDS 1.2 §8.2.2 — the third DLRL pragma, and the one
748            // OpenDDS never repurposed: `DCPS_DATA_TYPE`/`DCPS_DATA_KEY`
749            // collide with OpenDDS's own two-pragma dialect above, this
750            // one names nothing OpenDDS defines. Not routed through
751            // `diagnose_vendor`: it is not a vendor extension admitted
752            // under `ALL_VENDORS` and rejected under `--strict` — it is
753            // an OMG-specification construct this front end simply does
754            // not turn into a type-model facet, the same standing
755            // `dds_xtopics` below has. Kept in the bag as well as
756            // reported, so a round trip writes it back; `zerodds-idlc
757            // dlrl` is what reads it, together with the other two, into
758            // DLRL object and home classes.
759            "DCPS_DLRL_RELATION" => {
760                self.warn_at(
761                    DiagnosticCode::Unsupported,
762                    span,
763                    "`#pragma DCPS_DLRL_RELATION` is OMG DDS 1.2 §8's DLRL relation \
764                     directive; this front end does not turn it into a type-model facet — \
765                     `zerodds-idlc dlrl` reads it, together with `DCPS_DATA_TYPE` and \
766                     `DCPS_DATA_KEY`, into DLRL object and home classes",
767                    "OMG DDS 1.2 §8.2.2",
768                );
769                self.unrecognised_pragmas
770                    .push((directive.to_string(), rest.join(" ")));
771            }
772            // DDS-XTypes 1.3 §7.3.1.1.2. Kept in the bag below as
773            // well as read: the model has no typed field for it, and a
774            // round trip has to write back what was there.
775            "dds_xtopics" => {
776                self.dds_xtopics(span, &rest);
777                self.unrecognised_pragmas
778                    .push((directive.to_string(), rest.join(" ")));
779            }
780            // A pragma nobody recognises is kept rather than dropped: a
781            // round trip back to that vendor's dialect needs it, and the
782            // model has a place for exactly this.
783            //
784            // **And it is reported.** Keeping it was the whole of the
785            // handling, so `#pragma this_pragma_does_not_exist_anywhere`
786            // and `#pragma keylist Sample id` were indistinguishable
787            // from the outside: nought errors, nought warnings, exit
788            // zero, even under `--strict --deny-warnings`. A pragma is
789            // the one place in IDL where a typo has no syntax to fail
790            // against, so silence there is silence about a directive
791            // that was meant to change the type.
792            //
793            // A warning and not an error, and the level is the
794            // specification's rather than a choice. DDS-XTypes 1.3
795            // §7.3.1.1.1 on constructs outside the building blocks it
796            // uses: "Compilers that do not support them shall ignore
797            // them or issue a warning rather than halting with an
798            // error." Ignoring is what was measured and is what the
799            // finding is about, so the other permitted level is the one
800            // taken — and `--deny-warnings` is the caller who wants it
801            // to fail.
802            other => {
803                self.warn_at(
804                    DiagnosticCode::Unsupported,
805                    span,
806                    &alloc::format!(
807                        "`#pragma {other}` is not a directive this front end interprets; it is \
808                         kept verbatim on the model and changes nothing"
809                    ),
810                    "DDS-XTypes 1.3 §7.3.1.1.1",
811                );
812                self.unrecognised_pragmas
813                    .push((other.to_string(), rest.join(" ")));
814            }
815        }
816    }
817
818    /// `#pragma dds_xtopics begin [<version>]` and its `end`.
819    /// DDS-XTypes 1.3 §7.3.1.1.2.
820    ///
821    /// The clause defines the directive in exactly those two forms, as
822    /// a marker "to indicate to their tools and to human readers that
823    /// their IDL file (or a portion of it) makes use of constructs
824    /// defined by this specification". It carries no meaning for a
825    /// declaration: the same clause calls the declarations "informative
826    /// only", which is why nothing here reaches a type. What it does
827    /// carry is a version, and the clause names three things that can
828    /// be wrong with a pair of them.
829    ///
830    /// Every one of them is a warning. The clause admits "halting with
831    /// an error" as one behaviour a compiler may choose, and the
832    /// requirement it states in its own words is a "should" — "those
833    /// version numbers should be the same as those used with the
834    /// matching 'begin' declarations". A marker the specification calls
835    /// informative does not stop a build; `--deny-warnings` is the
836    /// caller who wants it to.
837    fn dds_xtopics(&mut self, span: Span, rest: &[&str]) {
838        let spec = "DDS-XTypes 1.3 §7.3.1.1.2";
839        let Some((keyword, version)) = rest.split_first() else {
840            self.warn_at(
841                DiagnosticCode::Unsupported,
842                span,
843                "`#pragma dds_xtopics` was written with neither `begin` nor `end`; \
844                 §7.3.1.1.2 defines the directive in those two forms only",
845                spec,
846            );
847            return;
848        };
849        let version = xtopics_version(version);
850        match *keyword {
851            "begin" => self.xtopics_regions.push((span, version)),
852            "end" => {
853                let Some((opened_at, opened)) = self.xtopics_regions.pop() else {
854                    self.warn_at(
855                        DiagnosticCode::Unsupported,
856                        span,
857                        "`#pragma dds_xtopics end` closes a region no `begin` opened",
858                        spec,
859                    );
860                    return;
861                };
862                if opened.is_empty() || version.is_empty() || opened == version {
863                    return;
864                }
865                let message = alloc::format!(
866                    "`#pragma dds_xtopics end {version}` closes a region opened as \
867                     `begin {opened}`; §7.3.1.1.2 has the two version numbers the same"
868                );
869                self.diagnostics.push(
870                    Diagnostic::warning(DiagnosticCode::Unsupported, span, message, spec)
871                        .with_label(opened_at, alloc::format!("opened as `begin {opened}`")),
872                );
873            }
874            other => self.warn_at(
875                DiagnosticCode::Unsupported,
876                span,
877                &alloc::format!(
878                    "`#pragma dds_xtopics {other}` is neither `begin` nor `end`; \
879                     §7.3.1.1.2 defines the directive in those two forms only"
880                ),
881                spec,
882            ),
883        }
884    }
885
886    /// The innermost module whose source range contains `span`.
887    /// Every scope the type name a `#pragma` writes could denote, innermost
888    /// first — [`Self::directive_candidates`] for a directive the walk no
889    /// longer stands in, whose scope is the module around its position.
890    ///
891    /// `pragma_name` alone read every name from the root: `module M {
892    /// struct T {…}; #pragma keylist T k };` named `::T`, which does not
893    /// exist, and the directive keyed nothing — the placement OpenSplice's
894    /// `idlpp` requires for it. A name written `::T` still means the root
895    /// and nothing else.
896    fn pragma_candidates(&self, span: Span, written: &str) -> Vec<ScopedName> {
897        let name = pragma_name(written);
898        if written.starts_with("::") {
899            return alloc::vec![name];
900        }
901        let module = self.module_containing(span);
902        let enclosing = module.segments();
903        (0..=enclosing.len())
904            .rev()
905            .map(|depth| {
906                let mut path = enclosing[..depth].to_vec();
907                path.extend_from_slice(name.segments());
908                ScopedName::absolute(path)
909            })
910            .collect()
911    }
912
913    fn module_containing(&self, span: Span) -> ScopedName {
914        // The narrowest range around `span`: modules nest, and the
915        // innermost is the one a directive stands in.
916        self.module_ranges
917            .iter()
918            .filter(|(range, _)| range.start <= span.start && range.end >= span.end)
919            .min_by_key(|(range, _)| range.end.saturating_sub(range.start))
920            .map_or_else(
921                || ScopedName::absolute(Vec::new()),
922                |(_, scoped)| scoped.clone(),
923            )
924    }
925
926    /// One declaration, with whatever annotations reach it.
927    ///
928    /// A prefix annotation sits in the wrapper node the parser puts
929    /// around the declaration, and [`Self::apply_waiting_annotations`]
930    /// reads it from there. The suffix form of DDS-XTypes 1.3
931    /// §7.3.1.2.5, Table 24 — `}; //@my_type_annotation` — has no
932    /// wrapper to sit in: it belongs to the declaration it follows, so
933    /// the parser puts it inside the declaration's own node. Both reach
934    /// the same frame here, which is what makes Table 24's claim that
935    /// the two forms are equivalent true of a type as well as of a
936    /// member.
937    fn definition(&mut self, node: &Arc<SyntaxNode>) {
938        let suffix = self.suffix_annotations_of(node);
939        // DDS-XTypes 1.3 §7.3.1.2.4, Table 22: `struct <ann_identifier> {
940        // <ann_members> }; //@annotation` "is actually an annotation type
941        // containing the members <ann_members>". It is the declaration of
942        // an annotation for compilers that do not know the `@annotation`
943        // token, not a structure that the annotation `@annotation` is
944        // applied to.
945        if node.kind() == CstKind::StructDecl
946            && suffix
947                .iter()
948                .any(|applied| applied.name == "annotation" && applied.arguments.is_empty())
949        {
950            self.annotation_from_struct(node);
951            return;
952        }
953        if suffix.is_empty() {
954            self.definition_inner(node);
955            return;
956        }
957        let outer = self.pending_annotations.pop();
958        let mut frame = outer.clone().unwrap_or_default();
959        frame.extend(suffix);
960        self.pending_annotations.push(frame);
961        self.definition_inner(node);
962        let _ = self.pending_annotations.pop();
963        if let Some(outer) = outer {
964            self.pending_annotations.push(outer);
965        }
966    }
967
968    fn definition_inner(&mut self, node: &Arc<SyntaxNode>) {
969        match node.kind() {
970            CstKind::Module => self.module(node),
971            CstKind::TemplateModuleInst => self.template_module_inst(node),
972            CstKind::TemplateModuleRef => self.template_module_ref(node),
973            CstKind::StructDecl => self.structure(node),
974            CstKind::EnumDecl => self.enumeration(node),
975            CstKind::BitmaskDecl => self.bitmask(node),
976            CstKind::UnionDecl => self.union(node),
977            CstKind::TypedefDecl => self.typedef(node),
978            CstKind::ExceptionDecl => self.exception(node),
979            CstKind::ForwardDecl => self.forward(node),
980            CstKind::NativeDecl => self.native(node),
981            // An annotated declaration wraps its annotations and the
982            // declaration itself. The annotations are read here and
983            // applied to whatever the inner declaration produced, which
984            // is why `declare_type` reports the node it made.
985            CstKind::Member => {
986                let annotations = self.annotations_of(node);
987                self.pending_annotations.push(annotations);
988                for child in node.children() {
989                    self.definition(&child);
990                }
991                let _ = self.pending_annotations.pop();
992            }
993            CstKind::InterfaceDecl => self.interface(node),
994            CstKind::ValuetypeDecl => self.valuetype(node),
995            CstKind::ValueBoxDecl => self.value_box(node),
996            CstKind::ConstDecl => self.constant(node),
997            CstKind::BitsetDecl => self.bitset(node),
998            CstKind::AnnotationDecl => self.annotation_decl(node),
999            CstKind::RepositoryDirective => self.repository_directive(node),
1000            CstKind::ImportDecl => self.import(node),
1001            CstKind::KeylistDecl => self.keylist(node),
1002            CstKind::Error => {}
1003            _ => {
1004                for child in node.children() {
1005                    self.definition(&child);
1006                }
1007            }
1008        }
1009    }
1010
1011    fn module(&mut self, node: &Arc<SyntaxNode>) {
1012        let Some(name) = first_ident(node) else {
1013            return;
1014        };
1015        let Some(parent) = self.scopes.current_module() else {
1016            return;
1017        };
1018        // Reopening converges here rather than duplicating. Spec §4.
1019        let Ok(module) = self.ast.ensure_module(parent, &name) else {
1020            return;
1021        };
1022        if let Some(parameters) = node
1023            .children()
1024            .into_iter()
1025            .find(|child| child.kind() == CstKind::TemplateParameters)
1026        {
1027            self.template_parameters(&parameters, module);
1028            // The body is the template's, not the model's. It is kept
1029            // whole and lowered once per instantiation, with the formal
1030            // parameters bound to what the instantiation supplied.
1031            let qualified = self.scopes.qualify(&name);
1032            self.templates.push((qualified, node.clone()));
1033            return;
1034        }
1035        // A module is an annotation target of its own. DDS-XTypes 1.3
1036        // §7.3.1.2.1.7 puts `@default_nested` on one, and Table 21 lists
1037        // "Module" for it; the annotations in front of the declaration
1038        // reached nothing until this call, so `@default_nested(TRUE)
1039        // module M { … }` was parsed and dropped.
1040        //
1041        // Before the body, because a reopened module keeps the facets of
1042        // its first appearance and the annotation belongs to the
1043        // declaration that carries it.
1044        if let Ok(node_id) = self.ast.module(module).map(|m| m.node_id) {
1045            self.apply_waiting_annotations(AnyId::Module(module, node_id));
1046        }
1047        let range = self.syntax_spans.trim(node.span());
1048        self.module_ranges.push((range, self.scopes.qualify(&name)));
1049        self.scopes.push(&name, Some(module));
1050        for child in node.children() {
1051            self.definition(&child);
1052        }
1053        self.scopes.pop();
1054    }
1055
1056    /// `module M<long, 8> N;` — rules (190), (191) and (192).
1057    ///
1058    /// Instantiation is substitution: the template's body is lowered
1059    /// into a module of the instance's name, with each formal parameter
1060    /// bound to the actual the instantiation gave it. Binding rather
1061    /// than rewriting the tree, because every reference the body makes
1062    /// to a formal is a name, and a name is what a scope answers.
1063    fn template_module_inst(&mut self, node: &Arc<SyntaxNode>) {
1064        // The instance's own name is the last identifier, after the
1065        // template's scoped name and its actual parameters.
1066        let idents: Vec<String> = node
1067            .syntax_tokens()
1068            .into_iter()
1069            .filter(|(_, token)| token.kind() == TokenKind::Ident)
1070            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
1071            .collect();
1072        let Some(instance) = idents.last().cloned() else {
1073            return;
1074        };
1075        let Some(template_name) = self.scoped_name_of(node) else {
1076            return;
1077        };
1078        let Some(template) = self.template_body(node, &template_name) else {
1079            let message = alloc::format!(
1080                "`{}` is not a template module declared in this file",
1081                template_name.segments().join("::")
1082            );
1083            self.diagnose_at(
1084                DiagnosticCode::UnresolvedName,
1085                node.span(),
1086                &message,
1087                "OMG IDL 4.2 §7.4.12.3 rule (190)",
1088            );
1089            return;
1090        };
1091
1092        let formals = template_formals(&template);
1093        let actuals = self.actual_parameters(node);
1094        if formals.len() != actuals.len() {
1095            let message = alloc::format!(
1096                "`{}` takes {} actual parameter(s) and was given {}",
1097                template_name.segments().join("::"),
1098                formals.len(),
1099                actuals.len()
1100            );
1101            self.diagnose_at(
1102                DiagnosticCode::UnexpectedToken,
1103                node.span(),
1104                &message,
1105                "OMG IDL 4.2 §7.4.12.3 rule (191)",
1106            );
1107            return;
1108        }
1109
1110        let Some(parent) = self.scopes.current_module() else {
1111            return;
1112        };
1113        self.check_collision(&instance, node);
1114        let Ok(module) = self.ast.ensure_module(parent, &instance) else {
1115            return;
1116        };
1117        self.instantiate(&template, &formals, &actuals, module, &instance, node);
1118    }
1119
1120    /// `alias M<T> R;` — rules (193) and (194).
1121    ///
1122    /// A template module body may name another template module,
1123    /// instantiated with its **own** formal parameters. Inside an
1124    /// instantiation those formals are bound to actuals already, so the
1125    /// reference hands the bindings on unchanged; outside one there is
1126    /// nothing to hand on, and (189) admits the form only as a
1127    /// `<tpl_definition>`.
1128    fn template_module_ref(&mut self, node: &Arc<SyntaxNode>) {
1129        let Some(instance) = node
1130            .syntax_tokens()
1131            .into_iter()
1132            .filter(|(_, token)| token.kind() == TokenKind::Ident)
1133            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
1134            .next_back()
1135        else {
1136            return;
1137        };
1138        let Some(template_name) = self.scoped_name_of(node) else {
1139            return;
1140        };
1141        let Some(template) = self.template_body(node, &template_name) else {
1142            let message = alloc::format!(
1143                "`alias` names `{}`, which is not a template module declared in this file",
1144                template_name.segments().join("::")
1145            );
1146            self.diagnose_at(
1147                DiagnosticCode::UnresolvedName,
1148                node.span(),
1149                &message,
1150                "OMG IDL 4.2 §7.4.12.3 rule (193)",
1151            );
1152            return;
1153        };
1154
1155        let mut actuals = Vec::new();
1156        for written in self.formal_parameter_names(node) {
1157            let Some(any) = self.scopes.lookup(&written) else {
1158                let message = alloc::format!(
1159                    "`{written}` is not a formal parameter of the template this `alias` is \
1160                     written in; rule (193) instantiates with the enclosing template's own \
1161                     parameters"
1162                );
1163                self.diagnose_at(
1164                    DiagnosticCode::UnresolvedName,
1165                    node.span(),
1166                    &message,
1167                    "OMG IDL 4.2 §7.4.12.3 rule (194)",
1168                );
1169                return;
1170            };
1171            actuals.push(Actual::Bound(any));
1172        }
1173
1174        let formals = template_formals(&template);
1175        if formals.len() != actuals.len() {
1176            let message = alloc::format!(
1177                "`{}` takes {} formal parameter(s) and the `alias` names {}",
1178                template_name.segments().join("::"),
1179                formals.len(),
1180                actuals.len()
1181            );
1182            self.diagnose_at(
1183                DiagnosticCode::UnexpectedToken,
1184                node.span(),
1185                &message,
1186                "OMG IDL 4.2 §7.4.12.3 rule (194)",
1187            );
1188            return;
1189        }
1190
1191        let Some(parent) = self.scopes.current_module() else {
1192            return;
1193        };
1194        self.check_collision(&instance, node);
1195        let Ok(module) = self.ast.ensure_module(parent, &instance) else {
1196            return;
1197        };
1198        self.instantiate(&template, &formals, &actuals, module, &instance, node);
1199    }
1200
1201    /// The names written between the angle brackets of an `alias`.
1202    fn formal_parameter_names(&self, node: &Arc<SyntaxNode>) -> Vec<String> {
1203        node.children()
1204            .into_iter()
1205            .find(|child| child.kind() == CstKind::ActualParameters)
1206            .map_or_else(Vec::new, |list| {
1207                list.children()
1208                    .into_iter()
1209                    .filter(|child| child.kind() == CstKind::ScopedName)
1210                    .filter_map(|scoped| first_ident(&scoped))
1211                    .collect()
1212            })
1213    }
1214
1215    /// Lower a template body into one instance module.
1216    fn instantiate(
1217        &mut self,
1218        template: &Arc<SyntaxNode>,
1219        formals: &[(String, bool)],
1220        actuals: &[Actual],
1221        module: Id<zerodds_ast::ModuleNode>,
1222        instance: &str,
1223        at: &Arc<SyntaxNode>,
1224    ) {
1225        if let Ok(node_id) = self.ast.module(module).map(|m| m.node_id) {
1226            self.record_origin(AnyId::Module(module, node_id), at.span());
1227        }
1228        self.scopes.push(instance, Some(module));
1229        for (formal, actual) in formals.iter().zip(actuals) {
1230            self.bind_actual(formal, actual, module, at);
1231        }
1232        for child in template.children() {
1233            self.definition(&child);
1234        }
1235        self.scopes.pop();
1236    }
1237
1238    /// The stored body of the template a name denotes.
1239    fn template_body(
1240        &self,
1241        node: &Arc<SyntaxNode>,
1242        written: &ScopedName,
1243    ) -> Option<Arc<SyntaxNode>> {
1244        for candidate in self.directive_candidates(node, written) {
1245            if let Some((_, body)) = self
1246                .templates
1247                .iter()
1248                .find(|(name, _)| name.segments() == candidate.segments())
1249            {
1250                return Some(body.clone());
1251            }
1252        }
1253        None
1254    }
1255
1256    /// The actual parameters an instantiation wrote, in order.
1257    fn actual_parameters(&mut self, node: &Arc<SyntaxNode>) -> Vec<Actual> {
1258        node.children()
1259            .into_iter()
1260            .find(|child| child.kind() == CstKind::ActualParameters)
1261            .map_or_else(Vec::new, |list| {
1262                list.children()
1263                    .into_iter()
1264                    .filter(|child| {
1265                        matches!(
1266                            child.kind(),
1267                            CstKind::PrimitiveType
1268                                | CstKind::NamedType
1269                                | CstKind::SequenceType
1270                                | CstKind::StringType
1271                                | CstKind::WStringType
1272                                | CstKind::MapType
1273                                | CstKind::FixedType
1274                        ) || crate::expr::is_expr(child.kind())
1275                    })
1276                    .map(Actual::Written)
1277                    .collect()
1278            })
1279    }
1280
1281    /// Bind one formal parameter to the actual the instantiation gave.
1282    ///
1283    /// A `const` formal becomes a constant of the instance module, so a
1284    /// bound written as `sequence<T, Capacity>` in the template body
1285    /// resolves the way any other named constant does. Every other kind
1286    /// binds the actual type under the formal's name.
1287    fn bind_actual(
1288        &mut self,
1289        formal: &(String, bool),
1290        actual: &Actual,
1291        module: Id<zerodds_ast::ModuleNode>,
1292        at: &Arc<SyntaxNode>,
1293    ) {
1294        let (name, is_const) = (formal.0.clone(), formal.1);
1295        // (193) hands on what the enclosing instantiation already bound,
1296        // so there is nothing to evaluate: the actual is a node of the
1297        // model and the formal takes the same one under its own name.
1298        let actual = match actual {
1299            Actual::Bound(any) => {
1300                self.scopes.bind(&name, *any);
1301                return;
1302            }
1303            Actual::Written(node) => node.clone(),
1304        };
1305        let actual = &actual;
1306        if is_const {
1307            let Some(expr) = self.expr(actual) else {
1308                self.diagnose_at(
1309                    DiagnosticCode::UnexpectedToken,
1310                    at.span(),
1311                    "a `const` formal parameter needs a constant expression as its actual",
1312                    "OMG IDL 4.2 §7.4.12.3 rule (192)",
1313                );
1314                return;
1315            };
1316            let type_ = Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(Vec::new())));
1317            if let Ok(id) = self.ast.add_const(module, &name, type_, expr) {
1318                if let Ok(node_id) = self.ast.constant(id).map(|c| c.node_id) {
1319                    self.record_origin(AnyId::Const(id, node_id), at.span());
1320                    self.record_declaration_scope(AnyId::Const(id, node_id), at.span());
1321                    self.scopes.bind(&name, AnyId::Const(id, node_id));
1322                }
1323            }
1324            return;
1325        }
1326        let type_ref = self.type_ref_of(actual);
1327        match type_ref.resolved() {
1328            Some(id) => self.bind(&name, id),
1329            None => self.diagnose_at(
1330                DiagnosticCode::UnresolvedName,
1331                at.span(),
1332                "an actual parameter names a type that is not declared in this file",
1333                "OMG IDL 4.2 §7.4.12.3 rule (192)",
1334            ),
1335        }
1336    }
1337
1338    fn structure(&mut self, node: &Arc<SyntaxNode>) {
1339        let Some(name) = first_ident(node) else {
1340            return;
1341        };
1342        let base = node
1343            .children()
1344            .iter()
1345            .find(|child| child.kind() == CstKind::InheritanceClause)
1346            .and_then(|clause| self.type_ref_from_scoped_name(clause));
1347
1348        let Some(id) = self.declare_type(
1349            &name,
1350            TypeKind::Structure {
1351                base,
1352                members: Vec::new(),
1353            },
1354            node,
1355        ) else {
1356            return;
1357        };
1358
1359        self.one_namespace_per_scope(node);
1360        for child in node.children() {
1361            if child.kind() == CstKind::Member {
1362                self.member(&child, id);
1363            }
1364        }
1365    }
1366
1367    fn exception(&mut self, node: &Arc<SyntaxNode>) {
1368        let Some(name) = first_ident(node) else {
1369            return;
1370        };
1371        self.check_collision(&name, node);
1372        let Some(module) = self.scopes.current_module() else {
1373            return;
1374        };
1375        let Ok(id) = self.ast.add_exception(module, &name) else {
1376            return;
1377        };
1378        if let Ok(node_id) = self.ast.exception(id).map(|e| e.node_id) {
1379            self.record_origin(AnyId::Exception(id, node_id), node.span());
1380            self.record_declaration_scope(AnyId::Exception(id, node_id), node.span());
1381            self.scopes.bind(&name, AnyId::Exception(id, node_id));
1382            self.apply_waiting_annotations(AnyId::Exception(id, node_id));
1383        }
1384        self.one_namespace_per_scope(node);
1385        for child in node.children() {
1386            if child.kind() == CstKind::Member {
1387                let annotations = self.annotations_of(&child);
1388                for (member_name, type_ref, at) in self.declared(&child) {
1389                    let spec = self.member_spec(&member_name, type_ref, &at);
1390                    let result = self.ast.add_exception_member(id, spec);
1391                    let Some(member) = self.report_model(result, at.span()) else {
1392                        continue;
1393                    };
1394                    let Ok(node_id) = self.ast.member(member).map(|m| m.node_id) else {
1395                        continue;
1396                    };
1397                    self.apply_annotations(&annotations, AnyId::Member(member, node_id));
1398                }
1399            }
1400        }
1401    }
1402
1403    fn member(&mut self, node: &Arc<SyntaxNode>, aggregate: Id<TypeNode>) {
1404        let annotations = self.annotations_of(node);
1405        // `public` and `private` are the only two ways to open a
1406        // valuetype state member — rule (106) — and the keyword was
1407        // consumed and thrown away. CCM 4.0 §6.7.2 forbids a private
1408        // state member in a primary-key valuetype, a rule nothing could
1409        // check once the keyword was gone.
1410        let visibility = state_visibility(node);
1411        for (name, type_ref, at) in self.declared(node) {
1412            let spec = self.member_spec(&name, type_ref, &at);
1413            let result = self.ast.add_member(aggregate, spec);
1414            let Some(id) = self.report_model(result, at.span()) else {
1415                continue;
1416            };
1417            let Ok(node_id) = self.ast.member(id).map(|m| m.node_id) else {
1418                continue;
1419            };
1420            if let Some(visibility) = visibility {
1421                if let Some(facets) = self.ast.facets_mut(AnyId::Member(id, node_id)) {
1422                    facets.corba_mut().state_visibility = Some(visibility);
1423                }
1424            }
1425            // The name is stored stripped (§7.2.3.2); whether the
1426            // declaration spelled it `_name` survives only here, for
1427            // the one dialect whose member-id hash reads the spelling.
1428            if declarator_is_escaped(&at) {
1429                if let Some(facets) = self.ast.facets_mut(AnyId::Member(id, node_id)) {
1430                    facets.xtypes_mut().escaped = true;
1431                }
1432            }
1433            self.apply_annotations(&annotations, AnyId::Member(id, node_id));
1434        }
1435    }
1436
1437    /// Every name a declaration introduces, with the type each one has.
1438    ///
1439    /// One type specification, several declarators: `double x, y, z;`.
1440    /// An array declarator gives its own name an array type over the
1441    /// shared element type — `long a, b[4];` is one `long` and one
1442    /// `long[4]`, not two arrays. IDL 4.2 §7.4.1.4.4.
1443    fn declared(
1444        &mut self,
1445        node: &Arc<SyntaxNode>,
1446    ) -> Vec<(String, Ref<TypeNode>, Arc<SyntaxNode>)> {
1447        let Some(element) = self.type_ref(node) else {
1448            return Vec::new();
1449        };
1450        let declarators: Vec<Arc<SyntaxNode>> = node
1451            .children()
1452            .into_iter()
1453            .filter(|child| child.kind() == CstKind::Declarator)
1454            .collect();
1455
1456        let mut declared = Vec::new();
1457        for declarator in declarators {
1458            let Some(name) = first_ident(&declarator) else {
1459                continue;
1460            };
1461            let type_ref = self.declarator_type(&element, &declarator);
1462            declared.push((name, type_ref, declarator));
1463        }
1464        declared
1465    }
1466
1467    /// The type a declarator gives `element`: the element itself, or an
1468    /// array of it when the declarator writes dimensions.
1469    ///
1470    /// DDS-XTypes 1.3 §7.3.1.2.1.4 writes the annotations of an array's
1471    /// elements between the declarator's name and its dimensions — `Foo
1472    /// array_of_foo @external [42];` — so they are applied to the array
1473    /// as the annotations of its element, the way `sequence<@external
1474    /// Foo, 42>` applies them to a sequence's. Written on a declarator
1475    /// that has no dimensions they have no element to apply to, and are
1476    /// reported rather than dropped.
1477    fn declarator_type(
1478        &mut self,
1479        element: &Ref<TypeNode>,
1480        declarator: &Arc<SyntaxNode>,
1481    ) -> Ref<TypeNode> {
1482        let dims = self.array_dimensions(declarator);
1483        let written = self.annotations_of(declarator);
1484        if dims.is_empty() {
1485            if let Some(first) = written.first() {
1486                self.annotation_error(
1487                    first,
1488                    &alloc::format!(
1489                        "`@{}` stands between a declarator's name and its dimensions, and applies \
1490                         to the elements of an array; this declarator is not an array",
1491                        first.name
1492                    ),
1493                    "DDS-XTypes 1.3 §7.3.1.2.1.4",
1494                );
1495            }
1496            return element.clone();
1497        }
1498        let array = self.anonymous(
1499            TypeKind::Array {
1500                element: element.clone(),
1501                dims,
1502            },
1503            declarator.span(),
1504        );
1505        if !written.is_empty() {
1506            if let Some(id) = array.resolved() {
1507                let any = AnyId::Type(id, self.node_id_of(id));
1508                self.apply_use_site(&written, any, UseSite::Element);
1509            }
1510        }
1511        array
1512    }
1513
1514    /// The `[expr]` dimensions of a declarator, outermost first.
1515    fn array_dimensions(&mut self, declarator: &Arc<SyntaxNode>) -> Vec<Id<ConstExprNode>> {
1516        declarator
1517            .children()
1518            .into_iter()
1519            .filter(|child| child.kind() == CstKind::ArrayDimension)
1520            .filter_map(|dim| self.first_expr_child(&dim))
1521            .collect()
1522    }
1523
1524    fn member_spec(&self, name: &str, type_: Ref<TypeNode>, at: &Arc<SyntaxNode>) -> MemberSpec {
1525        MemberSpec {
1526            name: name.to_string(),
1527            type_,
1528            facets: self.facets_at(at.span()),
1529        }
1530    }
1531
1532    fn enumeration(&mut self, node: &Arc<SyntaxNode>) {
1533        let Some(name) = first_ident(node) else {
1534            return;
1535        };
1536        let Some(id) = self.declare_type(
1537            &name,
1538            TypeKind::Enumeration {
1539                enumerators: Vec::new(),
1540            },
1541            node,
1542        ) else {
1543            return;
1544        };
1545        for child in node.children() {
1546            if child.kind() == CstKind::Enumerator {
1547                if let Some(name) = first_ident(&child) {
1548                    // No explicit value here: `@value(n)` is applied
1549                    // with the other annotations, and a positional one
1550                    // is filled in by the completion pass. Spec §5.
1551                    let result = self.ast.add_enumerator(id, &name, None);
1552                    let Some(enumerator) = self.report_model(result, child.span()) else {
1553                        continue;
1554                    };
1555                    let annotations = self.annotations_of(&child);
1556                    let Ok(node_id) = self.ast.enumerator(enumerator).map(|e| e.node_id) else {
1557                        continue;
1558                    };
1559                    // The enumerator goes into the scope enclosing the
1560                    // enumeration, not into one of its own. OMG IDL 4.2
1561                    // §7.4.1.4.2: `module M { enum Size { small, medium,
1562                    // large }; }; const M::Size MYSIZE = M::medium;`.
1563                    // This is what lets `case RED:` name an enumerator,
1564                    // and what makes two enumerations sharing a literal
1565                    // name the collision §7.2.3 says it is.
1566                    self.check_collision(&name, &child);
1567                    self.scopes
1568                        .bind(&name, AnyId::Enumerator(enumerator, node_id));
1569                    self.apply_annotations(&annotations, AnyId::Enumerator(enumerator, node_id));
1570                }
1571            }
1572        }
1573    }
1574
1575    fn bitmask(&mut self, node: &Arc<SyntaxNode>) {
1576        let Some(name) = first_ident(node) else {
1577            return;
1578        };
1579        let Some(id) = self.declare_type(&name, TypeKind::Bitmask { bits: Vec::new() }, node)
1580        else {
1581            return;
1582        };
1583        for child in node.children() {
1584            if child.kind() == CstKind::BitValue {
1585                if let Some(name) = first_ident(&child) {
1586                    let result = self.ast.add_bit_value(id, &name, None);
1587                    let Some(bit) = self.report_model(result, child.span()) else {
1588                        continue;
1589                    };
1590                    let annotations = self.annotations_of(&child);
1591                    let Ok(node_id) = self.ast.bit_value(bit).map(|b| b.node_id) else {
1592                        continue;
1593                    };
1594                    self.apply_annotations(&annotations, AnyId::BitValue(bit, node_id));
1595                }
1596            }
1597        }
1598    }
1599
1600    fn union(&mut self, node: &Arc<SyntaxNode>) {
1601        let Some(name) = first_ident(node) else {
1602            return;
1603        };
1604        let discriminator = self.type_ref(node).unwrap_or_else(|| {
1605            Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(Vec::new())))
1606        });
1607        let Some(id) = self.declare_type(
1608            &name,
1609            TypeKind::Union {
1610                discriminator,
1611                cases: Vec::new(),
1612                default_case: None,
1613            },
1614            node,
1615        ) else {
1616            return;
1617        };
1618
1619        // `union U switch (@try_construct K)` — §7.3.1.2.1.12.5. An
1620        // `Annotation` child of the declaration itself can only be the
1621        // discriminator's: the union's own annotations sit on the
1622        // enclosing `Member` node and a case's on its `UnionCase`.
1623        let discriminator_annotations: Vec<Applied> = node
1624            .children()
1625            .into_iter()
1626            .filter(|child| child.kind() == CstKind::Annotation)
1627            .filter_map(|child| self.applied_annotation(&child))
1628            .collect();
1629        if !discriminator_annotations.is_empty() {
1630            let any = AnyId::Type(id, self.node_id_of(id));
1631            self.apply_use_site(&discriminator_annotations, any, UseSite::Discriminator);
1632        }
1633        self.one_namespace_per_scope(node);
1634
1635        for child in node.children() {
1636            if child.kind() != CstKind::UnionCase {
1637                continue;
1638            }
1639            let label_nodes: Vec<Arc<SyntaxNode>> = child
1640                .children()
1641                .into_iter()
1642                .filter(|c| c.kind() == CstKind::CaseLabel)
1643                .collect();
1644            let mut labels: Vec<zerodds_ast::CaseLabel> = Vec::new();
1645            for label in label_nodes {
1646                let is_default = label
1647                    .syntax_tokens()
1648                    .iter()
1649                    .any(|(_, token)| token.kind() == TokenKind::Default);
1650                if is_default {
1651                    labels.push(zerodds_ast::CaseLabel::Default);
1652                    continue;
1653                }
1654                // `case <expr>:` keeps its expression: the label may name
1655                // a constant or an enumerator, and the value it stands
1656                // for is not known until those resolve.
1657                match self.first_expr_child(&label) {
1658                    Some(expr) => labels.push(zerodds_ast::CaseLabel::Value(expr)),
1659                    None => self.diagnose_at(
1660                        DiagnosticCode::MissingToken,
1661                        label.span(),
1662                        "a `case` label needs a constant expression",
1663                        "OMG IDL 4.2 §7.4.1.4.4.3",
1664                    ),
1665                }
1666            }
1667            // `element_spec ::= type_spec declarator` — one declarator,
1668            // where a struct member's `member ::= type_spec
1669            // declarators ";"` takes a list. `parse_union_case` shares
1670            // `parse_declarator_list` with the struct member and so
1671            // accepts `case 1: long a, b;` without a word, and
1672            // `member_parts` answers with the first declarator only:
1673            // every branch after the comma was lowered into nothing,
1674            // named in no diagnostic, and absent from the model the
1675            // codec and every generator read.
1676            let declared = self.declared(&child);
1677            for (_, _, extra) in declared.iter().skip(1) {
1678                self.diagnose_at(
1679                    DiagnosticCode::UnexpectedToken,
1680                    extra.span(),
1681                    "a union case declares one member; `element_spec` takes a single declarator",
1682                    "OMG IDL 4.2 §7.4.1.4.4.3",
1683                );
1684            }
1685            let Some((member_name, type_ref, declarator)) = declared.into_iter().next() else {
1686                continue;
1687            };
1688            // The branch and the member behind it are two ranges, and a
1689            // reader needs both: the case is the whole `case 1: long a`,
1690            // the member is its declarator — the same range a struct
1691            // member of the same declaration gets. Giving the member the
1692            // branch's range made every message about it point at the
1693            // `case` keyword.
1694            let spec = self.member_spec(&member_name, type_ref, &declarator);
1695            let annotations = self.annotations_of(&child);
1696            let result = self.ast.add_case(id, labels, spec);
1697            let Some(case) = self.report_model(result, child.span()) else {
1698                continue;
1699            };
1700            let Ok(node_id) = self.ast.case(case).map(|c| c.node_id) else {
1701                continue;
1702            };
1703            // `Ast::add_case` marks a case's facets synthetic, and
1704            // nothing overwrote them: every rule that reports a branch —
1705            // a repeated label, `@key` on a case member — pointed at
1706            // byte zero of the file.
1707            self.record_origin(AnyId::Case(case, node_id), child.span());
1708            // The same trace of the escape spelling a struct member
1709            // keeps, for the same one consumer.
1710            if declarator_is_escaped(&declarator) {
1711                if let Some(facets) = self.ast.facets_mut(AnyId::Case(case, node_id)) {
1712                    facets.xtypes_mut().escaped = true;
1713                }
1714            }
1715            self.apply_annotations(&annotations, AnyId::Case(case, node_id));
1716            // Mirror the case's XTypes facet onto the member behind it.
1717            // The source writes a branch's annotations on the case, and
1718            // that is where `apply_annotations` puts them — but the
1719            // layout planner (`effective_member_id`, `must_understand`),
1720            // the codec walks (`is_external`, `try_construct`) and every
1721            // language adapter read `MemberNode::facets`. Left unmirrored,
1722            // `case 1: @external Inner i;` lowered without error and no
1723            // consumer ever saw the annotation — a silent drop.
1724            // `validate.rs` already reads both sides for its union rules,
1725            // so the mirror changes no diagnostic; it is applied once,
1726            // after every annotation of the branch, so no diagnostic is
1727            // emitted twice either.
1728            let mirrored = self
1729                .ast
1730                .facets(AnyId::Case(case, node_id))
1731                .and_then(|facets| facets.xtypes.clone());
1732            if let Some(xtypes) = mirrored {
1733                let member_any = self
1734                    .ast
1735                    .case(case)
1736                    .ok()
1737                    .map(|c| c.member)
1738                    .and_then(|member| self.ast.any_of_member(member).ok());
1739                if let Some(facets) = member_any.and_then(|any| self.ast.facets_mut(any)) {
1740                    *facets.xtypes_mut() = xtypes;
1741                }
1742            }
1743        }
1744    }
1745
1746    /// `typedef <type> <declarator>, ...;`
1747    ///
1748    /// A declarator here carries dimensions exactly as one on a struct
1749    /// member does: `typedef long Matrix[2][3];` aliases an array of
1750    /// `long`, not a `long`. IDL 4.2 §7.4.1.4.4 gives both forms the same
1751    /// `<declarator>` production, so both give their own name the array
1752    /// type over the shared element type.
1753    fn typedef(&mut self, node: &Arc<SyntaxNode>) {
1754        // Rule (64)'s third alternative: the type is defined inside the
1755        // typedef. It is declared first, in the typedef's own scope,
1756        // and the declarators alias it.
1757        let defined = node.children().into_iter().find(|child| {
1758            matches!(
1759                child.kind(),
1760                CstKind::StructDecl
1761                    | CstKind::UnionDecl
1762                    | CstKind::EnumDecl
1763                    | CstKind::BitsetDecl
1764                    | CstKind::BitmaskDecl
1765            )
1766        });
1767        let element = match defined {
1768            Some(definition) => {
1769                self.definition_inner(&definition);
1770                let Some(name) = first_ident(&definition) else {
1771                    return;
1772                };
1773                match self.scopes.lookup(&name) {
1774                    Some(AnyId::Type(id, _)) => Ref::Resolved(id),
1775                    _ => return,
1776                }
1777            }
1778            None => {
1779                let Some(element) = self.type_ref(node) else {
1780                    return;
1781                };
1782                element
1783            }
1784        };
1785        for declarator in node
1786            .children()
1787            .iter()
1788            .filter(|child| child.kind() == CstKind::Declarator)
1789        {
1790            let Some(name) = first_ident(declarator) else {
1791                continue;
1792            };
1793            let aliased = self.declarator_type(&element, declarator);
1794            let _ = self.declare_type(&name, TypeKind::Alias { aliased }, declarator);
1795        }
1796    }
1797
1798    fn native(&mut self, node: &Arc<SyntaxNode>) {
1799        let Some(name) = first_ident(node) else {
1800            return;
1801        };
1802        let _ = self.declare_type(&name, TypeKind::Native, node);
1803    }
1804
1805    fn forward(&mut self, node: &Arc<SyntaxNode>) {
1806        let Some(name) = first_ident(node) else {
1807            return;
1808        };
1809        let Some(module) = self.scopes.current_module() else {
1810            return;
1811        };
1812        // A forward declaration is a completeness state, not a kind: the
1813        // definition completes this node rather than replacing it, so
1814        // references taken now stay valid. Spec §3.2 of the AST.
1815        if self.scopes.lookup(&name).is_some() {
1816            return;
1817        }
1818        // *Which* node, though, is the keyword's decision. Every forward
1819        // used to bind a `Structure` placeholder whatever it said, so
1820        // `interface I; interface I { … };` collided with itself:
1821        // `DuplicateName` for the definition, which could not complete a
1822        // type it was not, and `UndefinedForward` for the placeholder
1823        // nothing ever finished. Rules (75) and (135), the same bug
1824        // twice. `struct S;` and `union U;` were right all along, which
1825        // is what made it invisible.
1826        if let Some(kind) = forward_interface_kind(node) {
1827            if let Ok(id) = self.ast.add_forward_interface(module, &name, kind) {
1828                if let Ok(node_id) = self.ast.interface(id).map(|node| node.node_id) {
1829                    self.record_origin(AnyId::Interface(id, node_id), node.span());
1830                    self.scopes.bind(&name, AnyId::Interface(id, node_id));
1831                }
1832            }
1833            return;
1834        }
1835        if let Ok(id) = self.ast.add_forward_type(
1836            module,
1837            &name,
1838            TypeKind::Structure {
1839                base: None,
1840                members: Vec::new(),
1841            },
1842        ) {
1843            // The interface branch above records where the declaration
1844            // was and this one did not, so `UndefinedForward` for a
1845            // `struct S;` that nothing completes — the one message whose
1846            // whole content is *which* declaration was left open — named
1847            // byte zero instead of the declaration.
1848            self.record_origin(AnyId::Type(id, self.node_id_of(id)), node.span());
1849            self.record_declaration_scope(AnyId::Type(id, self.node_id_of(id)), node.span());
1850            self.bind(&name, id);
1851        }
1852    }
1853
1854    fn constant(&mut self, node: &Arc<SyntaxNode>) {
1855        let Some(name) = first_ident(node) else {
1856            return;
1857        };
1858        let Some(expr) = self.first_expr_child(node) else {
1859            self.diagnose_at(
1860                DiagnosticCode::MissingToken,
1861                node.span(),
1862                "a constant needs a value expression",
1863                "OMG IDL 4.2 §7.4.1.4.2",
1864            );
1865            return;
1866        };
1867        // The declared type is what the constant has; the expression is
1868        // checked against it rather than used to derive it. Spec §5.
1869        //
1870        // Rule (43) is the one exception the language itself makes:
1871        // `<fixed_pt_const_type> ::= "fixed"` has no parameters, so a
1872        // `const fixed` takes its digits and scale from its value.
1873        let type_ = if bare_fixed(node) {
1874            self.fixed_const_type(expr, node)
1875        } else {
1876            self.type_ref(node)
1877                .unwrap_or_else(|| Ref::Broken(broken_cause()))
1878        };
1879        self.check_const_value_form(&type_, expr, node);
1880        self.check_collision(&name, node);
1881        let Some(module) = self.scopes.current_module() else {
1882            return;
1883        };
1884        if let Ok(id) = self.ast.add_const(module, &name, type_, expr) {
1885            let node_id = self.ast.constant(id).map(|c| c.node_id).ok();
1886            if let Some(node_id) = node_id {
1887                self.record_origin(AnyId::Const(id, node_id), node.span());
1888                self.record_declaration_scope(AnyId::Const(id, node_id), node.span());
1889                self.scopes.bind(&name, AnyId::Const(id, node_id));
1890                self.apply_waiting_annotations(AnyId::Const(id, node_id));
1891            }
1892        }
1893    }
1894
1895    fn bitset(&mut self, node: &Arc<SyntaxNode>) {
1896        let Some(name) = first_ident(node) else {
1897            return;
1898        };
1899        let base = node
1900            .children()
1901            .iter()
1902            .find(|child| child.kind() == CstKind::InheritanceClause)
1903            .and_then(|clause| self.type_ref_from_scoped_name(clause));
1904        let Some(id) = self.declare_type(
1905            &name,
1906            TypeKind::Bitset {
1907                base,
1908                fields: Vec::new(),
1909            },
1910            node,
1911        ) else {
1912            return;
1913        };
1914
1915        for field in node
1916            .children()
1917            .into_iter()
1918            .filter(|child| child.kind() == CstKind::BitField)
1919        {
1920            self.bitfield(&field, id);
1921        }
1922    }
1923
1924    /// One `bitfield<n>` or `bitfield<n, T>` of a bitset.
1925    ///
1926    /// An unnamed bitfield is padding and keeps its width, so the layout
1927    /// of the ones that follow stays right. XTypes 1.3 §7.3.1.2.1.11.
1928    fn bitfield(&mut self, node: &Arc<SyntaxNode>, bitset: Id<TypeNode>) {
1929        let Some(expr) = self.first_expr_child(node) else {
1930            return;
1931        };
1932        let Ok(width) = self.ast.eval_integral(expr) else {
1933            self.diagnose_at(
1934                DiagnosticCode::UnexpectedToken,
1935                node.span(),
1936                "a bitfield width must be an integer constant",
1937                "DDS-XTypes 1.3 §7.3.1.2.1.11",
1938            );
1939            return;
1940        };
1941        let Ok(bits) = u16::try_from(width) else {
1942            self.diagnose_at(
1943                DiagnosticCode::UnexpectedToken,
1944                node.span(),
1945                "a bitfield width does not fit in 16 bits",
1946                "DDS-XTypes 1.3 §7.3.1.2.1.11",
1947            );
1948            return;
1949        };
1950        let holder = self
1951            .type_ref(node)
1952            .filter(|type_| !matches!(type_, Ref::Broken(_)));
1953        let name = first_ident(node);
1954        let result = self.ast.add_bitfield(bitset, name.as_deref(), bits, holder);
1955        let Some(id) = self.report_model(result, node.span()) else {
1956            return;
1957        };
1958        let annotations = self.annotations_of(node);
1959        if let Ok(node_id) = self.ast.bitfield(id).map(|f| f.node_id) {
1960            self.apply_annotations(&annotations, AnyId::Bitfield(id, node_id));
1961        }
1962    }
1963
1964    fn interface(&mut self, node: &Arc<SyntaxNode>) {
1965        let Some(name) = first_ident(node) else {
1966            return;
1967        };
1968        let kind = interface_kind(node);
1969        let (bases, supports) = self.interface_bases(node);
1970        let Some(id) = self.declare_interface(&name, kind, bases, node) else {
1971            return;
1972        };
1973        let node_id = self.ast.interface(id).map(|i| i.node_id).ok();
1974        if let Some(node_id) = node_id {
1975            self.record_origin(AnyId::Interface(id, node_id), node.span());
1976            self.scopes.bind(&name, AnyId::Interface(id, node_id));
1977            self.apply_waiting_annotations(AnyId::Interface(id, node_id));
1978            if !supports.is_empty() {
1979                if let Some(facets) = self.ast.facets_mut(AnyId::Interface(id, node_id)) {
1980                    facets.corba_mut().supports = supports;
1981                }
1982                self.ast.reindex_pending(AnyId::Interface(id, node_id));
1983            }
1984        }
1985
1986        // The body is its own scope: a type declared inside an interface
1987        // is named through the interface. IDL 4.2 §7.5.
1988        if let Some(clause) = node
1989            .children()
1990            .into_iter()
1991            .find(|child| child.kind() == CstKind::ManagesClause)
1992        {
1993            self.manages_clause(&clause, id);
1994        }
1995
1996        self.one_namespace_per_scope(node);
1997        self.scopes.push_container(&name);
1998        for child in node.children() {
1999            match child.kind() {
2000                CstKind::Operation => self.operation(&child, id),
2001                CstKind::AttributeDecl => self.attribute(&child, id),
2002                CstKind::PortDecl => self.port(&child, id),
2003                CstKind::InheritanceClause | CstKind::ManagesClause => {}
2004                _ => self.definition(&child),
2005            }
2006        }
2007        self.scopes.pop();
2008    }
2009
2010    /// The base interfaces of an interface, and the ones it supports.
2011    ///
2012    /// `component C : B supports I1, I2` writes two inheritance clauses
2013    /// and they mean different things. Taking only the first dropped
2014    /// every `supports` clause a component, home or connector carried,
2015    /// with no diagnostic — CCM 4.0 §6.3.2 needs both lists and cannot
2016    /// tell them apart once they are merged. The valuetype path already
2017    /// walked every clause; this one stopped at the first.
2018    fn interface_bases(
2019        &mut self,
2020        node: &Arc<SyntaxNode>,
2021    ) -> (
2022        Vec<Ref<zerodds_ast::InterfaceNode>>,
2023        Vec<Ref<zerodds_ast::InterfaceNode>>,
2024    ) {
2025        let mut bases = Vec::new();
2026        let mut supports = Vec::new();
2027        for clause in node
2028            .children()
2029            .into_iter()
2030            .filter(|child| child.kind() == CstKind::InheritanceClause)
2031        {
2032            let is_supports = clause
2033                .syntax_tokens()
2034                .iter()
2035                .any(|(_, token)| token.kind() == TokenKind::Supports);
2036            let names: Vec<Ref<zerodds_ast::InterfaceNode>> = clause
2037                .children()
2038                .into_iter()
2039                .filter(|child| child.kind() == CstKind::ScopedName)
2040                .filter_map(|scoped| self.scoped_name_of(&scoped))
2041                .map(|name| self.interface_ref(&name))
2042                .collect();
2043            if is_supports {
2044                supports.extend(names);
2045            } else {
2046                bases.extend(names);
2047            }
2048        }
2049        (bases, supports)
2050    }
2051
2052    /// One port of a component, home, porttype or connector.
2053    ///
2054    /// A port is a named access point with an interface or event type
2055    /// behind it, which is an attribute with a component role on it.
2056    /// Making each port kind its own node kind would widen the type
2057    /// space every consumer matches on for a dialect most of them never
2058    /// see. Spec §4.2 of the AST.
2059    fn port(&mut self, node: &Arc<SyntaxNode>, interface: Id<zerodds_ast::InterfaceNode>) {
2060        let keyword = node.syntax_tokens().first().map(|(_, token)| token.kind());
2061        // The port's own name is the last identifier; the one before it
2062        // names the interface or event type.
2063        let idents: Vec<String> = node
2064            .syntax_tokens()
2065            .into_iter()
2066            .filter(|(_, token)| token.kind() == TokenKind::Ident)
2067            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
2068            .collect();
2069        let Some(port_name) = idents.last().cloned() else {
2070            return;
2071        };
2072        let target = self
2073            .scoped_names_of(node)
2074            .into_iter()
2075            .next()
2076            .or_else(|| self.scoped_name_of(node));
2077
2078        let type_ = target.as_ref().map_or_else(
2079            || Ref::Broken(broken_cause()),
2080            |name| self.named_type_ref(name),
2081        );
2082        let Ok(id) = self.ast.add_attribute(interface, &port_name, type_, false) else {
2083            return;
2084        };
2085        let Ok(node_id) = self.ast.attribute(id).map(|a| a.node_id) else {
2086            return;
2087        };
2088        self.record_origin(AnyId::Attribute(id, node_id), node.span());
2089
2090        let multiple = node
2091            .syntax_tokens()
2092            .iter()
2093            .any(|(_, token)| token.kind() == TokenKind::Multiple);
2094        let interface_ref = target.as_ref().map_or_else(
2095            || Ref::Broken(broken_cause()),
2096            |name| self.interface_ref(name),
2097        );
2098        let event_ref = type_ref_of_port(self.ast, id);
2099        let role = match keyword {
2100            Some(TokenKind::Provides) => Some(zerodds_ast::ComponentRole::Facet {
2101                name: port_name.clone(),
2102                interface: interface_ref,
2103            }),
2104            Some(TokenKind::Uses) => Some(zerodds_ast::ComponentRole::Receptacle {
2105                name: port_name.clone(),
2106                interface: interface_ref,
2107                multiple,
2108            }),
2109            Some(TokenKind::Publishes | TokenKind::Emits) => {
2110                Some(zerodds_ast::ComponentRole::EventSource {
2111                    name: port_name.clone(),
2112                    event: event_ref,
2113                    publisher: keyword == Some(TokenKind::Publishes),
2114                })
2115            }
2116            Some(TokenKind::Consumes) => Some(zerodds_ast::ComponentRole::EventSink {
2117                name: port_name.clone(),
2118                event: event_ref,
2119            }),
2120            Some(TokenKind::Port) => Some(zerodds_ast::ComponentRole::PortTypeRole),
2121            Some(TokenKind::Mirrorport) => Some(zerodds_ast::ComponentRole::ConnectorRole),
2122            _ => None,
2123        };
2124        if let Some(role) = role {
2125            if let Some(facets) = self.ast.facets_mut(AnyId::Attribute(id, node_id)) {
2126                facets.corba_mut().component_role = Some(role);
2127            }
2128        }
2129        self.ast.reindex_pending(AnyId::Attribute(id, node_id));
2130    }
2131
2132    /// A home's `manages X primarykey K` clause.
2133    fn manages_clause(
2134        &mut self,
2135        node: &Arc<SyntaxNode>,
2136        interface: Id<zerodds_ast::InterfaceNode>,
2137    ) {
2138        let names = self.scoped_names_of(node);
2139        let Some(component) = names.first() else {
2140            return;
2141        };
2142        let component_ref = self.interface_ref(component);
2143        let primary_key = names.get(1).map(|name| self.named_type_ref(name));
2144        let Ok(node_id) = self.ast.interface(interface).map(|i| i.node_id) else {
2145            return;
2146        };
2147        if let Some(facets) = self.ast.facets_mut(AnyId::Interface(interface, node_id)) {
2148            facets.corba_mut().manages = Some(zerodds_ast::HomeManages {
2149                component: component_ref,
2150                primary_key,
2151            });
2152        }
2153        // Both references may name a declaration that has not arrived
2154        // yet; without this the waiter index never learns of them and
2155        // `manages C primarykey K` stays pending for the life of the
2156        // model.
2157        self.ast
2158            .reindex_pending(AnyId::Interface(interface, node_id));
2159    }
2160
2161    /// `typeid X "id";` and `typeprefix X "prefix";`
2162    ///
2163    /// Both name a declaration that may not exist yet, so they are kept
2164    /// until the whole file is walked and applied in the completion
2165    /// pass. Spec §5.
2166    fn repository_directive(&mut self, node: &Arc<SyntaxNode>) {
2167        let is_prefix = node
2168            .syntax_tokens()
2169            .iter()
2170            .any(|(_, token)| token.kind() == TokenKind::Typeprefix);
2171        let Some(name) = self.scoped_name_of(node) else {
2172            return;
2173        };
2174        let Some(text) = joined_string_literals(node.syntax_tokens(), TokenKind::StringLiteral)
2175            .into_iter()
2176            .next()
2177        else {
2178            return;
2179        };
2180        self.record_repository_directive(if is_prefix { "typeprefix" } else { "typeid" }, &text);
2181        self.repository_directives
2182            .push((self.directive_candidates(node, &name), text, is_prefix));
2183    }
2184
2185    /// Note on the root module that a `typeid`/`typeprefix` directive was
2186    /// used, so the RPC layer can see it.
2187    ///
2188    /// The directive itself writes onto its target and leaves no node
2189    /// behind; DDS-RPC 1.0 §7.3.1.1 removes the directive from the
2190    /// service-definition grammar, so a service definition that carries
2191    /// one is outside it and `lower_service` has to report it.
2192    fn record_repository_directive(&mut self, keyword: &str, text: &str) {
2193        let root = self.ast.root_module();
2194        let Ok(node_id) = self.ast.module(root).map(|m| m.node_id) else {
2195            return;
2196        };
2197        if let Some(facets) = self.ast.facets_mut(AnyId::Module(root, node_id)) {
2198            facets
2199                .corba_mut()
2200                .repository_directives
2201                .push(alloc::format!("{keyword} {text}"));
2202        }
2203    }
2204
2205    /// `import X;` — kept as written on the root module.
2206    fn import(&mut self, node: &Arc<SyntaxNode>) {
2207        let written = joined_string_literals(node.syntax_tokens(), TokenKind::StringLiteral)
2208            .into_iter()
2209            .next()
2210            .or_else(|| {
2211                self.scoped_name_of(node)
2212                    .map(|name| name.segments().join("::"))
2213            });
2214        let Some(written) = written else {
2215            return;
2216        };
2217        let root = self.ast.root_module();
2218        let Ok(node_id) = self.ast.module(root).map(|m| m.node_id) else {
2219            return;
2220        };
2221        if let Some(facets) = self.ast.facets_mut(AnyId::Module(root, node_id)) {
2222            facets.corba_mut().imports.push(written);
2223        }
2224    }
2225
2226    /// `keylist T (a, b);` — RTI's way of declaring a type's keys.
2227    fn keylist(&mut self, node: &Arc<SyntaxNode>) {
2228        let Some(name) = self.scoped_name_of(node) else {
2229            return;
2230        };
2231        // The type's name is a `ScopedName` child; the identifiers
2232        // directly under the declaration are the key fields.
2233        let fields: Vec<String> = node
2234            .syntax_tokens()
2235            .into_iter()
2236            .filter(|(_, token)| token.kind() == TokenKind::Ident)
2237            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
2238            .collect();
2239        self.keylists
2240            .push((self.directive_candidates(node, &name), fields, node.span()));
2241    }
2242
2243    /// Every scope a directive's target name could be in, innermost
2244    /// first.
2245    ///
2246    /// §7.5 resolves a name from the scope that writes it outwards, and
2247    /// a directive's argument is a name being used. Qualifying it with
2248    /// the current scope and stopping there made `module m { typeprefix
2249    /// m "…"; };` name `m::m` — a scope that does not exist — and the
2250    /// directive was reported as naming nothing while `::m` sat one step
2251    /// out. `typeid`, `typeprefix` and `keylist` all read their target
2252    /// this way.
2253    ///
2254    /// A name written root-anchored means the root and nothing else, so
2255    /// it gets one candidate. Rule (4) `<scoped_name> ::+ "::"
2256    /// <identifier>`.
2257    fn directive_candidates(&self, node: &Arc<SyntaxNode>, name: &ScopedName) -> Vec<ScopedName> {
2258        if is_root_anchored(node) {
2259            return alloc::vec![name.clone()];
2260        }
2261        self.scopes.lookup_candidates(name.segments())
2262    }
2263
2264    /// A template module's formal parameters.
2265    fn template_parameters(&mut self, node: &Arc<SyntaxNode>, module: Id<zerodds_ast::ModuleNode>) {
2266        let mut params = Vec::new();
2267        for parameter in node
2268            .children()
2269            .into_iter()
2270            .filter(|child| child.kind() == CstKind::TemplateParameter)
2271        {
2272            let Some(name) = first_ident(&parameter) else {
2273                continue;
2274            };
2275            let kind = template_param_kind(&parameter);
2276            // Two of (188)'s eleven alternatives carry a type: `const
2277            // <const_type>` names the type an actual constant must have,
2278            // and `<sequence_type>` names the sequence the actual must
2279            // be. Both are the parameter's constraint.
2280            let constraint = if matches!(
2281                kind,
2282                zerodds_ast::TemplateParamKind::Const | zerodds_ast::TemplateParamKind::Sequence
2283            ) {
2284                self.type_ref(&parameter)
2285            } else {
2286                None
2287            };
2288            params.push(zerodds_ast::TemplateParam {
2289                name,
2290                kind,
2291                constraint,
2292            });
2293        }
2294        if params.is_empty() {
2295            return;
2296        }
2297        let Ok(node_id) = self.ast.module(module).map(|m| m.node_id) else {
2298            return;
2299        };
2300        if let Some(facets) = self.ast.facets_mut(AnyId::Module(module, node_id)) {
2301            facets.corba_mut().template_params = params;
2302        }
2303        self.ast.reindex_pending(AnyId::Module(module, node_id));
2304    }
2305
2306    /// Every scoped name written directly under a node.
2307    pub(crate) fn scoped_names_of(&self, node: &Arc<SyntaxNode>) -> Vec<ScopedName> {
2308        node.children()
2309            .into_iter()
2310            .filter(|child| child.kind() == CstKind::ScopedName)
2311            .filter_map(|scoped| self.scoped_name_of(&scoped))
2312            .collect()
2313    }
2314
2315    /// The string literals of a `context (...)` clause, with escapes
2316    /// resolved.
2317    fn context_strings(&self, node: &Arc<SyntaxNode>) -> Vec<String> {
2318        joined_string_literals(node.syntax_tokens(), TokenKind::StringLiteral)
2319    }
2320
2321    /// A reference to an exception by name, resolved if already bound.
2322    fn exception_ref(&self, name: &ScopedName) -> Ref<zerodds_ast::ExceptionNode> {
2323        if let Some(segment) = name.segments().last() {
2324            if let Some(AnyId::Exception(id, _)) = self.scopes.lookup(segment) {
2325                return Ref::Resolved(id);
2326            }
2327        }
2328        Ref::Pending(PendingRef::ScopedName(name.clone()))
2329    }
2330
2331    /// A reference to a named type, resolved if already bound.
2332    fn named_type_ref(&self, name: &ScopedName) -> Ref<TypeNode> {
2333        if let Some(segment) = name.segments().last() {
2334            if let Some(found) = self.scopes.lookup(segment).and_then(AnyId::as_type) {
2335                return Ref::Resolved(found);
2336            }
2337        }
2338        Ref::Pending(PendingRef::ScopedName(name.clone()))
2339    }
2340
2341    /// A reference to an interface by name, resolved if already bound.
2342    fn interface_ref(&self, name: &ScopedName) -> Ref<zerodds_ast::InterfaceNode> {
2343        if let Some(segment) = name.segments().last() {
2344            if let Some(AnyId::Interface(id, _)) = self.scopes.lookup(segment) {
2345                return Ref::Resolved(id);
2346            }
2347        }
2348        Ref::Pending(PendingRef::ScopedName(name.clone()))
2349    }
2350
2351    fn operation(&mut self, node: &Arc<SyntaxNode>, interface: Id<zerodds_ast::InterfaceNode>) {
2352        let Some(name) = first_ident(node) else {
2353            return;
2354        };
2355        // `void` has no type specification; the model's own void is the
2356        // primitive of that name.
2357        // `void` writes no type specification at all; the model records
2358        // the absence rather than a stand-in type.
2359        let return_type = self.type_ref(node);
2360        let oneway = node
2361            .syntax_tokens()
2362            .iter()
2363            .any(|(_, token)| token.kind() == TokenKind::Oneway);
2364        let Ok(id) = self
2365            .ast
2366            .add_operation(interface, &name, return_type, oneway)
2367        else {
2368            return;
2369        };
2370        let annotations = self.annotations_of(node);
2371        let node_id = self.ast.operation(id).map(|o| o.node_id).ok();
2372        if let Some(node_id) = node_id {
2373            self.record_origin(AnyId::Operation(id, node_id), node.span());
2374            self.apply_annotations(&annotations, AnyId::Operation(id, node_id));
2375        }
2376
2377        self.one_namespace_per_scope(node);
2378        for parameter in node
2379            .children()
2380            .into_iter()
2381            .filter(|child| child.kind() == CstKind::Parameter)
2382        {
2383            self.parameter(&parameter, id);
2384        }
2385        for raises in node
2386            .children()
2387            .into_iter()
2388            .filter(|child| child.kind() == CstKind::RaisesClause)
2389        {
2390            for name in self.scoped_names_of(&raises) {
2391                let reference = self.exception_ref(&name);
2392                let _ = self.ast.add_raises(id, reference);
2393            }
2394        }
2395        for context in node
2396            .children()
2397            .into_iter()
2398            .filter(|child| child.kind() == CstKind::ContextClause)
2399        {
2400            for context in self.context_strings(&context) {
2401                let _ = self.ast.add_context(id, &context);
2402            }
2403        }
2404    }
2405
2406    fn parameter(&mut self, node: &Arc<SyntaxNode>, operation: Id<zerodds_ast::OperationNode>) {
2407        let Some(name) = first_ident(node) else {
2408            return;
2409        };
2410        let direction = node
2411            .syntax_tokens()
2412            .iter()
2413            .find_map(|(_, token)| match token.kind() {
2414                TokenKind::In => Some(ParamDirection::In),
2415                TokenKind::Out => Some(ParamDirection::Out),
2416                TokenKind::Inout => Some(ParamDirection::InOut),
2417                _ => None,
2418            })
2419            .unwrap_or(ParamDirection::In);
2420        let type_ = self
2421            .type_ref(node)
2422            .unwrap_or_else(|| Ref::Broken(broken_cause()));
2423        if let Ok(id) = self.ast.add_param(operation, &name, direction, type_) {
2424            let annotations = self.annotations_of(node);
2425            let node_id = self.ast.param(id).map(|p| p.node_id).ok();
2426            if let Some(node_id) = node_id {
2427                self.record_origin(AnyId::Param(id, node_id), node.span());
2428                self.apply_annotations(&annotations, AnyId::Param(id, node_id));
2429            }
2430        }
2431    }
2432
2433    fn attribute(&mut self, node: &Arc<SyntaxNode>, interface: Id<zerodds_ast::InterfaceNode>) {
2434        let readonly = node
2435            .syntax_tokens()
2436            .iter()
2437            .any(|(_, token)| token.kind() == TokenKind::Readonly);
2438        // `attribute long a, b;` declares two attributes of one type.
2439        for (name, type_, at) in self.declared(node) {
2440            let Ok(id) = self.ast.add_attribute(interface, &name, type_, readonly) else {
2441                continue;
2442            };
2443            let annotations = self.annotations_of(node);
2444            let node_id = self.ast.attribute(id).map(|a| a.node_id).ok();
2445            if let Some(node_id) = node_id {
2446                self.record_origin(AnyId::Attribute(id, node_id), at.span());
2447                self.apply_annotations(&annotations, AnyId::Attribute(id, node_id));
2448            }
2449            for raises in node
2450                .children()
2451                .into_iter()
2452                .filter(|child| child.kind() == CstKind::RaisesClause)
2453            {
2454                let setter = raises
2455                    .syntax_tokens()
2456                    .iter()
2457                    .any(|(_, token)| token.kind() == TokenKind::Setraises);
2458                for name in self.scoped_names_of(&raises) {
2459                    let reference = self.exception_ref(&name);
2460                    let _ = self.ast.add_attribute_raises(id, reference, setter);
2461                }
2462            }
2463        }
2464    }
2465
2466    fn valuetype(&mut self, node: &Arc<SyntaxNode>) {
2467        let Some(name) = first_ident(node) else {
2468            return;
2469        };
2470        let kind = valuetype_kind(node);
2471        let clauses: Vec<Arc<SyntaxNode>> = node
2472            .children()
2473            .into_iter()
2474            .filter(|child| child.kind() == CstKind::InheritanceClause)
2475            .collect();
2476        // The first clause inherits valuetypes, a `supports` clause
2477        // names interfaces; both are written the same way.
2478        let mut bases = Vec::new();
2479        let mut supported = Vec::new();
2480        for clause in clauses {
2481            let supports = clause
2482                .syntax_tokens()
2483                .iter()
2484                .any(|(_, token)| token.kind() == TokenKind::Supports);
2485            for scoped in self.scoped_names_of(&clause) {
2486                if supports {
2487                    supported.push(self.interface_ref(&scoped));
2488                } else {
2489                    bases.push(self.named_type_ref(&scoped));
2490                }
2491            }
2492        }
2493
2494        let declared = TypeKind::Valuetype {
2495            kind,
2496            bases,
2497            state: Vec::new(),
2498            initializers: Vec::new(),
2499            supported,
2500        };
2501
2502        // `valuetype V;` and `abstract eventtype E;` are forward
2503        // declarations — rule (105) — and a forward declaration is a
2504        // completeness state. Declaring one complete made the definition
2505        // that follows a `DuplicateName` instead of the body it is.
2506        if !has_body(node) {
2507            if self.scopes.lookup(&name).is_some() {
2508                return;
2509            }
2510            let Some(module) = self.scopes.current_module() else {
2511                return;
2512            };
2513            if let Ok(id) = self.ast.add_forward_type(module, &name, declared) {
2514                self.record_origin(AnyId::Type(id, self.node_id_of(id)), node.span());
2515                self.record_declaration_scope(AnyId::Type(id, self.node_id_of(id)), node.span());
2516                if is_event_type(node) {
2517                    if let Some(facets) = self.ast.facets_mut(AnyId::Type(id, self.node_id_of(id)))
2518                    {
2519                        facets.corba_mut().is_event_type = true;
2520                    }
2521                }
2522                self.bind(&name, id);
2523            }
2524            return;
2525        }
2526
2527        let Some(id) = self.declare_type(&name, declared, node) else {
2528            return;
2529        };
2530        // `eventtype` and `valuetype` lower to the same kind of node, so
2531        // the keyword is the only thing left that tells them apart, and
2532        // CCM 4.0 §6.6.1.1 maps the two differently.
2533        if is_event_type(node) {
2534            if let Some(facets) = self.ast.facets_mut(AnyId::Type(id, self.node_id_of(id))) {
2535                facets.corba_mut().is_event_type = true;
2536            }
2537        }
2538        self.one_namespace_per_scope(node);
2539        // The body's own scope, so a `raises` clause and a parameter
2540        // type inside an initialiser resolve the way an interface's do.
2541        self.scopes.push_container(&name);
2542        for child in node.children() {
2543            match child.kind() {
2544                CstKind::Member => self.member(&child, id),
2545                // Rule (105) puts `factory` initialisers and exported
2546                // operations in a valuetype body beside its state. This
2547                // loop used to take `Member` and nothing else, so every
2548                // one of them was parsed, kept in the CST and dropped
2549                // here — `TypeKind::Valuetype::initializers` came back
2550                // empty for every input in the tree, and the CORBA Rust
2551                // generator's `factory` output disappeared with no
2552                // diagnostic.
2553                CstKind::Operation => self.valuetype_operation(&child, id),
2554                // Rule (105)'s `<export>` also admits an attribute.
2555                // `AttributeNode::parent` is an `Id<InterfaceNode>`, so
2556                // the model has no place to put one declared here — and
2557                // an arm that did nothing dropped the declaration with
2558                // no diagnostic. Named instead: a gap that reports is a
2559                // gap, a gap that is silent is an acceptance claim.
2560                CstKind::AttributeDecl => self.diagnose_at(
2561                    DiagnosticCode::Unsupported,
2562                    child.span(),
2563                    "an attribute declared in a valuetype body has no model form yet; \
2564                     the model attaches an attribute to an interface",
2565                    "OMG IDL 4.2 §7.4.5",
2566                ),
2567                // The clause was read into `bases` and `supported`
2568                // above, before the body's scope was pushed.
2569                CstKind::InheritanceClause => {}
2570                // Rule (105)'s remaining `<export>` forms — a nested
2571                // `const`, `typedef`, `struct`, `union`, `enum`,
2572                // `bitset`, `bitmask`, `native` or `exception`. They
2573                // lower into the scope this body pushed, exactly as an
2574                // interface's do, and taking `Member` and `Operation`
2575                // and nothing else dropped every one of them:
2576                // `crates/idl-parse/tests/fixtures/spec_features/corba_value_repository.idl`
2577                // declares `exception Bad {};` inside `valuetype V` and
2578                // names it in `factory create(..) raises (Bad)`, so the
2579                // `raises` reference had no declaration to resolve to.
2580                _ => self.definition(&child),
2581            }
2582        }
2583        self.scopes.pop();
2584    }
2585
2586    /// An operation inside a `valuetype` body — a `factory` initialiser
2587    /// or an exported operation. Rule (105) and rule (107).
2588    ///
2589    /// The two are told apart by the `factory` keyword, which the
2590    /// parser keeps as a token of the operation's own node.
2591    fn valuetype_operation(&mut self, node: &Arc<SyntaxNode>, valuetype: Id<TypeNode>) {
2592        let Some(name) = first_ident(node) else {
2593            return;
2594        };
2595        let initializer = node
2596            .syntax_tokens()
2597            .iter()
2598            .any(|(_, token)| token.kind() == TokenKind::Factory);
2599        // An initialiser has no return type — rule (107) — and an
2600        // exported operation reads one the same way an interface's
2601        // does.
2602        let return_type = if initializer {
2603            None
2604        } else {
2605            self.type_ref(node)
2606        };
2607        let Ok(id) = self
2608            .ast
2609            .add_valuetype_operation(valuetype, &name, return_type, initializer)
2610        else {
2611            return;
2612        };
2613        let annotations = self.annotations_of(node);
2614        let node_id = self.ast.operation(id).map(|o| o.node_id).ok();
2615        if let Some(node_id) = node_id {
2616            self.record_origin(AnyId::Operation(id, node_id), node.span());
2617            self.apply_annotations(&annotations, AnyId::Operation(id, node_id));
2618        }
2619        for parameter in node
2620            .children()
2621            .into_iter()
2622            .filter(|child| child.kind() == CstKind::Parameter)
2623        {
2624            self.parameter(&parameter, id);
2625        }
2626        for raises in node
2627            .children()
2628            .into_iter()
2629            .filter(|child| child.kind() == CstKind::RaisesClause)
2630        {
2631            for scoped in self.scoped_names_of(&raises) {
2632                let reference = self.exception_ref(&scoped);
2633                let _ = self.ast.add_raises(id, reference);
2634            }
2635        }
2636    }
2637
2638    /// `valuetype VB long;` — a boxed value. Rule (126).
2639    ///
2640    /// `TypeKind::ValueBox` was in `crates/ast` from the start and
2641    /// unreachable from IDL: the parser committed to a body at the name
2642    /// and answered "expected `{` opening the valuetype body".
2643    fn value_box(&mut self, node: &Arc<SyntaxNode>) {
2644        let Some(name) = first_ident(node) else {
2645            return;
2646        };
2647        let boxed = self
2648            .type_ref(node)
2649            .unwrap_or_else(|| Ref::Broken(broken_cause()));
2650        let _ = self.declare_type(&name, TypeKind::ValueBox { boxed }, node);
2651    }
2652
2653    /// The `struct … //@annotation` form of an annotation declaration
2654    /// (DDS-XTypes 1.3 §7.3.1.2.4, Table 22).
2655    ///
2656    /// The members are the structure's. A default value is written as the
2657    /// member's `//@default(value)`, the one place a legacy compiler lets
2658    /// a member carry one.
2659    fn annotation_from_struct(&mut self, node: &Arc<SyntaxNode>) {
2660        let Some(name) = first_ident(node) else {
2661            return;
2662        };
2663        self.check_collision(&name, node);
2664        let Some(module) = self.scopes.current_module() else {
2665            return;
2666        };
2667        let Ok(id) = self.ast.add_annotation_def(module, &name) else {
2668            return;
2669        };
2670        if let Ok(node_id) = self.ast.annotation_def(id).map(|a| a.node_id) {
2671            self.record_origin(AnyId::AnnotationDef(id, node_id), node.span());
2672            self.scopes.bind(&name, AnyId::AnnotationDef(id, node_id));
2673        }
2674        self.scopes.push(&name, None);
2675        for child in node
2676            .children()
2677            .into_iter()
2678            .filter(|child| child.kind() == CstKind::Member)
2679        {
2680            let default = self
2681                .annotations_of(&child)
2682                .into_iter()
2683                .chain(self.suffix_annotations_of(&child))
2684                .find(|applied| applied.name == "default")
2685                .and_then(|applied| applied.arguments.first().and_then(|a| a.value));
2686            for (member_name, type_ref, at) in self.declared(&child) {
2687                let result = self
2688                    .ast
2689                    .add_annotation_member(id, &member_name, type_ref, default);
2690                self.report_model(result, at.span());
2691            }
2692        }
2693        self.scopes.pop();
2694    }
2695
2696    /// An `@annotation` declaration.
2697    fn annotation_decl(&mut self, node: &Arc<SyntaxNode>) {
2698        let Some(name) = first_ident(node) else {
2699            return;
2700        };
2701        self.check_collision(&name, node);
2702        let Some(module) = self.scopes.current_module() else {
2703            return;
2704        };
2705        let Ok(id) = self.ast.add_annotation_def(module, &name) else {
2706            return;
2707        };
2708        let node_id = self.ast.annotation_def(id).map(|a| a.node_id).ok();
2709        if let Some(node_id) = node_id {
2710            self.record_origin(AnyId::AnnotationDef(id, node_id), node.span());
2711            // Bound in the scope so a use of `@Name` finds its
2712            // declaration rather than waiting on a name forever.
2713            self.scopes.bind(&name, AnyId::AnnotationDef(id, node_id));
2714        }
2715        // The body is its own scope, the way an interface body is. Rule
2716        // (221) admits an `enum`, a `const` and a `typedef` beside the
2717        // members — `@annotation Mode { enum Kind { READ, WRITE }; Kind
2718        // kind default READ; };` — and a member's type names one of
2719        // them. They were parsed and never lowered, which nothing
2720        // noticed while the members themselves were being dropped; with
2721        // the members read, `Kind` and `READ` had nowhere to resolve.
2722        self.scopes.push(&name, None);
2723        for child in node.children() {
2724            if matches!(
2725                child.kind(),
2726                CstKind::EnumDecl | CstKind::ConstDecl | CstKind::TypedefDecl
2727            ) {
2728                self.definition(&child);
2729            }
2730        }
2731
2732        for child in node
2733            .children()
2734            .into_iter()
2735            .filter(|child| child.kind() == CstKind::Member)
2736        {
2737            // Read directly rather than through `Self::declared`.
2738            //
2739            // `declared` walks `CstKind::Declarator` children, and rule
2740            // (222) `<annotation_member> ::= <annotation_member_type>
2741            // <simple_declarator> [ "default" <const_expr> ] ";"` admits
2742            // exactly one name, which `parse_annotation_decl` reads with
2743            // a bare `expect(Ident)` and wraps in no `Declarator` node.
2744            // So `declared` found none, returned an empty list, and
2745            // every annotation's members were dropped without a word:
2746            // `@annotation Aa { long v; };` reached the model as a
2747            // declaration with zero members. Nothing downstream could
2748            // check an application against it, which is why all four of
2749            // IDL 4.2 §7.4.15.4.2's rules had nothing to hold.
2750            //
2751            // `first_ident` reads the node's own tokens and not its
2752            // descendants', so the name of `Ea v;` is `v` and not `Ea`.
2753            let Some(member_name) = first_ident(&child) else {
2754                self.diagnose_at(
2755                    DiagnosticCode::MissingToken,
2756                    child.span(),
2757                    "an annotation member needs a name",
2758                    "OMG IDL 4.2 §7.4.15.3 rule (222)",
2759                );
2760                continue;
2761            };
2762            let type_ = self
2763                .type_ref(&child)
2764                .unwrap_or_else(|| Ref::Broken(broken_cause()));
2765            let default = self.first_expr_child(&child);
2766            let result = self
2767                .ast
2768                .add_annotation_member(id, &member_name, type_, default);
2769            self.report_model(result, child.span());
2770        }
2771        self.scopes.pop();
2772    }
2773
2774    // -- helpers ---------------------------------------------------------
2775
2776    /// The first type specification among a node's children.
2777    fn type_ref(&mut self, node: &Arc<SyntaxNode>) -> Option<Ref<TypeNode>> {
2778        let type_node = node.children().into_iter().find(|child| {
2779            matches!(
2780                child.kind(),
2781                CstKind::PrimitiveType
2782                    | CstKind::NamedType
2783                    | CstKind::SequenceType
2784                    | CstKind::StringType
2785                    | CstKind::WStringType
2786                    | CstKind::MapType
2787                    | CstKind::FixedType
2788            )
2789        })?;
2790        Some(self.type_ref_of(&type_node))
2791    }
2792
2793    fn type_ref_of(&mut self, node: &Arc<SyntaxNode>) -> Ref<TypeNode> {
2794        match node.kind() {
2795            CstKind::PrimitiveType => self.primitive_ref(node),
2796            CstKind::StringType | CstKind::WStringType => {
2797                let width = if node.kind() == CstKind::StringType {
2798                    CharWidth::Char
2799                } else {
2800                    CharWidth::WChar
2801                };
2802                // `string<N>` writes its bound in the argument list.
2803                let bound = self.bound_argument(node, 0);
2804                self.anonymous(TypeKind::String { width, bound }, node.span())
2805            }
2806            CstKind::SequenceType => {
2807                let element = self.type_argument(node, 0);
2808                let bound = self.bound_argument(node, 1);
2809                let sequence = self.anonymous(TypeKind::Sequence { element, bound }, node.span());
2810                self.use_site_annotations(node, 0, &sequence, UseSite::Element);
2811                sequence
2812            }
2813            CstKind::MapType => {
2814                let key = self.type_argument(node, 0);
2815                let value = self.type_argument(node, 1);
2816                let bound = self.bound_argument(node, 2);
2817                let map = self.anonymous(TypeKind::Map { key, value, bound }, node.span());
2818                self.use_site_annotations(node, 0, &map, UseSite::MapKey);
2819                self.use_site_annotations(node, 1, &map, UseSite::Element);
2820                map
2821            }
2822            CstKind::FixedType => self.fixed_ref(node),
2823            CstKind::NamedType => self.type_ref_from_scoped_name(node).unwrap_or_else(|| {
2824                Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(Vec::new())))
2825            }),
2826            _ => Ref::Broken(broken_cause()),
2827        }
2828    }
2829
2830    /// The type at one position of a parameterised type's argument list.
2831    fn type_argument(&mut self, node: &Arc<SyntaxNode>, position: usize) -> Ref<TypeNode> {
2832        let Some(argument) = self.argument_at(node, position) else {
2833            return Ref::Broken(broken_cause());
2834        };
2835        self.type_ref_of(&argument)
2836    }
2837
2838    /// The bound at one position of a parameterised type's argument list.
2839    ///
2840    /// Absent means unbounded; a malformed expression is not silently
2841    /// taken for one, it becomes a diagnostic in the expression lowering.
2842    fn bound_argument(&mut self, node: &Arc<SyntaxNode>, position: usize) -> Bound {
2843        let Some(argument) = self.argument_at(node, position) else {
2844            return Bound::Unbounded;
2845        };
2846        self.expr(&argument).map_or(Bound::Unbounded, Bound::Const)
2847    }
2848
2849    fn argument_at(&self, node: &Arc<SyntaxNode>, position: usize) -> Option<Arc<SyntaxNode>> {
2850        self.arguments_of(node)?
2851            .into_iter()
2852            .nth(position)
2853            .map(|(_, argument)| argument)
2854    }
2855
2856    /// A parameterised type's arguments, each with the annotations
2857    /// written immediately in front of it.
2858    ///
2859    /// `sequence<@try_construct(TRIM) String5, 10>` puts the annotation
2860    /// in the same child list as the arguments, so indexing by child
2861    /// position would count it as an argument and read the bound out of
2862    /// the element slot. Grouping is by adjacency: annotations attach to
2863    /// the argument that follows them, which is the order the source
2864    /// writes and the only grouping the grammar admits.
2865    fn arguments_of(&self, node: &Arc<SyntaxNode>) -> Option<Vec<TemplateArgument>> {
2866        let list = node
2867            .children()
2868            .into_iter()
2869            .find(|child| child.kind() == CstKind::TypeArguments)?;
2870        let mut arguments: Vec<TemplateArgument> = Vec::new();
2871        let mut pending: Vec<Arc<SyntaxNode>> = Vec::new();
2872        for child in list.children() {
2873            if child.kind() == CstKind::Annotation {
2874                pending.push(child);
2875            } else {
2876                arguments.push((core::mem::take(&mut pending), child));
2877            }
2878        }
2879        // Annotations with no argument after them are a malformed
2880        // template — `sequence<@try_construct(TRIM)>`. They are dropped
2881        // here rather than attached to the previous argument, and the
2882        // parser has already reported the missing type.
2883        Some(arguments)
2884    }
2885
2886    /// Read the annotations in front of one template argument and apply
2887    /// them to the slot they belong to on the collection itself.
2888    ///
2889    /// The collection and not the element type: the element type is
2890    /// shared — `String5` in §7.3.1.2.1.12.4 is a `typedef` that any
2891    /// number of members may use — and the behaviour belongs to this one
2892    /// use of it.
2893    fn use_site_annotations(
2894        &mut self,
2895        node: &Arc<SyntaxNode>,
2896        position: usize,
2897        collection: &Ref<TypeNode>,
2898        site: UseSite,
2899    ) {
2900        let written = self.argument_annotations(node, position);
2901        if written.is_empty() {
2902            return;
2903        }
2904        let Some(id) = collection.resolved() else {
2905            return;
2906        };
2907        let any = AnyId::Type(id, self.node_id_of(id));
2908        self.apply_use_site(&written, any, site);
2909    }
2910
2911    /// The annotations written in front of one template argument.
2912    fn argument_annotations(
2913        &mut self,
2914        node: &Arc<SyntaxNode>,
2915        position: usize,
2916    ) -> Vec<crate::annotation::Applied> {
2917        let Some(arguments) = self.arguments_of(node) else {
2918            return Vec::new();
2919        };
2920        let Some((written, _)) = arguments.into_iter().nth(position) else {
2921            return Vec::new();
2922        };
2923        written
2924            .iter()
2925            .filter_map(|annotation| self.applied_annotation(annotation))
2926            .collect()
2927    }
2928
2929    /// `fixed<d, s>`, whose parameters are digits and scale rather than
2930    /// types. IDL 4.2 §7.4.1.4.4.
2931    fn fixed_ref(&mut self, node: &Arc<SyntaxNode>) -> Ref<TypeNode> {
2932        let digits = self.fixed_parameter(node, 0);
2933        let scale = self.fixed_parameter(node, 1);
2934        match (digits, scale) {
2935            (Some(digits), Some(scale)) => {
2936                self.anonymous(TypeKind::Fixed { digits, scale }, node.span())
2937            }
2938            // A `fixed` whose parameters do not evaluate is broken rather
2939            // than defaulted: a silently chosen precision would change
2940            // the wire representation.
2941            _ => {
2942                // Two rules and two messages. (42) `<fixed_pt_type> ::=
2943                // "fixed" "<" <positive_int_const> ","
2944                // <positive_int_const> ">"` is a member's; (43)
2945                // `<fixed_pt_const_type> ::= "fixed"` is a constant's
2946                // and takes none. Naming (42) for a `const fixed` was
2947                // what made rule (43) look refused for a reason.
2948                let message = if has_type_arguments(node) {
2949                    "the digits and scale of `fixed` must both be integer constants"
2950                } else {
2951                    "a `fixed` used as a type needs its digits and scale — `fixed<d,s>`. Only a \
2952                     constant's `fixed` is written without them, and takes them from its value"
2953                };
2954                self.diagnose_at(
2955                    DiagnosticCode::UnexpectedToken,
2956                    node.span(),
2957                    message,
2958                    "OMG IDL 4.2 §7.4.1.4.4 rule (42)",
2959                );
2960                Ref::Broken(broken_cause())
2961            }
2962        }
2963    }
2964
2965    /// The `fixed<d,s>` a `const fixed` takes from its own value.
2966    ///
2967    /// Rule (43) writes the type without parameters, so the value
2968    /// decides. §7.4.1.4.3, p. 31 says how, and Table 7-11 gives the
2969    /// arithmetic: "A fixed-point literal has the apparent number of
2970    /// total and fractional digits. For example, 0123.450d is considered
2971    /// to be fixed<7,3> and 3000.00d is fixed<6,2>. Prefix operators do
2972    /// not affect the precision".
2973    fn fixed_const_type(
2974        &mut self,
2975        expr: Id<ConstExprNode>,
2976        node: &Arc<SyntaxNode>,
2977    ) -> Ref<TypeNode> {
2978        match self.fixed_type_of(expr, 0) {
2979            Ok((digits, scale)) => self.anonymous(TypeKind::Fixed { digits, scale }, node.span()),
2980            Err(reason) => {
2981                self.diagnose_at(
2982                    DiagnosticCode::UnexpectedToken,
2983                    node.span(),
2984                    reason,
2985                    "OMG IDL 4.2 §7.4.1.4.3, Table 7-11",
2986                );
2987                Ref::Broken(broken_cause())
2988            }
2989        }
2990    }
2991
2992    /// The digits and scale one expression of a fixed constant has.
2993    ///
2994    /// Table 7-11, p. 31, for `fixed<d1,s1> op fixed<d2,s2>`:
2995    ///
2996    /// | op | result |
2997    /// |---|---|
2998    /// | `+` | `fixed<max(d1-s1,d2-s2)+max(s1,s2)+1, max(s1,s2)>` |
2999    /// | `-` | the same as `+` |
3000    /// | `*` | `fixed<d1+d2, s1+s2>` |
3001    /// | `/` | `fixed<(d1-s1+s2)+sinf, sinf>` |
3002    ///
3003    /// `sinf` is the clause's own name for a precision it leaves to the
3004    /// implementation. The broadest reading is taken and written down
3005    /// here: `sinf` fills the result to the 31 digits §7.4.1.4.3 caps
3006    /// every fixed at, so a quotient keeps as many fractional digits as
3007    /// the type can hold. A narrower choice would discard digits the
3008    /// type has room for, and the clause gives no reason to.
3009    ///
3010    /// An integer operand is refused rather than promoted. Measured on
3011    /// the Linux test host, all four front ends that take `const fixed`
3012    /// at all refuse `3.14d + 1`: omniORB with "Cannot interpret integer
3013    /// literal as fixed point", JacORB with "Expecting fixed point
3014    /// constant", OpenDDS and TAO with "incompatible types in constant
3015    /// assignment".
3016    fn fixed_type_of(
3017        &self,
3018        expr: Id<ConstExprNode>,
3019        depth: usize,
3020    ) -> Result<(u8, u8), &'static str> {
3021        if depth > MAX_FIXED_EXPR_DEPTH {
3022            return Err("this fixed-point constant expression nests too deeply to evaluate");
3023        }
3024        let Ok(node) = self.ast.expr(expr) else {
3025            return Err("this fixed-point constant expression has no value");
3026        };
3027        match &node.expr {
3028            ConstExpr::Literal(zerodds_ast::Literal::Fixed { digits, scale }) => {
3029                let total = u8::try_from(digits.len()).map_err(
3030                    |_| "this fixed-point literal has more digits than `fixed` can hold",
3031                )?;
3032                Ok(cap_to_31_digits(total, *scale))
3033            }
3034            ConstExpr::Literal(zerodds_ast::Literal::Integer(_)) => Err(
3035                "an integer literal is not a fixed-point value; write it with a `d` suffix, as \
3036                 `1.0d`",
3037            ),
3038            ConstExpr::Literal(_) => {
3039                Err("a `const fixed` takes a fixed-point value, written with a `d` suffix")
3040            }
3041            // A name standing for another `const fixed` carries that
3042            // constant's own digits and scale.
3043            ConstExpr::NameRef(reference) => self.fixed_type_of_name(reference),
3044            ConstExpr::Unary { op, operand } => match op {
3045                // "Prefix operators do not affect the precision."
3046                UnaryOp::Plus | UnaryOp::Minus => self.fixed_type_of(*operand, depth + 1),
3047                UnaryOp::Not => Err(
3048                    "`~` is not applicable to a fixed-point expression: §7.4.1.4.3 admits unary \
3049                     `+` and `-` and binary `*`, `/`, `+` and `-`",
3050                ),
3051            },
3052            ConstExpr::Binary { op, lhs, rhs } => {
3053                let (d1, s1) = self.fixed_type_of(*lhs, depth + 1)?;
3054                let (d2, s2) = self.fixed_type_of(*rhs, depth + 1)?;
3055                fixed_binary(*op, d1, s1, d2, s2)
3056            }
3057        }
3058    }
3059
3060    /// The digits and scale of the constant a name stands for.
3061    fn fixed_type_of_name(
3062        &self,
3063        reference: &Ref<zerodds_ast::ConstNode>,
3064    ) -> Result<(u8, u8), &'static str> {
3065        let named = match reference {
3066            Ref::Resolved(id) => Some(*id),
3067            Ref::Pending(PendingRef::ScopedName(name)) => name
3068                .segments()
3069                .last()
3070                .and_then(|segment| self.scopes.lookup(segment))
3071                .and_then(|any| match any {
3072                    AnyId::Const(id, _) => Some(id),
3073                    _ => None,
3074                }),
3075            _ => None,
3076        };
3077        let Some(named) = named else {
3078            return Err("this name does not stand for a constant declared before it");
3079        };
3080        let kind = self
3081            .ast
3082            .constant(named)
3083            .ok()
3084            .and_then(|constant| constant.type_.resolved())
3085            .and_then(|type_| self.ast.type_(type_).ok())
3086            .map(|node| node.kind.clone());
3087        match kind {
3088            Some(TypeKind::Fixed { digits, scale }) => Ok((digits, scale)),
3089            _ => Err("a fixed-point constant expression may only name another `const fixed`"),
3090        }
3091    }
3092
3093    /// One parameter of `fixed<d, s>`, evaluated.
3094    fn fixed_parameter(&mut self, node: &Arc<SyntaxNode>, position: usize) -> Option<u8> {
3095        let argument = self.argument_at(node, position)?;
3096        let expr = self.expr(&argument)?;
3097        let value = self.ast.eval_integral(expr).ok()?;
3098        u8::try_from(value).ok()
3099    }
3100
3101    /// A reference to the built-in a type keyword names.
3102    ///
3103    /// The fifteen primitives, and the built-in type names IDL provides
3104    /// that are not primitives. `any` is the first of those:
3105    /// `is_primitive_keyword` in `zerodds_idl_parse` admitted it, this
3106    /// function had no arm for it, and the member's type became
3107    /// `Ref::Broken` with **neither the parser nor the lowering saying
3108    /// anything**. The first complaint came from the layout planner
3109    /// three layers later, in a vocabulary about node ids.
3110    fn primitive_ref(&mut self, node: &Arc<SyntaxNode>) -> Ref<TypeNode> {
3111        let kinds: Vec<TokenKind> = node
3112            .syntax_tokens()
3113            .iter()
3114            .map(|(_, token)| token.kind())
3115            .filter(|kind| *kind != TokenKind::Error)
3116            .collect();
3117        if let Some((name, kind)) = builtin_of(&kinds) {
3118            return self.builtin_ref(name, kind);
3119        }
3120        let Some(primitive) = primitive_of(&kinds) else {
3121            return Ref::Broken(broken_cause());
3122        };
3123        self.builtin_ref(primitive.as_str(), TypeKind::Primitive(primitive))
3124    }
3125
3126    /// One node per built-in per model, reused rather than repeated.
3127    ///
3128    /// A file with a hundred `long` members should not hold a hundred
3129    /// primitive types, and the same holds for `any`.
3130    fn builtin_ref(&mut self, name: &str, kind: TypeKind) -> Ref<TypeNode> {
3131        if let Some(existing) = self.scopes.lookup(name).and_then(AnyId::as_type) {
3132            return Ref::Resolved(existing);
3133        }
3134        let root = self.ast.root_module();
3135        match self.ast.add_type(root, name, kind) {
3136            Ok(id) => {
3137                self.scopes.bind(name, AnyId::Type(id, self.node_id_of(id)));
3138                Ref::Resolved(id)
3139            }
3140            Err(_) => Ref::Broken(broken_cause()),
3141        }
3142    }
3143
3144    /// An anonymous type, which IDL allows inline in a member.
3145    fn anonymous(&mut self, kind: TypeKind, at: Span) -> Ref<TypeNode> {
3146        let root = self.ast.root_module();
3147        let result = self.ast.add_type(root, "", kind);
3148        self.report_model(result, at)
3149            .map_or_else(|| Ref::Broken(broken_cause()), Ref::Resolved)
3150    }
3151
3152    /// A reference by name, resolved if the name is already bound and
3153    /// pending otherwise.
3154    ///
3155    /// Pending rather than an error: forward references are legal and
3156    /// the model is built to carry them. Spec §4.
3157    fn type_ref_from_scoped_name(&mut self, node: &Arc<SyntaxNode>) -> Option<Ref<TypeNode>> {
3158        let scoped = node
3159            .descendants()
3160            .into_iter()
3161            .find(|child| child.kind() == CstKind::ScopedName)?;
3162        let segments: Vec<String> = scoped
3163            .syntax_tokens()
3164            .iter()
3165            .filter(|(_, token)| token.kind() == TokenKind::Ident)
3166            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
3167            .collect();
3168        if segments.is_empty() {
3169            return None;
3170        }
3171        if segments.len() == 1 {
3172            if let Some(found) = self.scopes.lookup(&segments[0]) {
3173                if let Some(as_type) = found.as_type() {
3174                    return Some(Ref::Resolved(as_type));
3175                }
3176                // Found, and not a type. An interface named where a
3177                // type is expected is an object reference — §7.4.6 —
3178                // and `Ast::resolve_all_by_name` has the rule for that;
3179                // what it needs is a key it can look the declaration up
3180                // by. The written segment is *relative* to the scope
3181                // that wrote it, so recording it as an absolute name
3182                // asks the index for `::I` when the declaration is
3183                // `m::I`, and the reference breaks with `NotFound`.
3184                //
3185                // Measured: `module m { interface I {}; struct S { I r; };
3186                // };` broke, `interface I {}; struct S { I r; };` did
3187                // not, and `m::I` written out did not — the failure was
3188                // exactly the unqualified form inside a module, which is
3189                // how interface-typed members are normally written.
3190                if let Some(scoped) = self.ast.scoped_name_of(found) {
3191                    return Some(Ref::Pending(PendingRef::ScopedName(scoped)));
3192                }
3193            }
3194        }
3195        Some(Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(
3196            segments,
3197        ))))
3198    }
3199
3200    /// The scoped name written under a node, absolute or relative as
3201    /// written.
3202    pub(crate) fn scoped_name_of(&self, node: &Arc<SyntaxNode>) -> Option<ScopedName> {
3203        let scoped = if node.kind() == CstKind::ScopedName {
3204            node.clone()
3205        } else {
3206            node.descendants()
3207                .into_iter()
3208                .find(|child| child.kind() == CstKind::ScopedName)?
3209        };
3210        let segments: Vec<String> = scoped
3211            .syntax_tokens()
3212            .iter()
3213            .filter(|(_, token)| token.kind() == TokenKind::Ident)
3214            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
3215            .collect();
3216        if segments.is_empty() {
3217            return None;
3218        }
3219        Some(ScopedName::absolute(segments))
3220    }
3221
3222    /// Record a diagnostic at a byte range.
3223    pub(crate) fn diagnose_at(
3224        &mut self,
3225        code: DiagnosticCode,
3226        span: Span,
3227        message: &str,
3228        spec: &'static str,
3229    ) {
3230        self.diagnostics
3231            .push(Diagnostic::error(code, span, message.to_string(), spec));
3232    }
3233
3234    /// Record a warning at a byte range.
3235    ///
3236    /// For what the file may keep and the reader still has to hear
3237    /// about. The counterpart of [`Self::diagnose_at`], and separate
3238    /// from [`Self::diagnose_vendor`] because that one's severity is
3239    /// the dialect's answer, not the rule's.
3240    pub(crate) fn warn_at(
3241        &mut self,
3242        code: DiagnosticCode,
3243        span: Span,
3244        message: &str,
3245        spec: &'static str,
3246    ) {
3247        self.diagnostics
3248            .push(Diagnostic::warning(code, span, message.to_string(), spec));
3249    }
3250
3251    /// Record that a construct outside OMG IDL 4.2 was written here.
3252    ///
3253    /// Always recorded; the dialect decides only the severity — an error
3254    /// when the flag is off, a warning when it is on. The parser's
3255    /// `diagnose_vendor` is the same rule one stage earlier, for the
3256    /// vendor constructs that are grammar rather than pragmas.
3257    ///
3258    /// Spec §3.5.
3259    pub(crate) fn diagnose_vendor(
3260        &mut self,
3261        flag: zerodds_idl_parse::VendorFlag,
3262        span: Span,
3263        message: &str,
3264    ) {
3265        let spec = "zerodds-idl-frontend-1.0 §3.5";
3266        let diagnostic = if self.options.dialect.admits(flag) {
3267            Diagnostic::warning(
3268                DiagnosticCode::VendorExtension,
3269                span,
3270                message.to_string(),
3271                spec,
3272            )
3273        } else {
3274            Diagnostic::error(
3275                DiagnosticCode::VendorExtension,
3276                span,
3277                message.to_string(),
3278                spec,
3279            )
3280        };
3281        self.diagnostics.push(diagnostic);
3282    }
3283
3284    /// The declared type of a constant, checked against its value.
3285    ///
3286    /// IDL 4.2 §7.4.1.4.3: a constant declaration carries "a value
3287    /// expression (`<const_expr>`), which shall be consistent with the
3288    /// type declared for the constant". §7.2.6.3 states two of the cases
3289    /// as a requirement in so many words:
3290    ///
3291    /// > Attempts to assign a wide string literal to a non-wide string
3292    /// > constant or to assign a non-wide string literal to a wide string
3293    /// > constant result in a compile-time diagnostic.
3294    ///
3295    /// Without this the declared type was discarded and the literal's own
3296    /// type took its place: `const long K = "x";` reached the Rust
3297    /// backend as `pub const K: &'static str = "x";`, and the author
3298    /// heard about it from the Rust compiler at the use site rather than
3299    /// here.
3300    ///
3301    /// **Silent where it is not certain.** The check fires only when the
3302    /// declared type resolves here to a primitive, a `string`/`wstring`
3303    /// or a `fixed`, *and* the expression settles to one literal form. A
3304    /// named type, an alias, an enumeration, a reference to another
3305    /// constant and an unresolved reference all pass without a word: a
3306    /// rejection that reaches too far breaks valid IDL, which costs more
3307    /// than the defect it removes.
3308    fn check_const_value_form(
3309        &mut self,
3310        declared: &Ref<TypeNode>,
3311        expr: Id<ConstExprNode>,
3312        node: &Arc<SyntaxNode>,
3313    ) {
3314        let Some(complaint) = self.value_disagrees_with(declared, expr) else {
3315            return;
3316        };
3317        // §7.2.6.3 states the two string cases outright; the rest are
3318        // §7.4.1.4.3's "consistent with the type declared".
3319        let spec = if complaint.contains("string literal") {
3320            "OMG IDL 4.2 §7.2.6.3"
3321        } else {
3322            "OMG IDL 4.2 §7.4.1.4.3"
3323        };
3324        self.diagnose_at(
3325            DiagnosticCode::TypeMismatch,
3326            node.span(),
3327            &alloc::format!("the constant {complaint}"),
3328            spec,
3329        );
3330    }
3331
3332    /// Whether `expr` is a value the type `declared` can hold, and if
3333    /// not, how to say so.
3334    ///
3335    /// `None` means "no complaint", which includes every case the answer
3336    /// is not settled in: an unresolved reference, a named type, an
3337    /// alias, an enumeration, a reference to another constant, or an
3338    /// expression that does not reduce to one literal form. A rejection
3339    /// that reaches too far breaks valid IDL, which costs more than the
3340    /// defect it removes.
3341    ///
3342    /// Two clauses ask this question and get the same answer here: IDL
3343    /// 4.2 §7.4.1.4.3 for a constant's value, and §7.4.15.4.2 for an
3344    /// annotation argument — "a constant expression, whose type must be
3345    /// compatible with the member's declaration". One reading, so the
3346    /// two cannot drift.
3347    pub(crate) fn value_disagrees_with(
3348        &self,
3349        declared: &Ref<TypeNode>,
3350        expr: Id<ConstExprNode>,
3351    ) -> Option<alloc::string::String> {
3352        let Ref::Resolved(id) = declared else {
3353            return None;
3354        };
3355        let kind = self.ast.type_(*id).ok()?.kind.clone();
3356        let form = self.value_form(expr, 0)?;
3357        let (admitted, wanted) = match &kind {
3358            TypeKind::Primitive(PrimitiveKind::Boolean) => {
3359                (form == ValueForm::Boolean, "a boolean")
3360            }
3361            TypeKind::Primitive(
3362                PrimitiveKind::Octet
3363                | PrimitiveKind::Int8
3364                | PrimitiveKind::UInt8
3365                | PrimitiveKind::Int16
3366                | PrimitiveKind::UInt16
3367                | PrimitiveKind::Int32
3368                | PrimitiveKind::UInt32
3369                | PrimitiveKind::Int64
3370                | PrimitiveKind::UInt64,
3371            ) => (form == ValueForm::Integer, "an integer"),
3372            TypeKind::Primitive(
3373                PrimitiveKind::Float32 | PrimitiveKind::Float64 | PrimitiveKind::Float128,
3374            ) => (
3375                matches!(form, ValueForm::Integer | ValueForm::Float),
3376                "a number",
3377            ),
3378            TypeKind::Fixed { .. } => (
3379                matches!(
3380                    form,
3381                    ValueForm::Integer | ValueForm::Float | ValueForm::Fixed
3382                ),
3383                "a number",
3384            ),
3385            TypeKind::Primitive(PrimitiveKind::Char) => (form == ValueForm::Char, "a character"),
3386            // A narrow character literal is a wide one written narrowly;
3387            // the reverse is not, because the code point may not fit.
3388            TypeKind::Primitive(PrimitiveKind::WChar) => (
3389                matches!(form, ValueForm::Char | ValueForm::WChar),
3390                "a character",
3391            ),
3392            TypeKind::String {
3393                width: CharWidth::Char,
3394                ..
3395            } => (form == ValueForm::String, "a string literal"),
3396            TypeKind::String {
3397                width: CharWidth::WChar,
3398                ..
3399            } => (form == ValueForm::WString, "a wide string literal"),
3400            // Everything else — an enumeration, a sequence, an aggregate
3401            // — is not settled here.
3402            _ => return None,
3403        };
3404        if admitted {
3405            return None;
3406        }
3407        Some(alloc::format!(
3408            "is declared as a type that takes {wanted}, and the value is {}; the declared \
3409             type is what it has, so the two have to agree",
3410            form.describe()
3411        ))
3412    }
3413
3414    /// Which literal form a constant expression settles to, if one.
3415    ///
3416    /// `None` wherever the answer is not settled here: a reference to
3417    /// another constant, an enumerator, a mixed arithmetic expression, or
3418    /// a tree deeper than evaluation follows. `None` means "say nothing",
3419    /// never "assume".
3420    fn value_form(&self, expr: Id<ConstExprNode>, depth: usize) -> Option<ValueForm> {
3421        if depth > zerodds_ast::node::constant::MAX_EXPR_DEPTH {
3422            return None;
3423        }
3424        let node = self.ast.expr(expr).ok()?;
3425        match &node.expr {
3426            ConstExpr::Literal(literal) => match literal {
3427                Literal::Boolean(_) => Some(ValueForm::Boolean),
3428                Literal::Integer(_) => Some(ValueForm::Integer),
3429                Literal::Float { .. } => Some(ValueForm::Float),
3430                Literal::Fixed { .. } => Some(ValueForm::Fixed),
3431                Literal::Char(_) => Some(ValueForm::Char),
3432                Literal::WChar(_) => Some(ValueForm::WChar),
3433                Literal::String(_) => Some(ValueForm::String),
3434                Literal::WString(_) => Some(ValueForm::WString),
3435                // An enumerator names a type this does not resolve here.
3436                Literal::Enumerator(_) => None,
3437            },
3438            // The operand carries the form: `-1` is an integer, `~1` is
3439            // an integer, and neither operator changes that.
3440            ConstExpr::Unary { operand, .. } => self.value_form(*operand, depth + 1),
3441            // Only when both sides agree. `1 + K` where `K` is another
3442            // constant answers `None`, which is the honest answer at this
3443            // stage.
3444            ConstExpr::Binary { lhs, rhs, .. } => {
3445                let left = self.value_form(*lhs, depth + 1)?;
3446                let right = self.value_form(*rhs, depth + 1)?;
3447                if left == right {
3448                    Some(left)
3449                } else if left.is_numeric() && right.is_numeric() {
3450                    // `1 + 1.5` is floating; the wider form wins.
3451                    Some(if left == ValueForm::Fixed || right == ValueForm::Fixed {
3452                        ValueForm::Fixed
3453                    } else {
3454                        ValueForm::Float
3455                    })
3456                } else {
3457                    None
3458                }
3459            }
3460            ConstExpr::NameRef(_) => None,
3461        }
3462    }
3463
3464    /// Give an expression node its source facet.
3465    pub(crate) fn record_expr_origin(&mut self, id: Id<zerodds_ast::ConstExprNode>, span: Span) {
3466        let node_id = self.ast.expr(id).map(|node| node.node_id).ok();
3467        if let Some(node_id) = node_id {
3468            self.record_origin(AnyId::ConstExpr(id, node_id), span);
3469        }
3470    }
3471
3472    fn bind(&mut self, name: &str, id: Id<TypeNode>) {
3473        let node_id = self.node_id_of(id);
3474        self.scopes.bind(name, AnyId::Type(id, node_id));
3475    }
3476
3477    fn node_id_of(&self, id: Id<TypeNode>) -> zerodds_ast_core::NodeId {
3478        self.ast.type_(id).map_or(
3479            zerodds_ast_core::NodeId::from_parts(zerodds_ast_core::IdSpace::SYNTHETIC, 0),
3480            |node| node.node_id,
3481        )
3482    }
3483
3484    /// Facets naming where a node came from.
3485    fn facets_at(&self, span: Span) -> Facets {
3486        Facets {
3487            source: self.source_at(span),
3488            ..Facets::default()
3489        }
3490    }
3491
3492    /// The source facet for a byte range of the file being lowered.
3493    ///
3494    /// The range is narrowed to the syntax inside it: a node's own span
3495    /// starts at its leading trivia, and a facet that kept it would put
3496    /// every consumer — the diagnostics of `complete.rs` and
3497    /// `validate.rs` among them — one blank line above the declaration.
3498    fn source_at(&self, span: Span) -> SourceFacet {
3499        let span = self.syntax_spans.trim(span);
3500        SourceFacet {
3501            origin: SourceOrigin::File {
3502                path: self.path.clone(),
3503            },
3504            span: Some(zerodds_ast::ByteSpan {
3505                start: span.start,
3506                end: span.end,
3507            }),
3508            macro_chain: Vec::new(),
3509            doc_comments: Vec::new(),
3510        }
3511    }
3512
3513    fn record_origin(&mut self, any: AnyId, span: Span) {
3514        let source = self.source_at(span);
3515        if let Some(facets) = self.ast.facets_mut(any) {
3516            facets.source = source;
3517        }
3518    }
3519
3520    /// Declare a named type in the current scope, or complete the
3521    /// forward declaration already standing there.
3522    ///
3523    /// The one place a named type enters the model, so the forward rule
3524    /// holds for every declaration form rather than for whichever ones
3525    /// remembered it. A definition following `struct S;` completes that
3526    /// node: references taken in between stay valid, which is why
3527    /// forwardness is a state and not a kind. Spec §3.2 of the AST.
3528    ///
3529    /// A genuine duplicate is reported and still declared, so the rest
3530    /// of the file is checked rather than abandoned at the first clash.
3531    /// Records the interface or value type a declaration sits in, when it
3532    /// sits in one: its scoped name and its repository id go through it
3533    /// (`ScopeStack::push_container`).
3534    fn record_declaration_scope(&mut self, any: AnyId, at: zerodds_idl_cst::Span) {
3535        let Some(scope) = self.scopes.enclosing_container().map(String::from) else {
3536            return;
3537        };
3538        let result = self.ast.set_declaration_scope(any, &scope);
3539        self.report_model(result, at);
3540    }
3541
3542    fn declare_type(
3543        &mut self,
3544        name: &str,
3545        kind: TypeKind,
3546        at: &Arc<SyntaxNode>,
3547    ) -> Option<Id<TypeNode>> {
3548        if let Some(existing) = self.scopes.lookup_here(name).and_then(AnyId::as_type) {
3549            let forward = self
3550                .ast
3551                .type_(existing)
3552                .is_ok_and(|node| node.completeness == Completeness::Forward);
3553            if forward {
3554                return match self.ast.complete_type(existing, kind) {
3555                    Ok(()) => {
3556                        let any = AnyId::Type(existing, self.node_id_of(existing));
3557                        self.record_origin(any, at.span());
3558                        // The definition's own annotations. The fresh
3559                        // branch below applies them; this branch did
3560                        // not, so `@mutable struct Later { ... };`
3561                        // behind a `struct Later;` lost its
3562                        // extensibility and serialised under the
3563                        // default — invisibly wherever the default was
3564                        // `appendable` and it said `@appendable`,
3565                        // measured under Cyclone's `final` on
3566                        // `resolve-forward-declared`.
3567                        self.apply_waiting_annotations(any);
3568                        Some(existing)
3569                    }
3570                    Err(_) => None,
3571                };
3572            }
3573        }
3574        self.check_collision(name, at);
3575        let module = self.scopes.current_module()?;
3576        let result = self.ast.add_type(module, name, kind);
3577        let id = self.report_model(result, at.span())?;
3578        self.record_origin(AnyId::Type(id, self.node_id_of(id)), at.span());
3579        self.record_declaration_scope(AnyId::Type(id, self.node_id_of(id)), at.span());
3580        self.bind(name, id);
3581        self.apply_waiting_annotations(AnyId::Type(id, self.node_id_of(id)));
3582        Some(id)
3583    }
3584
3585    /// Declare an interface in the current scope, or complete the
3586    /// forward declaration already standing there.
3587    ///
3588    /// The counterpart of [`Self::declare_type`], and it exists for the
3589    /// same reason: the forward rule has to hold for every declaration
3590    /// form rather than for whichever ones remembered it. `struct S;`
3591    /// and `union U;` had it and `interface I;` did not.
3592    fn declare_interface(
3593        &mut self,
3594        name: &str,
3595        kind: InterfaceKind,
3596        bases: Vec<Ref<zerodds_ast::InterfaceNode>>,
3597        at: &Arc<SyntaxNode>,
3598    ) -> Option<Id<zerodds_ast::InterfaceNode>> {
3599        if let Some(AnyId::Interface(existing, _)) = self.scopes.lookup_here(name) {
3600            let forward = self
3601                .ast
3602                .interface(existing)
3603                .is_ok_and(|node| node.completeness == Completeness::Forward);
3604            if forward {
3605                return match self.ast.complete_interface(existing, kind, bases) {
3606                    Ok(()) => {
3607                        // The definition's annotations, exactly as
3608                        // `declare_type`'s forward branch applies them.
3609                        if let Ok(node) = self.ast.interface(existing) {
3610                            let node_id = node.node_id;
3611                            self.apply_waiting_annotations(AnyId::Interface(existing, node_id));
3612                        }
3613                        Some(existing)
3614                    }
3615                    Err(_) => None,
3616                };
3617            }
3618        }
3619        self.check_collision(name, at);
3620        let module = self.scopes.current_module()?;
3621        let result = self.ast.add_interface(module, name, kind, bases);
3622        self.report_model(result, at.span())
3623    }
3624
3625    /// Apply the annotations of the wrapper node this declaration sits
3626    /// in, and any written directly on the declaration itself.
3627    pub(crate) fn apply_waiting_annotations(&mut self, target: AnyId) {
3628        let Some(annotations) = self.pending_annotations.pop() else {
3629            return;
3630        };
3631        self.apply_annotations(&annotations, target);
3632        // Put them back: one wrapper may hold several declarators, and
3633        // each of them carries the same annotations.
3634        self.pending_annotations.push(annotations);
3635    }
3636
3637    /// One namespace per scope, compared without regard to case.
3638    ///
3639    /// OMG IDL 4.2 §7.2.3.1: "There is only one namespace for IDL
3640    /// identifiers in each scope", and when two identifiers are compared
3641    /// to see whether they collide, "upper- and lower-case letters are
3642    /// treated as the same letter". The clause's own example is this
3643    /// shape —
3644    ///
3645    /// ```text
3646    /// module M {
3647    ///     typedef long Foo;
3648    ///     const long thing = 1;
3649    ///     interface thing { void doit (in Foo foo); };
3650    /// };
3651    /// ```
3652    ///
3653    /// — and the errors the specification lists for it include "`Foo`
3654    /// and `foo` collide", although `Foo` is declared in `M` and `foo`
3655    /// in the scope of `doit`. What binds the two is that the *simple*
3656    /// name `Foo` is **used** in the scope `foo` is **declared** in. An
3657    /// aggregate is that scope for its members, so `struct S { Inner
3658    /// inner; };` is the same collision.
3659    ///
3660    /// A type reached through a scoped name — `::common::First first;`
3661    /// — puts no simple identifier in the member's scope and does not
3662    /// collide. `tao_idl` and `opendds_idl`, the two front ends that
3663    /// answer this in the clause's own words, draw the line in the same
3664    /// place.
3665    ///
3666    /// Reported here rather than through `check_collision` because a
3667    /// member is not bound in `ScopeStack`: an aggregate is a scope for
3668    /// its member names and for nothing else, and binding members there
3669    /// would put them in the way of every later type lookup.
3670    ///
3671    /// Four scopes have this shape and all four are checked: an
3672    /// aggregate over its members, a union additionally over its
3673    /// discriminator, an interface over its attributes and operation
3674    /// names, and an operation over its parameters — which is the
3675    /// example's own scope.
3676    fn one_namespace_per_scope(&mut self, node: &Arc<SyntaxNode>) {
3677        // Every simple name the scope uses as a type, and every name it
3678        // declares, in one list — the clause has one namespace and so
3679        // has this. A name is kept with the span to report it at, which
3680        // is the declaration's: the use is legal on its own and only the
3681        // declaration can move.
3682        //
3683        // Track which body child (declaration) each name comes from, so
3684        // that under the ROS 2 dialect we can admit a collision between a
3685        // member/parameter name and the simple type name used *in that
3686        // same declaration* (e.g., `Pose pose;`), while still rejecting
3687        // cross-declaration collisions and declared/declared collisions.
3688        let mut used: Vec<UsedName> = Vec::new();
3689        let mut declared: Vec<DeclaredName> = Vec::new();
3690
3691        // Which children hold the declarations of this scope, and which
3692        // an inner scope of their own. An operation's parameters are the
3693        // operation's scope and not the interface's, so an interface
3694        // stops at the operation's name and its return type.
3695        let (bodies, stop_at_inner_scope) = match node.kind() {
3696            CstKind::InterfaceDecl => (
3697                [CstKind::AttributeDecl, CstKind::Operation].as_slice(),
3698                true,
3699            ),
3700            CstKind::Operation => ([CstKind::Parameter].as_slice(), false),
3701            _ => ([CstKind::Member, CstKind::UnionCase].as_slice(), false),
3702        };
3703
3704        let mut child_index = 0;
3705        for child in node.children() {
3706            let is_body = bodies.contains(&child.kind());
3707            // A type written directly on the declaration is used in this
3708            // scope: a union's discriminator and an operation's return
3709            // type are both written there.
3710            if !is_body && child.kind() != CstKind::NamedType {
3711                continue;
3712            }
3713            let inner_scope = stop_at_inner_scope && child.kind() == CstKind::Operation;
3714            if inner_scope {
3715                // Only the operation's own name and its return type
3716                // belong to the interface; the parameters do not.
3717                if let Some(name) = first_ident(&child) {
3718                    declared.push(DeclaredName {
3719                        name,
3720                        span: child.span(),
3721                        child_index,
3722                    });
3723                }
3724                for grandchild in child.children() {
3725                    if grandchild.kind() == CstKind::NamedType {
3726                        collect_simple_type_names_with_index(&grandchild, &mut used, child_index);
3727                    }
3728                }
3729                child_index += 1;
3730                continue;
3731            }
3732            collect_simple_type_names_with_index(&child, &mut used, child_index);
3733            if !is_body {
3734                child_index += 1;
3735                continue;
3736            }
3737            for declarator in child
3738                .descendants()
3739                .into_iter()
3740                .filter(|inner| inner.kind() == CstKind::Declarator)
3741            {
3742                if let Some(name) = first_ident(&declarator) {
3743                    declared.push(DeclaredName {
3744                        name,
3745                        span: declarator.span(),
3746                        child_index,
3747                    });
3748                }
3749            }
3750            // A parameter writes its name as a plain identifier rather
3751            // than through a declarator.
3752            if child.kind() == CstKind::Parameter
3753                && !child
3754                    .children()
3755                    .iter()
3756                    .any(|inner| inner.kind() == CstKind::Declarator)
3757            {
3758                if let Some(name) = first_ident(&child) {
3759                    declared.push(DeclaredName {
3760                        name,
3761                        span: child.span(),
3762                        child_index,
3763                    });
3764                }
3765            }
3766            child_index += 1;
3767        }
3768
3769        for index in 0..declared.len() {
3770            let current = &declared[index];
3771            let name = &current.name;
3772            let span = current.span;
3773            let child_idx = current.child_index;
3774
3775            // Check against used type names from other declarations
3776            let clash_used = used
3777                .iter()
3778                .filter(|u| u.child_index != child_idx)
3779                .map(|u| u.name.as_str())
3780                .find(|other| *other != name && other.eq_ignore_ascii_case(name));
3781
3782            // Check against previously declared names (always an error)
3783            let clash_declared = declared[..index]
3784                .iter()
3785                .map(|d| d.name.as_str())
3786                .find(|other| *other != name && other.eq_ignore_ascii_case(name));
3787
3788            // Check against used type names from the same declaration
3789            // Only relevant under ROS 2 dialect: admit this specific case
3790            let clash_same_decl = used
3791                .iter()
3792                .filter(|u| u.child_index == child_idx)
3793                .map(|u| u.name.as_str())
3794                .find(|other| *other != name && other.eq_ignore_ascii_case(name));
3795
3796            if let Some(clash) = clash_declared {
3797                // Two declared names in the same scope differing only in case — always an error
3798                let message = alloc::format!(
3799                    "`{name}` and `{clash}` are in one scope and differ only in case; IDL has a \
3800                     single namespace per scope and compares identifiers without regard to case"
3801                );
3802                self.diagnose_at(
3803                    DiagnosticCode::DuplicateName,
3804                    span,
3805                    &message,
3806                    "OMG IDL 4.2 §7.2.3.1",
3807                );
3808            } else if let Some(clash) = clash_used {
3809                // Declared name collides with a used type name from a different declaration — error
3810                let message = alloc::format!(
3811                    "`{name}` and `{clash}` are in one scope and differ only in case; IDL has a \
3812                     single namespace per scope and compares identifiers without regard to case. \
3813                     Rename the declaration, or write the type's scoped name"
3814                );
3815                self.diagnose_at(
3816                    DiagnosticCode::DuplicateName,
3817                    span,
3818                    &message,
3819                    "OMG IDL 4.2 §7.2.3.1",
3820                );
3821            } else if let Some(clash) = clash_same_decl {
3822                // Declared name collides with a used type name from the same declaration
3823                // Admit under ROS 2 dialect, error otherwise
3824                if self
3825                    .options
3826                    .dialect
3827                    .admits(zerodds_idl_parse::VendorFlag::Ros2)
3828                {
3829                    // Record that this construct was admitted by the ROS 2 flag
3830                    // (no diagnostic emitted, but the construct is accepted)
3831                } else {
3832                    let message = alloc::format!(
3833                        "`{name}` and `{clash}` are in one scope and differ only in case; IDL has a \
3834                         single namespace per scope and compares identifiers without regard to case. \
3835                         Rename the declaration, or write the type's scoped name"
3836                    );
3837                    self.diagnose_at(
3838                        DiagnosticCode::DuplicateName,
3839                        span,
3840                        &message,
3841                        "OMG IDL 4.2 §7.2.3.1",
3842                    );
3843                }
3844            }
3845        }
3846    }
3847
3848    /// Collision reporting, kept where the duplicate rule is stated.
3849    fn duplicate(&mut self, name: &str, at: &Arc<SyntaxNode>) {
3850        let message = alloc::format!(
3851            "`{name}` is already declared in this scope; IDL compares identifiers case-insensitively"
3852        );
3853        self.diagnose_at(
3854            DiagnosticCode::DuplicateName,
3855            at.span(),
3856            &message,
3857            "OMG IDL 4.2 §7.2.3",
3858        );
3859    }
3860
3861    /// Turn a rejected model mutation into a diagnostic.
3862    ///
3863    /// The model refuses what would break a structural invariant, and
3864    /// the refusal is the only record of it: a lowering that discarded
3865    /// the `Err` would drop the declaration and say nothing, which is
3866    /// how `long a[0]` reached a model with no array in it and no
3867    /// complaint about one.
3868    pub(crate) fn report_model<T>(
3869        &mut self,
3870        result: Result<T, zerodds_ast::AstError>,
3871        at: Span,
3872    ) -> Option<T> {
3873        match result {
3874            Ok(value) => Some(value),
3875            Err(error) => {
3876                let (code, spec) = classify(&error);
3877                let message = self.explain(&error);
3878                self.diagnose_at(code, at, &message, spec);
3879                None
3880            }
3881        }
3882    }
3883
3884    /// A rejected mutation, in the terms the source used.
3885    ///
3886    /// The model names nodes by identity, which is what it has; a
3887    /// diagnostic has to name them the way they were written.
3888    fn explain(&self, error: &zerodds_ast::AstError) -> String {
3889        use zerodds_ast::{AstError, InvariantViolation as V};
3890        let AstError::Invariant(violations) = error else {
3891            return alloc::format!("{error}");
3892        };
3893        match violations.first() {
3894            Some(V::DuplicateMemberId {
3895                id, first, second, ..
3896            }) => {
3897                let (first_name, first_origin) = self.member_id_source(*first);
3898                let (second_name, second_origin) = self.member_id_source(*second);
3899                member_id_collision(&second_name, second_origin, &first_name, first_origin, *id)
3900            }
3901            _ => alloc::format!("{error}"),
3902        }
3903    }
3904
3905    /// A member's name and where its id came from.
3906    fn member_id_source(&self, node: zerodds_ast_core::NodeId) -> (String, &'static str) {
3907        let Some(any) = self.ast.resolve_node_id(node) else {
3908            return (String::from("a member"), "an unknown rule");
3909        };
3910        let name = match any {
3911            AnyId::Member(id, _) => self
3912                .ast
3913                .member(id)
3914                .map_or_else(|_| String::from("a member"), |m| m.name.clone()),
3915            _ => String::from("a member"),
3916        };
3917        let origin = self
3918            .ast
3919            .facets(any)
3920            .and_then(|facets| facets.xtypes.as_ref())
3921            .and_then(|x| x.member_id.as_ref())
3922            .map_or("an unknown rule", |assignment| assignment.origin.as_str());
3923        (name, origin)
3924    }
3925
3926    /// Report a name that collides case-insensitively in the same scope.
3927    ///
3928    /// IDL is case-insensitive for collisions and case-preserving
3929    /// otherwise, so `Pose` and `pose` are the same name. Spec §6.
3930    fn check_collision(&mut self, name: &str, at: &Arc<SyntaxNode>) {
3931        if self.scopes.is_bound_here(name) {
3932            self.duplicate(name, at);
3933            return;
3934        }
3935        self.check_redefines_enclosing_scope(name, at);
3936    }
3937
3938    /// A declaration may not reuse the name of the scope it is written
3939    /// in. IDL 4.2 §7.5.2.
3940    ///
3941    /// > The name of a module, structure, union, map, interface, value
3942    /// > type, event type, exception or home may not be redefined within
3943    /// > the immediate scope of the module, structure, union, map,
3944    /// > interface, value type, event type, exception or home.
3945    ///
3946    /// The specification gives it as an example with the comment
3947    /// `// Error`:
3948    ///
3949    /// ```text
3950    /// module M {
3951    ///   typedef short M;      // Error
3952    /// };
3953    /// ```
3954    ///
3955    /// **Immediate scope only**, which is what makes the rule safe to
3956    /// have: `module Ma { module Mb { typedef short Ma; }; };` is legal
3957    /// and stays legal, because `Ma` is not the name of the scope `Mb`.
3958    /// That distinction is the whole difference between this rule and
3959    /// one that would break valid IDL, and it is why only the innermost
3960    /// scope's name is compared.
3961    ///
3962    /// Case-insensitively, because §7.2.3 makes every identifier
3963    /// comparison in IDL case-insensitive and this is one.
3964    fn check_redefines_enclosing_scope(&mut self, name: &str, at: &Arc<SyntaxNode>) {
3965        let Some(scope) = self.scopes.innermost_name() else {
3966            return;
3967        };
3968        if !scope.eq_ignore_ascii_case(name) {
3969            return;
3970        }
3971        // The vendors split here, so the axis decides. Under
3972        // `NameOfTheEnclosingScope::Admitted` the declaration stands and
3973        // the model is the one Fast DDS and RTI Connext build — which is
3974        // the only way a probe reproduces the octets they wrote.
3975        if self.options.name_of_the_enclosing_scope == NameOfTheEnclosingScope::Admitted {
3976            return;
3977        }
3978        let message = alloc::format!(
3979            "`{name}` is the name of the scope it is declared in, and OMG IDL 4.2 §7.5.2 does \
3980             not admit redefining that name within its own immediate scope"
3981        );
3982        self.diagnose_at(
3983            DiagnosticCode::DuplicateName,
3984            at.span(),
3985            &message,
3986            "OMG IDL 4.2 §7.5.2",
3987        );
3988    }
3989}
3990
3991fn broken_cause() -> zerodds_ast_core::BreakCause {
3992    zerodds_ast_core::BreakCause::NotFound {
3993        what: PendingRef::ScopedName(ScopedName::absolute(Vec::new())),
3994    }
3995}
3996
3997/// A simple type name with the index of the body child it came from.
3998/// Used to distinguish same-declaration from cross-declaration collisions.
3999struct UsedName {
4000    name: String,
4001    child_index: usize,
4002}
4003
4004/// A declared name with its span and the index of the body child it came from.
4005struct DeclaredName {
4006    name: String,
4007    span: Span,
4008    child_index: usize,
4009}
4010
4011/// Like `collect_simple_type_names`, but records the child index.
4012fn collect_simple_type_names_with_index(
4013    node: &Arc<SyntaxNode>,
4014    into: &mut Vec<UsedName>,
4015    child_index: usize,
4016) {
4017    for named in node
4018        .descendants()
4019        .into_iter()
4020        .filter(|child| child.kind() == CstKind::NamedType)
4021    {
4022        let Some(scoped) = named
4023            .descendants()
4024            .into_iter()
4025            .find(|child| child.kind() == CstKind::ScopedName)
4026        else {
4027            continue;
4028        };
4029        let tokens = scoped.syntax_tokens();
4030        if tokens
4031            .iter()
4032            .any(|(_, token)| token.kind() == TokenKind::ColonColon)
4033        {
4034            continue;
4035        }
4036        let segments: Vec<&str> = tokens
4037            .iter()
4038            .filter(|(_, token)| token.kind() == TokenKind::Ident)
4039            .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()))
4040            .collect();
4041        if let [only] = segments.as_slice() {
4042            into.push(UsedName {
4043                name: (*only).to_string(),
4044                child_index,
4045            });
4046        }
4047    }
4048}
4049
4050/// The first identifier token directly under a node.
4051/// The string literals in `tokens`, a run of adjacent pieces of one
4052/// width joined into one value.
4053///
4054/// §7.2.6.3: "Adjacent string literals are concatenated" (§7.2.6.4 for
4055/// wide ones). The parser keeps each piece as its own token; this joins
4056/// a run of them, each piece decoded on its own so no escape reaches
4057/// across a seam. Any other significant token between two literals —
4058/// a comma in a `context` clause — ends the run. A piece that does not
4059/// decode drops the whole run rather than leaving a shorter string.
4060pub(crate) fn joined_string_literals(
4061    tokens: impl IntoIterator<Item = (zerodds_idl_cst::Span, zerodds_idl_cst::GreenToken)>,
4062    kind: TokenKind,
4063) -> Vec<String> {
4064    let mut joined = Vec::new();
4065    let mut run: Option<Option<String>> = None;
4066    for (_, token) in tokens {
4067        if token.kind() == kind {
4068            let piece = match zerodds_idl_cst::literal_value(kind, token.text()) {
4069                zerodds_idl_cst::TokenValue::Text { value } => Some(value),
4070                _ => None,
4071            };
4072            run = Some(match (run.take(), piece) {
4073                (None, piece) => piece,
4074                (Some(Some(mut value)), Some(piece)) => {
4075                    value.push_str(&piece);
4076                    Some(value)
4077                }
4078                (Some(_), _) => None,
4079            });
4080        } else if let Some(finished) = run.take() {
4081            joined.extend(finished);
4082        }
4083    }
4084    if let Some(finished) = run {
4085        joined.extend(finished);
4086    }
4087    joined
4088}
4089
4090fn first_ident(node: &Arc<SyntaxNode>) -> Option<String> {
4091    node.syntax_tokens()
4092        .iter()
4093        .find(|(_, token)| token.kind() == TokenKind::Ident)
4094        .map(|(_, token)| zerodds_idl_cst::identifier_name(token.text()).to_string())
4095}
4096
4097/// Whether the declarator's name token carries the escape underscore —
4098/// the spelling `identifier_name` strips and `XTypesFacet::escaped`
4099/// keeps.
4100fn declarator_is_escaped(node: &Arc<SyntaxNode>) -> bool {
4101    node.syntax_tokens()
4102        .iter()
4103        .find(|(_, token)| token.kind() == TokenKind::Ident)
4104        .is_some_and(|(_, token)| token.text().starts_with('_'))
4105}
4106
4107/// How deep a fixed-point constant expression is followed.
4108///
4109/// The same reason [`zerodds_ast::MAX_EXPR_DEPTH`] exists: a tree read
4110/// from somewhere else can be hostile, and a walk without a bound hangs
4111/// rather than reporting.
4112const MAX_FIXED_EXPR_DEPTH: usize = 256;
4113
4114/// The largest number of digits a `fixed` may have. §7.4.1.4.3.
4115const MAX_FIXED_DIGITS: u8 = 31;
4116
4117/// Whether a declaration's type is `fixed` written without parameters.
4118///
4119/// Rule (43) `<fixed_pt_const_type> ::= "fixed"` — the constant form.
4120fn bare_fixed(node: &Arc<SyntaxNode>) -> bool {
4121    node.children()
4122        .into_iter()
4123        .find(|child| child.kind() == CstKind::FixedType)
4124        .is_some_and(|fixed| !has_type_arguments(&fixed))
4125}
4126
4127/// Whether a declaration wrote a body at all.
4128///
4129/// A `valuetype` with no `{` is rule (105)'s forward declaration.
4130fn has_body(node: &Arc<SyntaxNode>) -> bool {
4131    node.syntax_tokens()
4132        .iter()
4133        .any(|(_, token)| token.kind() == TokenKind::LBrace)
4134}
4135
4136/// Whether a parameterised type wrote its `<...>` at all.
4137fn has_type_arguments(node: &Arc<SyntaxNode>) -> bool {
4138    node.children()
4139        .iter()
4140        .any(|child| child.kind() == CstKind::TypeArguments)
4141}
4142
4143/// Table 7-11's result type for one binary operator.
4144///
4145/// In `u16` throughout, and every intermediate is bounded: both operands
4146/// have already been through [`cap_to_31_digits`], so the widest result
4147/// any row can reach is `31 + 31 + 1`. The conversion back reports
4148/// rather than clamps all the same — a number this arithmetic cannot
4149/// have produced would otherwise become a precision nobody wrote.
4150fn fixed_binary(op: BinaryOp, d1: u8, s1: u8, d2: u8, s2: u8) -> Result<(u8, u8), &'static str> {
4151    // §7.4.1.4.3's own reading of a fixed type: the digits include the
4152    // fractional ones, so a scale above them is not a `fixed` at all and
4153    // the row's `d - s` would have nothing to subtract from.
4154    if s1 > d1 || s2 > d2 {
4155        return Err("a `fixed` scale cannot exceed its digits");
4156    }
4157    let (d1, s1, d2, s2) = (u16::from(d1), u16::from(s1), u16::from(d2), u16::from(s2));
4158    let (digits, scale) = match op {
4159        // fixed<max(d1-s1,d2-s2)+max(s1,s2)+1, max(s1,s2)>
4160        BinaryOp::Add | BinaryOp::Sub => {
4161            let scale = s1.max(s2);
4162            ((d1 - s1).max(d2 - s2) + scale + 1, scale)
4163        }
4164        // fixed<d1+d2, s1+s2>
4165        BinaryOp::Mul => (d1 + d2, s1 + s2),
4166        // fixed<(d1-s1+s2)+sinf, sinf>, with `sinf` filling the result
4167        // to the 31 digits the type holds.
4168        BinaryOp::Div => {
4169            let whole = d1 - s1 + s2;
4170            let sinf = u16::from(MAX_FIXED_DIGITS).saturating_sub(whole);
4171            (whole + sinf, sinf)
4172        }
4173        BinaryOp::Or
4174        | BinaryOp::Xor
4175        | BinaryOp::And
4176        | BinaryOp::Shl
4177        | BinaryOp::Shr
4178        | BinaryOp::Mod => {
4179            return Err(
4180                "§7.4.1.4.3 admits unary `+` and `-` and binary `*`, `/`, `+` and `-` in a \
4181                 fixed-point expression, and nothing else",
4182            );
4183        }
4184    };
4185    let too_wide = "this fixed-point expression reaches a precision `fixed` cannot state";
4186    Ok(cap_to_31_digits(
4187        u8::try_from(digits).map_err(|_| too_wide)?,
4188        u8::try_from(scale).map_err(|_| too_wide)?,
4189    ))
4190}
4191
4192/// §7.4.1.4.3's cap, applied after every computation.
4193///
4194/// "If an individual computation between a pair of fixed-point literals
4195/// actually generates more than 31 significant digits, then a 31-digit
4196/// result is retained as follows: `fixed<d,s> => fixed<31, 31-d+s>`."
4197///
4198/// `31 - d + s` goes negative when the integral part alone exceeds 31
4199/// digits. The clause does not say what then, and the only reading the
4200/// type admits is a scale of zero — a negative scale is not
4201/// representable and rounding is expressly forbidden, so the fractional
4202/// digits are the ones that go.
4203fn cap_to_31_digits(digits: u8, scale: u8) -> (u8, u8) {
4204    if digits <= MAX_FIXED_DIGITS {
4205        return (digits, scale);
4206    }
4207    let capped = i32::from(MAX_FIXED_DIGITS) - i32::from(digits) + i32::from(scale);
4208    (MAX_FIXED_DIGITS, u8::try_from(capped.max(0)).unwrap_or(0))
4209}
4210
4211/// Whether a declaration was written `eventtype`. Rule (166).
4212fn is_event_type(node: &Arc<SyntaxNode>) -> bool {
4213    node.syntax_tokens()
4214        .iter()
4215        .any(|(_, token)| token.kind() == TokenKind::Eventtype)
4216}
4217
4218/// Whether a member declaration opened with `public` or `private`.
4219///
4220/// Only a valuetype state member carries one; a struct member has no
4221/// visibility keyword and the answer is `None`. IDL 4.2 §7.4.4,
4222/// rule (106).
4223fn state_visibility(node: &Arc<SyntaxNode>) -> Option<zerodds_ast::StateVisibility> {
4224    node.syntax_tokens()
4225        .iter()
4226        .find_map(|(_, token)| match token.kind() {
4227            TokenKind::Public => Some(zerodds_ast::StateVisibility::Public),
4228            TokenKind::Private => Some(zerodds_ast::StateVisibility::Private),
4229            _ => None,
4230        })
4231}
4232
4233/// The built-in type names IDL provides that are not primitives.
4234///
4235/// `<base_type_spec>` reaches these through their own rules rather than
4236/// through `<primitive_type>`, and each is one keyword: `any` is
4237/// §7.4.2's rules (69) and (70), `Object` §7.4.6's (117) and (118),
4238/// `ValueBase` §7.4.7's (131) and (132).
4239/// They are named types in the model exactly as `long` is, so a member
4240/// declared with one carries a resolved reference and not a broken one.
4241fn builtin_of(kinds: &[TokenKind]) -> Option<(&'static str, TypeKind)> {
4242    match kinds {
4243        [TokenKind::Any] => Some(("any", TypeKind::Any)),
4244        // Rule (117) `<object_type> ::= "Object"` names no interface,
4245        // which is what the absent reference says.
4246        [TokenKind::Object] => Some(("Object", TypeKind::ObjectRef { interface: None })),
4247        // (131) `<value_base_type> ::= "ValueBase"`, the abstract base
4248        // every valuetype has, written as a type.
4249        [TokenKind::Valuebase] => Some(("ValueBase", TypeKind::ValueBase)),
4250        _ => None,
4251    }
4252}
4253
4254/// Which primitive a run of type keywords spells.
4255fn primitive_of(kinds: &[TokenKind]) -> Option<PrimitiveKind> {
4256    Some(match kinds {
4257        [TokenKind::Boolean] => PrimitiveKind::Boolean,
4258        [TokenKind::Octet] => PrimitiveKind::Octet,
4259        [TokenKind::Char] => PrimitiveKind::Char,
4260        [TokenKind::Wchar] => PrimitiveKind::WChar,
4261        [TokenKind::Short] => PrimitiveKind::Int16,
4262        [TokenKind::Long] => PrimitiveKind::Int32,
4263        [TokenKind::Long, TokenKind::Long] => PrimitiveKind::Int64,
4264        [TokenKind::Long, TokenKind::Double] => PrimitiveKind::Float128,
4265        [TokenKind::Unsigned, TokenKind::Short] => PrimitiveKind::UInt16,
4266        [TokenKind::Unsigned, TokenKind::Long] => PrimitiveKind::UInt32,
4267        [TokenKind::Unsigned, TokenKind::Long, TokenKind::Long] => PrimitiveKind::UInt64,
4268        [TokenKind::Float] => PrimitiveKind::Float32,
4269        [TokenKind::Double] => PrimitiveKind::Float64,
4270        [TokenKind::Int8] => PrimitiveKind::Int8,
4271        [TokenKind::Uint8] => PrimitiveKind::UInt8,
4272        [TokenKind::Int16] => PrimitiveKind::Int16,
4273        [TokenKind::Uint16] => PrimitiveKind::UInt16,
4274        [TokenKind::Int32] => PrimitiveKind::Int32,
4275        [TokenKind::Uint32] => PrimitiveKind::UInt32,
4276        [TokenKind::Int64] => PrimitiveKind::Int64,
4277        [TokenKind::Uint64] => PrimitiveKind::UInt64,
4278        _ => return None,
4279    })
4280}
4281
4282/// Whether a scoped name was written anchored at the global scope.
4283///
4284/// Rule (4) `<scoped_name> ::+ "::" <identifier>`: the leading `::` says
4285/// the root and nothing else, so a name that carries it is not looked up
4286/// outwards from where it was written.
4287fn is_root_anchored(node: &Arc<SyntaxNode>) -> bool {
4288    let scoped = if node.kind() == CstKind::ScopedName {
4289        Some(node.clone())
4290    } else {
4291        node.descendants()
4292            .into_iter()
4293            .find(|child| child.kind() == CstKind::ScopedName)
4294    };
4295    scoped.is_some_and(|scoped| {
4296        scoped
4297            .syntax_tokens()
4298            .first()
4299            .is_some_and(|(_, token)| token.kind() == TokenKind::ColonColon)
4300    })
4301}
4302
4303/// Which interface a forward declaration forwards, if it is one.
4304///
4305/// `None` for `struct S;` and `union U;`, whose forward declaration is a
4306/// type. Rule (75) `<interface_forward_dcl> ::= <interface_header>` —
4307/// and the header carries the `local` or `abstract` modifier, so the
4308/// forward and its definition agree on the form before the body arrives.
4309///
4310/// (135) `<component_forward_dcl> ::= "component" <identifier>` is the
4311/// same rule one building block on, and the four component-model
4312/// declarations reach the model as interfaces of their own kind. They
4313/// are listed here rather than left to the type path for the reason
4314/// (75) is: a `component` forward completed by a `component` definition
4315/// has to be one node.
4316fn forward_interface_kind(node: &Arc<SyntaxNode>) -> Option<InterfaceKind> {
4317    let mut modifier = None;
4318    for (_, token) in node.syntax_tokens() {
4319        match token.kind() {
4320            TokenKind::Local => modifier = Some(InterfaceKind::Local),
4321            TokenKind::Abstract => modifier = Some(InterfaceKind::Abstract),
4322            TokenKind::Interface => {
4323                return Some(modifier.unwrap_or(InterfaceKind::Interface));
4324            }
4325            TokenKind::Component => return Some(InterfaceKind::Component),
4326            TokenKind::Home => return Some(InterfaceKind::Home),
4327            TokenKind::Connector => return Some(InterfaceKind::Connector),
4328            TokenKind::Porttype => return Some(InterfaceKind::PortType),
4329            // The name ends the header; a modifier with no keyword after
4330            // it is not an interface forward.
4331            TokenKind::Ident => return None,
4332            _ => {}
4333        }
4334    }
4335    None
4336}
4337
4338/// Which interface form a declaration wrote. IDL 4.2 §7.4.3.
4339fn interface_kind(node: &Arc<SyntaxNode>) -> InterfaceKind {
4340    for (_, token) in node.syntax_tokens() {
4341        match token.kind() {
4342            TokenKind::Local => return InterfaceKind::Local,
4343            TokenKind::Abstract => return InterfaceKind::Abstract,
4344            TokenKind::Component => return InterfaceKind::Component,
4345            TokenKind::Home => return InterfaceKind::Home,
4346            TokenKind::Connector => return InterfaceKind::Connector,
4347            TokenKind::Porttype => return InterfaceKind::PortType,
4348            // The modifier precedes `interface`, so the keyword itself
4349            // ends the search rather than continuing into the body.
4350            TokenKind::Interface => break,
4351            _ => {}
4352        }
4353    }
4354    InterfaceKind::Interface
4355}
4356
4357/// Which valuetype form a declaration wrote. IDL 4.2 §7.4.4.
4358fn valuetype_kind(node: &Arc<SyntaxNode>) -> ValuetypeKind {
4359    let mut kind = ValuetypeKind::Regular;
4360    for (_, token) in node.syntax_tokens() {
4361        match token.kind() {
4362            TokenKind::Custom => kind = ValuetypeKind::Custom,
4363            TokenKind::Abstract => kind = ValuetypeKind::Abstract,
4364            TokenKind::LBrace => break,
4365            _ => {}
4366        }
4367    }
4368    if kind != ValuetypeKind::Regular {
4369        return kind;
4370    }
4371    // `truncatable` is written on the **inheritance** clause — rule
4372    // (109) `<value_inheritance_spec> ::= [ ":" [ "truncatable" ]
4373    // <value_name> …` — and not beside the `valuetype` keyword. The
4374    // loop above reads the declaration's own tokens, and the keyword is
4375    // a token of the clause's node, so it was never seen: every
4376    // `valuetype D : truncatable B` came back `Regular`, and the
4377    // §15.3.4.3 base-identifier list a peer needs in order to truncate
4378    // was not emitted. The three are mutually exclusive in valid IDL —
4379    // CORBA 3.3 part 1 §5.2.5 forbids a truncatable `custom` valuetype
4380    // and an `abstract` one has no state to truncate — so one field
4381    // holds the answer.
4382    let truncatable = node
4383        .children()
4384        .into_iter()
4385        .filter(|child| child.kind() == CstKind::InheritanceClause)
4386        .any(|clause| {
4387            clause
4388                .syntax_tokens()
4389                .iter()
4390                .any(|(_, token)| token.kind() == TokenKind::Truncatable)
4391        });
4392    if truncatable {
4393        ValuetypeKind::Truncatable
4394    } else {
4395        ValuetypeKind::Regular
4396    }
4397}
4398
4399/// The diagnostic a rejected mutation gets.
4400///
4401/// One arm per violation the model can raise, with no catch-all: a new
4402/// invariant is a compile error here rather than a diagnostic that says
4403/// only "unexpected token".
4404fn classify(error: &zerodds_ast::AstError) -> (DiagnosticCode, &'static str) {
4405    use zerodds_ast::{AstError, InvariantViolation as V};
4406    let AstError::Invariant(violations) = error else {
4407        return (DiagnosticCode::UnexpectedToken, "zerodds-ast-1.0 §8.1");
4408    };
4409    match violations.first() {
4410        Some(V::NonPositiveArrayDimension { .. }) => {
4411            (DiagnosticCode::InvalidBound, "OMG IDL 4.2 §7.4.1.4.4")
4412        }
4413        Some(V::IncompatibleCaseLabel { .. }) => {
4414            (DiagnosticCode::InvalidCaseLabel, "OMG IDL 4.2 §7.4.1.4.4.3")
4415        }
4416        Some(
4417            V::DuplicateMemberName { .. }
4418            | V::DuplicateEnumeratorName { .. }
4419            | V::NameCollidesOnlyInCase { .. },
4420        ) => (DiagnosticCode::DuplicateName, "OMG IDL 4.2 §7.2.3"),
4421        Some(V::DuplicateMemberId { .. }) => (
4422            DiagnosticCode::DuplicateMemberId,
4423            "DDS-XTypes 1.3 §7.3.1.2.1.1",
4424        ),
4425        Some(V::MultipleDefaultCases { .. }) => {
4426            (DiagnosticCode::InvalidCaseLabel, "OMG IDL 4.2 §7.4.1.4.4.3")
4427        }
4428        Some(V::DanglingReference { .. }) | None => {
4429            (DiagnosticCode::UnexpectedToken, "zerodds-ast-1.0 §8.1")
4430        }
4431    }
4432}
4433
4434/// Drop diagnostics that repeat one already reported.
4435///
4436/// Compared by code and message rather than by byte range: the same
4437/// fact reported from two places points at two different parts of it.
4438fn dedup(diagnostics: Vec<Diagnostic>) -> Vec<Diagnostic> {
4439    let mut seen: Vec<(DiagnosticCode, String)> = Vec::new();
4440    let mut out = Vec::new();
4441    for diagnostic in diagnostics {
4442        let key = (diagnostic.code, diagnostic.message.clone());
4443        if seen.contains(&key) {
4444            continue;
4445        }
4446        seen.push(key);
4447        out.push(diagnostic);
4448    }
4449    out
4450}
4451
4452/// How a member-id collision reads.
4453///
4454/// One wording, whether the collision surfaced as a rejected commit or
4455/// as a validation pass over the finished model: two spellings of one
4456/// fact would read as two problems.
4457pub(crate) fn member_id_collision(
4458    name: &str,
4459    origin: &str,
4460    other: &str,
4461    other_origin: &str,
4462    id: u32,
4463) -> String {
4464    alloc::format!(
4465        "`{name}` and `{other}` both have member id {id}: {name} from {origin}, {other} from {other_origin}"
4466    )
4467}
4468
4469/// Which kind of entity a template parameter stands for.
4470/// What an instantiation supplies for one formal parameter.
4471enum Actual {
4472    /// A type specification or constant expression written at the
4473    /// instantiation — rule (192).
4474    Written(Arc<SyntaxNode>),
4475    /// A binding handed on by an enclosing instantiation, which is what
4476    /// rule (193)'s `alias` does.
4477    Bound(AnyId),
4478}
4479
4480/// The formal parameters of a stored template body, in order.
4481///
4482/// Each is its name and whether it is the `const` alternative, which is
4483/// the only one whose actual is a value rather than a type.
4484fn template_formals(body: &Arc<SyntaxNode>) -> Vec<(String, bool)> {
4485    body.children()
4486        .into_iter()
4487        .find(|child| child.kind() == CstKind::TemplateParameters)
4488        .map_or_else(Vec::new, |list| {
4489            list.children()
4490                .into_iter()
4491                .filter(|child| child.kind() == CstKind::TemplateParameter)
4492                .filter_map(|parameter| {
4493                    let name = first_ident(&parameter)?;
4494                    let is_const =
4495                        template_param_kind(&parameter) == zerodds_ast::TemplateParamKind::Const;
4496                    Some((name, is_const))
4497                })
4498                .collect()
4499        })
4500}
4501
4502fn template_param_kind(node: &Arc<SyntaxNode>) -> zerodds_ast::TemplateParamKind {
4503    use zerodds_ast::TemplateParamKind as K;
4504    // (188)'s `<sequence_type>` alternative writes the keyword inside a
4505    // type specification of its own, so it is not one of this node's own
4506    // tokens the way the ten classifiers are.
4507    if node
4508        .children()
4509        .iter()
4510        .any(|child| child.kind() == CstKind::SequenceType)
4511    {
4512        return K::Sequence;
4513    }
4514    for (_, token) in node.syntax_tokens() {
4515        match token.kind() {
4516            TokenKind::Typename => return K::TypeName,
4517            TokenKind::Const => return K::Const,
4518            TokenKind::Sequence => return K::Sequence,
4519            TokenKind::Interface => return K::Interface,
4520            TokenKind::Valuetype => return K::ValueType,
4521            TokenKind::Eventtype => return K::EventType,
4522            TokenKind::Struct => return K::Struct,
4523            TokenKind::Union => return K::Union,
4524            TokenKind::Exception => return K::Exception,
4525            TokenKind::Enum => return K::Enum,
4526            _ => {}
4527        }
4528    }
4529    K::TypeName
4530}
4531
4532/// The type an event port carries, as a reference.
4533fn type_ref_of_port(ast: &Ast, attribute: Id<zerodds_ast::AttributeNode>) -> Ref<TypeNode> {
4534    ast.attribute(attribute)
4535        .map_or_else(|_| Ref::Broken(broken_cause()), |node| node.type_.clone())
4536}
4537
4538/// The content of a quoted pragma argument, or the word itself.
4539fn unquote(text: &str) -> Option<String> {
4540    let trimmed = text.trim();
4541    if trimmed.is_empty() {
4542        return None;
4543    }
4544    Some(
4545        trimmed
4546            .strip_prefix('"')
4547            .and_then(|rest| rest.strip_suffix('"'))
4548            .unwrap_or(trimmed)
4549            .to_string(),
4550    )
4551}
4552
4553/// The version number a `#pragma dds_xtopics` declaration named, in the
4554/// one spelling DDS-XTypes 1.3 §7.3.1.1.2 compares.
4555///
4556/// "It shall be interpreted without respect to case, and any spaces (for
4557/// example, in `1.0 Beta 1`) shall be replaced with underscores." Both
4558/// halves are needed to compare a pair: the words arrive here already
4559/// split on the spaces the clause replaces, so joining them with
4560/// underscores is that replacement, and the case is folded so that
4561/// `1.0_beta_1` and `1.0_Beta_1` are the one version they name.
4562///
4563/// An absent version number is the empty string, which the clause makes
4564/// a legal declaration — the number is optional in both forms — and
4565/// which therefore compares against nothing.
4566fn xtopics_version(words: &[&str]) -> String {
4567    words.join("_").to_ascii_lowercase()
4568}
4569
4570/// The scoped name a pragma argument names.
4571fn pragma_name(written: &str) -> ScopedName {
4572    ScopedName::absolute(
4573        written
4574            .split("::")
4575            .filter(|segment| !segment.is_empty())
4576            .map(ToString::to_string)
4577            .collect(),
4578    )
4579}