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}