Skip to main content

prebindgen_flat/flat/
mod.rs

1//! The prebindgen **source language**: one parser from captured records to
2//! [`Element`]s.
3//!
4//! > Naming: `flat` is the *source* side — the flat API a
5//! > `#[prebindgen]` crate may write. The destination adapters (C, JNI) ship
6//! > as the separate `prebindgen-c` / `prebindgen-jni` crates. They are
7//! > opposite ends of the pipeline.
8//!
9//! ```text
10//! Source(s) ──items──> Flat ──Elements──> Registry ──> adapters
11//!   raw records          parse +               indexes       classify off `kind`
12//!   (syn::Item)          validate              elements      spell with `spell()`
13//! ```
14//!
15//! [`FlatBuilder::source`] folds the first arrow in for the common case, so a build
16//! script names one directory and gets elements; [`FlatBuilder::items`] keeps the
17//! arrow itself, for a stream that needs shaping first.
18//!
19//! # What an element is
20//!
21//! Two things at once, and that pairing is the whole design:
22//!
23//! * a **closed model** — [`TypeKind`], the field list, which of the two enum
24//!   shapes an item is — where the type grammar is the accepted Rust syntax and
25//!   the element structure is the concept above it;
26//! * one [`Origin`], carrying the **exact syntax** the node was built from and
27//!   the source it arrived in.
28//!
29//! The `Origin` is uniform: every node has one, at every level — item,
30//! parameter, field, variant, type, array extent. Some levels know less than
31//! others (a field has no line of its own, so it shares its item's), but the
32//! shape does not change with the level, and no level copies a piece of
33//! provenance down from the one above. That copying is what previously let the
34//! same crate name appear under three field names with two meanings.
35//!
36//! So the rule for every consumer is:
37//!
38//! > **Classify off `kind`, spell with [`spell()`](Origin::spell).**
39//!
40//! And the model enforces it rather than asking. [`Origin`]'s syntax is
41//! private, and every route to it — `spell()`, `as_syn()` — is visible only
42//! inside this crate. Matching a `syn::Type` or `syn::Expr` variant outside
43//! this module is a classifier, and issue #211 says classification lives here
44//! alone; the visibility is what makes that hold rather than a convention
45//! anyone has to remember. Code whose job *is* producing Rust reaches the
46//! syntax through [`Emit`](crate::Emit), which the registry pipeline
47//! (`prebindgen-registry`'s `write_rust`) hands only to the emission
48//! callbacks.
49//!
50//! # What earns a variant
51//!
52//! For a **type**, a Rust form — and nothing else. [`TypeKind`] is the accepted
53//! subset of `syn::Type`, so two spellings are two variants even when every
54//! destination language would treat them alike. Deciding that `&str` and
55//! `String` are both "a string" is a destination's decision, taken in an
56//! adapter, on a reading the model provides:
57//!
58//! | Rust writes | The model says | The reading, where a consumer wants one |
59//! |---|---|---|
60//! | `String`, `str` | [`String`](TypeKind::String), [`Str`](TypeKind::Str) | the adapter's, at its own site |
61//! | `Vec<T>`, `[T]` | [`Vec`](TypeKind::Vec), [`Slice`](TypeKind::Slice) | [`TypeRef::sequence_elem`] — one run of `T` |
62//! | `Box<T>`, `Cow<'_, T>` | [`Boxed`](TypeKind::Boxed), [`Cow`](TypeKind::Cow) | [`TypeRef::unwrapped`] — a `T` either way |
63//! | `&mut MaybeUninit<T>` | `Ref` over [`Uninit`](TypeKind::Uninit) | [`TypeRef::borrow_target`] — the value, not its slot |
64//! | no `->`, `-> ()` | [`TypeKind::Unit`] | the same function |
65//! | `*const T` | *rejected* | a source crate is idiomatic Rust; the adapter owns pointers |
66//!
67//! It buys one property: the syntax is **recoverable from the kind**
68//! (checked, over the whole acceptance corpus, by rebuilding it). Which is
69//! the difference between a slice that rides along because it is exact, and one
70//! the model cannot do without.
71//!
72//! An **element** is not a type, and there the rule is still the concept:
73//!
74//! | Rust writes | The model says | Because |
75//! |---|---|---|
76//! | `struct S;`, `struct S {}` | zero fields | the delimiters are spelling |
77//! | `enum E { A(u8) }` | [`Variant`] | a sum, identified by position |
78//! | `enum E { A = 7 }` | [`Enum`] | a named integer, identified by its value |
79//! | `type X = ..`, `struct X(..)` | [`Extern`] | named here; contents not modelled |
80//!
81//! The two enum shapes are the clearest case of a *concept* splitting where Rust
82//! has one spelling. Both are `enum` and both keep a `syn::ItemEnum`, but a sum's
83//! alternatives are identified by **position** — the mirror an adapter builds
84//! carries no `repr` and numbers its own arms — while a fieldless enum's members
85//! are identified by the **value Rust assigns**, which a C header re-states and a
86//! Kotlin `enum class` entry carries. Neither numbering means anything for the
87//! other shape, so one model covering both would carry a field that is dead in
88//! each direction, and worse than dead: Rust does assign a discriminant to a
89//! sum's alternatives, and using it would be wrong.
90//!
91//! The identities follow the same rule: a nominal type is a [`TypeId`] — a
92//! name — not a `syn::Path`, so nothing downstream has to take a path apart to
93//! learn what a type is. And a name is *all* it is: **a reference carries a
94//! name, the declaration carries the origin**, so the same type never compares
95//! unequal to itself because two source crates mentioned it. The one place a
96//! crate name rides with an identity is [`ConstId`], and that is the const's
97//! *declaring* crate, resolved by lookup — which is exactly what lets an array
98//! extent refuse a const from another source.
99//!
100//! # Why the syntax rides along
101//!
102//! The generated Rust glue is itself a destination artifact, and the only one
103//! that needs syntax fidelity: `B()` must not be re-spelled `B`, `= 0x07` must
104//! not become `= 7`. Carrying the source's own slice is how it gets that —
105//! exactly, and at no modelling cost, so a delimiter and a literal's base need
106//! never become fields.
107//!
108//! For a **type** the slice is no longer where facts go to survive:
109//! [`TypeKind`] keeps the lifetime, the wrapper and the argument it once
110//! dropped, and rebuilding the syntax from it is the round-trip that says
111//! so. What is
112//! left is the reason a slice beats a reconstruction anywhere — it is what the
113//! source wrote, and it is already there.
114//!
115//! # Where acceptance is enforced
116//!
117//! Lowering is **total over the accepted grammar**: a form with no variant in
118//! [`TypeKind`] is a form the language does not accept, so there is no second
119//! acceptance list to drift from it. One rule cannot be stated that way and is
120//! stated in the lowering instead: [`Uninit`](TypeKind::Uninit) is accepted only
121//! directly under a `&mut`, which is a fact about a **position** and not about a
122//! form.
123//!
124//! **Parsing diagnoses; ingestion raises.** Those are two different points, and
125//! the split is what lets one model serve both.
126//!
127//! Parsing never fails on a single item: an item the language cannot express
128//! becomes [`Element::Unsupported`] carrying its diagnosis, and
129//! [`FlatBuilder::build`] returns the model with it in place. Only whole-stream
130//! rules — a duplicate name in the flat namespace — are [`ParseError`]s, because
131//! no declaration can make two items with one name unambiguous. So a consumer
132//! that wants to *inspect* what a source crate marked, refusals included, gets
133//! exactly that from [`Flat::unsupported`].
134//!
135//! `Registry` ingestion is where the diagnoses are raised.
136//! Building a registry from this model **fails if any element is
137//! `Unsupported`** — all of them at once, so a source crate that needs migrating
138//! sees one list rather than one rebuild per item — and it fails before any
139//! adapter declaration is examined. A binding is built against a model the
140//! frontend could read in full, or it is not built.
141//!
142//! No **marked** item passes through verbatim, because a `#[prebindgen]` crate
143//! marks the items that cross the boundary and leaves the supporting code to the
144//! consumer. The proc-macro already enforces that — a `use`, `mod`, `impl` or
145//! `macro_rules!` cannot be marked at all — so the only item kind left that this
146//! module does not model is a `union`, and it is diagnosed like anything else the
147//! language cannot express.
148//!
149//! The one item that *is* re-emitted verbatim is a [`Guard`] — an anonymous
150//! const, which has no address and so cannot be part of an API addressed by name.
151//! Today these are the feature checks [`Source`](prebindgen::Source) injects on its own
152//! behalf. Modelled rather than dropped because this module must be total over
153//! what it is handed, and a separate element so nothing that consumes the API has
154//! to remember to skip it.
155//!
156//! # Declaring a handle
157//!
158//! `#[prebindgen] pub type X = path::To<Thing>;` declares an [`Extern`]: it gives
159//! a foreign or crate-private type a **name in the flat API** without claiming
160//! anything about its contents. That is what makes the API closable — a handle
161//! enters it deliberately rather than by being mentioned — and it is why a
162//! reference can be required to resolve. A marked tuple struct declares the same
163//! thing, since no adapter has ever crossed its fields.
164//!
165//! # Shapes that must be refused rather than approximated
166//!
167//! An [`Element`] holds what it holds: ordinary parameters, a direct return, no
168//! generic binder. A shape with no slot in that structure cannot be *partly*
169//! accepted — the missing piece would simply be dropped, and silently:
170//!
171//! | Shape | Would become | So |
172//! |---|---|---|
173//! | `async fn` | a function returning `()` | the future is dropped and the export's body never runs |
174//! | `fn f(a: u8, ...)` | a function without the tail | the variadic arguments vanish |
175//! | `struct S<T>`, `fn f<T>()`, `struct S<const N: usize>` | `T` as a nominal reference | a parameter is indistinguishable from an item named `T` |
176//!
177//! All three are [`ItemError`]s, carried like any other refusal and raised at
178//! registry ingestion. A
179//! **lifetime** binder is not among them: lifetimes are spelling, and the
180//! spelling already travels. Nor is `impl Trait` in argument position — Rust
181//! calls it an anonymous type parameter, but it is not a binder in the syntax,
182//! so the callback form is untouched.
183
184use std::{fmt, rc::Rc};
185
186use quote::ToTokens;
187
188mod array_len;
189mod element;
190pub mod emit;
191mod key;
192mod origin;
193pub(crate) mod spell;
194pub(crate) mod spelling;
195mod ty;
196
197#[cfg(test)]
198mod tests;
199
200use prebindgen::SourceLocation;
201
202use self::{array_len::ConstIndex, ty::lower_type};
203pub use self::{
204    array_len::{ArrayExtent, ArrayLenReason, ConstId, ExtentSource, UnsupportedArrayLen},
205    element::{
206        Alternative, Constant, Element, Enum, EnumValue, Extern, Field, Function, Guard, Param,
207        Struct, Type, Unsupported, Variant,
208    },
209    key::{TypeKey, TypeKeyParseError},
210    origin::Origin,
211    spelling::{canonical_spelling, canonical_type},
212    ty::{
213        peel_transparent, GenericArg, ScalarKind, TypeId, TypeKind, TypeRef, UnsupportedType,
214        UnsupportedTypeReason, TRANSPARENT_WRAPPERS,
215    },
216};
217
218/// Collects what to parse, then hands over the model.
219///
220/// Carries no configuration about what the language *accepts* — that is a
221/// property of the language, not of the call site. What it carries is **what to
222/// parse**: feed the inputs, then [`build`](Self::build) once.
223///
224/// # Reading a source directory
225///
226/// A build script's whole job, in one expression — the
227/// [`Source`](prebindgen::Source) step included. Pass
228/// `<source_crate>::PREBINDGEN_OUT_DIR`:
229///
230/// ```
231/// # prebindgen::Source::init_doctest_simulate();
232/// use prebindgen_flat::Flat;
233///
234/// let flat = Flat::builder().source("source_ffi").build()?;
235/// assert!(flat.function("test_function").is_some());
236/// assert!(flat.declared_type("TestStruct").is_some());
237/// # Ok::<_, prebindgen_flat::flat::ParseError>(())
238/// ```
239///
240/// # Reading a stream
241///
242/// [`Self::items`] takes any `(syn::Item, SourceLocation)` iterator, so
243/// everything a [`Source`](prebindgen::Source) can express still composes — a group
244/// selection, a renamed dependency, several sources at once. The feeders
245/// accumulate, so mix them freely:
246///
247/// ```
248/// # prebindgen::Source::init_doctest_simulate();
249/// use prebindgen::Source;
250/// use prebindgen_flat::Flat;
251///
252/// // A dependency renamed in Cargo.toml needs the name THIS crate uses, so it
253/// // is configured rather than named by directory.
254/// let helpers = Source::builder("source_ffi").crate_name("helpers").build();
255/// let flat = Flat::builder()
256///     .items(helpers.items_in_groups(&["functions"]))
257///     .build()?;
258/// assert_eq!(flat.functions().count(), 1);
259/// # Ok::<_, prebindgen_flat::flat::ParseError>(())
260/// ```
261///
262/// # Why accumulate, rather than parse each input
263///
264/// The rules that make a parse fail are **whole-stream** rules: one flat
265/// namespace across every ingested crate, one const index an array length may
266/// reach into, one set of source modules to normalize paths against, and every
267/// type reference resolving against every declaration. None can be
268/// decided per input, so every input is in hand before any of it is classified.
269#[derive(Debug, Default, Clone)]
270pub struct FlatBuilder {
271    items: Vec<(syn::Item, SourceLocation)>,
272}
273
274impl FlatBuilder {
275    /// Every `#[prebindgen]` item captured in `dir`.
276    ///
277    /// Sugar for [`Self::items`] over [`Source::items_all`](prebindgen::Source::items_all),
278    /// which is the whole of what a build script normally needs — pass
279    /// `<source_crate>::PREBINDGEN_OUT_DIR`. Reach for a
280    /// [`Source`](prebindgen::Source) directly, and feed it through [`Self::items`],
281    /// only when it needs configuring.
282    ///
283    /// Panics the way [`Source::new`](prebindgen::Source::new) does if `dir` is not
284    /// readable prebindgen output: a build script has nothing to recover with.
285    ///
286    /// ```
287    /// # prebindgen::Source::init_doctest_simulate();
288    /// use prebindgen_flat::Flat;
289    ///
290    /// let flat = Flat::builder().source("source_ffi").build()?;
291    /// assert!(flat.function("test_function").is_some());
292    /// assert!(flat.declared_type("TestStruct").is_some());
293    /// # Ok::<_, prebindgen_flat::flat::ParseError>(())
294    /// ```
295    pub fn source<P: AsRef<std::path::Path>>(self, dir: P) -> Self {
296        let source = prebindgen::Source::new(dir);
297        self.items(source.items_all())
298    }
299
300    /// The same, for a dependency this crate **renames** in `Cargo.toml`.
301    ///
302    /// The origin recorded at capture time is the dependency's real package name,
303    /// which will not resolve from a crate that refers to it by another name.
304    /// `crate_name` is the name *this* crate uses.
305    ///
306    /// Per directory, deliberately: an override on the whole parse could only fix
307    /// one module, and a flat API may layer several sources.
308    pub fn source_named<P: AsRef<std::path::Path>>(
309        self,
310        dir: P,
311        crate_name: impl Into<String>,
312    ) -> Self {
313        let source = prebindgen::Source::builder(dir)
314            .crate_name(crate_name)
315            .build();
316        self.items(source.items_all())
317    }
318
319    /// Add a captured item stream.
320    ///
321    /// The general feeder: any `(syn::Item, SourceLocation)` iterator, so
322    /// item-level selection and multi-source composition stay upstream where
323    /// they already are. Call it as often as needed; the streams accumulate.
324    ///
325    /// ```
326    /// # prebindgen::Source::init_doctest_simulate();
327    /// use prebindgen::Source;
328    /// use prebindgen_flat::Flat;
329    ///
330    /// let source = Source::new("source_ffi");
331    /// let flat = Flat::builder()
332    ///     .items(source.items_in_groups(&["structs"]))
333    ///     .build()?;
334    /// assert_eq!(flat.types().count(), 1);
335    /// # Ok::<_, prebindgen_flat::flat::ParseError>(())
336    /// ```
337    pub fn items<I>(mut self, items: I) -> Self
338    where
339        I: IntoIterator<Item = (syn::Item, SourceLocation)>,
340    {
341        self.items.extend(items);
342        self
343    }
344
345    /// Parse everything collected so far into the model.
346    ///
347    /// **Transactional**: an `Err` yields no model at all, so a refused stream
348    /// cannot leave a half-built one behind.
349    ///
350    /// Order-independent: source modules are gathered, consts indexed, and every
351    /// item lowered before any reference is resolved — so a type reference, an
352    /// array length and a cross-source mention may each name something declared
353    /// later, in this input or another.
354    pub fn build(self) -> Result<Flat, ParseError> {
355        let mut items = self.items;
356
357        // Pass 0: normalize every item's types to the canonical flat spelling
358        // before a single one is classified. `std::option::Option<T>` is an
359        // `Option`, `source_a::TypeA` is `TypeA`, and `zenoh::Session` is whatever
360        // an alias named it — decisions this module owns, so it must be the one to
361        // see the reduced form. Gathering EVERY module and alias first is what
362        // makes a reference in an earlier item normalize the same as in a later
363        // one.
364        //
365        // The consequence is deliberate and stated on `Origin`: a slice
366        // is the spelling generation must EMIT, which is the normalized one —
367        // the flat namespace is what the generated crate can actually name.
368        let normalization = crate::flat::spelling::Normalization::from_items(&items);
369        for (item, _) in &mut items {
370            crate::flat::spelling::normalize_item_types(item, &normalization);
371        }
372
373        // Pass 1: the consts an array length may name. Unnamed items are
374        // excluded because no length can name one — the same fact that makes
375        // them `Guard`s. This is the one place that tests the spelling rather
376        // than the classification, and it has to: it runs before Pass 2, so no
377        // classification exists yet.
378        let consts = ConstIndex::new(items.iter().filter_map(|(item, loc)| match item {
379            syn::Item::Const(c) if c.ident != "_" => Some((
380                c.ident.to_string(),
381                (*c.expr).clone(),
382                loc.crate_name.clone(),
383            )),
384            _ => None,
385        }));
386
387        // Pass 2: lower, checking the flat namespace as we go.
388        let mut elements: Vec<Element> = Vec::with_capacity(items.len());
389        let mut seen: Vec<(syn::Ident, SourceLocation)> = Vec::new();
390        for (item, loc) in items {
391            let element = lower_item(item, loc, &consts);
392            if let Some(name) = element.name() {
393                if let Some((first_name, first)) = seen.iter().find(|(n, _)| n == name) {
394                    return Err(ParseError::DuplicateName(Box::new(DuplicateName {
395                        name: first_name.clone(),
396                        first: first.clone(),
397                        second: element.location().clone(),
398                        first_crate: first.crate_name.clone(),
399                        second_crate: element.location().crate_name.clone(),
400                    })));
401                }
402                seen.push((name.clone(), element.location().clone()));
403            }
404            elements.push(element);
405        }
406
407        // Pass 3: resolve references, now that every declaration is in hand.
408        resolve_references(&mut elements);
409
410        // Indexed after resolution, because refusing an item can change its kind
411        // (a `Type` becomes `Unsupported`) though never its name.
412        let by_name = elements
413            .iter()
414            .enumerate()
415            .filter_map(|(i, e)| e.name().map(|n| (n.to_string(), i)))
416            .collect();
417        // Frozen here, from the captured stream alone. See the field's docs.
418        let mut source_modules: Vec<String> = Vec::new();
419        for element in &elements {
420            if let Some(crate_name) = element.location().crate_name.as_ref() {
421                let module = crate_name.replace('-', "_");
422                if !source_modules.contains(&module) {
423                    source_modules.push(module);
424                }
425            }
426        }
427        let mut flat = Flat {
428            elements,
429            by_name,
430            source_modules,
431            by_type: std::collections::HashMap::new(),
432        };
433        for i in 0..flat.elements.len() {
434            flat.index_types_of(i);
435        }
436        Ok(flat)
437    }
438}
439
440/// The flat API: every `#[prebindgen]` item from every ingested source, parsed,
441/// indexed by name, and with every type reference resolved.
442///
443/// # Direct access, not a stream
444///
445/// Names are unique across the whole model — a duplicate is a
446/// [`ParseError::DuplicateName`] — so a name is a complete address, and the
447/// model answers by it. That is what every later stage needs: an adapter asks
448/// what a declared name *is*, rather than scanning a list for it.
449///
450/// # References are already resolved
451///
452/// Every [`TypeKind::Named`] in a surviving element denotes a [`Type`] this model
453/// holds, and [`Self::resolve`] hands it over. An item that named something the
454/// flat API does not declare is [`Element::Unsupported`] with
455/// [`ItemError::UnresolvedType`], exactly like every other refusal — carried
456/// here, raised by `Registry` ingestion.
457///
458/// Resolving here rather than in the adapters is the point of #211: a dangling
459/// name used to surface much later as an unresolved-converter error, from
460/// whichever adapter happened to look first.
461#[derive(Debug, Default)]
462pub struct Flat {
463    /// Source order, so iteration reports items as the sources were fed.
464    elements: Vec<Element>,
465    /// Module name of every **captured** source, in first-seen order (crate
466    /// names, dashes normalized to underscores). The first doubles as the
467    /// default module for a reference with no recorded origin.
468    ///
469    /// Computed once in [`FlatBuilder::build`] and frozen: it is a property of
470    /// the ingested stream, so a binding-local function added later must not
471    /// extend it — that would change which module an unqualified reference
472    /// resolves against.
473    source_modules: Vec<String>,
474    /// Name → position in [`Self::elements`].
475    ///
476    /// A map rather than a scan because every typed accessor and every
477    /// [`Self::resolve`] routes through it, and later stages resolve references
478    /// in a loop — a linear scan would make that quadratic in the size of the
479    /// API. Positions rather than clones, so there is one copy of each element
480    /// and source order stays available.
481    by_name: std::collections::HashMap<String, usize>,
482    /// Normalized type spelling → this module's reading of it.
483    ///
484    /// Every type the API **mentions** — a parameter, a return, a field, a
485    /// constant's type, and everything nested inside those — keyed so a consumer
486    /// holding a `syn::Type` can ask what the frontend made of it without
487    /// lowering it a second time.
488    ///
489    /// A type mentioned in several places keeps the **first mention in element
490    /// order**, a property of the model rather than of ingestion order.
491    ///
492    /// Unlike [`Self::source_modules`] this *is* extended by
493    /// [`Self::add_local_function`]: a binding-local fn's parameter types have
494    /// readings like any others, and a lookup that missed them would report
495    /// "no reading" for one that exists.
496    by_type: std::collections::HashMap<String, TypeRef>,
497}
498
499/// A name a lookup can be performed with.
500///
501/// Exists because callers hold different spellings of the same fact: an adapter
502/// walking captured items has a `syn::Ident`, a resolved reference has the
503/// `String` inside a [`TypeId`], and a test has a literal. One accessor takes all
504/// three rather than each call site converting.
505///
506/// **The conversion is moved, not removed.** `proc_macro2::Ident` hashes by
507/// `to_string()` and offers no borrow as `str`, so an `Ident` lookup allocates
508/// wherever it happens; doing it here keeps `&str` and `&String` callers — among
509/// them the per-edge and per-reference lookups in the scan and the resolver —
510/// allocation-free.
511///
512/// Sealed: what may name an element is the language's business, not a caller's.
513///
514/// ```
515/// # prebindgen::Source::init_doctest_simulate();
516/// use prebindgen_flat::flat::Flat;
517///
518/// let flat = Flat::builder().source("source_ffi").build()?;
519/// let ident = quote::format_ident!("test_function");
520///
521/// // The same element, whichever spelling the caller happens to hold.
522/// assert!(flat.function("test_function").is_some());
523/// assert!(flat.function(&ident).is_some());
524/// # Ok::<_, prebindgen_flat::flat::ParseError>(())
525/// ```
526pub trait Name: sealed::Sealed {
527    /// The name as a string, borrowed when the caller already holds one.
528    fn as_name(&self) -> std::borrow::Cow<'_, str>;
529}
530
531mod sealed {
532    pub trait Sealed {}
533    impl Sealed for str {}
534    impl Sealed for String {}
535    impl Sealed for syn::Ident {}
536    impl<T: ?Sized + Sealed> Sealed for &T {}
537}
538
539impl Name for str {
540    fn as_name(&self) -> std::borrow::Cow<'_, str> {
541        std::borrow::Cow::Borrowed(self)
542    }
543}
544
545impl Name for String {
546    fn as_name(&self) -> std::borrow::Cow<'_, str> {
547        std::borrow::Cow::Borrowed(self)
548    }
549}
550
551impl Name for syn::Ident {
552    fn as_name(&self) -> std::borrow::Cow<'_, str> {
553        std::borrow::Cow::Owned(self.to_string())
554    }
555}
556
557/// So a caller already holding a reference does not have to reborrow.
558impl<T: ?Sized + Name> Name for &T {
559    fn as_name(&self) -> std::borrow::Cow<'_, str> {
560        T::as_name(self)
561    }
562}
563
564impl Flat {
565    /// Start collecting what to parse.
566    pub fn builder() -> FlatBuilder {
567        FlatBuilder { items: Vec::new() }
568    }
569
570    /// Every element, in the order the sources were fed.
571    pub fn elements(&self) -> impl Iterator<Item = &Element> {
572        self.elements.iter()
573    }
574
575    /// The element with this name, whatever kind it is — including an
576    /// [`Element::Unsupported`], which still holds its name against the
577    /// namespace.
578    pub fn element<N: Name + ?Sized>(&self, name: &N) -> Option<&Element> {
579        self.elements
580            .get(*self.by_name.get(name.as_name().as_ref())?)
581    }
582
583    pub fn function<N: Name + ?Sized>(&self, name: &N) -> Option<&Function> {
584        match self.element(name)? {
585            Element::Function(f) => Some(f),
586            _ => None,
587        }
588    }
589
590    /// The type declared under this name.
591    ///
592    /// Named `declared_type` because `type` is a keyword; it is the accessor a
593    /// resolved [`TypeKind::Named`] reference leads to, and [`Self::resolve`] is
594    /// the same lookup taking a [`TypeId`].
595    pub fn declared_type<N: Name + ?Sized>(&self, name: &N) -> Option<&Type> {
596        match self.element(name)? {
597            Element::Type(t) => Some(t),
598            _ => None,
599        }
600    }
601
602    pub fn constant<N: Name + ?Sized>(&self, name: &N) -> Option<&Constant> {
603        match self.element(name)? {
604            Element::Constant(c) => Some(c),
605            _ => None,
606        }
607    }
608
609    pub fn functions(&self) -> impl Iterator<Item = &Function> {
610        self.elements.iter().filter_map(|e| match e {
611            Element::Function(f) => Some(f),
612            _ => None,
613        })
614    }
615
616    pub fn types(&self) -> impl Iterator<Item = &Type> {
617        self.elements.iter().filter_map(|e| match e {
618            Element::Type(t) => Some(t),
619            _ => None,
620        })
621    }
622
623    pub fn constants(&self) -> impl Iterator<Item = &Constant> {
624        self.elements.iter().filter_map(|e| match e {
625            Element::Constant(c) => Some(c),
626            _ => None,
627        })
628    }
629
630    /// The `struct` declared under this name, or `None` for any other shape.
631    ///
632    /// A tuple struct is an [`Extern`] rather than a `Struct`, so this answers
633    /// only for a product of fields that cross the boundary.
634    pub fn struct_type<N: Name + ?Sized>(&self, name: &N) -> Option<&Struct> {
635        match self.declared_type(name)? {
636            Type::Struct(s) => Some(s),
637            _ => None,
638        }
639    }
640
641    /// The `syn::ItemEnum` behind **either** enum shape.
642    ///
643    /// A sum and a C-style enum are different elements — numbered differently
644    /// and consumed as different constructs — but both were spelled `enum` in
645    /// Rust and both keep that item. A consumer re-emitting the source wants the
646    /// item without caring which shape it is; one that acts on the distinction
647    /// reaches for [`Self::declared_type`].
648    // Test-only since S42: `unit_enum`, `payload_enum`, `enum_alternatives` and
649    // `declared_member_names` each ask the model which shape a declared enum is
650    // and get the element that answers, so nothing in a built crate needs the
651    // item. The registry pipeline's own tests (now in the separate
652    // `prebindgen-registry` crate) still exercise it, which is why this is
653    // `pub` rather than `pub(crate)` — see `TypeRef`'s doc for
654    // why that seal is now a convention rather than a compiler check.
655    #[allow(dead_code)]
656    pub fn enum_item<N: Name + ?Sized>(&self, name: &N) -> Option<&syn::ItemEnum> {
657        match self.declared_type(name)? {
658            Type::Variant(v) => Some(v.origin.as_syn()),
659            Type::Enum(e) => Some(e.origin.as_syn()),
660            _ => None,
661        }
662    }
663
664    /// Module name of every captured source, in first-seen order.
665    ///
666    /// The first entry is the default module for a reference with no recorded
667    /// origin. Empty for a hand-built stream that carried no crate stamps.
668    pub fn source_modules(&self) -> &[String] {
669        &self.source_modules
670    }
671
672    /// Every anonymous const, in stream order — **zero or more**.
673    ///
674    /// Not part of the flat API — see [`Guard`] — but ingested with it, and a
675    /// consumer that re-emits the source must re-emit these too.
676    pub fn guards(&self) -> impl Iterator<Item = &Guard> {
677        self.elements.iter().filter_map(|e| match e {
678            Element::Guard(g) => Some(g),
679            _ => None,
680        })
681    }
682
683    /// This module's reading of `ty`, if the flat API mentions that type.
684    ///
685    /// `None` means no captured item and no binding-local fn writes this type —
686    /// it is one the binding invented, and there is nothing for the frontend to
687    /// have decided about it.
688    ///
689    /// The argument is normalized the way [`TypeKey`] does
690    /// before lookup, so an adapter-authored spelling finds the same entry a
691    /// captured one does.
692    pub fn type_ref(&self, ty: &syn::Type) -> Option<&TypeRef> {
693        self.by_type.get(&crate::flat::canonical_spelling(ty))
694    }
695
696    /// This module's reading of `ty` — the index's if the source wrote it, freshly
697    /// lowered if not.
698    ///
699    /// **Answers without remembering, and that is deliberate.** The model is what
700    /// the source said, and stays that way: [`Self::type_ref`]'s index means *every
701    /// type the API mentions*, so growing it with a spelling no source wrote would
702    /// destroy the one thing it is good for. This is the grammar being consulted,
703    /// not the model being extended.
704    ///
705    /// It is therefore **not** the peer of [`Self::add_local_function`], which does
706    /// extend the model — a binding-local `sig!(..)` is an API item, a function the
707    /// binding declares as if it had been marked. A composed type is not an API
708    /// item; it is an intermediate in some binding's crossing graph, and it belongs
709    /// in the table that tracks crossings.
710    ///
711    /// **The scan's entry point, and nowhere else's.** A caller holding an element
712    /// already has the reading — `Function::ret`, `Param::ty`, `Field::ty` are
713    /// `TypeRef`s computed at parse time — and re-deriving one from
714    /// `spell()` is reasoning from the spelling, which is what `origin` is
715    /// not for. This exists for the one case with no element behind it: a type a
716    /// build script declared, or one expansion composed. `ensure_entry` is its
717    /// only caller in the registry pipeline.
718    ///
719    /// Whoever asks is expected to keep the answer. The registry does: a reading is
720    /// taken once when a type-table cell is born, and lives in that cell — and
721    /// `Registry::reading` (in the registry layer above) hands
722    /// back only what is in one, so a second source of readings cannot reappear
723    /// here (#266).
724    ///
725    /// `Err` means the spelling is outside the accepted grammar — a real diagnosis
726    /// about a type the *binding* built, not a cache miss.
727    ///
728    /// **`pub`, not `pub(crate)`.** The registry pipeline that is
729    /// this method's sole legitimate caller now lives in the separate
730    /// `prebindgen-registry` crate, so a module-path seal can no longer express
731    /// "the pipeline, and nothing else" — there is no path inside this crate for
732    /// it to name. The seal is now a documented convention (this doc comment)
733    /// rather than a compiler-enforced one; #280's intent (an adapter must not
734    /// mint a `TypeRef` from tokens of its own) is no longer structurally
735    /// guaranteed and would need a real API (e.g. a sealed trait token minted
736    /// only by `prebindgen-registry`) to restore.
737    pub fn classify(&self, ty: &syn::Type) -> Result<TypeRef, UnsupportedType> {
738        if let Some(indexed) = self.type_ref(ty) {
739            return Ok(indexed.clone());
740        }
741        // Rebuilt rather than kept, for the reason `lower_signature` gives: a
742        // stored index would be a second copy of what `constants()` says.
743        let consts = ConstIndex::new(self.constants().map(|c| {
744            (
745                c.name.to_string(),
746                (*c.origin.as_syn().expr).clone(),
747                c.origin.crate_name().map(str::to_owned),
748            )
749        }));
750        // No file wrote this one; `has_position` already gates what a diagnostic
751        // prints for a positionless location.
752        let at = Rc::new(SourceLocation::default());
753        lower_type(ty, &consts, &at)
754    }
755
756    /// Index every type the element at `pos` writes. Idempotent per key: the
757    /// first mention in element order wins.
758    fn index_types_of(&mut self, pos: usize) {
759        let refs: Vec<TypeRef> = element_type_refs(&self.elements[pos])
760            .into_iter()
761            .flat_map(TypeRef::walk)
762            .cloned()
763            .collect();
764        for ty in refs {
765            self.by_type
766                .entry(crate::flat::canonical_spelling(ty.origin.as_syn()))
767                .or_insert(ty);
768        }
769    }
770
771    /// Every item the language could not express, with its diagnosis.
772    ///
773    /// Present in the model so a consumer can inspect what a source crate marked
774    /// — building a `Registry` from a model holding any of
775    /// these fails, and reports all of them. See the [module docs](self) on where
776    /// acceptance is enforced.
777    pub fn unsupported(&self) -> impl Iterator<Item = &Unsupported> {
778        self.elements.iter().filter_map(|e| match e {
779            Element::Unsupported(u) => Some(u),
780            _ => None,
781        })
782    }
783
784    /// Lower a function signature written outside the captured stream.
785    ///
786    /// For the **one input that does not come through this module**: a binding's
787    /// `local_functions`, whose signatures are written by hand in a build script
788    /// and inserted straight into the registry. Everything else was already
789    /// lowered here, so this exists to keep the grammar decided in one place
790    /// rather than re-checked at the far end.
791    ///
792    /// Grammar only, and it **validates by lowering**: an `Err` is a shape the
793    /// language cannot express, an `Ok` is the element to admit. Whether the types
794    /// it names are *declared* is a whole-model question, settled when the model
795    /// is built, and a binding-local fn may legitimately name types the source
796    /// crate never did.
797    pub fn lower_signature(&self, f: &syn::ItemFn) -> Result<Function, ItemError> {
798        // Rebuilt from the model rather than kept: this runs once per local fn,
799        // and a stored index would be a second copy of what `constants()` says.
800        let consts = ConstIndex::new(self.constants().map(|c| {
801            (
802                c.name.to_string(),
803                (*c.origin.as_syn().expr).clone(),
804                c.origin.crate_name().map(str::to_owned),
805            )
806        }));
807        // A synthesized fn has no captured location, but it does have an origin
808        // crate — the caller supplies it, and `add_local_function` records it.
809        let at = Rc::new(SourceLocation::default());
810        lower_fn(f, &at, &consts)
811    }
812
813    /// Admit a binding-local function: one a build script wrote via `sig!(..)`
814    /// rather than one a source crate marked.
815    ///
816    /// The model is the pipeline's only index, so a function nothing captured
817    /// still has to live here or nothing downstream can find it. `crate_name` is
818    /// the module its generated call qualifies against, stamped onto the
819    /// element's location where [`Element::location`] already looks for it.
820    ///
821    /// Deliberately does **not** extend [`Self::source_modules`]: see that
822    /// field's docs.
823    ///
824    /// `pub`: its caller (`RegistryBuilder::fun`, on a binding-local
825    /// [`fun!`](https://docs.rs/prebindgen-registry/latest/prebindgen_registry/macro.fun.html)
826    /// path) now lives in the separate `prebindgen-registry` crate.
827    pub fn add_local_function(&mut self, mut f: Function, crate_name: String) {
828        f.origin.location = Rc::new(SourceLocation {
829            crate_name: Some(crate_name),
830            ..SourceLocation::default()
831        });
832        self.by_name.insert(f.name.to_string(), self.elements.len());
833        self.elements.push(Element::Function(f));
834        self.index_types_of(self.elements.len() - 1);
835    }
836
837    /// The declaration a reference denotes.
838    ///
839    /// Infallible in practice for any reference reached from a surviving element:
840    /// [`FlatBuilder::build`] made unresolvable references into
841    /// [`ItemError::UnresolvedType`], so what is left resolves.
842    pub fn resolve(&self, id: &TypeId) -> Option<&Type> {
843        self.declared_type(&id.name)
844    }
845}
846
847/// Turn every element that names an undeclared type into an
848/// [`Element::Unsupported`], **transitively**.
849///
850/// Runs once every declaration is in hand, so the order sources were fed in does
851/// not matter and a reference may point forward or across crates.
852///
853/// # Why this iterates
854///
855/// Refusing a type *removes a declaration*, which can strand its dependents:
856///
857/// ```ignore
858/// pub struct Broken { pub field: Missing }   // refused: `Missing` undeclared
859/// pub fn use_broken(value: Broken) {}        // `Broken` is now gone too
860/// ```
861///
862/// A single pass against a snapshot of the initial declarations would keep
863/// `use_broken`, and [`Flat::resolve`] would then return `None` for its parameter
864/// — breaking the invariant that a surviving element's references all resolve.
865/// So this runs to a fixed point: each round drops the declarations it refused,
866/// and stops when a round refuses nothing. Chains of any length collapse, in
867/// either declaration order, because the set only ever shrinks.
868fn resolve_references(elements: &mut [Element]) {
869    let mut declared: std::collections::HashSet<String> = elements
870        .iter()
871        .filter_map(|e| match e {
872            Element::Type(t) => Some(t.name().to_string()),
873            _ => None,
874        })
875        .collect();
876
877    loop {
878        let mut refused = Vec::new();
879        for (i, element) in elements.iter().enumerate() {
880            if let Some(unresolved) = first_unresolved(element, &declared) {
881                refused.push((i, unresolved));
882            }
883        }
884        if refused.is_empty() {
885            return;
886        }
887        for (i, unresolved) in refused {
888            // A refused type stops being a declaration, which is what lets the
889            // next round see its dependents as unresolved.
890            if let Element::Type(t) = &elements[i] {
891                declared.remove(&t.name().to_string());
892            }
893            let element = &mut elements[i];
894            let name = element.name().cloned();
895            let origin = Origin::new(
896                element.as_syn(),
897                Rc::clone(match element {
898                    Element::Function(f) => &f.origin.location,
899                    Element::Type(t) => t.location_rc(),
900                    Element::Constant(c) => &c.origin.location,
901                    Element::Guard(g) => &g.origin.location,
902                    Element::Unsupported(u) => &u.origin.location,
903                }),
904            );
905            *element = Element::Unsupported(Unsupported {
906                name,
907                error: Box::new(ItemError::UnresolvedType { name: unresolved }),
908                origin,
909            });
910        }
911    }
912}
913
914/// Every type slot this element writes, outermost only — a parameter, a return,
915/// a field, a constant's type.
916///
917/// The one place the slots are enumerated, so a new element shape is taught to
918/// every consumer at once instead of drifting between them.
919fn element_type_refs(element: &Element) -> Vec<&TypeRef> {
920    let mut refs: Vec<&TypeRef> = Vec::new();
921    match element {
922        Element::Function(f) => {
923            refs.extend(f.params.iter().map(|p| &p.ty));
924            refs.push(&f.ret);
925        }
926        Element::Constant(c) => refs.push(&c.ty),
927        Element::Type(Type::Struct(s)) => refs.extend(s.fields.iter().map(|f| &f.ty)),
928        Element::Type(Type::Variant(v)) => refs.extend(
929            v.alternatives
930                .iter()
931                .flat_map(|a| a.fields.iter().map(|f| &f.ty)),
932        ),
933        // An enum names nothing, an extern hides what it names, a guard is
934        // emitted verbatim so its types are the consumer's business, and an
935        // unsupported item already has a diagnosis worth keeping.
936        Element::Type(Type::Enum(_) | Type::Extern(_))
937        | Element::Guard(_)
938        | Element::Unsupported(_) => {}
939    }
940    refs
941}
942
943/// The first type this element names that the flat API does not declare.
944fn first_unresolved(
945    element: &Element,
946    declared: &std::collections::HashSet<String>,
947) -> Option<String> {
948    element_type_refs(element)
949        .into_iter()
950        .find_map(|r| r.first_unresolved(declared))
951}
952
953/// If `ty` is `impl Fn(T1, T2, ...) + Send + Sync + 'static`, return the `Fn`
954/// argument types in declaration order. Otherwise `None`.
955///
956/// A callback **returns nothing**, and that is checked, not assumed: a written
957/// `-> ()` is the same thing spelled out, and any other return is refused.
958/// [`TypeKind::Callback`] has no slot for one, so accepting `impl Fn() -> u8`
959/// would drop a fact a destination language needs — and drop it silently, which
960/// is worse than the refusal.
961///
962/// The callback grammar, and the language's alone: [`TypeKind::Callback`] is
963/// exactly what this accepts, so acceptance cannot drift from classification.
964/// The registry re-exports it for the consumers that have not migrated yet.
965pub fn extract_fn_trait_args(ty: &syn::Type) -> Option<Vec<syn::Type>> {
966    let syn::Type::ImplTrait(it) = ty else {
967        return None;
968    };
969    let mut args: Option<Vec<syn::Type>> = None;
970    let mut has_send = false;
971    let mut has_sync = false;
972    let mut has_static = false;
973    for bound in &it.bounds {
974        match bound {
975            syn::TypeParamBound::Trait(tb) => {
976                let last = tb.path.segments.last()?;
977                let name = last.ident.to_string();
978                match name.as_str() {
979                    "Fn" => {
980                        let syn::PathArguments::Parenthesized(p) = &last.arguments else {
981                            return None;
982                        };
983                        match &p.output {
984                            syn::ReturnType::Default => {}
985                            syn::ReturnType::Type(_, t) if ty::is_unit_type(t) => {}
986                            syn::ReturnType::Type(..) => return None,
987                        }
988                        args = Some(p.inputs.iter().cloned().collect());
989                    }
990                    "Send" => has_send = true,
991                    "Sync" => has_sync = true,
992                    _ => return None,
993                }
994            }
995            syn::TypeParamBound::Lifetime(lt) if lt.ident == "static" => has_static = true,
996            _ => return None,
997        }
998    }
999    if has_send && has_sync && has_static {
1000        args
1001    } else {
1002        None
1003    }
1004}
1005
1006/// A rule of the language that no single item can satisfy on its own, and that
1007/// no adapter declaration can excuse.
1008#[derive(Clone, Debug)]
1009pub enum ParseError {
1010    /// Two `#[prebindgen]` items share a name. Names live in one flat namespace
1011    /// across every ingested source crate, so this is ambiguous however the
1012    /// crates are arranged.
1013    DuplicateName(Box<DuplicateName>),
1014}
1015
1016/// The two items of a [`ParseError::DuplicateName`].
1017#[derive(Clone, Debug)]
1018pub struct DuplicateName {
1019    pub name: syn::Ident,
1020    pub first: SourceLocation,
1021    pub second: SourceLocation,
1022    /// The crate each was marked in. A captured file path is crate-relative
1023    /// (both are `src/lib.rs`), so these are the only unambiguous coordinates
1024    /// when two sources collide.
1025    pub first_crate: Option<String>,
1026    pub second_crate: Option<String>,
1027}
1028
1029impl fmt::Display for ParseError {
1030    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1031        match self {
1032            ParseError::DuplicateName(d) => {
1033                let at = |loc: &SourceLocation, krate: &Option<String>| match krate {
1034                    Some(k) => format!("{loc} (crate `{k}`)"),
1035                    None => loc.to_string(),
1036                };
1037                write!(
1038                    f,
1039                    "duplicate `#[prebindgen]` name `{}`: first at {}, again at {} — marked items \
1040                     share one flat namespace across all source crates",
1041                    d.name,
1042                    at(&d.first, &d.first_crate),
1043                    at(&d.second, &d.second_crate)
1044                )
1045            }
1046        }
1047    }
1048}
1049
1050impl std::error::Error for ParseError {}
1051
1052/// Why one item could not be expressed in the language.
1053///
1054/// Carried by [`Element::Unsupported`] rather than raised at parse time: see
1055/// the [module docs](self) on where acceptance is enforced.
1056#[derive(Clone, Debug)]
1057pub enum ItemError {
1058    /// A `self` receiver. `#[prebindgen]` captures free functions only.
1059    UnsupportedReceiver,
1060    /// A parameter pattern that is not a plain name — `(a, b): (u8, u8)`.
1061    UnsupportedParamPattern { pattern: String },
1062    /// A parameter's type is not in the language.
1063    ParamType {
1064        param: syn::Ident,
1065        source: UnsupportedType,
1066    },
1067    /// A return type is not in the language.
1068    ReturnType { source: UnsupportedType },
1069    /// A named struct field's type is not in the language.
1070    FieldType {
1071        field: syn::Ident,
1072        source: UnsupportedType,
1073    },
1074    /// A variant payload's type is not in the language.
1075    VariantFieldType {
1076        variant: syn::Ident,
1077        /// The field's name, or its position for a tuple variant.
1078        field: String,
1079        source: UnsupportedType,
1080    },
1081    /// A const's type is not in the language.
1082    ConstType { source: UnsupportedType },
1083    /// An `async fn`.
1084    ///
1085    /// The most dangerous shape to accept quietly: [`Function`] has a direct
1086    /// return, so an `async fn ping()` lowers as one returning `()`, and a
1087    /// generated wrapper calls it, drops the future and exports a function whose
1088    /// body never runs.
1089    UnsupportedAsync,
1090    /// A C-variadic tail — `fn f(a: u8, ...)`.
1091    ///
1092    /// [`Function`] holds ordinary parameters only, so the tail would simply be
1093    /// dropped from the signature.
1094    UnsupportedVariadic,
1095    /// A type or const generic parameter on the item.
1096    ///
1097    /// The elements have no generic binder, so a `T` in a field or parameter
1098    /// would lower as [`TypeKind::Named`] — an ordinary nominal reference into
1099    /// the flat namespace, indistinguishable from a real item called `T`. That
1100    /// loses the scoping every downstream resolver needs, and no destination
1101    /// language can express an uninstantiated parameter anyway.
1102    ///
1103    /// A lifetime parameter is *not* this: lifetimes are spelling and already
1104    /// travel in the syntax.
1105    UnsupportedGenericParam {
1106        param: String,
1107        /// `a type parameter` / `a const generic parameter`.
1108        kind: &'static str,
1109    },
1110    /// The item names a type the flat API does not declare.
1111    ///
1112    /// The flat API is closed over its own names: a handle enters it through
1113    /// `#[prebindgen] pub type X = ..`, so a name with no declaration is either a
1114    /// missing marker or a typo. Reporting it here replaces discovering it much
1115    /// later as an unresolved converter, from whichever adapter looked first.
1116    UnresolvedType { name: String },
1117    /// A whole item kind the language does not model — a `union`, a type alias.
1118    ///
1119    /// The proc-macro refuses to mark a `use`, `mod`, `impl` or `macro_rules!`
1120    /// at all, so only the kinds it accepts can reach here.
1121    UnsupportedItemKind { kind: &'static str },
1122}
1123
1124impl fmt::Display for ItemError {
1125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1126        match self {
1127            ItemError::UnsupportedReceiver => write!(
1128                f,
1129                "takes a `self` receiver; `#[prebindgen]` captures free functions only"
1130            ),
1131            ItemError::UnsupportedParamPattern { pattern } => write!(
1132                f,
1133                "parameter pattern `{pattern}` is not a plain name — bind each parameter to one \
1134                 identifier"
1135            ),
1136            ItemError::ParamType { param, source } => {
1137                write!(f, "parameter `{param}`: {source}")
1138            }
1139            ItemError::ReturnType { source } => write!(f, "return type: {source}"),
1140            ItemError::FieldType { field, source } => write!(f, "field `{field}`: {source}"),
1141            ItemError::VariantFieldType {
1142                variant,
1143                field,
1144                source,
1145            } => write!(f, "variant `{variant}` field `{field}`: {source}"),
1146            ItemError::ConstType { source } => write!(f, "const type: {source}"),
1147            ItemError::UnsupportedAsync => write!(
1148                f,
1149                "is an `async fn`; the boundary has no way to drive a future, and the generated \
1150                 wrapper would drop it and export a function whose body never runs — expose a \
1151                 blocking wrapper instead"
1152            ),
1153            ItemError::UnsupportedVariadic => write!(
1154                f,
1155                "has a C-variadic tail, which the prebindgen source language does not model — \
1156                 take a slice, or one parameter per value"
1157            ),
1158            ItemError::UnsupportedGenericParam { param, kind } => write!(
1159                f,
1160                "declares `{param}`, {kind}: the prebindgen source language has no generic \
1161                 binder, so an uninstantiated parameter is indistinguishable from a nominal type \
1162                 of the same name and no destination language can express it — write the \
1163                 concrete types, one marked item per instantiation (a newtype is the usual way)"
1164            ),
1165            ItemError::UnresolvedType { name } if name.contains("::") => write!(
1166                f,
1167                "names the type `{name}`, which the flat API does not declare \u{2014} and being \
1168                 path-qualified it never could, because marked items live in one flat namespace \
1169                 of bare names. Give the type a name here with `#[prebindgen] pub type <Name> = \
1170                 {name};` and refer to that"
1171            ),
1172            ItemError::UnresolvedType { name } => write!(
1173                f,
1174                "names the type `{name}`, which the flat API does not declare \u{2014} mark its \
1175                 declaration `#[prebindgen]`, or, for a foreign or crate-private type used as a \
1176                 handle, give it a name here with `#[prebindgen] pub type {name} = ..;`"
1177            ),
1178            ItemError::UnsupportedItemKind { kind } => write!(
1179                f,
1180                "is {kind}; the prebindgen source language models functions, structs, enums and \
1181                 consts — everything else belongs in the consumer crate"
1182            ),
1183        }
1184    }
1185}
1186
1187impl std::error::Error for ItemError {}
1188
1189/// Lower one captured item. Total: every item becomes an element, and an item
1190/// whose contents the language cannot express becomes [`Element::Unsupported`]
1191/// rather than failing the parse.
1192fn lower_item(item: syn::Item, loc: SourceLocation, consts: &ConstIndex) -> Element {
1193    // One captured record is one item, so this is allocated once and shared by
1194    // the item and every node lowered out of it.
1195    let at = Rc::new(loc);
1196    match item {
1197        syn::Item::Fn(f) => match lower_fn(&f, &at, consts) {
1198            Ok(func) => Element::Function(func),
1199            Err(error) => unsupported(f.sig.ident.clone(), syn::Item::Fn(f), &at, error),
1200        },
1201        syn::Item::Struct(s) => match lower_struct(&s, &at, consts) {
1202            Ok(ty) => Element::Type(ty),
1203            Err(error) => unsupported(s.ident.clone(), syn::Item::Struct(s), &at, error),
1204        },
1205        syn::Item::Enum(e) => match lower_enum(&e, &at, consts) {
1206            Ok(ty) => Element::Type(ty),
1207            Err(error) => unsupported(e.ident.clone(), syn::Item::Enum(e), &at, error),
1208        },
1209        // `#[prebindgen] pub type X = path;` DECLARES an opaque type: it gives a
1210        // foreign or crate-private type a name in the flat API, without claiming
1211        // anything about its contents. That is the only way a handle enters the
1212        // API deliberately, and the reason references can be required to resolve.
1213        syn::Item::Type(t) => match reject_generic_params(&t.generics) {
1214            // `Extern` has no binder and no arity, so a generic alias would be
1215            // accepted as one declaration that `Handle<u8>` then resolves against
1216            // — losing exactly the scoped-parameter distinction every other item
1217            // kind refuses. It is also why `MaybeUninit` needed grammar support
1218            // rather than an alias.
1219            Err(error) => unsupported(t.ident.clone(), syn::Item::Type(t), &at, error),
1220            Ok(()) => {
1221                let target = Some(t.ty.to_token_stream().to_string());
1222                Element::Type(Type::Extern(Extern {
1223                    name: t.ident.clone(),
1224                    target,
1225                    origin: Origin::new(syn::Item::Type(t), at),
1226                }))
1227            }
1228        },
1229        // An unnamed const is a `Guard`, not a constant: nothing can name it, so
1230        // it is not part of the API, and several sources' guards coexist because
1231        // none of them has an address to collide on.
1232        syn::Item::Const(c) if c.ident == "_" => Element::Guard(Guard {
1233            origin: Origin::new(c, at),
1234        }),
1235        syn::Item::Const(c) => match lower_type(&c.ty, consts, &at) {
1236            Ok(ty) => Element::Constant(Constant {
1237                name: c.ident.clone(),
1238                ty,
1239                origin: Origin::new(c, at),
1240            }),
1241            Err(source) => unsupported(
1242                c.ident.clone(),
1243                syn::Item::Const(c),
1244                &at,
1245                ItemError::ConstType { source },
1246            ),
1247        },
1248        // An item kind the language does not model. The proc-macro accepts only
1249        // six kinds and the five above cover the rest, so in practice this is a
1250        // `union` — never written by any source crate. It is diagnosed rather
1251        // than carried: a `#[prebindgen]` crate marks what crosses the boundary,
1252        // and the code around that belongs to the consumer.
1253        other => {
1254            let (name, kind) = match &other {
1255                syn::Item::Union(u) => (Some(u.ident.clone()), "a union"),
1256                _ => (None, "an item kind"),
1257            };
1258            unsupported(name, other, &at, ItemError::UnsupportedItemKind { kind })
1259        }
1260    }
1261}
1262
1263fn unsupported(
1264    name: impl Into<Option<syn::Ident>>,
1265    syntax: syn::Item,
1266    at: &Rc<SourceLocation>,
1267    error: ItemError,
1268) -> Element {
1269    Element::Unsupported(Unsupported {
1270        name: name.into(),
1271        error: Box::new(error),
1272        origin: Origin::new(syntax, Rc::clone(at)),
1273    })
1274}
1275
1276/// Refuse a type or const generic parameter, naming the first one found.
1277///
1278/// Lifetimes pass: they say nothing a destination language can act on, and the
1279/// spelling that needs them is already in the syntax — the same call
1280/// [`lower_type`] makes for a lifetime *argument*.
1281fn reject_generic_params(generics: &syn::Generics) -> Result<(), ItemError> {
1282    for param in &generics.params {
1283        let (name, kind) = match param {
1284            syn::GenericParam::Lifetime(_) => continue,
1285            syn::GenericParam::Type(t) => (t.ident.to_string(), "a type parameter"),
1286            syn::GenericParam::Const(c) => (c.ident.to_string(), "a const generic parameter"),
1287        };
1288        return Err(ItemError::UnsupportedGenericParam { param: name, kind });
1289    }
1290    Ok(())
1291}
1292
1293fn lower_fn(
1294    f: &syn::ItemFn,
1295    at: &Rc<SourceLocation>,
1296    consts: &ConstIndex,
1297) -> Result<Function, ItemError> {
1298    // Shapes `Function` has no slot for, and would therefore drop in silence.
1299    if f.sig.asyncness.is_some() {
1300        return Err(ItemError::UnsupportedAsync);
1301    }
1302    if f.sig.variadic.is_some() {
1303        return Err(ItemError::UnsupportedVariadic);
1304    }
1305    reject_generic_params(&f.sig.generics)?;
1306    let mut params = Vec::with_capacity(f.sig.inputs.len());
1307    for input in &f.sig.inputs {
1308        let pt = match input {
1309            syn::FnArg::Receiver(_) => return Err(ItemError::UnsupportedReceiver),
1310            syn::FnArg::Typed(pt) => pt,
1311        };
1312        let syn::Pat::Ident(pat) = &*pt.pat else {
1313            return Err(ItemError::UnsupportedParamPattern {
1314                pattern: pt.pat.to_token_stream().to_string(),
1315            });
1316        };
1317        let name = pat.ident.clone();
1318        let ty = lower_type(&pt.ty, consts, at).map_err(|source| ItemError::ParamType {
1319            param: name.clone(),
1320            source,
1321        })?;
1322        params.push(Param {
1323            name,
1324            ty,
1325            origin: Origin::new(pt.clone(), Rc::clone(at)),
1326        });
1327    }
1328    // An elided return and a written `-> ()` are the same function. The model
1329    // says so once, here, instead of leaving every consumer to normalize one to
1330    // the other — which is what they all do today, in eight separate copies.
1331    let ret = match &f.sig.output {
1332        syn::ReturnType::Default => TypeRef {
1333            kind: TypeKind::Unit,
1334            origin: Origin::new(syn::parse_quote!(()), Rc::clone(at)),
1335        },
1336        syn::ReturnType::Type(_, t) => {
1337            lower_type(t, consts, at).map_err(|source| ItemError::ReturnType { source })?
1338        }
1339    };
1340    Ok(Function {
1341        name: f.sig.ident.clone(),
1342        params,
1343        ret,
1344        origin: Origin::new(f.clone(), Rc::clone(at)),
1345    })
1346}
1347
1348/// Lower a `struct` item to whichever of the two shapes it is.
1349///
1350/// A **tuple struct** is an [`Extern`]: no adapter has ever crossed its fields,
1351/// so they are deliberately not lowered and a field type outside the grammar is
1352/// not an error. Anything else is a product of fields that do cross.
1353fn lower_struct(
1354    s: &syn::ItemStruct,
1355    at: &Rc<SourceLocation>,
1356    consts: &ConstIndex,
1357) -> Result<Type, ItemError> {
1358    reject_generic_params(&s.generics)?;
1359    let fields = match &s.fields {
1360        syn::Fields::Named(named) => {
1361            let mut out = Vec::with_capacity(named.named.len());
1362            for (index, f) in named.named.iter().enumerate() {
1363                let name = f.ident.clone().expect("named fields have idents");
1364                let ty = lower_type(&f.ty, consts, at).map_err(|source| ItemError::FieldType {
1365                    field: name.clone(),
1366                    source,
1367                })?;
1368                out.push(Field {
1369                    name: Some(name),
1370                    index,
1371                    ty,
1372                    origin: Origin::new(f.clone(), Rc::clone(at)),
1373                });
1374            }
1375            out
1376        }
1377        // Its contents are not a boundary surface, so nothing is lowered.
1378        syn::Fields::Unnamed(_) => {
1379            return Ok(Type::Extern(Extern {
1380                name: s.ident.clone(),
1381                // A tuple struct IS the definition; it points at nothing.
1382                target: None,
1383                origin: Origin::new(syn::Item::Struct(s.clone()), Rc::clone(at)),
1384            }));
1385        }
1386        syn::Fields::Unit => Vec::new(),
1387    };
1388    Ok(Type::Struct(Struct {
1389        reading: TypeRef::named(&s.ident),
1390        name: s.ident.clone(),
1391        fields,
1392        origin: Origin::new(s.clone(), Rc::clone(at)),
1393    }))
1394}
1395
1396/// Lower an `enum` item to whichever of the two shapes it is.
1397///
1398/// **The classification**: any alternative with a field makes it a [`Variant`] —
1399/// a sum, numbered by position. Otherwise it is an [`Enum`] — a named set of
1400/// integers, identified by the value Rust assigns. Both are spelled `enum` in
1401/// Rust and both keep the `syn::ItemEnum`; only what a destination language can
1402/// do with them differs, and that is what the model records.
1403///
1404/// `enum E {}` has no alternative carrying anything, so it is the degenerate
1405/// `Enum`.
1406fn lower_enum(
1407    e: &syn::ItemEnum,
1408    at: &Rc<SourceLocation>,
1409    consts: &ConstIndex,
1410) -> Result<Type, ItemError> {
1411    reject_generic_params(&e.generics)?;
1412
1413    if e.variants.iter().any(|v| !v.fields.is_empty()) {
1414        return Ok(Type::Variant(lower_variant(e, at, consts)?));
1415    }
1416    Ok(Type::Enum(lower_c_enum(e, at)))
1417}
1418
1419/// The payload-carrying shape. Position is the only numbering a sum has, so no
1420/// discriminant is evaluated: the mirror an adapter builds numbers its own arms.
1421fn lower_variant(
1422    e: &syn::ItemEnum,
1423    at: &Rc<SourceLocation>,
1424    consts: &ConstIndex,
1425) -> Result<Variant, ItemError> {
1426    let mut alternatives = Vec::with_capacity(e.variants.len());
1427    for (index, v) in e.variants.iter().enumerate() {
1428        let mut fields = Vec::with_capacity(v.fields.len());
1429        for (field_index, f) in v.fields.iter().enumerate() {
1430            let ty =
1431                lower_type(&f.ty, consts, at).map_err(|source| ItemError::VariantFieldType {
1432                    variant: v.ident.clone(),
1433                    field: match &f.ident {
1434                        Some(id) => id.to_string(),
1435                        None => field_index.to_string(),
1436                    },
1437                    source,
1438                })?;
1439            fields.push(Field {
1440                name: f.ident.clone(),
1441                index: field_index,
1442                ty,
1443                origin: Origin::new(f.clone(), Rc::clone(at)),
1444            });
1445        }
1446        alternatives.push(Alternative {
1447            name: v.ident.clone(),
1448            index,
1449            fields,
1450            origin: Origin::new(v.clone(), Rc::clone(at)),
1451        });
1452    }
1453    Ok(Variant {
1454        reading: TypeRef::named(&e.ident),
1455        name: e.ident.clone(),
1456        alternatives,
1457        origin: Origin::new(e.clone(), Rc::clone(at)),
1458    })
1459}
1460
1461/// The fieldless shape. Nothing here can fail to lower — there are no field
1462/// types — so an unevaluable discriminant ends the numeric chain rather than
1463/// refusing the item.
1464fn lower_c_enum(e: &syn::ItemEnum, at: &Rc<SourceLocation>) -> Enum {
1465    let mut values = Vec::with_capacity(e.variants.len());
1466    // Rust's own numbering rule: an explicit `= N` sets the value, an implicit
1467    // one takes the previous plus one, starting at 0.
1468    let mut next: Option<i64> = Some(0);
1469    for (index, v) in e.variants.iter().enumerate() {
1470        let discriminant = match v.discriminant.as_ref() {
1471            Some((_, expr)) => int_literal(expr),
1472            None => next,
1473        };
1474        // `checked_add`: a discriminant at the top of the range is valid Rust
1475        // (`#[repr(u64)] enum E { A = i64::MAX as u64, B }`), so running out of
1476        // `i64` ends the numeric chain exactly as an unevaluable spelling does.
1477        // The spelling is untouched either way — it is in `EnumValue::origin`.
1478        next = discriminant.and_then(|n| n.checked_add(1));
1479
1480        values.push(EnumValue {
1481            name: v.ident.clone(),
1482            index,
1483            discriminant,
1484            origin: Origin::new(v.clone(), Rc::clone(at)),
1485        });
1486    }
1487    Enum {
1488        reading: TypeRef::named(&e.ident),
1489        name: e.ident.clone(),
1490        values,
1491        origin: Origin::new(e.clone(), Rc::clone(at)),
1492    }
1493}
1494
1495/// Pull a signed integer out of a literal expression (`5`, `-3`, `0x07`).
1496/// `None` for anything else — a `const`, a path, arithmetic.
1497fn int_literal(expr: &syn::Expr) -> Option<i64> {
1498    i64::try_from(int_literal_wide(expr)?).ok()
1499}
1500
1501/// [`int_literal`] before the range check.
1502///
1503/// The magnitude is parsed **wider than the result** so the sign can be applied
1504/// first: `-9223372036854775808` is `i64::MIN` and a valid Rust discriminant,
1505/// but its magnitude is one past `i64::MAX`, so parsing the digits as `i64`
1506/// would reject the whole literal. A magnitude too large for `i128` fails here
1507/// and is reported as an unevaluable discriminant, which is the existing
1508/// contract for anything the frontend cannot reduce to a number.
1509fn int_literal_wide(expr: &syn::Expr) -> Option<i128> {
1510    match expr {
1511        syn::Expr::Lit(lit) => match &lit.lit {
1512            syn::Lit::Int(int) => int.base10_parse::<i128>().ok(),
1513            _ => None,
1514        },
1515        syn::Expr::Unary(syn::ExprUnary {
1516            op: syn::UnOp::Neg(_),
1517            expr,
1518            ..
1519        }) => int_literal_wide(expr).map(|v| -v),
1520        _ => None,
1521    }
1522}