Skip to main content

cinrs_core/
lib.rs

1//! The `cinrs` C front end.
2//!
3//! This crate holds everything the `c89!`, `c99!`, `c11!`, `c17!` and `c23!`
4//! procedural macros do — they are one pipeline parameterised by
5//! [`Standard`] — but is built on [`proc_macro2`] alone and never touches
6//! `proc_macro`. That makes the whole pipeline — input capture, lexing,
7//! preprocessing, parsing, sema and code generation — unit-testable outside a
8//! procedural macro context.
9//!
10//! The pipeline is:
11//!
12//! ```text
13//! TokenStream ──capture──▶ (C source text, SourceMap)
14//!             ──lex─────▶ Vec<lex::Token>     (byte ranges into that text)
15//!             ──pp──────▶ Vec<pp::Token>      (directives gone, macros gone)
16//!             ──parse───▶ TranslationUnit     (every node carries a range)
17//!             ──sema────▶ ir::Program         (typed, conversions explicit)
18//!             ──codegen─▶ TokenStream         (every token spanned at its C)
19//! ```
20//!
21//! A function that jumps takes two more steps on the way: sema hands its body
22//! to [`cfg`](mod@cfg), which turns it into basic blocks, and
23//! [`reloop`](mod@reloop) reads those back into loops, `if`s and `match`es for
24//! codegen to emit. Most `goto`s take neither — [`regions`](mod@regions) is
25//! the line between an outward jump, which stays a labelled block or a
26//! labelled loop over the statements themselves, and one that does not fit.
27//!
28//! Which revision is being compiled reaches every pass that has an opinion
29//! about it: the [lexer](mod@lex) (which spellings are keywords, and the C23
30//! constant forms), the [preprocessor](mod@pp) (`__VA_OPT__`, `#elifdef`,
31//! `__STDC_VERSION__`), the [parser](mod@parse) (the C11 and C23 grammar) and
32//! [sema](mod@sema) (a C23 keyword used as a name in an older block). A
33//! feature from a later revision is a diagnostic naming the macro that would
34//! have it; see [`Standard::requires`].
35//!
36//! Everything downstream reports problems as [`Diagnostic`]s carrying a
37//! [`SourceRange`]; [`Diagnostics::to_token_stream`] turns those into
38//! `compile_error!` invocations whose tokens are spanned at the exact C token
39//! that caused them. That is the point of the whole design: an error — ours or
40//! `rustc`'s — must land on the C code the user wrote. The lexer takes part in
41//! that by *not* reporting: its problems ride on the tokens they were found
42//! in, and the [preprocessor](mod@pp) reports only the ones whose token
43//! survives, because a group skipped by `#if 0` may hold anything at all.
44//! A token a macro produced carries the range of the *invocation*, which is
45//! the only place the user can look; [`pp::Expansions::annotate`] adds the
46//! `in expansion of macro 'X'` note that says how it got there.
47//!
48//! Capture also hands sema a [unit id](capture::Source::unit_id) identifying
49//! the invocation, which every synthetic name the expansion needs is built
50//! from — the renamed `extern` *objects*, the mangled function-local
51//! `static`s, the names given to anonymous tags, and the module the whole
52//! expansion goes into (see [`expand`]), so that two `c99!` blocks in one Rust
53//! module never collide.
54//!
55//! # Where `#include` fits
56//!
57//! A header is another file in the same offset space, so nothing downstream of
58//! the preprocessor has to know it exists: a [`SourceRange`] identifies a file
59//! as well as a position, and a diagnostic inside a header resolves to the
60//! span of the `#include` that pulled it in, with the header's own path, line
61//! and column put into the message text.
62//!
63//! The one seam is that the preprocessor runs on a thread where a
64//! `proc_macro2::Span` cannot follow it, so it *allocates* the offsets of the
65//! files it opens and hands them back — see [`pp::Preprocessed::included`] and
66//! [`SourceMap::next_base`] — and [`analyze`] adds them to the map afterwards,
67//! in the order they were opened. The [`include`](mod@include) module is where
68//! a header name is resolved and what is bundled; the user headers that were
69//! read come back as [`Analysis::user_headers`], which [`expand`] turns into
70//! the `include_str!` items that make Cargo rebuild when one changes. C23's
71//! `#embed` takes the same route with [`Analysis::embedded_files`] and
72//! `include_bytes!`, since a resource is bytes rather than text.
73//!
74//! # Where `include_c99!` fits
75//!
76//! [`expand_include`] is [`expand`] with a `.c` file in place of the token
77//! stream: the file becomes the map's root — see
78//! [`capture::capture_c_file`] — and every pass after that is the same one.
79//! Since nothing in the `.rs` file corresponds to a position in the `.c`, that
80//! root resolves every range to the span of the macro invocation and carries
81//! its own `path:line:column` into the message, which is exactly what a header
82//! already does.
83//!
84//! # Example
85//!
86//! ```
87//! use std::str::FromStr;
88//! use cinrs_core::{expand, Options, Standard};
89//!
90//! let input = proc_macro2::TokenStream::from_str("int add(int a, int b) { return a + b; }")
91//!     .unwrap();
92//! let out = expand(input, &Options::new(Standard::C99));
93//! assert!(out.to_string().contains("extern \"C\" fn add"));
94//! ```
95
96#![warn(missing_docs)]
97
98pub mod ast;
99pub mod capture;
100pub mod cfg;
101pub mod codegen;
102pub mod complex;
103pub mod diag;
104pub mod dump;
105pub mod gnu;
106pub mod include;
107pub mod ir;
108pub mod lex;
109mod locate;
110pub mod parse;
111pub mod pp;
112pub mod regions;
113pub mod reloop;
114pub mod sema;
115pub mod target;
116pub mod x86;
117
118use std::path::{Path, PathBuf};
119
120use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
121use quote::quote;
122
123pub use ast::TranslationUnit;
124pub use capture::{FileId, InputMode, Origin, Pos, Source, SourceMap, SourceRange, Subspan};
125pub use diag::{Diagnostic, Diagnostics, Level};
126pub use ir::{Program, Ty};
127pub use pp::Token;
128pub use target::{Arch, Env, Os, TargetModel, TargetSource, UnknownTarget};
129
130/// The environment variable that names the target the expansion is for.
131///
132/// A procedural macro cannot ask `rustc` what it is compiling for, so the
133/// crate being built says it, from its own build script:
134///
135/// ```text
136/// println!("cargo:rustc-env=CINRS_TARGET={}", std::env::var("TARGET").unwrap());
137/// ```
138///
139/// `cargo:rustc-env` reaches the very `rustc` process that runs the macro, and
140/// Cargo makes the value part of the crate's fingerprint, so changing the
141/// `--target` rebuilds. See [`target`] for the whole order of precedence.
142pub const TARGET_ENV_VAR: &str = "CINRS_TARGET";
143
144/// Which revision of the C standard to accept.
145///
146/// The variants are ordered, so a feature is gated by comparing: a construct
147/// C11 introduced is accepted when `standard >= Standard::C11`. C17 is C11
148/// with a different `__STDC_VERSION__` and adds nothing else.
149#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
150pub enum Standard {
151    /// ISO/IEC 9899:1990, the standard everyone still calls C89.
152    ///
153    /// C90 is the ISO republication of ANSI X3.159-1989 with no technical
154    /// change, so `c89!` and `c90!` are one entry point under two names.
155    C89,
156    /// ISO/IEC 9899:1999.
157    #[default]
158    C99,
159    /// ISO/IEC 9899:2011.
160    C11,
161    /// ISO/IEC 9899:2018.
162    C17,
163    /// ISO/IEC 9899:2024.
164    C23,
165}
166
167impl Standard {
168    /// The name used in diagnostics.
169    pub fn as_str(self) -> &'static str {
170        match self {
171            Standard::C89 => "C89",
172            Standard::C99 => "C99",
173            Standard::C11 => "C11",
174            Standard::C17 => "C17",
175            Standard::C23 => "C23",
176        }
177    }
178
179    /// The macro that selects this standard, as a diagnostic names it.
180    pub fn macro_name(self) -> &'static str {
181        self.macro_name_in(Dialect::Iso)
182    }
183
184    /// The macro that selects this standard in `dialect`.
185    pub fn macro_name_in(self, dialect: Dialect) -> &'static str {
186        match (dialect, self) {
187            (Dialect::Iso, Standard::C89) => "c89!",
188            (Dialect::Gnu, Standard::C89) => "gnu89!",
189            (Dialect::Iso, Standard::C99) => "c99!",
190            (Dialect::Iso, Standard::C11) => "c11!",
191            (Dialect::Iso, Standard::C17) => "c17!",
192            (Dialect::Iso, Standard::C23) => "c23!",
193            (Dialect::Gnu, Standard::C99) => "gnu99!",
194            (Dialect::Gnu, Standard::C11) => "gnu11!",
195            (Dialect::Gnu, Standard::C17) => "gnu17!",
196            (Dialect::Gnu, Standard::C23) => "gnu23!",
197        }
198    }
199
200    /// Every name the macro that selects this standard in `dialect` may be
201    /// written with, without the `!`.
202    ///
203    /// Two of them only for C89, which has two names and one entry point; the
204    /// point of the list is that [the search](capture::Origin) for an invocation
205    /// in the crate's sources knows what to look for, and `c90! { … }` has to be
206    /// found as readily as `c89! { … }`.
207    pub fn macro_names_in(self, dialect: Dialect) -> &'static [&'static str] {
208        match (dialect, self) {
209            (Dialect::Iso, Standard::C89) => &["c89", "c90"],
210            (Dialect::Gnu, Standard::C89) => &["gnu89"],
211            (Dialect::Iso, Standard::C99) => &["c99"],
212            (Dialect::Iso, Standard::C11) => &["c11"],
213            (Dialect::Iso, Standard::C17) => &["c17"],
214            (Dialect::Iso, Standard::C23) => &["c23"],
215            (Dialect::Gnu, Standard::C99) => &["gnu99"],
216            (Dialect::Gnu, Standard::C11) => &["gnu11"],
217            (Dialect::Gnu, Standard::C17) => &["gnu17"],
218            (Dialect::Gnu, Standard::C23) => &["gnu23"],
219        }
220    }
221
222    /// The same for the `include_…!` family, which is one entry point per name
223    /// of [`Standard::macro_names_in`].
224    pub fn include_macro_names_in(self, dialect: Dialect) -> &'static [&'static str] {
225        match (dialect, self) {
226            (Dialect::Iso, Standard::C89) => &["include_c89", "include_c90"],
227            (Dialect::Gnu, Standard::C89) => &["include_gnu89"],
228            (Dialect::Iso, Standard::C99) => &["include_c99"],
229            (Dialect::Iso, Standard::C11) => &["include_c11"],
230            (Dialect::Iso, Standard::C17) => &["include_c17"],
231            (Dialect::Iso, Standard::C23) => &["include_c23"],
232            (Dialect::Gnu, Standard::C99) => &["include_gnu99"],
233            (Dialect::Gnu, Standard::C11) => &["include_gnu11"],
234            (Dialect::Gnu, Standard::C17) => &["include_gnu17"],
235            (Dialect::Gnu, Standard::C23) => &["include_gnu23"],
236        }
237    }
238
239    /// The message a feature from a newer revision gets in this one.
240    ///
241    /// `what` is the subject, already quoted where it names a token:
242    /// `standard.requires("'_Static_assert'", Standard::C11)` reads
243    /// `'_Static_assert' requires C11 or later (this block is c99!)`. Saying
244    /// which macro the block is written with is the point — the fix is to
245    /// change the macro, and nothing in the C says which one it is.
246    pub fn requires(self, what: &str, needed: Standard) -> String {
247        format!(
248            "{what} requires {} or later (this block is {})",
249            needed.as_str(),
250            self.macro_name()
251        )
252    }
253}
254
255/// Whether the GNU extensions that need a plain spelling are switched on.
256///
257/// GCC draws the same line between `-std=c99` and `-std=gnu99`: everything
258/// spelled with a double underscore (`__typeof__`, `__attribute__`,
259/// `__builtin_*`, `__extension__`) is available either way, because those names
260/// are reserved and cannot collide with a user's own; only the plain spellings
261/// — `typeof`, `asm` — need the GNU dialect, and only there is a feature of a
262/// *newer* revision accepted without a diagnostic (GCC takes `_Static_assert`
263/// in `gnu99`).
264///
265/// See [`doc/gnu-extensions.md`](https://github.com/tanakh/cinrs/blob/master/doc/gnu-extensions.md)
266/// in the repository for the whole catalogue.
267#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
268pub enum Dialect {
269    /// Strict ISO C: `c89!`, `c99!`, `c11!`, `c17!`, `c23!`.
270    #[default]
271    Iso,
272    /// ISO C plus the GNU extensions: `gnu89!`, `gnu99!`, `gnu11!`, `gnu17!`,
273    /// `gnu23!`.
274    Gnu,
275}
276
277impl Dialect {
278    /// Whether this is a GNU dialect.
279    pub fn is_gnu(self) -> bool {
280        self == Dialect::Gnu
281    }
282}
283
284/// Whether the complex types are available, which is this crate's `complex`
285/// feature.
286///
287/// The feature exists because the generated code for a `_Complex` value names
288/// a *runtime* type — `::cinrs::rt::Complex`, which is `num_complex::Complex`
289/// — and a build that does not want the dependency says
290/// `default-features = false` on the `cinrs` crate. It is only the default of
291/// [`Options::complex`]; the front end itself is compiled either way.
292pub const COMPLEX_SUPPORTED: bool = cfg!(feature = "complex");
293
294/// What every diagnostic about a complex type says when
295/// [`Options::complex`] is off.
296///
297/// One message, in one place, because the lexer (an imaginary constant), the
298/// parser (`__real__`) and sema (the type itself) all have to give it.
299pub const COMPLEX_UNSUPPORTED: &str = "complex types are not supported here: '_Complex' needs the 'complex' feature of the \
300     cinrs crate, which is on by default and supplies the runtime the generated code links \
301     against";
302
303/// Knobs for one macro expansion.
304///
305/// Not [`Copy`], because [`Options::include_paths`] owns its list; the front
306/// end clones it once per invocation.
307#[derive(Clone, Debug)]
308pub struct Options {
309    /// Which standard to accept.
310    pub standard: Standard,
311    /// Whether the plain-spelled GNU extensions are switched on, and whether a
312    /// feature of a newer revision is accepted silently. See [`Dialect`].
313    pub dialect: Dialect,
314    /// Accept `$` in identifiers, like GCC's `-fdollars-in-identifiers`.
315    ///
316    /// **On by default**, which is where GCC and Clang both keep it: WG14
317    /// DR027 lets an implementation put characters outside the basic source
318    /// character set into an identifier, GCC takes `$` unconditionally, and
319    /// Clang takes it with a warning that only `-pedantic-errors` promotes.
320    /// A `$` that reaches the generated Rust — which has no such spelling —
321    /// is written `_dollar_` there, and the C name is what the symbol still
322    /// links by.
323    pub dollar_in_identifiers: bool,
324    /// Directories `#include` searches, after the ones the unit itself names
325    /// with `#pragma cinrs include_path` and before the bundled headers.
326    ///
327    /// This is the programmatic way in; see [`crate::include`] for the whole
328    /// search order and for the `CINRS_INCLUDE_PATH` environment variable,
329    /// which comes last.
330    pub include_paths: Vec<PathBuf>,
331    /// Whether `#include` also searches the platform's own directories, and
332    /// where in the order they go.
333    ///
334    /// **Off by default**, which is what keeps a unit self-contained and
335    /// portable across [target models](target). [`analyze`] replaces this with
336    /// what [`include::SYSTEM_ENV_VAR`] says when the caller left it at
337    /// [`include::System::Off`], and the unit's own
338    /// `#pragma cinrs system_include` replaces it again; see
339    /// [`mod@include`] for the whole order and for the directories themselves.
340    pub system_include: include::System,
341    /// The data model the generated code is compiled for.
342    ///
343    /// Defaults to the host's, which [`analyze`] replaces with the one
344    /// [`TARGET_ENV_VAR`] names and the unit's own
345    /// `#pragma cinrs target` replaces again; see [`target`] for the order.
346    /// Whatever it ends up being, the expansion states it — the
347    /// `const _: () = { assert!(…); };` block every unit opens with is built
348    /// from *this* model, so a unit expanded for one data model and compiled
349    /// for another fails to compile.
350    ///
351    /// Setting it directly — [`Options::for_target`] is the tidy way — marks
352    /// [`Options::target_source`] `Explicit`, and the environment variable is
353    /// then left alone.
354    pub target: TargetModel,
355    /// Where [`Options::target`] came from, which the diagnostics name.
356    pub target_source: TargetSource,
357    /// Whether the complex types are available.
358    ///
359    /// Defaults to [`COMPLEX_SUPPORTED`], which is this crate's `complex`
360    /// feature — the one the `cinrs` crate turns on to supply the `cinrs-rt`
361    /// runtime the generated code names. With it off, `_Complex` is a
362    /// diagnostic, `__STDC_NO_COMPLEX__` is predefined, and nothing generated
363    /// needs more than `core`. The front end carries the support either way, so
364    /// a test may set this in either direction.
365    pub complex: bool,
366}
367
368impl Default for Options {
369    fn default() -> Self {
370        Self::new(Standard::default())
371    }
372}
373
374impl Options {
375    /// Default options for `standard`, in the strict ISO dialect.
376    pub fn new(standard: Standard) -> Self {
377        Self::with_dialect(standard, Dialect::Iso)
378    }
379
380    /// Default options for `standard` in the GNU dialect — what `gnu99!` and
381    /// friends use.
382    pub fn gnu(standard: Standard) -> Self {
383        Self::with_dialect(standard, Dialect::Gnu)
384    }
385
386    /// Default options for `standard` in `dialect`.
387    pub fn with_dialect(standard: Standard, dialect: Dialect) -> Self {
388        Self {
389            standard,
390            dialect,
391            dollar_in_identifiers: true,
392            include_paths: Vec::new(),
393            system_include: include::System::Off,
394            target: TargetModel::host(),
395            target_source: TargetSource::Host,
396            complex: COMPLEX_SUPPORTED,
397        }
398    }
399
400    /// These options with the complex types switched on or off; see
401    /// [`Options::complex`].
402    pub fn with_complex(mut self, complex: bool) -> Self {
403        self.complex = complex;
404        self
405    }
406
407    /// These options with `target` set, and marked as the caller's choice so
408    /// that [`TARGET_ENV_VAR`] does not override it.
409    ///
410    /// What a test that wants the ILP32 or LLP64 rules uses; a unit still
411    /// overrides it with `#pragma cinrs target`.
412    pub fn for_target(mut self, target: TargetModel) -> Self {
413        self.target = target;
414        self.target_source = TargetSource::Explicit;
415        self
416    }
417
418    /// The macro that selects this entry point, as a diagnostic names it.
419    pub fn macro_name(&self) -> &'static str {
420        self.standard.macro_name_in(self.dialect)
421    }
422
423    /// The [origin](Origin) a real expansion of this entry point has: the names
424    /// the invocation may be written with, and the crate directory Cargo names.
425    ///
426    /// This is what the procedural macros hand to [`expand_with`], and the only
427    /// thing that lets capture find the invocation in the crate's sources when
428    /// the host reports no positions. [`expand`] and [`analyze`] deliberately do
429    /// not use it; see [`Origin`].
430    pub fn origin(&self) -> Origin {
431        Origin::new(self.standard.macro_names_in(self.dialect))
432    }
433
434    /// The same for the `include_…!` entry point of this standard and dialect.
435    pub fn include_origin(&self) -> Origin {
436        Origin::new(self.standard.include_macro_names_in(self.dialect))
437    }
438
439    /// How a pass gates the features of a newer revision.
440    pub fn gating(&self) -> Gating {
441        Gating {
442            standard: self.standard,
443            dialect: self.dialect,
444        }
445    }
446}
447
448/// The pair every pass needs to answer "may this block write that?".
449///
450/// A GNU dialect answers yes to everything a later revision added, exactly as
451/// GCC's `-std=gnu99` does; the strict entry points keep the diagnostic that
452/// names the macro to write instead.
453#[derive(Clone, Copy, Debug, Default)]
454pub struct Gating {
455    /// The revision the block is written in.
456    pub standard: Standard,
457    /// Whether the GNU extensions are switched on.
458    pub dialect: Dialect,
459}
460
461impl Gating {
462    /// The message a feature from a newer revision gets here, or `None` when it
463    /// is accepted.
464    pub fn requires(self, what: &str, needed: Standard) -> Option<String> {
465        if self.dialect.is_gnu() || self.standard >= needed {
466            return None;
467        }
468        Some(format!(
469            "{what} requires {} or later (this block is {})",
470            needed.as_str(),
471            self.standard.macro_name_in(self.dialect)
472        ))
473    }
474
475    /// Whether a declaration with no type specifier at all means `int`
476    /// (C89 6.5.2).
477    ///
478    /// C99 removed implicit `int` (N635) and GCC diagnoses it in every later
479    /// mode, `-std=gnu99` included, so this is one of the three places where
480    /// `gnu89!` is *older* than `gnu99!` rather than a superset of it.
481    pub fn implicit_int(self) -> bool {
482        self.standard < Standard::C99
483    }
484
485    /// Whether a call to a function nobody declared declares `extern int f();`
486    /// at file scope (C89 6.3.2.2).
487    ///
488    /// C99 removed the rule (N636); see [`Gating::implicit_int`] for why the
489    /// GNU dialect does not bring it back.
490    pub fn implicit_function_declarations(self) -> bool {
491        self.standard < Standard::C99
492    }
493
494    /// Whether an old-style (K&R) function definition may be written.
495    ///
496    /// Obsolescent from C89 onwards and *removed* by C23 (N2432), so every
497    /// entry point below `c23!` has it and the two C23 ones do not.
498    pub fn old_style_definitions(self) -> bool {
499        self.standard < Standard::C23
500    }
501
502    /// The gate message for `name`, if another entry point would have made it
503    /// a keyword.
504    ///
505    /// `nullptr`, `bool` and the rest are ordinary identifiers before C23 — the
506    /// bundled `<stdbool.h>` writes `#define bool _Bool` — so a `c11!` block
507    /// that uses one gets told what it would have meant. A GNU dialect says
508    /// nothing about those: GCC's `gnu11` has no `bool` keyword either, so
509    /// "use of undeclared identifier" is the honest answer there.
510    ///
511    /// `typeof` and `asm` are the two the *dialect* decides, so their message
512    /// names the entry point that has them.
513    pub fn newer_keyword(self, name: &str) -> Option<String> {
514        if self.dialect.is_gnu() {
515            return None;
516        }
517        let gnu = self.standard.macro_name_in(Dialect::Gnu);
518        let here = self.standard.macro_name_in(self.dialect);
519        match name {
520            "typeof" | "typeof_unqual" if self.standard < Standard::C23 => {
521                return Some(format!(
522                    "'{name}' requires a GNU dialect ({gnu}) or C23 or later \
523                     (this block is {here})"
524                ));
525            }
526            "asm" => {
527                return Some(format!(
528                    "'{name}' requires a GNU dialect ({gnu}); the spelling '__asm__' is \
529                     available everywhere (this block is {here})"
530                ));
531            }
532            _ => {}
533        }
534        let needed = crate::lex::Keyword::from_str(name, Standard::C23)?.since();
535        self.requires(&format!("'{name}'"), needed)
536    }
537}
538
539/// The result of running the front end over one macro invocation.
540pub struct Analysis {
541    /// The captured C source and its map back to Rust spans, headers and all.
542    pub source: Source,
543    /// The preprocessed C token list, ending with [`lex::TokenKind::Eof`].
544    pub tokens: Vec<Token>,
545    /// The parsed translation unit.
546    pub unit: TranslationUnit,
547    /// Everything that went wrong.
548    pub diagnostics: Diagnostics,
549    /// The macro invocations the preprocessor replaced, which later passes'
550    /// diagnostics are annotated from.
551    pub expansions: pp::Expansions,
552    /// The absolute paths of the user headers that were read, which the
553    /// expansion mentions so that Cargo rebuilds when one changes.
554    pub user_headers: Vec<PathBuf>,
555    /// The absolute paths of the resources `#embed` read, mentioned in the
556    /// expansion for the same reason.
557    pub embedded_files: Vec<PathBuf>,
558    /// The libraries `#pragma cinrs link` asked the expansion to be linked
559    /// against, each of which becomes an `extern` block of its own.
560    pub link_libraries: Vec<String>,
561    /// The functions `#pragma cinrs safe` asked to be generated without
562    /// `unsafe`; see [`sema::check_safe`].
563    pub safe_functions: Vec<pp::SafeName>,
564    /// Whether `#pragma cinrs export` asked for real C symbols.
565    pub export: bool,
566    /// Whether `#pragma cinrs no_std` said the expansion goes into a
567    /// `#![no_std]` crate, so that the storage a variable length array or
568    /// `alloca` needs comes from `alloc` rather than from `std`.
569    pub no_std: bool,
570    /// The Rust path `#pragma cinrs crate` gave the `cinrs` facade crate, if
571    /// any; see [`ir::DEFAULT_CRATE_PATH`].
572    pub crate_path: Option<String>,
573    /// The options the rest of the pipeline is to run with.
574    ///
575    /// These are the caller's, with the target model resolved: whatever
576    /// [`TARGET_ENV_VAR`] and the unit's own `#pragma cinrs target` had to say
577    /// is already in [`Options::target`], so sema and code generation must use
578    /// *these* rather than the ones they were handed.
579    pub options: Options,
580}
581
582/// Stack size for the thread the recursive passes run on.
583///
584/// Recursive descent turns nesting in the input into stack frames, and an
585/// unoptimised build of a procedural macro — which is what a `cargo build`
586/// uses — spends tens of kilobytes on each one. `rustc` runs macro expansion
587/// on an 8 MiB stack, which combined with the parser's own recursion limit
588/// would be uncomfortably tight, and a stack overflow inside a procedural
589/// macro aborts the compiler with no useful message at all. Reserving address
590/// space is free until it is touched, so the deep passes get a roomy thread of
591/// their own and the recursion limit stays the only way to run out.
592const WORKER_STACK_SIZE: usize = 64 << 20;
593
594/// Runs `f` on a thread with [`WORKER_STACK_SIZE`] of stack.
595///
596/// The argument travels through a cell rather than being captured directly so
597/// that it can be recovered and the work done in place if the thread cannot be
598/// created at all.
599fn on_large_stack<A, T>(arg: A, f: fn(A) -> T) -> T
600where
601    A: Send + 'static,
602    T: Send + 'static,
603{
604    use std::sync::{Arc, Mutex};
605
606    let cell = Arc::new(Mutex::new(Some(arg)));
607    let worker = Arc::clone(&cell);
608    let spawned = std::thread::Builder::new()
609        .name("cinrs-worker".to_owned())
610        .stack_size(WORKER_STACK_SIZE)
611        .spawn(move || {
612            let arg = worker
613                .lock()
614                .expect("the argument cell is never poisoned before use")
615                .take()
616                .expect("the argument is taken exactly once");
617            f(arg)
618        });
619    match spawned {
620        Ok(handle) => match handle.join() {
621            Ok(result) => result,
622            Err(payload) => std::panic::resume_unwind(payload),
623        },
624        Err(_) => {
625            // The thread could not be created; run here and rely on the
626            // recursion limit alone.
627            let arg = cell
628                .lock()
629                .expect("the thread never started, so nothing poisoned the cell")
630                .take()
631                .expect("the argument is still there");
632            f(arg)
633        }
634    }
635}
636
637/// Everything the lexer, preprocessor and parser need, in one `Send` bundle.
638struct FrontEndInput {
639    /// The captured C source text.
640    ctx: pp::Context,
641    /// The range the whole translation unit covers.
642    unit_range: SourceRange,
643    options: Options,
644}
645
646/// The lexer's, preprocessor's and parser's results.
647struct FrontEndOutput {
648    tokens: Vec<Token>,
649    unit: TranslationUnit,
650    diagnostics: Diagnostics,
651    expansions: pp::Expansions,
652    included: Vec<pp::IncludedFile>,
653    user_headers: Vec<PathBuf>,
654    embedded_files: Vec<PathBuf>,
655    link_libraries: Vec<String>,
656    safe_functions: Vec<pp::SafeName>,
657    export: bool,
658    no_std: bool,
659    crate_path: Option<String>,
660    /// The options with the target model resolved; see [`Analysis::options`].
661    options: Options,
662}
663
664/// Lexes, preprocesses and parses one translation unit.
665fn front_end(input: FrontEndInput) -> FrontEndOutput {
666    let FrontEndInput {
667        mut ctx,
668        unit_range,
669        mut options,
670    } = input;
671    let mut diagnostics = Diagnostics::new();
672    let mut raw = lex::lex_file(&ctx.text, ctx.base, &(&options).into());
673    // `#pragma cinrs target` has to be answered before anything else looks at
674    // the model: the predefined macros are built from it, so it cannot be a
675    // pragma like the others, handled where it stands. The scan is lexical and
676    // over the unit's own text only; see [`pp::scan_target_pragma`]. A pragma
677    // that really did change the model means the text has to be lexed again,
678    // because how wide `wchar_t` is decides what `L'…'` may hold.
679    let (target_pragmas, relex) = pp::scan_target_pragma(&raw, &mut options, &mut diagnostics);
680    ctx.target_pragmas = target_pragmas;
681    if relex {
682        raw = lex::lex_file(&ctx.text, ctx.base, &(&options).into());
683    }
684    let pp::Preprocessed {
685        tokens,
686        expansions,
687        included,
688        user_headers,
689        embedded_files,
690        link_libraries,
691        safe_functions,
692        export,
693        no_std,
694        crate_path,
695        pack_events,
696        target_events,
697    } = pp::preprocess(&raw, &ctx, &options, &mut diagnostics);
698    let packing = pp::PackMap::new(pack_events);
699    let targets = pp::TargetOptionMap::new(target_events);
700    let unit = parse::parse(
701        &tokens,
702        unit_range,
703        &packing,
704        &targets,
705        &options,
706        &mut diagnostics,
707    );
708    // Annotate here rather than at the end: every diagnostic is annotated
709    // exactly once, right after the pass that produced it.
710    expansions.annotate(&mut diagnostics);
711    FrontEndOutput {
712        tokens,
713        unit,
714        diagnostics,
715        expansions,
716        included,
717        user_headers,
718        embedded_files,
719        link_libraries,
720        safe_functions,
721        export,
722        no_std,
723        crate_path,
724        options,
725    }
726}
727
728/// Runs capture, lexing, preprocessing and parsing over `input`.
729///
730/// This is the entry point tests use when they want to look at the
731/// intermediate results; [`expand`] is the one the macro calls, and it adds
732/// semantic analysis and code generation on top.
733pub fn analyze(input: TokenStream, options: &Options) -> Analysis {
734    analyze_with(input, options, &Origin::unknown())
735}
736
737/// Runs capture, lexing, preprocessing and parsing over `input`, with
738/// everything the host says about the invocation itself.
739///
740/// See [`Origin`]; [`analyze`] is this with an origin that says nothing.
741pub fn analyze_with(input: TokenStream, options: &Options, origin: &Origin) -> Analysis {
742    let mut diagnostics = Diagnostics::new();
743    // Capture must stay on this thread: it handles `proc_macro2::Span`s, which
744    // are not `Send`. Everything after it works on plain byte offsets.
745    let source = capture::capture_with(input, &mut diagnostics, origin);
746    analyze_source(source, options, diagnostics)
747}
748
749/// Runs the front end over a [`Source`] that has already been captured.
750///
751/// [`analyze_with`] is this with the capture in front of it; the other caller
752/// is [`expand_include`], whose source is a `.c` file rather than a token
753/// stream.
754fn analyze_source(mut source: Source, options: &Options, mut diagnostics: Diagnostics) -> Analysis {
755    // The environment is read here rather than in `Options::new`, so that the
756    // diagnostic a bad `CINRS_TARGET` deserves has a range to sit on — the
757    // whole invocation, there being nothing in the C to point at.
758    let mut options = options.clone();
759    apply_env_target(&mut options, source.root_range(), &mut diagnostics);
760    apply_env_system_include(&mut options, source.root_range(), &mut diagnostics);
761    let file = source.map.file(source.root);
762    let arg = FrontEndInput {
763        ctx: pp::Context {
764            text: file.text().to_owned(),
765            base: file.base(),
766            file_name: file.rust_path().unwrap_or(pp::DEFAULT_FILE_NAME).to_owned(),
767            first_line: file.first_line(),
768            dir: including_directory(file.rust_path()),
769            next_base: source.map.next_base(),
770            // Filled in by `front_end`, which is where the scan runs.
771            target_pragmas: pp::TargetPragmas::default(),
772        },
773        unit_range: source.root_range(),
774        options,
775    };
776    let out = on_large_stack(arg, front_end);
777    // The preprocessor allocated the offsets; the map hands out the spans for
778    // them. Adding the files in the order they were opened is what makes a
779    // diagnostic inside a nested header point at the outermost `#include`,
780    // since the file each directive is written in is already in the map.
781    for header in &out.included {
782        let span = source.map.span(header.directive);
783        let id = source
784            .map
785            .add_included_file(header.name.clone(), header.text.clone(), span);
786        debug_assert_eq!(
787            source.map.file(id).base(),
788            header.base,
789            "the preprocessor and the source map disagree about where '{}' starts",
790            header.name
791        );
792    }
793    diagnostics.extend(out.diagnostics);
794
795    Analysis {
796        source,
797        tokens: out.tokens,
798        unit: out.unit,
799        diagnostics,
800        expansions: out.expansions,
801        user_headers: out.user_headers,
802        embedded_files: out.embedded_files,
803        link_libraries: out.link_libraries,
804        safe_functions: out.safe_functions,
805        export: out.export,
806        no_std: out.no_std,
807        crate_path: out.crate_path,
808        options: out.options,
809    }
810}
811
812/// Resolves [`TARGET_ENV_VAR`] into `options`, reporting a triple that names
813/// no machine this crate models.
814///
815/// Only when the caller left the model at the host's: an
816/// [`Options::for_target`] is a deliberate choice, and a test that sets one
817/// must not have the developer's own environment change the answer.
818fn apply_env_target(options: &mut Options, range: SourceRange, diagnostics: &mut Diagnostics) {
819    if options.target_source != TargetSource::Host {
820        return;
821    }
822    let Ok(triple) = std::env::var(TARGET_ENV_VAR) else {
823        return;
824    };
825    let triple = triple.trim().to_owned();
826    if triple.is_empty() {
827        return;
828    }
829    let source = TargetSource::Env(triple);
830    match TargetModel::from_triple(source.triple().expect("Env carries its triple")) {
831        Ok(model) => {
832            options.target = model;
833            options.target_source = source;
834        }
835        Err(unknown) => diagnostics.error(range, unknown.message(&source)),
836    }
837}
838
839/// Resolves [`include::SYSTEM_ENV_VAR`] into `options`, reporting a value that
840/// is neither a boolean nor `first`.
841///
842/// Only when the caller left the switch off, for [`apply_env_target`]'s
843/// reason: an [`Options::system_include`] the caller set is a deliberate
844/// choice, and a test that sets one must not have the developer's own
845/// environment change the answer.
846fn apply_env_system_include(
847    options: &mut Options,
848    range: SourceRange,
849    diagnostics: &mut Diagnostics,
850) {
851    if options.system_include != include::System::Off {
852        return;
853    }
854    let Ok(value) = std::env::var(include::SYSTEM_ENV_VAR) else {
855        return;
856    };
857    match include::System::from_env_value(&value) {
858        Some(mode) => options.system_include = mode,
859        None => diagnostics.error(
860            range,
861            format!(
862                "{}={value:?} is not one of '1', 'first' or '0'",
863                include::SYSTEM_ENV_VAR
864            ),
865        ),
866    }
867}
868
869/// The directory an `#include "…"` in the macro's own text searches first.
870///
871/// `Span::local_file` gives the path of the `.rs` file exactly as `rustc` was
872/// told it, which for a Cargo build is relative to the working directory
873/// `rustc` runs in. Since this code *is* that `rustc` process, resolving it
874/// against the working directory is a no-op — the relative path already works
875/// — and taking its parent gives the directory the user is writing in, which
876/// is where C looks for a quoted header. A `.rs` file at the root of the
877/// working directory has `""` as its parent, which joins to a bare name and is
878/// therefore still right.
879fn including_directory(rust_path: Option<&str>) -> Option<PathBuf> {
880    Some(Path::new(rust_path?).parent()?.to_path_buf())
881}
882
883/// Expands one `include_c99!("path")` invocation: the same translation, over a
884/// `.c` file rather than over C written inside the `.rs`.
885///
886/// The file is read and translated exactly as [string-literal
887/// input](capture::InputMode::StringLiteral) would be — every C construct is
888/// accepted, `#pragma cinrs …` inside it configures the unit, and its own
889/// directory is what its `#include "…"` searches first, as a header's is.
890///
891/// # Where a relative path is resolved
892///
893/// Against the **directory of the `.rs` file the macro is written in**, which
894/// is what `#include "…"` in a `c99!` block already does and what the author
895/// is looking at. `Span::local_file` is how that directory is found; where the
896/// compiler will not say (input built by another macro, some IDE contexts), the
897/// invocation is looked for in the crate's own sources — see [`Origin`] — and
898/// `CARGO_MANIFEST_DIR` stands in when even that finds nothing, so that a path
899/// written relative to the package still resolves. An absolute path is used as
900/// it stands.
901///
902/// # Diagnostics
903///
904/// There is no C in the `.rs` file, so there is no span to point into: every
905/// diagnostic — this crate's and `rustc`'s about the generated code — lands on
906/// the macro invocation, and a message of ours carries `path:line:column` in
907/// front of it, exactly as one inside an `#include`d header does. The file is
908/// named in the expansion with `include_str!` as well, so that editing it
909/// rebuilds the crate.
910pub fn expand_include(input: TokenStream, options: &Options) -> TokenStream {
911    let macro_name = format!("include_{}", options.macro_name());
912    let mut trees = input.into_iter();
913    let (first, second) = (trees.next(), trees.next());
914    let span = first.as_ref().map_or_else(Span::call_site, TokenTree::span);
915    let name = match (&first, &second) {
916        (Some(TokenTree::Literal(literal)), None) => capture::string_literal_value(literal),
917        _ => None,
918    };
919    let Some(name) = name else {
920        return diag::compile_error_at(
921            span,
922            &format!(
923                "{macro_name} takes one string literal naming a C file, as in \
924                 {macro_name}(\"vendor/parser.c\")"
925            ),
926        );
927    };
928
929    let path = include_path(&name, span, &first, &options.include_origin());
930    let found = match include::read_source(&path) {
931        Ok(found) => found,
932        Err(include::Error::Unreadable { path, error }) => {
933            return diag::compile_error_at(span, &format!("cannot read '{path}': {error}"));
934        }
935        Err(include::Error::NotFound { searched }) => {
936            let looked = searched.join(", ");
937            return diag::compile_error_at(
938                span,
939                &format!(
940                    "{macro_name} cannot find '{name}': there is no file at {looked}. A relative \
941                     path is resolved against the directory of the .rs file this macro is \
942                     written in"
943                ),
944            );
945        }
946    };
947
948    // The file is the unit's own text, so it is tracked like a header: editing
949    // it has to rebuild the crate that names it.
950    let tracked: Vec<PathBuf> = found.path.into_iter().collect();
951    let source = capture::capture_c_file(found.name, found.text, span);
952    let analysis = analyze_source(source, options, Diagnostics::new());
953    generate_unit(analysis, &tracked)
954}
955
956/// Where `include_c99!("name")` looks for its file.
957///
958/// The directory of the `.rs` file the invocation is written in, which is what
959/// `Span::local_file` reports and what a quoted `#include` in a `c99!` block
960/// already searches first.
961///
962/// Where the compiler will not say where that is, the invocation is looked for
963/// in the crate's own sources: an `include_…!` whose argument is this very
964/// literal, in a directory that does hold the file it names — which is the test
965/// that tells two invocations of the same name apart, and the only thing this
966/// path can be checked against. Failing that `CARGO_MANIFEST_DIR` stands in, and
967/// failing even that the path is used as written, against the working directory,
968/// which is a unit test rather than a build.
969fn include_path(name: &str, span: Span, input: &Option<TokenTree>, origin: &Origin) -> PathBuf {
970    let path = Path::new(name);
971    if path.is_absolute() {
972        return path.to_path_buf();
973    }
974    if let Some(dir) = span
975        .local_file()
976        .and_then(|rs| rs.parent().map(Path::to_path_buf))
977    {
978        return dir.join(path);
979    }
980    if let Some(tree) = input
981        && let Some(dir) =
982            capture::invocation_directory(tree, origin, |dir| dir.join(path).is_file())
983    {
984        return dir.join(path);
985    }
986    match std::env::var_os(include::MANIFEST_DIR_VAR) {
987        Some(root) => Path::new(&root).join(path),
988        None => path.to_path_buf(),
989    }
990}
991
992/// Expands one `c99!`-style invocation.
993///
994/// The result is a private module holding the unit's items plus a glob
995/// re-export of it, so that two invocations in one Rust module are two
996/// namespaces and cannot collide; a unit that declares nothing expands to
997/// nothing at all.
998///
999/// # Where each pass runs
1000///
1001/// Capture and code generation must run on the caller's thread, because both
1002/// handle `proc_macro2::Span`s and those are deliberately not `Send`.
1003/// Everything in between — the lexer, the parser and semantic analysis —
1004/// works on byte offsets alone and runs on a thread with a large stack, where
1005/// the parser's recursion limit is the only thing that can stop it.
1006///
1007/// That leaves code generation recursing on the caller's stack, bounded by the
1008/// same limit: `parse::MAX_RECURSION_DEPTH` nested constructs. Code generation
1009/// frames are small (it allocates token streams rather than analysing
1010/// anything), and a debug build of the whole of capture plus code generation
1011/// for an expression nested to that limit fits inside a small fraction of a
1012/// megabyte — see the `codegen_of_deeply_nested_input_fits_in_a_small_stack`
1013/// test, which runs the entire expansion on a deliberately tiny thread.
1014///
1015/// # Errors
1016///
1017/// On failure the expansion holds a `compile_error!` per diagnostic *and* a
1018/// stub definition for every function whose signature was well formed, so that
1019/// Rust code calling those functions does not add a second layer of errors on
1020/// top of the real one.
1021pub fn expand(input: TokenStream, options: &Options) -> TokenStream {
1022    expand_with(input, options, &Origin::unknown())
1023}
1024
1025/// Expands one `c99!`-style invocation, with everything the host says about the
1026/// invocation itself.
1027///
1028/// This is what the procedural macros call, with [`Options::origin`]; see
1029/// [`Origin`] for what it adds and why [`expand`] — which is this with an origin
1030/// that says nothing — does not.
1031pub fn expand_with(input: TokenStream, options: &Options, origin: &Origin) -> TokenStream {
1032    generate_unit(analyze_with(input, options, origin), &[])
1033}
1034
1035/// Semantic analysis and code generation over a finished [`Analysis`].
1036///
1037/// `extra_tracking` names files the expansion must depend on that the
1038/// preprocessor did not read itself — the `.c` file [`expand_include`] was
1039/// pointed at, which is the unit's own text rather than a header of it.
1040fn generate_unit(analysis: Analysis, extra_tracking: &[PathBuf]) -> TokenStream {
1041    let Analysis {
1042        source,
1043        unit,
1044        mut diagnostics,
1045        expansions,
1046        user_headers,
1047        embedded_files,
1048        link_libraries,
1049        safe_functions,
1050        export,
1051        no_std,
1052        crate_path,
1053        // The target model the environment and the unit's own pragma settled
1054        // on; everything after the front end has to use these rather than the
1055        // options the caller handed in.
1056        options,
1057        ..
1058    } = analysis;
1059    let options = &options;
1060
1061    let unit_id = source.unit_id();
1062    let (mut program, mut sema_diagnostics) = on_large_stack(
1063        (unit, options.clone(), unit_id),
1064        |(unit, options, unit_id)| sema::analyze(&unit, &options, unit_id),
1065    );
1066    expansions.annotate(&mut sema_diagnostics);
1067    diagnostics.extend(sema_diagnostics);
1068    program.link_libraries = link_libraries;
1069    program.export = export;
1070    program.no_std = no_std;
1071    if let Some(path) = crate_path {
1072        program.crate_path = path;
1073    }
1074    // The pragmas are the preprocessor's, so the rules that depend on one can
1075    // only be checked now that the program and the pragmas are together.
1076    // `check_safe` also *applies* one, which is why it comes before code
1077    // generation reads `Function::safe`.
1078    let mut pragma_diagnostics = sema::check_pragmas(&program);
1079    pragma_diagnostics.extend(sema::check_safe(&mut program, &safe_functions));
1080    expansions.annotate(&mut pragma_diagnostics);
1081    diagnostics.extend(pragma_diagnostics);
1082
1083    // Emitted whether or not the unit compiled: a header that is being fixed
1084    // is exactly the one whose next edit has to trigger a rebuild.
1085    let mut tracked = extra_tracking.to_vec();
1086    tracked.extend(user_headers);
1087    let mut out = rebuild_tracking(&tracked, &embedded_files);
1088    if diagnostics.has_errors() {
1089        out.extend(diagnostics.to_token_stream(&source.map));
1090        out.extend(codegen::generate_stubs(&program, &source.map, options));
1091    } else {
1092        out.extend(codegen::generate(&program, &source.map, options));
1093    }
1094    in_module(out, unit_id)
1095}
1096
1097/// Wraps an expansion in a private module of its own, re-exported by a glob.
1098///
1099/// A translation unit is a namespace, and two of them written in one Rust
1100/// module are two namespaces: both may `#include "point.h"`, and each has to
1101/// generate the `struct Point` its own code refers to. Two `struct Point`
1102/// items side by side are `E0428`; two modules each holding one, glob
1103/// re-exported, are not — a glob re-export only conflicts when a name it
1104/// exports is *used* ambiguously, and only from Rust, which is exactly the
1105/// case where the user has to say which one they mean. An ordinary Rust `mod`
1106/// around the invocation is what they say it with, and what gives the unit's
1107/// items a path of their own.
1108///
1109/// The module is what makes C's own hygiene work too: a `static` function is
1110/// private to it, as C says it is, while everything with external linkage is
1111/// `pub` and glob re-exported into the module the invocation is written in, so
1112/// that Rust calls it by the name its author gave it.
1113///
1114/// There is no `use super::*`: C code never refers to a Rust item.
1115///
1116/// # The lint exemptions
1117///
1118/// The module's head carries the one `#![allow(…)]` the whole expansion
1119/// needs. A faithful translation of C trips a great many of Rust's lints, and
1120/// not one of them says anything about the C the user wrote: a parameter the
1121/// function never reads, a parenthesis C needed and Rust does not, a name
1122/// that is not `snake_case`, two declarations of one symbol that do not
1123/// match, a comparison that is always true, a statement after a `return`, an
1124/// `extern` signature Rust calls improper, arithmetic that overflows in a
1125/// branch that never runs. Clippy's lints are in the list for the same
1126/// reason. `unknown_lints` comes first so that a compiler that has not heard
1127/// of one of the newer names — `invalid_runtime_symbol_definitions`, say —
1128/// does not warn about the list itself.
1129///
1130/// It is an *inner* attribute, which is why it has to come before anything
1131/// else in the body; everything the unit generates is inside this module, and
1132/// lint levels are inherited, so one attribute covers every item, however
1133/// deeply nested, whatever the crate root denies, and for clippy as much as
1134/// for `rustc`.
1135///
1136/// One attribute per unit rather than one per item is what makes it
1137/// affordable. The list is 585 bytes and `#include <zlib.h>` generates 419
1138/// items, so the same exemption repeated on each of them was 83% of that
1139/// expansion — 245 KB of 294 KB — parsed by `rustc` and by rust-analyzer on
1140/// every build.
1141fn in_module(items: TokenStream, unit_id: u64) -> TokenStream {
1142    if items.is_empty() {
1143        // An empty translation unit expands to nothing at all, rather than to
1144        // an empty module and a glob re-export of it.
1145        return items;
1146    }
1147    let span = Span::call_site();
1148    let ident = Ident::new(&format!("__cinrs_unit_{:08x}", unit_id as u32), span);
1149    quote! {
1150        mod #ident {
1151            #![allow(
1152                unknown_lints,
1153                arithmetic_overflow,
1154                clashing_extern_declarations,
1155                dead_code,
1156                improper_ctypes,
1157                improper_ctypes_definitions,
1158                invalid_runtime_symbol_definitions,
1159                non_camel_case_types,
1160                non_snake_case,
1161                non_upper_case_globals,
1162                overflowing_literals,
1163                static_mut_refs,
1164                suspicious_runtime_symbol_definitions,
1165                unconditional_panic,
1166                unpredictable_function_pointer_comparisons,
1167                unreachable_code,
1168                unreachable_patterns,
1169                unused_assignments,
1170                unused_braces,
1171                unused_comparisons,
1172                unused_labels,
1173                unused_mut,
1174                unused_parens,
1175                unused_unsafe,
1176                unused_variables,
1177                clippy::all
1178            )]
1179
1180            #items
1181        }
1182        // The re-export needs one of its own, which the module's cannot
1183        // cover: `ambiguous_glob_reexports` is what a name two units both
1184        // export trips, and it is not a problem until Rust code uses that
1185        // name — which is an error of its own, with a message that says what
1186        // to do.
1187        #[allow(unknown_lints, ambiguous_glob_reexports, unused_imports)]
1188        pub use #ident::*;
1189    }
1190}
1191
1192/// `const _: &str = ::core::include_str!("…");` for every user header read,
1193/// and `include_bytes!` for every resource `#embed` read.
1194///
1195/// A procedural macro that reads a file has to tell the build system so, or
1196/// editing that file will not rebuild anything that included it. `include_str!`
1197/// is how a stable macro says it: `rustc` records the file as a dependency of
1198/// the crate, and Cargo re-runs the compilation when its timestamp moves. The
1199/// path is absolute so that it resolves the same from whichever module the
1200/// invocation is written in, and the item is anonymous (`const _`) so that any
1201/// number of them can coexist. An embedded resource is not text, so it takes
1202/// the byte-string form of the same trick.
1203///
1204/// Bundled headers are left out: they cannot change without the crate that
1205/// carries them changing, which Cargo already knows about.
1206///
1207/// `str` and `u8` take the [`core::primitive`] path for the same reason every
1208/// primitive the code generator writes does: these items go into the unit's
1209/// own module, where `typedef unsigned char u8;` may have put an alias of that
1210/// name.
1211fn rebuild_tracking(headers: &[PathBuf], embedded: &[PathBuf]) -> TokenStream {
1212    let span = Span::call_site();
1213    let mut out = TokenStream::new();
1214    for header in headers {
1215        let mut literal = Literal::string(&header.to_string_lossy());
1216        literal.set_span(span);
1217        let literal = TokenTree::Literal(literal);
1218        out.extend(quote! {
1219            const _: &::core::primitive::str = ::core::include_str!(#literal);
1220        });
1221    }
1222    for resource in embedded {
1223        let mut literal = Literal::string(&resource.to_string_lossy());
1224        literal.set_span(span);
1225        let literal = TokenTree::Literal(literal);
1226        out.extend(quote! {
1227            const _: &[::core::primitive::u8] = ::core::include_bytes!(#literal);
1228        });
1229    }
1230    out
1231}