Skip to main content

rucc_pp/
directive.rs

1//! The directive engine: translation phase 4 over one file's preprocessing tokens.
2//!
3//! Design: `spec/05-preprocessor.md` section 5.4.
4//!
5//! A directive is a line whose first token is `#`. That is the whole of the recognition rule,
6//! and the two halves of it are both load bearing: `#` has to be first on the line, and the
7//! line is what the lexer says it is after splices and comments have been resolved, which is
8//! why `x /*\n*/ #define F 1` really does define `F`.
9//!
10//! The part that is easy to get wrong is skipped regions. Inside `#if 0` a line beginning with
11//! `#` still has to be recognised well enough to keep the conditional nesting balanced, and it
12//! must not be diagnosed for anything else. Real code puts prose, unbalanced quotes and future
13//! syntax inside `#if 0`, and a preprocessor that reports errors from there is unusable. So
14//! skipping looks at the directive name and nothing else, and only the seven conditional
15//! directives mean anything while it is going on.
16
17use std::collections::HashMap;
18use std::path::{Path, PathBuf};
19
20use rucc_base::{Interner, Symbol};
21use rucc_diag::{Diagnostic, FileId, SourceMapFull, Span};
22use rucc_gnu::Kind;
23use rucc_lex::{Options, PpToken, PpTokenKind, Punct, TokenFlags, tokenize};
24use rucc_session::{Found, IncludeForm};
25use rucc_target::TargetInfo;
26
27use crate::cond;
28use crate::embed;
29use crate::expand::Expander;
30use crate::include::{
31    Context, Frame, Header, Reader, directory_of, header_from_token, header_from_tokens, spelling,
32};
33use crate::macros::{Builtin, MacroTable, parse_define};
34use crate::predef::{BUILT_IN, COMMAND_LINE, Predef, built_in, command_line};
35use crate::token::Tok;
36
37/// Why a file that has already been read does not need reading again.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39enum Guard {
40    /// `#pragma once`, so the file is read once however many times it is named.
41    Once,
42    /// The whole file is wrapped in `#ifndef NAME`, and `NAME` is now defined, so reading it
43    /// again would produce nothing at all. This is the multiple-include optimization, and on
44    /// a real code base it is the difference between reading a header once and reading it a
45    /// few hundred times.
46    Macro(Symbol),
47}
48
49/// How far through the file the guard shape has been recognised.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51enum Scan {
52    /// Nothing has been seen yet, so the next line may open the guard.
53    Start,
54    /// Inside the conditional the file opened with.
55    Inside(Symbol),
56    /// The conditional closed and the file has to end here for the shape to hold.
57    Closed(Symbol),
58    /// Something else was seen, so this file has no guard.
59    No,
60}
61
62/// One `#if` and everything hanging off it.
63#[derive(Debug)]
64struct Cond {
65    /// Where the `#if` was written, so an unterminated one can point at it.
66    span: Span,
67    /// Whether tokens in the branch currently open are kept. Already accounts for whether the
68    /// enclosing region was live, so [`Preprocessor::live`] only has to look at the top.
69    live: bool,
70    /// Whether some branch of this chain has been taken. A later `#elif` is not evaluated once
71    /// this is set, which is what makes `#elif 1/0` after a taken branch legal.
72    taken: bool,
73    /// Whether the enclosing region was live.
74    enclosing_live: bool,
75    /// Whether `#else` has been seen, so a second one is an error.
76    seen_else: bool,
77}
78
79/// A `#line` directive, as read and as applied to the source map.
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct LineDirective {
82    /// Where the directive is.
83    pub span: Span,
84    /// The line number the next line is to be called.
85    pub line: u32,
86    /// The file name the following lines are to be called, if one was given.
87    pub file: Option<Symbol>,
88    /// How many tokens had been emitted when this was read, which is where it sits in the
89    /// stream.
90    ///
91    /// A position is not enough to say that. A file included from here is added to the source
92    /// map after this file, so its bytes come after every byte of this one, and the token
93    /// after the `#include` is at a lower position than the tokens of the header. `-E` has to
94    /// write a marker for a `#line` where the directive was written rather than where its
95    /// bytes are, and this is what says where that is.
96    pub at: usize,
97}
98
99/// Translation phase 4 over one file.
100///
101/// Holds the macro table and the conditional stack, so a single instance processes a whole
102/// translation unit and the definitions a header makes are visible after it.
103#[derive(Debug, Default)]
104pub struct Preprocessor {
105    macros: MacroTable,
106    expander: Expander,
107    diagnostics: Vec<Diagnostic>,
108    conds: Vec<Cond>,
109    lines: Vec<LineDirective>,
110    /// The files currently open, innermost last. Empty between runs.
111    stack: Vec<Frame>,
112    /// The files a line marker said were entered, innermost last, by the name in force when it
113    /// said so. This is the nesting a `2` flag claims to be leaving, and it is kept apart from
114    /// `stack` because a marker set describes a nesting the real files never had.
115    markers: Vec<String>,
116    /// Files that do not need reading again, and why. Keyed by what the file system calls the
117    /// file rather than by the name an include used, so that a header reached two ways is one
118    /// entry here.
119    seen: HashMap<PathBuf, Guard>,
120}
121
122impl Preprocessor {
123    /// A preprocessor with an empty macro table.
124    pub fn new() -> Preprocessor {
125        Preprocessor::default()
126    }
127
128    /// The macros defined so far.
129    pub fn macros(&self) -> &MacroTable {
130        &self.macros
131    }
132
133    /// The macro table, for the driver to seed with `-D` and the predefined set.
134    pub fn macros_mut(&mut self) -> &mut MacroTable {
135        &mut self.macros
136    }
137
138    /// Everything reported so far.
139    pub fn diagnostics(&self) -> &[Diagnostic] {
140        &self.diagnostics
141    }
142
143    /// Takes the diagnostics, leaving the preprocessor able to carry on.
144    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
145        std::mem::take(&mut self.diagnostics)
146    }
147
148    /// The `#line` directives seen, in the order they appeared.
149    ///
150    /// Each one is also applied, to the source map, as it is read. This is the record of them
151    /// rather than the mechanism: what a caller wants it for is reporting on the directives
152    /// themselves, and asking the map is how to find out where anything is.
153    pub fn line_directives(&self) -> &[LineDirective] {
154        &self.lines
155    }
156
157    /// Defines the predefined macro set, and then `-D` and `-U` from the command line.
158    ///
159    /// Called before [`Preprocessor::run`], because a predefined macro is a macro like any
160    /// other by the time the source file is read. The set arrives as two synthetic files
161    /// rather than as a list of definitions, so a diagnostic about one of them points at
162    /// `<built-in>` or `<command-line>` the way GCC's does, and so that `-dM` has something
163    /// to print. The reasoning is in `crate::predef`.
164    ///
165    /// # Errors
166    ///
167    /// When the source map has no room left for the two synthetic files.
168    pub fn predefine(
169        &mut self,
170        target: &TargetInfo,
171        opts: &Predef,
172        cx: &mut Context<'_>,
173    ) -> Result<(), SourceMapFull> {
174        let names = Names::new(cx.interner);
175        let file = self.synthetic(BUILT_IN, built_in(target, opts), cx, &names)?;
176        // The macros that cannot be written as a `#define` line, because what they stand for
177        // depends on where they are used. They go in after the generated file and before the
178        // command line, so that `-U__FILE__` takes one away the way it takes any other away.
179        // The origin is the start of `<built-in>`, which is where a warning about redefining
180        // one points, and which is the truthful answer to where they came from.
181        let start = cx.sources.file(file).start;
182        for (spelling, builtin) in Builtin::ALL {
183            let name = cx.interner.intern(spelling);
184            self.macros.define_builtin(name, builtin, Span::new(start, start));
185        }
186        let text = command_line(opts);
187        if !text.is_empty() {
188            self.synthetic(COMMAND_LINE, text, cx, &names)?;
189        }
190        Ok(())
191    }
192
193    /// Reads a file the compiler wrote rather than one the user did.
194    fn synthetic(
195        &mut self,
196        name: &str,
197        text: String,
198        cx: &mut Context<'_>,
199        names: &Names,
200    ) -> Result<FileId, SourceMapFull> {
201        let file = cx.sources.add(name, text.into_bytes())?;
202        let mut out = Vec::new();
203        // A frame, so that the guard scan and the include depth see the same shape they see
204        // for a real file. There is no directory, because `#include "x.h"` written in a
205        // synthetic file has nowhere of its own to look.
206        let path = PathBuf::from(name);
207        let id = cx.fs.identity(&path);
208        self.stack.push(Frame { at: Span::DUMMY, path, id, dir: None, next: 0 });
209        self.process(file, &mut out, cx, names);
210        self.stack.clear();
211        debug_assert!(out.is_empty(), "{name} is directives only and produces no tokens");
212        Ok(file)
213    }
214
215    /// Runs phase 4 over `file` and everything it includes.
216    ///
217    /// The result is the tokens that survived the conditionals, with macros expanded. Nothing
218    /// is thrown away silently: an unterminated `#if` and a stray `#endif` are both reported.
219    pub fn run(&mut self, file: FileId, cx: &mut Context<'_>) -> Vec<Tok> {
220        let names = Names::new(cx.interner);
221        let mut out = Vec::new();
222        let name = cx.sources.file(file).name.clone();
223        let dir = directory_of(&name);
224        // The file named on the command line was not found through the search path, so an
225        // `#include_next` written in it starts at the top rather than partway down.
226        let path = PathBuf::from(name);
227        let id = cx.fs.identity(&path);
228        self.stack.push(Frame { at: Span::DUMMY, path, id, dir, next: 0 });
229        self.process(file, &mut out, cx, &names);
230        self.stack.clear();
231        out
232    }
233
234    /// Reads one file, appending what survives to `out`.
235    fn process(&mut self, file: FileId, out: &mut Vec<Tok>, cx: &mut Context<'_>, names: &Names) {
236        // The bytes are taken out of the map by sharing rather than by borrowing, because the
237        // rest of this function needs the map back to add an included file to it.
238        let bytes = cx.sources.file(file).shared_bytes();
239        let start = cx.sources.file(file).start;
240        let mut reader = Reader::new(bytes.as_slice(), start, cx.lex);
241        let depth_on_entry = self.conds.len();
242        // Consecutive text lines are expanded as one run rather than line by line, because a
243        // function-like macro invocation may span lines. It may not span a directive, which is
244        // undefined behaviour, so a directive is where the run ends.
245        let mut text: Vec<Tok> = Vec::new();
246        let mut body: Vec<PpToken> = Vec::new();
247        let mut scan = Scan::Start;
248
249        loop {
250            let was_live = self.live();
251            let first = reader.next(cx.interner);
252            if first.is_eof() {
253                break;
254            }
255            if is_directive(first) {
256                self.flush(&mut text, out, cx, names);
257                body.clear();
258                let name_tok = reader.next(cx.interner);
259                // The null directive. A line of just `#` is legal and does nothing, and there
260                // is a surprising amount of it in real headers as a visual separator.
261                if name_tok.is_eof() || name_tok.flags.has(TokenFlags::START_OF_LINE) {
262                    reader.put_back(name_tok);
263                    continue;
264                }
265                body.push(name_tok);
266                // The header name has to be scanned here or not at all: `<stdio.h>` and a run
267                // of comparisons are the same bytes, and once the line has been scanned the
268                // other way the difference is gone. Not in a skipped region, because scanning
269                // one there can report an unterminated name that nobody asked about.
270                if was_live && is_include(ident_of(&name_tok), names) {
271                    if let Some(header) = reader.header_name(cx.interner) {
272                        body.push(header);
273                    }
274                }
275                reader.line(cx.interner, &mut body);
276                let opens =
277                    matches!(scan, Scan::Start).then(|| guard_opener(&body, names)).flatten();
278                self.directive(&body, first.span, out, cx, names);
279                scan = match scan {
280                    // The guard has to be the first line of the file and it has to open a
281                    // conditional, which is why the depth is checked after the dispatch
282                    // rather than the directive name being trusted on its own.
283                    Scan::Start => match opens {
284                        Some(name) if self.conds.len() == depth_on_entry + 1 => Scan::Inside(name),
285                        _ => Scan::No,
286                    },
287                    Scan::Inside(name) if self.conds.len() == depth_on_entry => Scan::Closed(name),
288                    Scan::Inside(name) => Scan::Inside(name),
289                    Scan::Closed(_) | Scan::No => Scan::No,
290                };
291            } else {
292                body.clear();
293                reader.line(cx.interner, &mut body);
294                if self.live() {
295                    // A run of text lines is expanded in one go, and a `_Pragma` is a directive
296                    // wearing an operator's clothes: `pop_macro` changes what the names after it
297                    // mean. So a line that spells one is expanded on its own, or the line after a
298                    // pop would go through the expander in the same batch as the line before it
299                    // and would still see the definition the pop was there to undo.
300                    let operator = ident_of(&first) == Some(names.pragma_op)
301                        || body.iter().any(|t| ident_of(t) == Some(names.pragma_op));
302                    if operator {
303                        self.flush(&mut text, out, cx, names);
304                    }
305                    text.push(Tok::new(first));
306                    text.extend(body.iter().copied().map(Tok::new));
307                    if operator {
308                        self.flush(&mut text, out, cx, names);
309                    }
310                }
311                // A token outside the guard is a token that would be produced twice.
312                if !matches!(scan, Scan::Inside(_)) {
313                    scan = Scan::No;
314                }
315            }
316            // What the lexer complained about while reading that line. A skipped region keeps
317            // its complaints to itself, for the same reason it keeps its directives to itself.
318            let complaints = reader.take_diagnostics();
319            if was_live || self.live() {
320                self.diagnostics.extend(complaints);
321            }
322        }
323        self.flush(&mut text, out, cx, names);
324        self.diagnostics.extend(reader.take_diagnostics());
325
326        // The guard only counts if the macro really did get defined. A file that opens with
327        // `#ifndef X` and never defines `X` is a file that has to be read again.
328        if let Scan::Closed(name) = scan {
329            if self.macros.is_defined(name) {
330                if let Some(frame) = self.stack.last() {
331                    self.seen.entry(frame.id.clone()).or_insert(Guard::Macro(name));
332                }
333            }
334        }
335
336        // A file may not close a conditional it did not open. GCC reports this at the `#if`,
337        // which is the line the user has to go and look at.
338        for cond in self.conds.drain(depth_on_entry..) {
339            self.diagnostics
340                .push(Diagnostic::error("unterminated `#if`", cond.span).with_code("E0330"));
341        }
342    }
343
344    /// Whether tokens are currently being kept.
345    fn live(&self) -> bool {
346        self.conds.last().is_none_or(|c| c.live)
347    }
348
349    /// Expands a run of text lines and appends it to the output.
350    fn flush(
351        &mut self,
352        text: &mut Vec<Tok>,
353        out: &mut Vec<Tok>,
354        cx: &mut Context<'_>,
355        names: &Names,
356    ) {
357        if text.is_empty() {
358            return;
359        }
360        let taken = std::mem::take(text);
361        let expanded = self.expander.expand_toks(taken, &self.macros, cx.interner, cx.sources);
362        self.diagnostics.append(&mut self.expander.take_diagnostics());
363        // To GCC and clang the `__has_*` family are builtin macros rather than something the
364        // conditional parser knows about, so they answer in ordinary text too. After expansion
365        // and not before it, because a macro is allowed to expand to a call of one and because
366        // the operand is expanded first, which is what happens on a `#if` line as well.
367        let expanded = self.resolve_has(expanded, cx, names, Pass::Text);
368        self.pragma_operator(expanded, out, cx.interner, names);
369    }
370
371    /// Dispatches one directive. `body` is the line after the `#`.
372    fn directive(
373        &mut self,
374        body: &[PpToken],
375        hash: Span,
376        out: &mut Vec<Tok>,
377        cx: &mut Context<'_>,
378        names: &Names,
379    ) {
380        let Some(first) = body.first().copied() else {
381            return;
382        };
383        let name = ident_of(&first);
384        let rest = &body[1..];
385
386        // Conditionals are handled whether or not the region is live, because the nesting has
387        // to stay balanced through a skipped block.
388        if name == Some(names.r#if) {
389            let value = self.live() && self.eval(rest, hash, cx, names);
390            self.open(hash, value);
391            return;
392        }
393        if name == Some(names.ifdef) || name == Some(names.ifndef) {
394            let want = name == Some(names.ifdef);
395            let value = self.live() && self.defined_check(rest, hash, want, names);
396            self.open(hash, value);
397            return;
398        }
399        if name == Some(names.elif) || name == Some(names.elifdef) || name == Some(names.elifndef) {
400            self.elif(name, rest, hash, cx, names);
401            return;
402        }
403        if name == Some(names.r#else) {
404            self.branch_else(rest, hash);
405            return;
406        }
407        if name == Some(names.endif) {
408            self.endif(rest, hash);
409            return;
410        }
411        if !self.live() {
412            // Everything else inside a skipped region is text, not a directive. `#error` in
413            // the branch that was not taken must not fire, and `# 42 "f.c"` from another
414            // preprocessor must not be diagnosed.
415            return;
416        }
417
418        // A `#` and a number is a GNU line marker rather than a directive whose name happens to
419        // be missing, and it is what `-E` output is full of, so it is answered before anything
420        // asks what the directive is called.
421        if name.is_none() && decimal(&first, cx.interner).is_some() {
422            self.line_marker(body, hash, out.len(), cx);
423            return;
424        }
425
426        let interner = &mut *cx.interner;
427        if name == Some(names.define) {
428            let (def, diagnostics) = parse_define(rest, interner);
429            self.diagnostics.extend(diagnostics);
430            if let Some(def) = def {
431                if let Some(problem) = self.macros.define(def, interner) {
432                    self.diagnostics.push(problem);
433                }
434            }
435        } else if name == Some(names.undef) {
436            self.undef(rest, hash, interner);
437        } else if name == Some(names.error) || name == Some(names.warning) {
438            self.message(rest, hash, name == Some(names.error), interner);
439        } else if name == Some(names.line) {
440            self.line(rest, hash, out.len(), cx);
441        } else if name == Some(names.pragma) {
442            // `#pragma once` is answered here and does not reach the output, because it is a
443            // question about the file rather than something a later phase can act on.
444            // Everything else is passed through unchanged, which is what `-E` has to print
445            // and what a later phase looking for `#pragma pack` will read. Inventing an
446            // internal representation now, with no consumer, would only be a thing to
447            // migrate later.
448            if rest.len() == 1 && ident_of(&rest[0]) == Some(names.once) {
449                self.pragma_once(rest[0].span);
450            } else if !self.macro_stack_pragma(rest, hash, interner, names) {
451                self.pass_through(body, hash, out);
452            }
453        } else if name == Some(names.include) || name == Some(names.include_next) {
454            self.include(rest, hash, name == Some(names.include_next), out, cx, names);
455        } else if name == Some(names.embed) {
456            self.embed(rest, hash, out, cx);
457        } else {
458            self.diagnostics.push(
459                Diagnostic::error("invalid preprocessing directive", first.span).with_code("E0332"),
460            );
461        }
462    }
463
464    /// Answers `#pragma push_macro("X")` and `#pragma pop_macro("X")`, or says it is not one.
465    ///
466    /// These are the two pragmas that act on the macro table, so this phase is the only one that
467    /// can answer them, and like `#pragma once` they do not reach the output: gcc consumes them
468    /// and a later phase given one could not do anything with it. That is what clang's
469    /// `__clang_cuda_complex_builtins.h` needs, which pushes `__DEVICE__`, redefines it for the
470    /// file and pops it at the end.
471    ///
472    /// The `GCC` namespaced spelling is deliberately not accepted, because gcc does not accept
473    /// it either: `#pragma GCC push_macro("X")` is passed through and does nothing, and matching
474    /// that matters more than the spelling looking symmetric with the pragmas that do take it.
475    fn macro_stack_pragma(
476        &mut self,
477        rest: &[PpToken],
478        at: Span,
479        interner: &mut Interner,
480        names: &Names,
481    ) -> bool {
482        let which = match rest.first().and_then(ident_of) {
483            Some(name) if name == names.push_macro => names.push_macro,
484            Some(name) if name == names.pop_macro => names.pop_macro,
485            _ => return false,
486        };
487        let word = if which == names.push_macro { "push_macro" } else { "pop_macro" };
488        // Once the word is recognised the line is one of these whatever follows it, so a line
489        // that is not the shape is an error rather than something to pass through. gcc says the
490        // same thing, and warns about anything after the closing parenthesis the way it warns
491        // about anything after any other directive.
492        let [_, open, text, close, extra @ ..] = rest else {
493            self.invalid_pragma(word, at);
494            return true;
495        };
496        if open.punct() != Some(Punct::LParen)
497            || text.kind != PpTokenKind::StringLit
498            || close.punct() != Some(Punct::RParen)
499        {
500            self.invalid_pragma(word, at);
501            return true;
502        }
503        self.extra_tokens(extra, "#pragma");
504        // A string that does not spell one identifier names no macro, and gcc neither complains
505        // about it nor does anything with it. `push_macro("a b")` is quietly nothing, which is
506        // worth matching rather than improving on: a header that has one is a header that has
507        // been building against gcc for years.
508        let Some(name) = identifier_in(*text, interner) else {
509            return true;
510        };
511        if which == names.push_macro {
512            self.macros.push_macro(name);
513        } else {
514            self.macros.pop_macro(name);
515        }
516        true
517    }
518
519    fn invalid_pragma(&mut self, word: &str, at: Span) {
520        self.diagnostics.push(
521            Diagnostic::error(format!("invalid `#pragma {word}` directive"), at).with_code("E0672"),
522        );
523    }
524
525    /// Records that the file currently being read asked to be read only once.
526    fn pragma_once(&mut self, at: Span) {
527        // In the main file this is worth saying something about, since the file the user named
528        // is not one anything includes and the line usually means the user thought it was a
529        // header. It is still applied, because a file that includes itself is exactly where the
530        // line does work in a main file, and GCC both warns and applies it.
531        if self.stack.len() <= 1 {
532            self.diagnostics.push(
533                Diagnostic::warning("`#pragma once` in the main file", at).with_code("W0332"),
534            );
535        }
536        if let Some(frame) = self.stack.last() {
537            self.seen.insert(frame.id.clone(), Guard::Once);
538        }
539    }
540
541    /// Whether a file has already given everything it has to give.
542    fn skip(&self, id: &Path) -> bool {
543        match self.seen.get(id) {
544            Some(Guard::Once) => true,
545            Some(Guard::Macro(name)) => self.macros.is_defined(*name),
546            None => false,
547        }
548    }
549
550    /// Copies a directive line into the output, `#` included.
551    fn pass_through(&mut self, body: &[PpToken], hash: Span, out: &mut Vec<Tok>) {
552        let _ = self;
553        out.push(Tok::synthetic(
554            PpTokenKind::Punct(Punct::Hash),
555            None,
556            TokenFlags::START_OF_LINE,
557            hash,
558        ));
559        // The space between the hash and the word comes off, so that a directive written
560        // `#  pragma` inside a nest of conditionals, which is how glibc indents them, prints
561        // back as `#pragma`. gcc does the same, and the rest of the line keeps the spacing it
562        // was written with.
563        for (at, token) in body.iter().copied().enumerate() {
564            let mut token = Tok::new(token);
565            if at == 0 {
566                token.flags = token.flags.without(TokenFlags::LEADING_SPACE);
567            }
568            out.push(token);
569        }
570    }
571
572    /// Resolves an `#include` or `#include_next` and reads what it names.
573    fn include(
574        &mut self,
575        rest: &[PpToken],
576        hash: Span,
577        is_next: bool,
578        out: &mut Vec<Tok>,
579        cx: &mut Context<'_>,
580        names: &Names,
581    ) {
582        let Some(header) = self.header_of(rest, hash, cx) else {
583            return;
584        };
585        let (form, relative_to, from) = self.where_to_look(&header, is_next, cx);
586        let found = cx.search.resolve(cx.fs, &header.name, form, relative_to.as_deref(), from);
587        let Some(found) = found else {
588            let tried = cx.search.tried(&header.name, form, relative_to.as_deref(), from);
589            // Two ways to have looked nowhere. An absolute name is opened and not searched
590            // for, and a search path with nothing on it has nowhere to look. Saying the
591            // first when it was the second sends the reader after a path that is not there.
592            let where_looked = if tried.is_empty() && Path::new(&header.name).is_absolute() {
593                "the name is an absolute path, so the search path was not used".to_owned()
594            } else if tried.is_empty() {
595                "the include search path is empty".to_owned()
596            } else {
597                let list: Vec<String> =
598                    tried.iter().map(|d| d.to_string_lossy().into_owned()).collect();
599                format!("searched: {}", list.join(", "))
600            };
601            self.diagnostics.push(
602                Diagnostic::error(format!("`{}` file not found", header.name), hash)
603                    .with_code("E0341")
604                    .note(where_looked, hash),
605            );
606            return;
607        };
608        // The multiple-include optimization. A file wrapped in an include guard whose macro
609        // is now defined, or one that asked for `#pragma once`, would produce nothing, so it
610        // is not opened at all. On a real code base this is the difference between reading a
611        // header once and reading it a few hundred times.
612        let id = cx.fs.identity(&found.path);
613        if self.skip(&id) {
614            return;
615        }
616        if self.stack.len() >= cx.max_include_depth as usize {
617            let mut diagnostic =
618                Diagnostic::error("`#include` nested too deeply", hash).with_code("E0342").note(
619                    "a header that includes itself with no include guard is the usual cause",
620                    hash,
621                );
622            if let Some(outer) = self.stack.first().filter(|f| !f.at.is_dummy()) {
623                diagnostic = diagnostic.note("the outermost include is here", outer.at);
624            }
625            self.diagnostics.push(diagnostic);
626            return;
627        }
628        let added = cx.sources.add_shared(found.name.clone(), found.bytes.clone(), Some(hash));
629        let file = match added {
630            Ok(file) => file,
631            Err(full) => {
632                self.diagnostics.push(Diagnostic::error(full.to_string(), hash).with_code("E0344"));
633                return;
634            }
635        };
636        self.stack.push(Frame {
637            at: hash,
638            dir: found.path.parent().map(Path::to_path_buf),
639            id,
640            path: found.path,
641            next: found.next,
642        });
643        self.process(file, out, cx, names);
644        self.stack.pop();
645    }
646
647    /// Reads an `#embed` and puts the bytes of what it names into the output.
648    fn embed(&mut self, rest: &[PpToken], hash: Span, out: &mut Vec<Tok>, cx: &mut Context<'_>) {
649        let Some((header, params)) = self.embed_line(rest, hash, cx) else {
650            return;
651        };
652        let Some(found) = self.find(&header, false, cx) else {
653            self.diagnostics.push(
654                Diagnostic::error(format!("`{}` resource not found", header.name), hash)
655                    .with_code("E0341")
656                    .note("an `#embed` resource is looked for on the include path", hash),
657            );
658            return;
659        };
660        // The bytes are not added to the source map. Nothing will ever point a diagnostic
661        // into the middle of a PNG, and adding a few megabytes of binary to the map so that
662        // it can be sliced for a caret line nobody will print is the kind of cost that only
663        // shows up on the projects this directive exists for.
664        embed::tokens(found.bytes.as_slice(), &params, hash, cx.interner, out);
665    }
666
667    /// Splits an `#embed` line into the resource it names and the parameters after it.
668    fn embed_line(
669        &mut self,
670        rest: &[PpToken],
671        hash: Span,
672        cx: &mut Context<'_>,
673    ) -> Option<(Header, embed::Params)> {
674        if rest.is_empty() {
675            self.bad_header(hash);
676            return None;
677        }
678        let line: Vec<Tok> = rest.iter().copied().map(Tok::new).collect();
679        // A name the lexer already made a header name of is not expanded, exactly as with
680        // `#include`. A computed one has the whole line expanded, parameters included, which
681        // is a compromise: the end of the name cannot be found without expanding, and the
682        // parameter names would have to be found before expanding to protect them. A macro
683        // called `limit` in scope at an `#embed` is not a thing worth splitting the pass for.
684        let line = if line[0].kind == PpTokenKind::HeaderName {
685            line
686        } else {
687            let expanded = self.expander.expand_toks(line, &self.macros, cx.interner, cx.sources);
688            self.diagnostics.append(&mut self.expander.take_diagnostics());
689            expanded
690        };
691        let Some(used) = embed::header_length(&line) else {
692            self.bad_header(line.first().map_or(hash, |t| t.report_span()));
693            return None;
694        };
695        let header = if line[0].kind == PpTokenKind::HeaderName {
696            header_from_token(spelling(line[0], cx.interner))
697        } else {
698            let spellings: Vec<&str> =
699                line[..used].iter().map(|t| spelling(*t, cx.interner)).collect();
700            header_from_tokens(&spellings)
701        };
702        let Some(header) = header else {
703            self.bad_header(line[0].report_span());
704            return None;
705        };
706        let params = self.embed_params(&line[used..], hash, cx)?;
707        Some((header, params))
708    }
709
710    /// The parameter list of an `#embed`, or of the `__has_embed` that asks the same question.
711    fn embed_params(
712        &mut self,
713        line: &[Tok],
714        at: Span,
715        cx: &mut Context<'_>,
716    ) -> Option<embed::Params> {
717        let Preprocessor { expander, macros, diagnostics, .. } = self;
718        let sources = &mut *cx.sources;
719        let mut expand = |toks: Vec<Tok>, interner: &mut Interner| {
720            expander.expand_toks(toks, macros, interner, sources)
721        };
722        let params = embed::parse(line, at, cx.interner, diagnostics, &mut expand);
723        self.diagnostics.append(&mut self.expander.take_diagnostics());
724        params
725    }
726
727    /// Where a header written in the file being read is looked for.
728    ///
729    /// `#include_next` continues from the directory after the one the current file came from,
730    /// which is what glibc and the kernel use to wrap a system header with one of the same
731    /// name. It never looks next to the current file, because that directory is not on the
732    /// path and there would be nothing to continue past.
733    ///
734    /// `__has_include` has to ask the same question the directive would, so both go through
735    /// here. A header that answers yes and then fails to be found is the one outcome that
736    /// would make the operator useless.
737    fn where_to_look(
738        &self,
739        header: &Header,
740        is_next: bool,
741        cx: &Context<'_>,
742    ) -> (IncludeForm, Option<PathBuf>, usize) {
743        let form = if header.angled { IncludeForm::Angled } else { IncludeForm::Quoted };
744        let frame = self.stack.last();
745        let from = if is_next {
746            frame.map_or(0, |f| f.next).max(cx.search.start(form))
747        } else {
748            cx.search.start(form)
749        };
750        let relative_to = if is_next { None } else { frame.and_then(|f| f.dir.clone()) };
751        (form, relative_to, from)
752    }
753
754    /// Whether a header is there, which is all `__has_include` asks.
755    fn find(&self, header: &Header, is_next: bool, cx: &Context<'_>) -> Option<Found> {
756        let (form, relative_to, from) = self.where_to_look(header, is_next, cx);
757        cx.search.resolve(cx.fs, &header.name, form, relative_to.as_deref(), from)
758    }
759
760    /// The header name an include directive names, however it spelled it.
761    fn header_of(&mut self, rest: &[PpToken], hash: Span, cx: &mut Context<'_>) -> Option<Header> {
762        if let Some(first) = rest.first().copied() {
763            if first.kind == PpTokenKind::HeaderName {
764                let text = first.value.map_or("", |v| cx.interner.resolve(v));
765                let header = header_from_token(text);
766                if header.is_none() {
767                    self.bad_header(first.span);
768                }
769                self.extra_tokens(&rest[1..], "#include");
770                return header;
771            }
772        }
773        // The computed include, `#include MACRO`. The line is macro expanded and then has to
774        // look like a header name, which is the one place in the language where the spelling
775        // of a token matters after expansion.
776        if rest.is_empty() {
777            self.bad_header(hash);
778            return None;
779        }
780        let line: Vec<Tok> = rest.iter().copied().map(Tok::new).collect();
781        let expanded = self.expander.expand_toks(line, &self.macros, cx.interner, cx.sources);
782        self.diagnostics.append(&mut self.expander.take_diagnostics());
783        let spellings: Vec<&str> = expanded.iter().map(|t| spelling(*t, cx.interner)).collect();
784        let header = header_from_tokens(&spellings);
785        if header.is_none() {
786            let at = expanded.first().map_or(hash, |t| t.report_span());
787            self.bad_header(at);
788        }
789        header
790    }
791
792    /// The diagnostic for a `__has_*` operator whose operand is not an identifier.
793    fn bad_operand(&mut self, tok: Tok, at: Span, interner: &Interner) {
794        self.diagnostics.push(
795            Diagnostic::error(
796                format!("expected an identifier as the operand of `{}`", spelling(tok, interner)),
797                at,
798            )
799            .with_code("E0345"),
800        );
801    }
802
803    fn bad_header(&mut self, at: Span) {
804        self.diagnostics.push(
805            Diagnostic::error("expected a file name in `<>` or `\"\"`", at).with_code("E0343"),
806        );
807    }
808
809    /// Pushes a conditional whose first branch is or is not taken.
810    fn open(&mut self, span: Span, value: bool) {
811        let enclosing_live = self.live();
812        self.conds.push(Cond {
813            span,
814            live: enclosing_live && value,
815            taken: value,
816            enclosing_live,
817            seen_else: false,
818        });
819    }
820
821    fn elif(
822        &mut self,
823        name: Option<Symbol>,
824        rest: &[PpToken],
825        hash: Span,
826        cx: &mut Context<'_>,
827        names: &Names,
828    ) {
829        let Some(top) = self.conds.last() else {
830            self.stray("elif", hash);
831            return;
832        };
833        if top.seen_else {
834            self.diagnostics
835                .push(Diagnostic::error("`#elif` after `#else`", hash).with_code("E0333"));
836            return;
837        }
838        // Read what is needed before evaluating, because evaluation borrows the whole
839        // preprocessor to report into.
840        let (enclosing_live, already_taken) = (top.enclosing_live, top.taken);
841        let consider = enclosing_live && !already_taken;
842        let value = if !consider {
843            false
844        } else if name == Some(names.elif) {
845            self.eval(rest, hash, cx, names)
846        } else {
847            self.defined_check(rest, hash, name == Some(names.elifdef), names)
848        };
849        let top = self.conds.last_mut().expect("checked above and nothing popped");
850        top.live = consider && value;
851        top.taken = already_taken || value;
852    }
853
854    fn branch_else(&mut self, rest: &[PpToken], hash: Span) {
855        let Some(top) = self.conds.last_mut() else {
856            self.stray("else", hash);
857            return;
858        };
859        if top.seen_else {
860            self.diagnostics.push(Diagnostic::error("a second `#else`", hash).with_code("E0333"));
861            return;
862        }
863        top.live = top.enclosing_live && !top.taken;
864        top.taken = true;
865        top.seen_else = true;
866        let enclosing_live = top.enclosing_live;
867        if enclosing_live {
868            self.extra_tokens(rest, "#else");
869        }
870    }
871
872    fn endif(&mut self, rest: &[PpToken], hash: Span) {
873        if self.conds.pop().is_none() {
874            self.stray("endif", hash);
875            return;
876        }
877        if self.live() {
878            self.extra_tokens(rest, "#endif");
879        }
880    }
881
882    fn stray(&mut self, what: &str, hash: Span) {
883        self.diagnostics
884            .push(Diagnostic::error(format!("`#{what}` without `#if`"), hash).with_code("E0334"));
885    }
886
887    /// Warns about tokens after a directive that takes none.
888    ///
889    /// A warning rather than an error, because `#endif FOO` as a hand written comment is
890    /// everywhere in code written before `//` was portable.
891    fn extra_tokens(&mut self, rest: &[PpToken], what: &str) {
892        if let Some(first) = rest.first() {
893            self.diagnostics.push(
894                Diagnostic::warning(format!("extra tokens after `{what}`"), first.span)
895                    .with_code("W0330"),
896            );
897        }
898    }
899
900    /// Evaluates a `#if` or `#elif` expression.
901    fn eval(&mut self, rest: &[PpToken], hash: Span, cx: &mut Context<'_>, names: &Names) -> bool {
902        let line: Vec<Tok> = rest.iter().copied().map(Tok::new).collect();
903        // `defined X` is resolved before expansion, so that `#if defined FOO` does not depend
904        // on what `FOO` expands to. It is resolved again afterwards because a macro that
905        // expands to `defined(X)` is undefined behaviour that GCC supports and headers use.
906        // It goes first of all because `defined(__has_include)` is a question about the
907        // operator rather than a use of it.
908        let line = self.resolve_defined(line, cx.interner, names);
909        // `__has_include` is resolved before expansion too, and for a stronger reason: its
910        // operand is a header name, so expanding `<linux/version.h>` would turn `linux` into
911        // `1` on a target where that macro is predefined. The rest of the family take an
912        // identifier that GCC does expand, so they wait until afterwards.
913        let line = self.resolve_has(line, cx, names, Pass::Headers);
914        let line = self.expander.expand_toks(line, &self.macros, cx.interner, cx.sources);
915        self.diagnostics.append(&mut self.expander.take_diagnostics());
916        let line = self.resolve_defined(line, cx.interner, names);
917        let line = self.resolve_has(line, cx, names, Pass::Rest);
918        cond::evaluate(&line, cx.interner, &mut self.diagnostics, hash)
919    }
920
921    /// Replaces `__has_include(<x.h>)` and the rest of the family with what they answer.
922    ///
923    /// `pass` says which of the three positions is asking, and each of them answers a
924    /// different part of the family. See [`Pass`].
925    fn resolve_has(
926        &mut self,
927        line: Vec<Tok>,
928        cx: &mut Context<'_>,
929        names: &Names,
930        pass: Pass,
931    ) -> Vec<Tok> {
932        if !line.iter().any(|t| t.ident().is_some_and(|n| names.has.op(n).is_some())) {
933            return line;
934        }
935        let mut out = Vec::with_capacity(line.len());
936        let mut at = 0;
937        while at < line.len() {
938            let tok = line[at];
939            let op = tok.ident().and_then(|n| names.has.op(n));
940            let Some(op) = op.filter(|op| pass.answers(*op)) else {
941                if pass == Pass::Text && op.is_some_and(Op::is_header) {
942                    self.outside_a_directive(tok, cx);
943                }
944                out.push(tok);
945                at += 1;
946                continue;
947            };
948            let Some((operand, after)) = arguments(&line, at + 1) else {
949                // Reported in the pass after expansion and not in the one before it, because
950                // the operator is still there for that pass to find and one mistake is one
951                // diagnostic.
952                if pass != Pass::Headers {
953                    self.diagnostics.push(
954                        Diagnostic::error(
955                            format!("expected `(` after `{}`", spelling(tok, cx.interner)),
956                            tok.report_span(),
957                        )
958                        .with_code("E0345"),
959                    );
960                }
961                out.push(tok);
962                at += 1;
963                continue;
964            };
965            at = after;
966            // A number rather than a flag, because `__has_c_attribute` answers with the value
967            // the standard gives the attribute and a header compares that against a date.
968            let value = self.ask(op, operand, tok, cx);
969            let sym = cx.interner.intern(&value.to_string());
970            out.push(Tok::synthetic(PpTokenKind::Number, Some(sym), tok.flags, tok.report_span()));
971        }
972        out
973    }
974
975    /// Refuses one of the header operators used in ordinary text.
976    ///
977    /// Their operand is a header name, and outside a directive the line was scanned as
978    /// ordinary tokens, so `<stdio.h>` arrived as a chain of comparisons that no longer says
979    /// which of the two it was meant to be. GCC and clang both refuse it for that reason, and
980    /// a program that wants the answer in text can put the operator in a `#if` and define a
981    /// macro from it, which is what every header that needs one does anyway.
982    fn outside_a_directive(&mut self, tok: Tok, cx: &Context<'_>) {
983        self.diagnostics.push(
984            Diagnostic::error(
985                format!(
986                    "`{}` used outside of a preprocessing directive",
987                    spelling(tok, cx.interner)
988                ),
989                tok.report_span(),
990            )
991            .with_code("E0350"),
992        );
993    }
994
995    /// What one `__has_*` operator answers for one operand.
996    fn ask(&mut self, op: Op, operand: &[Tok], tok: Tok, cx: &mut Context<'_>) -> u32 {
997        let at = operand.first().map_or(tok.report_span(), |t| t.report_span());
998        match op {
999            Op::Include | Op::IncludeNext => {
1000                let spellings: Vec<&str> =
1001                    operand.iter().map(|t| spelling(*t, cx.interner)).collect();
1002                let Some(header) = header_from_tokens(&spellings) else {
1003                    self.bad_header(at);
1004                    return 0;
1005                };
1006                u32::from(self.find(&header, op == Op::IncludeNext, cx).is_some())
1007            }
1008            Op::Embed => {
1009                // Three answers, and the third one is the reason the operator exists. A
1010                // resource that is present but empty cannot be told from one that is missing
1011                // by a yes or no, and the two need different code: the empty one still needs
1012                // its `if_empty` written, the missing one needs a fallback.
1013                let Some(used) = embed::header_length(operand) else {
1014                    self.bad_header(at);
1015                    return 0;
1016                };
1017                let header = if operand[0].kind == PpTokenKind::HeaderName {
1018                    header_from_token(spelling(operand[0], cx.interner))
1019                } else {
1020                    let spellings: Vec<&str> =
1021                        operand[..used].iter().map(|t| spelling(*t, cx.interner)).collect();
1022                    header_from_tokens(&spellings)
1023                };
1024                let Some(header) = header else {
1025                    self.bad_header(at);
1026                    return 0;
1027                };
1028                // The parameters are read even though only `limit` and `gnu::offset` can
1029                // change the answer, because a misspelled parameter is the same mistake here
1030                // as it is on the directive and finding it only on the directive would mean
1031                // the guard passes and the embed it guards fails.
1032                let Some(params) = self.embed_params(&operand[used..], at, cx) else {
1033                    return 0;
1034                };
1035                match self.find(&header, false, cx) {
1036                    None => 0,
1037                    Some(found) => {
1038                        let taken = params.taken(found.bytes.as_slice().len() as u64);
1039                        if taken == 0 { 2 } else { 1 }
1040                    }
1041                }
1042            }
1043            Op::BuildingModule => {
1044                if attribute_name(operand, cx.interner).is_none() {
1045                    self.bad_operand(tok, at, cx.interner);
1046                }
1047                // Clang answers this with one only while it is compiling the module named
1048                // here, and we do not have modules, so the answer is always no. It is
1049                // recognised rather than left alone because clang's own `stddef.h` asks it
1050                // inside an `#if`, and an unknown identifier there leaves the parenthesised
1051                // operand behind as extra tokens, which fails the whole line rather than the
1052                // one operator.
1053                0
1054            }
1055            Op::Table(kind) => {
1056                let Some(name) = attribute_name(operand, cx.interner) else {
1057                    self.bad_operand(tok, at, cx.interner);
1058                    return 0;
1059                };
1060                match kind {
1061                    Kind::Attribute => rucc_gnu::has_attribute(name),
1062                    Kind::CAttribute => rucc_gnu::has_c_attribute(name),
1063                    Kind::Builtin => rucc_gnu::has_builtin(name),
1064                    Kind::Feature => rucc_gnu::has_feature(name),
1065                    Kind::Extension => rucc_gnu::has_extension(name),
1066                }
1067            }
1068        }
1069    }
1070
1071    /// Replaces `defined X` and `defined(X)` with `1` or `0`.
1072    fn resolve_defined(
1073        &mut self,
1074        line: Vec<Tok>,
1075        interner: &mut Interner,
1076        names: &Names,
1077    ) -> Vec<Tok> {
1078        if !line.iter().any(|t| t.ident() == Some(names.defined)) {
1079            return line;
1080        }
1081        let mut out = Vec::with_capacity(line.len());
1082        let mut at = 0;
1083        while at < line.len() {
1084            let tok = line[at];
1085            if tok.ident() != Some(names.defined) {
1086                out.push(tok);
1087                at += 1;
1088                continue;
1089            }
1090            let parenthesised = line.get(at + 1).is_some_and(|t| t.is(Punct::LParen));
1091            let name_at = if parenthesised { at + 2 } else { at + 1 };
1092            let name = line.get(name_at).and_then(|t| t.ident());
1093            let Some(name) = name else {
1094                self.diagnostics.push(
1095                    Diagnostic::error("`defined` without a macro name", tok.report_span())
1096                        .with_code("E0335"),
1097                );
1098                out.push(tok);
1099                at += 1;
1100                continue;
1101            };
1102            at = name_at + 1;
1103            if parenthesised {
1104                if line.get(at).is_some_and(|t| t.is(Punct::RParen)) {
1105                    at += 1;
1106                } else {
1107                    self.diagnostics.push(
1108                        Diagnostic::error("expected `)` after `defined`", tok.report_span())
1109                            .with_code("E0335"),
1110                    );
1111                }
1112            }
1113            // A header asks `#ifdef __has_include` before using it, because the operator is
1114            // newer than some of the compilers it has to build under. It is not a macro, but
1115            // the question being asked is whether the name means something, and it does.
1116            let value = self.macros.is_defined(name) || names.has.op(name).is_some();
1117            out.push(number(value, tok.flags, tok.report_span(), interner));
1118        }
1119        out
1120    }
1121
1122    /// The body of `#ifdef`, `#ifndef`, `#elifdef` and `#elifndef`.
1123    fn defined_check(
1124        &mut self,
1125        rest: &[PpToken],
1126        hash: Span,
1127        want_defined: bool,
1128        names: &Names,
1129    ) -> bool {
1130        let Some(name) = rest.first().and_then(ident_of) else {
1131            self.diagnostics.push(
1132                Diagnostic::error("expected a macro name", rest.first().map_or(hash, |t| t.span))
1133                    .with_code("E0336"),
1134            );
1135            return false;
1136        };
1137        self.extra_tokens(&rest[1..], if want_defined { "#ifdef" } else { "#ifndef" });
1138        let defined = self.macros.is_defined(name) || names.has.op(name).is_some();
1139        defined == want_defined
1140    }
1141
1142    fn undef(&mut self, rest: &[PpToken], hash: Span, interner: &Interner) {
1143        let Some(name) = rest.first().and_then(ident_of) else {
1144            self.diagnostics.push(
1145                Diagnostic::error("expected a macro name", rest.first().map_or(hash, |t| t.span))
1146                    .with_code("E0336"),
1147            );
1148            return;
1149        };
1150        // The standard reserves these and GCC refuses to let them go, because code that
1151        // undefines `__FILE__` and then uses it is broken in a way that is very hard to see.
1152        let text = interner.resolve(name);
1153        if text == "defined" || text.starts_with("__STDC_") {
1154            self.diagnostics.push(
1155                Diagnostic::error(format!("`{text}` cannot be undefined"), rest[0].span)
1156                    .with_code("E0337"),
1157            );
1158            return;
1159        }
1160        self.macros.undef(name);
1161        self.extra_tokens(&rest[1..], "#undef");
1162    }
1163
1164    /// `#error` and `#warning`. The message is the rest of the line, spelled back.
1165    fn message(&mut self, rest: &[PpToken], hash: Span, fatal: bool, interner: &Interner) {
1166        let text = spell_line(rest, interner);
1167        let span = rest.first().map_or(hash, |t| t.span.to(last_span(rest)));
1168        let diag = if fatal {
1169            Diagnostic::error(text, span).with_code("E0338")
1170        } else {
1171            Diagnostic::warning(text, span).with_code("W0331")
1172        };
1173        self.diagnostics.push(diag);
1174    }
1175
1176    /// `#line 42` and `#line 42 "file.c"`.
1177    ///
1178    /// The argument is macro expanded first, which is the one place a directive other than
1179    /// `#if` does that, and which exists because `#line __LINE__ + 1` is real code.
1180    fn line(&mut self, rest: &[PpToken], hash: Span, at: usize, cx: &mut Context<'_>) {
1181        let line: Vec<Tok> = rest.iter().copied().map(Tok::new).collect();
1182        let line = self.expander.expand_toks(line, &self.macros, cx.interner, cx.sources);
1183        self.diagnostics.append(&mut self.expander.take_diagnostics());
1184        let interner = &mut *cx.interner;
1185
1186        let number_text = line
1187            .first()
1188            .filter(|t| t.kind == PpTokenKind::Number)
1189            .and_then(|t| t.value)
1190            .map(|v| interner.resolve(v));
1191        let Some(parsed) = number_text.and_then(|t| t.parse::<u64>().ok()) else {
1192            self.diagnostics.push(
1193                Diagnostic::error(
1194                    "`#line` needs a decimal line number",
1195                    line.first().map_or(hash, |t| t.report_span()),
1196                )
1197                .with_code("E0339"),
1198            );
1199            return;
1200        };
1201        // 2147483647 is the largest line number the standard requires support for, and it is
1202        // also where every other compiler stops, so matching that keeps diagnostics comparable.
1203        if parsed == 0 || parsed > 2_147_483_647 {
1204            self.diagnostics.push(
1205                Diagnostic::error("`#line` number is out of range", line[0].report_span())
1206                    .with_code("E0339"),
1207            );
1208            return;
1209        }
1210
1211        let mut file = None;
1212        if let Some(second) = line.get(1) {
1213            if second.kind == PpTokenKind::StringLit {
1214                file = second.value;
1215            } else {
1216                self.diagnostics.push(
1217                    Diagnostic::error(
1218                        "`#line` file name must be a string literal",
1219                        second.report_span(),
1220                    )
1221                    .with_code("E0339"),
1222                );
1223                return;
1224            }
1225        }
1226        if let Some(extra) = line.get(2) {
1227            self.diagnostics.push(
1228                Diagnostic::warning("extra tokens after `#line`", extra.report_span())
1229                    .with_code("W0330"),
1230            );
1231        }
1232        #[expect(
1233            clippy::cast_possible_truncation,
1234            reason = "the range check above keeps this inside i32, let alone u32"
1235        )]
1236        let number = parsed as u32;
1237        self.lines.push(LineDirective { span: hash, line: number, file, at });
1238        let name = file.map(|v| destringize(cx.interner.resolve(v)));
1239        cx.sources.set_presumed(hash.lo, number, name);
1240    }
1241
1242    /// A GNU line marker: `# 42`, `# 42 "file.c"`, and either of those with flags after it.
1243    ///
1244    /// This is the form `-E` writes, so a preprocessed file handed back to the compiler is full
1245    /// of them, and a compiler that cannot read its own output is not much of a compiler. The
1246    /// directive is a `#` and a number rather than a `#` and a name, which is why it arrives
1247    /// here having failed to be anything else.
1248    ///
1249    /// It is `#line` with three differences. Nothing is macro expanded, because the tokens came
1250    /// from a preprocessor rather than from a person. Zero is a line number, since a generator
1251    /// counting from zero is allowed to say so and `#line 0` is an error only because somebody
1252    /// wrote it. And there may be flags: `1` for entering a file, `2` for returning from one,
1253    /// `3` for a system header and `4` for one whose contents are `extern "C"`. The last two
1254    /// say nothing this phase acts on. The first two are the nesting, and a `2` that does not
1255    /// name the file it claims to be returning to is ignored with a warning rather than
1256    /// applied, which is what gcc does and is the only honest answer to a marker set that does
1257    /// not describe a nesting anything was ever in.
1258    fn line_marker(&mut self, body: &[PpToken], hash: Span, at: usize, cx: &mut Context<'_>) {
1259        let Some(number) = decimal(&body[0], cx.interner) else { return };
1260        let mut rest = &body[1..];
1261        let mut file = None;
1262        if let Some(first) = rest.first().filter(|t| t.kind == PpTokenKind::StringLit) {
1263            file = first.value;
1264            rest = &rest[1..];
1265        }
1266
1267        let (mut entering, mut leaving) = (false, false);
1268        for flag in rest {
1269            match decimal(flag, cx.interner) {
1270                Some(1) => entering = true,
1271                Some(2) => leaving = true,
1272                Some(3 | 4) => {}
1273                _ => {
1274                    let text = spell_line(std::slice::from_ref(flag), cx.interner);
1275                    self.diagnostics.push(
1276                        Diagnostic::error(
1277                            format!("invalid flag `{text}` in line directive"),
1278                            flag.span,
1279                        )
1280                        .with_code("E0339"),
1281                    );
1282                    return;
1283                }
1284            }
1285        }
1286
1287        let name = file.map(|v| destringize(cx.interner.resolve(v)));
1288        if leaving {
1289            if let Some(name) = &name {
1290                if !self.leave_marker(name) {
1291                    self.diagnostics.push(
1292                        Diagnostic::warning(
1293                            format!("file `{name}` linemarker ignored due to incorrect nesting"),
1294                            last_span(body),
1295                        )
1296                        .with_code("W0330"),
1297                    );
1298                    return;
1299                }
1300            } else {
1301                self.markers.pop();
1302            }
1303        }
1304        if entering {
1305            let here = cx.sources.presumed(hash.lo).map(|loc| loc.name.to_owned());
1306            self.markers.push(here.unwrap_or_default());
1307        }
1308
1309        self.lines.push(LineDirective { span: hash, line: number, file, at });
1310        cx.sources.set_presumed(hash.lo, number, name);
1311    }
1312
1313    /// Unwinds the marker nesting to `name`, saying whether it was in it at all.
1314    ///
1315    /// GCC asks whether the file being returned to is the one directly outside, and this asks
1316    /// whether it is anywhere outside, because a marker set is generated and a generator that
1317    /// leaves out a return marker is common. Every `-E` that writes markers where its tokens
1318    /// are rather than where its files change writes such a set, this compiler's own included,
1319    /// since a header that contributes no tokens between two `#include` lines never gets a
1320    /// marker of its own. Answering that with a warning on every file would make the warning
1321    /// noise, and the nesting it describes is still enough to say what a `2` means.
1322    ///
1323    /// A name in neither the markers nor the real include stack is the one that is refused.
1324    /// That is the marker set that describes a nesting nothing was ever in, and gcc refuses it
1325    /// too, so `# 200 "xyz" 2` written at the top of a file is a warning in both compilers.
1326    fn leave_marker(&mut self, name: &str) -> bool {
1327        if let Some(at) = self.markers.iter().rposition(|outer| outer == name) {
1328            self.markers.truncate(at);
1329            return true;
1330        }
1331        // A marker set may begin partway down a real nesting it did not open, which is what a
1332        // header full of them looks like when it is included rather than compiled on its own.
1333        let found = self.stack.iter().rev().skip(1).any(|f| f.path.as_os_str() == name);
1334        if found {
1335            self.markers.clear();
1336        }
1337        found
1338    }
1339
1340    /// Applies the `_Pragma` operator to an expanded run and appends the result.
1341    ///
1342    /// `_Pragma("x")` is a pragma written as an expression, which is what makes a pragma
1343    /// usable from inside a macro. It is handled after expansion because the string it takes
1344    /// is very often produced by one.
1345    fn pragma_operator(
1346        &mut self,
1347        expanded: Vec<Tok>,
1348        out: &mut Vec<Tok>,
1349        interner: &mut Interner,
1350        names: &Names,
1351    ) {
1352        if !expanded.iter().any(|t| t.ident() == Some(names.pragma_op)) {
1353            out.extend(expanded);
1354            return;
1355        }
1356        let mut at = 0;
1357        // A pragma is a line, so whatever comes after one has to start a line, even when the
1358        // source wrote `_Pragma("x") int y;` all on one. Without this the `int` would read as
1359        // part of the pragma to anything that takes the line as the unit, which is what the
1360        // phase that turns these into tokens does.
1361        let mut ends_a_line = false;
1362        while at < expanded.len() {
1363            let mut tok = expanded[at];
1364            if tok.ident() != Some(names.pragma_op) {
1365                if ends_a_line {
1366                    tok.flags = tok.flags.with(TokenFlags::START_OF_LINE);
1367                    ends_a_line = false;
1368                }
1369                out.push(tok);
1370                at += 1;
1371                continue;
1372            }
1373            let open = expanded.get(at + 1).is_some_and(|t| t.is(Punct::LParen));
1374            let text = expanded.get(at + 2).filter(|t| t.kind == PpTokenKind::StringLit);
1375            let close = expanded.get(at + 3).is_some_and(|t| t.is(Punct::RParen));
1376            let (Some(text), true, true) = (text, open, close) else {
1377                self.diagnostics.push(
1378                    Diagnostic::error("`_Pragma` takes a single string literal", tok.report_span())
1379                        .with_code("E0340"),
1380                );
1381                out.push(tok);
1382                at += 1;
1383                continue;
1384            };
1385            let literal = text.value.map(|v| interner.resolve(v)).unwrap_or_default();
1386            let body = destringize(literal);
1387            self.emit_pragma(&body, tok, out, interner, names);
1388            ends_a_line = true;
1389            at += 4;
1390        }
1391    }
1392
1393    /// Turns destringized `_Pragma` text into the `# pragma ...` tokens a later phase reads.
1394    fn emit_pragma(
1395        &mut self,
1396        body: &str,
1397        at: Tok,
1398        out: &mut Vec<Tok>,
1399        interner: &mut Interner,
1400        names: &Names,
1401    ) {
1402        let span = at.report_span();
1403        let (tokens, diagnostics) = tokenize(body.as_bytes(), 0, Options::new(), interner);
1404        // The text came out of a string literal, so a span into it would point at bytes the
1405        // user cannot see. Every token reports at the `_Pragma` instead.
1406        self.diagnostics.extend(
1407            diagnostics
1408                .into_iter()
1409                .map(|d| Diagnostic::new(d.severity, d.message, span).with_code("E0340")),
1410        );
1411        let tokens: Vec<PpToken> = tokens.into_iter().filter(|t| !t.is_eof()).collect();
1412        // `_Pragma("push_macro(\"X\")")` is the same pragma written the other way, and the two
1413        // spellings have to mean the same thing because a macro that wants to save a name has no
1414        // other way to say it: a `#pragma` line cannot come out of a macro body.
1415        if self.macro_stack_pragma(&tokens, span, interner, names) {
1416            return;
1417        }
1418        out.push(Tok::synthetic(
1419            PpTokenKind::Punct(Punct::Hash),
1420            None,
1421            TokenFlags::START_OF_LINE,
1422            span,
1423        ));
1424        out.push(Tok::synthetic(PpTokenKind::Ident, Some(names.pragma), TokenFlags::EMPTY, span));
1425        // The tokens keep the spacing they were written with inside the string, so
1426        // `_Pragma("pack(push)")` prints back as `pack(push)` rather than `pack ( push )`.
1427        // Only the first one is forced apart, from the `pragma` before it.
1428        for (at, t) in tokens.into_iter().enumerate() {
1429            // Start of line has to come off: the line is the `#pragma` we just emitted, not
1430            // the inside of the string these came from.
1431            let spaced = at == 0 || t.flags.has(TokenFlags::LEADING_SPACE);
1432            let flags = if spaced {
1433                TokenFlags::EMPTY.with(TokenFlags::LEADING_SPACE)
1434            } else {
1435                TokenFlags::EMPTY
1436            };
1437            out.push(Tok::synthetic(t.kind, t.value, flags, span));
1438        }
1439    }
1440}
1441
1442/// The macro a file's opening line guards the whole file with, if the line has that shape.
1443///
1444/// `#ifndef NAME` and both spellings of `#if !defined NAME`, which between them are what
1445/// every header in glibc, musl and the kernel is wrapped in.
1446fn guard_opener(body: &[PpToken], names: &Names) -> Option<Symbol> {
1447    let name = ident_of(body.first()?)?;
1448    let rest = &body[1..];
1449    if name == names.ifndef {
1450        let [only] = rest else {
1451            return None;
1452        };
1453        return ident_of(only);
1454    }
1455    if name != names.r#if {
1456        return None;
1457    }
1458    let [bang, defined, tail @ ..] = rest else {
1459        return None;
1460    };
1461    if bang.punct() != Some(Punct::Bang) || ident_of(defined) != Some(names.defined) {
1462        return None;
1463    }
1464    match tail {
1465        [only] => ident_of(only),
1466        [open, only, close]
1467            if open.punct() == Some(Punct::LParen) && close.punct() == Some(Punct::RParen) =>
1468        {
1469            ident_of(only)
1470        }
1471        _ => None,
1472    }
1473}
1474
1475/// Whether a directive name is one that may be followed by a header name.
1476fn is_include(name: Option<Symbol>, names: &Names) -> bool {
1477    name == Some(names.include) || name == Some(names.include_next) || name == Some(names.embed)
1478}
1479
1480/// Whether this token opens a directive line.
1481fn is_directive(tok: PpToken) -> bool {
1482    tok.flags.has(TokenFlags::START_OF_LINE) && tok.punct() == Some(Punct::Hash)
1483}
1484
1485fn ident_of(tok: &PpToken) -> Option<Symbol> {
1486    match tok.kind {
1487        PpTokenKind::Ident => tok.value,
1488        _ => None,
1489    }
1490}
1491
1492fn last_span(tokens: &[PpToken]) -> Span {
1493    tokens.last().map_or(Span::DUMMY, |t| t.span)
1494}
1495
1496/// The value of `tok` when it is a plain decimal number a line can be called.
1497///
1498/// A preprocessing number is a wider thing than a number: `1.5`, `0x10` and `1f` are all one,
1499/// and none of them is a line. Nothing but digits is accepted, so `# 1.5` stays what it was
1500/// before this existed, which is a directive nobody recognises.
1501fn decimal(tok: &PpToken, interner: &Interner) -> Option<u32> {
1502    if tok.kind != PpTokenKind::Number {
1503        return None;
1504    }
1505    let text = interner.resolve(tok.value?);
1506    if text.is_empty() || !text.bytes().all(|b| b.is_ascii_digit()) {
1507        return None;
1508    }
1509    // 2147483647 is the largest line number the standard requires support for, and it is also
1510    // where every other compiler stops, so matching that keeps diagnostics comparable.
1511    text.parse::<u32>().ok().filter(|n| *n <= 2_147_483_647)
1512}
1513
1514/// A synthetic `1` or `0`.
1515fn number(value: bool, flags: TokenFlags, span: Span, interner: &mut Interner) -> Tok {
1516    let sym = interner.intern(if value { "1" } else { "0" });
1517    Tok::synthetic(PpTokenKind::Number, Some(sym), flags, span)
1518}
1519
1520/// Spells a directive's tokens back for an `#error` message.
1521fn spell_line(tokens: &[PpToken], interner: &Interner) -> String {
1522    let mut out = String::new();
1523    for (index, tok) in tokens.iter().enumerate() {
1524        if index > 0 && tok.flags.has(TokenFlags::LEADING_SPACE) {
1525            out.push(' ');
1526        }
1527        match tok.value {
1528            Some(sym) => out.push_str(interner.resolve(sym)),
1529            None => {
1530                if let Some(p) = tok.punct() {
1531                    out.push_str(p.as_str());
1532                }
1533            }
1534        }
1535    }
1536    out
1537}
1538
1539/// The single identifier a string literal spells, if that is all it spells.
1540///
1541/// The name a `push_macro` saves lives inside a string, so it is destringized and lexed rather
1542/// than read off a token. Anything that is not exactly one identifier names no macro.
1543fn identifier_in(text: PpToken, interner: &mut Interner) -> Option<Symbol> {
1544    let literal = interner.resolve(text.value?).to_string();
1545    let (tokens, _) = tokenize(destringize(&literal).as_bytes(), 0, Options::new(), interner);
1546    let mut real = tokens.into_iter().filter(|t| !t.is_eof());
1547    let first = real.next()?;
1548    if first.kind != PpTokenKind::Ident || real.next().is_some() {
1549        return None;
1550    }
1551    first.value
1552}
1553
1554/// Undoes what `#` would have done, per C23 6.10.10.
1555///
1556/// The `L` or `u8` prefix and the quotes come off, then `\"` becomes `"` and `\\` becomes `\`.
1557/// No other escape is touched, because no other escape was introduced.
1558fn destringize(literal: &str) -> String {
1559    let body = literal
1560        .trim_start_matches(['L', 'u', 'U', '8'])
1561        .strip_prefix('"')
1562        .and_then(|s| s.strip_suffix('"'))
1563        .unwrap_or(literal);
1564    let mut out = String::with_capacity(body.len());
1565    let mut chars = body.chars();
1566    while let Some(c) = chars.next() {
1567        if c != '\\' {
1568            out.push(c);
1569            continue;
1570        }
1571        match chars.next() {
1572            Some('"') => out.push('"'),
1573            Some('\\') => out.push('\\'),
1574            Some(other) => {
1575                out.push('\\');
1576                out.push(other);
1577            }
1578            None => out.push('\\'),
1579        }
1580    }
1581    out
1582}
1583
1584/// The parenthesised operand of a `__has_*` operator, and where the line carries on.
1585///
1586/// `None` when the next token is not `(`, which is the only shape the operators take. Nesting
1587/// is counted rather than stopping at the first `)`, so that `__has_include(HEADER(x))` after
1588/// expansion still finds the end of its own operand.
1589fn arguments(line: &[Tok], at: usize) -> Option<(&[Tok], usize)> {
1590    if !line.get(at)?.is(Punct::LParen) {
1591        return None;
1592    }
1593    let mut depth = 1u32;
1594    let mut end = at + 1;
1595    while end < line.len() {
1596        if line[end].is(Punct::LParen) {
1597            depth += 1;
1598        } else if line[end].is(Punct::RParen) {
1599            depth -= 1;
1600            if depth == 0 {
1601                return Some((&line[at + 1..end], end + 1));
1602            }
1603        }
1604        end += 1;
1605    }
1606    None
1607}
1608
1609/// The name `__has_attribute` and its relatives are asked about.
1610///
1611/// A bare identifier, or the scoped form `gnu::always_inline` that C23 gives the attributes
1612/// that came from GCC. The scope is dropped: `__has_c_attribute(gnu::x)` and
1613/// `__has_attribute(x)` are the same question, and the matrix has one row for it.
1614fn attribute_name<'i>(operand: &[Tok], interner: &'i Interner) -> Option<&'i str> {
1615    let name = match operand {
1616        [one] => one,
1617        [_, scope, name] if scope.is(Punct::ColonColon) => name,
1618        _ => return None,
1619    };
1620    name.ident().map(|sym| interner.resolve(sym))
1621}
1622
1623/// Which of the three sweeps over a line is resolving the `__has_*` operators.
1624///
1625/// A `#if` line is swept twice, once either side of macro expansion, because the two halves of
1626/// the family disagree about whether their operand may be expanded. A text line is swept once,
1627/// after expansion, and the half whose operand is a header name is refused there rather than
1628/// answered.
1629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1630enum Pass {
1631    /// Before expansion on a directive line, where only the header operators are answered.
1632    Headers,
1633    /// After expansion on a directive line, where everything left over is answered. That
1634    /// includes a header operator, which reaches here when a macro expanded to one.
1635    Rest,
1636    /// After expansion on a text line, where everything but the header operators is answered.
1637    Text,
1638}
1639
1640impl Pass {
1641    /// Whether this sweep is the one that answers `op`.
1642    fn answers(self, op: Op) -> bool {
1643        match self {
1644            Pass::Headers => op.is_header(),
1645            Pass::Rest => true,
1646            Pass::Text => !op.is_header(),
1647        }
1648    }
1649}
1650
1651/// Which `__has_*` operator a name is, and what answers it.
1652#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1653enum Op {
1654    /// `__has_include`, answered by looking for the header.
1655    Include,
1656    /// `__has_include_next`, the same question from further down the search path.
1657    IncludeNext,
1658    /// `__has_embed`, which answers with three values rather than two because a resource that
1659    /// exists and is empty is a case the program has to be able to tell apart.
1660    Embed,
1661    /// `__building_module`, which is always no because there are no modules.
1662    BuildingModule,
1663    /// The rest of the family, answered out of the matrix in `rucc-gnu`.
1664    Table(Kind),
1665}
1666
1667impl Op {
1668    /// Whether the operand is a header name, which must not be macro expanded.
1669    fn is_header(self) -> bool {
1670        matches!(self, Op::Include | Op::IncludeNext | Op::Embed)
1671    }
1672}
1673
1674/// The `__has_*` operators, interned once per file.
1675///
1676/// A short array rather than a map: there are nine of them, the comparison is on interned
1677/// symbols, and it is only reached for a line that mentions one.
1678struct HasOps {
1679    ops: [(Symbol, Op); 9],
1680    /// The lowest and the highest symbol in `ops`.
1681    ///
1682    /// Now that text lines are swept too, every identifier in the translation unit is offered
1683    /// to [`HasOps::op`], so the answer it almost always gives has to be cheap. These nine are
1684    /// interned before any file is read, so a name out of the source sorts above the range and
1685    /// one comparison turns it away.
1686    range: (Symbol, Symbol),
1687}
1688
1689impl HasOps {
1690    fn new(interner: &mut Interner) -> HasOps {
1691        let ops = [
1692            (interner.intern("__has_include"), Op::Include),
1693            (interner.intern("__has_include_next"), Op::IncludeNext),
1694            (interner.intern("__has_embed"), Op::Embed),
1695            (interner.intern("__has_attribute"), Op::Table(Kind::Attribute)),
1696            (interner.intern("__has_c_attribute"), Op::Table(Kind::CAttribute)),
1697            (interner.intern("__has_builtin"), Op::Table(Kind::Builtin)),
1698            (interner.intern("__has_feature"), Op::Table(Kind::Feature)),
1699            (interner.intern("__has_extension"), Op::Table(Kind::Extension)),
1700            (interner.intern("__building_module"), Op::BuildingModule),
1701        ];
1702        let mut range = (ops[0].0, ops[0].0);
1703        for &(sym, _) in &ops {
1704            range = (range.0.min(sym), range.1.max(sym));
1705        }
1706        HasOps { ops, range }
1707    }
1708
1709    /// The operator a name is, if it is one.
1710    #[inline]
1711    fn op(&self, name: Symbol) -> Option<Op> {
1712        if name < self.range.0 || name > self.range.1 {
1713            return None;
1714        }
1715        self.ops.iter().find(|(sym, _)| *sym == name).map(|(_, op)| *op)
1716    }
1717}
1718
1719/// The directive names and the two operators, interned once per file.
1720///
1721/// Comparing symbols rather than strings is the point: a directive line is recognised with
1722/// integer comparisons, and the identifiers were interned during the scan, so there is no
1723/// string work in the hot path.
1724struct Names {
1725    define: Symbol,
1726    undef: Symbol,
1727    r#if: Symbol,
1728    ifdef: Symbol,
1729    ifndef: Symbol,
1730    elif: Symbol,
1731    elifdef: Symbol,
1732    elifndef: Symbol,
1733    r#else: Symbol,
1734    endif: Symbol,
1735    line: Symbol,
1736    error: Symbol,
1737    warning: Symbol,
1738    pragma: Symbol,
1739    include: Symbol,
1740    include_next: Symbol,
1741    embed: Symbol,
1742    defined: Symbol,
1743    once: Symbol,
1744    push_macro: Symbol,
1745    pop_macro: Symbol,
1746    pragma_op: Symbol,
1747    has: HasOps,
1748}
1749
1750impl Names {
1751    fn new(interner: &mut Interner) -> Names {
1752        Names {
1753            define: interner.intern("define"),
1754            undef: interner.intern("undef"),
1755            r#if: interner.intern("if"),
1756            ifdef: interner.intern("ifdef"),
1757            ifndef: interner.intern("ifndef"),
1758            elif: interner.intern("elif"),
1759            elifdef: interner.intern("elifdef"),
1760            elifndef: interner.intern("elifndef"),
1761            r#else: interner.intern("else"),
1762            endif: interner.intern("endif"),
1763            line: interner.intern("line"),
1764            error: interner.intern("error"),
1765            warning: interner.intern("warning"),
1766            pragma: interner.intern("pragma"),
1767            include: interner.intern("include"),
1768            include_next: interner.intern("include_next"),
1769            embed: interner.intern("embed"),
1770            defined: interner.intern("defined"),
1771            once: interner.intern("once"),
1772            push_macro: interner.intern("push_macro"),
1773            pop_macro: interner.intern("pop_macro"),
1774            pragma_op: interner.intern("_Pragma"),
1775            has: HasOps::new(interner),
1776        }
1777    }
1778}
1779
1780#[cfg(test)]
1781mod tests {
1782    use rucc_diag::{Severity, SourceMap};
1783    use rucc_session::{MemoryFileSystem, SearchPath};
1784
1785    use super::*;
1786    use rucc_session::Std;
1787
1788    use crate::predef::Timestamp;
1789
1790    /// A whole file through phase 4, which is what almost every test here wants.
1791    ///
1792    /// The main file is always `/main.c`, so a quoted include with no search path set up
1793    /// finds a header the test put at `/name.h`.
1794    struct Run {
1795        interner: Interner,
1796        sources: SourceMap,
1797        fs: MemoryFileSystem,
1798        search: SearchPath,
1799        pp: Preprocessor,
1800    }
1801
1802    impl Run {
1803        fn new() -> Run {
1804            Run {
1805                interner: Interner::new(),
1806                sources: SourceMap::new(),
1807                fs: MemoryFileSystem::new(),
1808                search: SearchPath::new(),
1809                pp: Preprocessor::new(),
1810            }
1811        }
1812
1813        /// Puts a header where an include can find it.
1814        fn file(&mut self, path: &str, contents: &str) {
1815            self.fs.insert(path, contents.as_bytes().to_vec());
1816        }
1817
1818        /// Puts a resource where an `#embed` can find it. Bytes rather than text, because the
1819        /// whole point of the directive is the files that are not text.
1820        fn bytes(&mut self, path: &str, contents: &[u8]) {
1821            self.fs.insert(path, contents.to_vec());
1822        }
1823
1824        /// Adds a directory to the `-I` part of the search path.
1825        fn dir(&mut self, path: &str) {
1826            self.search.push_bracket(path);
1827        }
1828
1829        /// Defines the predefined set for a target, as the driver does before reading input.
1830        fn predefine(&mut self, triple: &str, opts: &Predef) {
1831            let target = TargetInfo::new(triple.parse().expect("a supported triple"));
1832            let mut cx =
1833                Context::new(&mut self.interner, &mut self.sources, &self.fs, &self.search);
1834            self.pp.predefine(&target, opts, &mut cx).expect("the map has room");
1835        }
1836
1837        /// The surviving tokens, spelled with one space wherever they were separated.
1838        fn go(&mut self, src: &str) -> String {
1839            self.go_named("/main.c", src)
1840        }
1841
1842        /// The surviving tokens themselves, for a test about a flag rather than a spelling.
1843        fn raw(&mut self, src: &str) -> Vec<Tok> {
1844            let file = self.sources.add("/main.c", src.as_bytes().to_vec()).expect("room");
1845            let mut cx =
1846                Context::new(&mut self.interner, &mut self.sources, &self.fs, &self.search);
1847            self.pp.run(file, &mut cx)
1848        }
1849
1850        /// The same, for a test that cares what the main file is called.
1851        fn go_named(&mut self, path: &str, src: &str) -> String {
1852            let file = self.sources.add(path, src.as_bytes().to_vec()).expect("the map has room");
1853            let out = {
1854                let mut cx =
1855                    Context::new(&mut self.interner, &mut self.sources, &self.fs, &self.search);
1856                self.pp.run(file, &mut cx)
1857            };
1858            let mut text = String::new();
1859            for (at, tok) in out.iter().enumerate() {
1860                let spaced = tok.flags.has(TokenFlags::LEADING_SPACE)
1861                    || tok.flags.has(TokenFlags::START_OF_LINE);
1862                if at > 0 && spaced {
1863                    text.push(' ');
1864                }
1865                match tok.kind {
1866                    PpTokenKind::Punct(p) => text.push_str(p.as_str()),
1867                    _ => text.push_str(
1868                        self.interner.resolve(tok.value.expect("every non-punctuator interns")),
1869                    ),
1870                }
1871            }
1872            text
1873        }
1874
1875        /// How many files were opened, main file included. A header that the guard
1876        /// optimization skipped never reaches the source map, so this is what says whether
1877        /// it was really skipped rather than read and thrown away.
1878        fn files(&self) -> usize {
1879            self.sources.files().len()
1880        }
1881
1882        fn messages(&mut self) -> Vec<String> {
1883            self.pp.take_diagnostics().into_iter().map(|d| d.message).collect()
1884        }
1885
1886        fn severities(&mut self) -> Vec<Severity> {
1887            self.pp.diagnostics().iter().map(|d| d.severity).collect()
1888        }
1889    }
1890
1891    fn clean(src: &str) -> String {
1892        let mut run = Run::new();
1893        let text = run.go(src);
1894        assert!(run.messages().is_empty(), "expected no diagnostics from {src:?}");
1895        text
1896    }
1897
1898    #[test]
1899    fn a_taken_branch_is_kept_and_the_other_is_not() {
1900        assert_eq!(clean("#if 1\nyes\n#else\nno\n#endif\n"), "yes");
1901        assert_eq!(clean("#if 0\nyes\n#else\nno\n#endif\n"), "no");
1902    }
1903
1904    #[test]
1905    fn ifdef_and_ifndef_ask_the_macro_table() {
1906        assert_eq!(clean("#define F 1\n#ifdef F\nyes\n#endif\n"), "yes");
1907        assert_eq!(clean("#ifdef F\nyes\n#endif\n"), "");
1908        assert_eq!(clean("#ifndef F\nyes\n#endif\n"), "yes");
1909        // C23 spells the two of them as `#elifdef` and `#elifndef` as well.
1910        assert_eq!(clean("#define F 1\n#if 0\na\n#elifdef F\nb\n#endif\n"), "b");
1911        assert_eq!(clean("#if 0\na\n#elifndef F\nb\n#endif\n"), "b");
1912    }
1913
1914    #[test]
1915    fn only_the_first_true_branch_of_a_chain_is_taken() {
1916        assert_eq!(clean("#if 0\na\n#elif 1\nb\n#elif 1\nc\n#else\nd\n#endif\n"), "b");
1917        assert_eq!(clean("#if 0\na\n#elif 0\nb\n#else\nc\n#endif\n"), "c");
1918    }
1919
1920    #[test]
1921    fn a_branch_after_one_that_was_taken_is_not_evaluated() {
1922        // `1/0` in a branch that cannot be reached is legal, and headers rely on it: the
1923        // guard that made the branch dead is often the thing that made the expression safe.
1924        assert_eq!(clean("#if 1\na\n#elif 1/0\nb\n#endif\n"), "a");
1925    }
1926
1927    #[test]
1928    fn a_skipped_region_is_not_read_for_anything_but_nesting() {
1929        // Prose, an unknown directive and a broken `#define` all have to pass silently.
1930        let src = "#if 0\nthis is not C at all\n#frobnicate\n#define\n#if 1\ninner\n#endif\n#endif\nafter\n";
1931        assert_eq!(clean(src), "after");
1932    }
1933
1934    #[test]
1935    fn nesting_inside_a_dead_branch_stays_balanced() {
1936        let src = "#if 0\n#ifdef X\na\n#else\nb\n#endif\n#else\nc\n#endif\n";
1937        assert_eq!(clean(src), "c");
1938    }
1939
1940    #[test]
1941    fn defined_works_in_both_spellings_and_before_expansion() {
1942        assert_eq!(clean("#define F 0\n#if defined F\nyes\n#endif\n"), "yes");
1943        assert_eq!(clean("#define F 0\n#if defined(F)\nyes\n#endif\n"), "yes");
1944        assert_eq!(clean("#if defined(F)\nyes\n#endif\n"), "");
1945        // `F` expands to 0, but `defined F` is answered before that happens, which is the
1946        // whole reason `defined` is resolved in a pass of its own.
1947        assert_eq!(clean("#define F 0\n#if defined F && !F\nyes\n#endif\n"), "yes");
1948    }
1949
1950    #[test]
1951    fn an_identifier_that_survived_expansion_is_zero() {
1952        assert_eq!(clean("#if NOT_DEFINED_ANYWHERE\nyes\n#else\nno\n#endif\n"), "no");
1953        assert_eq!(clean("#if !NOT_DEFINED_ANYWHERE\nyes\n#endif\n"), "yes");
1954    }
1955
1956    #[test]
1957    fn short_circuiting_keeps_a_guarded_expression_safe() {
1958        // The reason `&&` has to short circuit rather than merely produce the right answer:
1959        // the right hand side divides by zero when the guard is false.
1960        assert_eq!(clean("#if defined(F) && 1/F\nyes\n#else\nno\n#endif\n"), "no");
1961        assert_eq!(clean("#if 1 ? 2 : 1/0\nyes\n#endif\n"), "yes");
1962    }
1963
1964    #[test]
1965    fn the_operators_have_the_precedence_they_do_in_c() {
1966        assert_eq!(clean("#if 1 + 2 * 3 == 7\nyes\n#endif\n"), "yes");
1967        assert_eq!(clean("#if (1 + 2) * 3 == 9\nyes\n#endif\n"), "yes");
1968        assert_eq!(clean("#if 1 << 4 == 16\nyes\n#endif\n"), "yes");
1969        assert_eq!(clean("#if -8 / 3 == -2\nyes\n#endif\n"), "yes");
1970        assert_eq!(clean("#if (0xff & 0x0f) == 15\nyes\n#endif\n"), "yes");
1971    }
1972
1973    #[test]
1974    fn an_unsigned_operand_makes_the_whole_comparison_unsigned() {
1975        // The rule that catches everyone out in C catches them out here too, and a
1976        // preprocessor that quietly disagreed with the compiler would be worse than one that
1977        // is merely surprising.
1978        assert_eq!(clean("#if -1 < 0u\nyes\n#else\nno\n#endif\n"), "no");
1979        assert_eq!(clean("#if -1 < 0\nyes\n#else\nno\n#endif\n"), "yes");
1980    }
1981
1982    #[test]
1983    fn character_constants_evaluate() {
1984        assert_eq!(clean("#if 'A' == 65\nyes\n#endif\n"), "yes");
1985        assert_eq!(clean("#if '\\n' == 10\nyes\n#endif\n"), "yes");
1986    }
1987
1988    #[test]
1989    fn a_macro_is_expanded_before_the_expression_is_evaluated() {
1990        assert_eq!(clean("#define V 3\n#if V > 2\nyes\n#endif\n"), "yes");
1991        assert_eq!(clean("#define M(a) ((a) * 2)\n#if M(3) == 6\nyes\n#endif\n"), "yes");
1992    }
1993
1994    #[test]
1995    fn an_invocation_may_span_lines_within_a_run_of_text() {
1996        assert_eq!(clean("#define M(a, b) a + b\nM(1,\n2)\n"), "1 + 2");
1997    }
1998
1999    #[test]
2000    fn undef_removes_a_definition() {
2001        assert_eq!(clean("#define F 1\n#undef F\n#ifdef F\nyes\n#else\nno\n#endif\n"), "no");
2002        // Undefining something that was never defined is not an error, and configure scripts
2003        // emit it constantly.
2004        assert_eq!(clean("#undef NEVER_DEFINED\nok\n"), "ok");
2005    }
2006
2007    #[test]
2008    fn some_names_cannot_be_undefined() {
2009        let mut run = Run::new();
2010        run.go("#undef defined\n");
2011        assert_eq!(run.messages(), vec!["`defined` cannot be undefined".to_owned()]);
2012    }
2013
2014    #[test]
2015    fn error_reports_the_rest_of_the_line() {
2016        let mut run = Run::new();
2017        run.go("#if 0\n#error not this one\n#else\n#error unsupported target\n#endif\n");
2018        assert_eq!(run.messages(), vec!["unsupported target".to_owned()]);
2019    }
2020
2021    #[test]
2022    fn warning_is_a_warning() {
2023        let mut run = Run::new();
2024        run.go("#warning this is fine\n");
2025        assert_eq!(run.severities(), vec![Severity::Warning]);
2026        assert_eq!(run.messages(), vec!["this is fine".to_owned()]);
2027    }
2028
2029    #[test]
2030    fn an_unterminated_conditional_is_reported() {
2031        let mut run = Run::new();
2032        assert_eq!(run.go("#if 1\nyes\n"), "yes");
2033        assert_eq!(run.messages(), vec!["unterminated `#if`".to_owned()]);
2034    }
2035
2036    #[test]
2037    fn a_conditional_without_an_if_is_reported() {
2038        let mut run = Run::new();
2039        run.go("#endif\n");
2040        assert_eq!(run.messages(), vec!["`#endif` without `#if`".to_owned()]);
2041
2042        let mut run = Run::new();
2043        run.go("#if 1\n#else\n#else\n#endif\n");
2044        assert_eq!(run.messages(), vec!["a second `#else`".to_owned()]);
2045
2046        let mut run = Run::new();
2047        run.go("#if 1\n#else\n#elif 1\n#endif\n");
2048        assert_eq!(run.messages(), vec!["`#elif` after `#else`".to_owned()]);
2049    }
2050
2051    #[test]
2052    fn tokens_after_endif_are_a_warning_rather_than_an_error() {
2053        // `#endif FOO` as a hand written comment predates `//` being portable and there is a
2054        // great deal of it about. Refusing to compile it would be correct and useless.
2055        let mut run = Run::new();
2056        assert_eq!(run.go("#if 1\nyes\n#endif FOO\n"), "yes");
2057        assert_eq!(run.severities(), vec![Severity::Warning]);
2058        assert_eq!(run.messages(), vec!["extra tokens after `#endif`".to_owned()]);
2059    }
2060
2061    #[test]
2062    fn the_null_directive_does_nothing() {
2063        assert_eq!(clean("#\na\n#\nb\n"), "a b");
2064    }
2065
2066    #[test]
2067    fn an_unknown_directive_is_an_error_when_the_region_is_live() {
2068        let mut run = Run::new();
2069        run.go("#frobnicate\n");
2070        assert_eq!(run.messages(), vec!["invalid preprocessing directive".to_owned()]);
2071    }
2072
2073    #[test]
2074    fn line_is_recorded_for_the_source_map() {
2075        let mut run = Run::new();
2076        run.go("#line 42 \"other.c\"\n");
2077        assert!(run.messages().is_empty());
2078        let recorded = run.pp.line_directives();
2079        assert_eq!(recorded.len(), 1);
2080        assert_eq!(recorded[0].line, 42);
2081        let file = recorded[0].file.expect("a file name was given");
2082        assert_eq!(run.interner.resolve(file), "\"other.c\"");
2083    }
2084
2085    #[test]
2086    fn line_moves_what_line_and_file_the_lines_after_it_are_on() {
2087        let mut run = Run::new();
2088        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2089        assert_eq!(run.go("#line 1000\n__LINE__ __FILE__\n__LINE__\n"), "1000 \"/main.c\" 1001");
2090    }
2091
2092    #[test]
2093    fn a_line_marker_moves_the_lines_after_it_the_way_line_does() {
2094        let mut run = Run::new();
2095        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2096        assert_eq!(run.go("# 200 \"xyz\"\n__FILE__ __LINE__\n"), "\"xyz\" 200");
2097    }
2098
2099    #[test]
2100    fn a_line_marker_with_no_name_leaves_the_name_alone() {
2101        let mut run = Run::new();
2102        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2103        assert_eq!(run.go("# 20\n__FILE__ __LINE__\n"), "\"/main.c\" 20");
2104    }
2105
2106    #[test]
2107    fn a_line_marker_may_say_line_zero() {
2108        // `#line 0` is an error and this is not, because a marker is written by a program and a
2109        // program counting from zero is allowed to say so.
2110        let mut run = Run::new();
2111        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2112        assert_eq!(run.go("# 0 \"xyz\"\n__LINE__\n"), "0");
2113    }
2114
2115    #[test]
2116    fn entering_and_returning_are_a_nesting_the_marker_flags_keep() {
2117        let mut run = Run::new();
2118        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2119        let text =
2120            run.go("# 200 \"xyz\" 1\n__FILE__\n# 5 \"/main.c\" 2\n__FILE__ __LINE__\n# 9 3 4\n");
2121        assert_eq!(text, "\"xyz\" \"/main.c\" 5");
2122        assert!(run.messages().is_empty());
2123    }
2124
2125    #[test]
2126    fn returning_to_a_file_nothing_was_ever_in_is_ignored() {
2127        let mut run = Run::new();
2128        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2129        assert_eq!(run.go("# 200 \"xyz\" 2 3\n__FILE__ __LINE__\n"), "\"/main.c\" 2");
2130        assert_eq!(
2131            run.messages(),
2132            vec!["file `xyz` linemarker ignored due to incorrect nesting".to_owned()]
2133        );
2134    }
2135
2136    #[test]
2137    fn a_flag_that_is_not_one_of_the_four_is_an_error() {
2138        let mut run = Run::new();
2139        run.go("# 20 \"a\" 7\n");
2140        assert_eq!(run.messages(), vec!["invalid flag `7` in line directive".to_owned()]);
2141    }
2142
2143    #[test]
2144    fn a_hash_and_something_that_is_not_a_line_number_is_still_an_unknown_directive() {
2145        // A preprocessing number is a wider thing than a number, and `1.5` is one of them.
2146        let mut run = Run::new();
2147        run.go("# 1.5 \"a\"\n");
2148        assert_eq!(run.messages(), vec!["invalid preprocessing directive".to_owned()]);
2149    }
2150
2151    #[test]
2152    fn a_name_on_the_directive_is_the_name_from_there_on() {
2153        let mut run = Run::new();
2154        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2155        assert_eq!(run.go("#line 7 \"gen.y\"\n__FILE__ __LINE__\n"), "\"gen.y\" 7");
2156    }
2157
2158    #[test]
2159    fn a_directive_with_no_name_keeps_the_one_already_in_force() {
2160        let mut run = Run::new();
2161        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2162        assert_eq!(run.go("#line 7 \"gen.y\"\n#line 20\n__FILE__ __LINE__\n"), "\"gen.y\" 20");
2163    }
2164
2165    #[test]
2166    fn the_number_is_expanded_first_because_line_plus_one_is_real_code() {
2167        let mut run = Run::new();
2168        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2169        assert_eq!(run.go("#define WHERE 300\n#line WHERE\n__LINE__\n"), "300");
2170    }
2171
2172    #[test]
2173    fn a_directive_in_a_header_does_not_move_the_file_that_included_it() {
2174        let mut run = Run::new();
2175        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2176        run.file("/h.h", "#line 500\n__LINE__\n");
2177        run.dir("/");
2178        assert_eq!(run.go("#include <h.h>\n__LINE__\n"), "500 2");
2179    }
2180
2181    #[test]
2182    fn extra_tokens_after_the_file_name_are_a_warning_and_not_an_error() {
2183        let mut run = Run::new();
2184        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2185        assert_eq!(run.go("#line 7 \"gen.y\" and more\n__LINE__\n"), "7");
2186        assert_eq!(run.messages(), vec!["extra tokens after `#line`".to_owned()]);
2187    }
2188
2189    #[test]
2190    fn a_line_number_out_of_range_is_refused() {
2191        let mut run = Run::new();
2192        run.go("#line 0\n");
2193        assert_eq!(run.messages(), vec!["`#line` number is out of range".to_owned()]);
2194
2195        let mut run = Run::new();
2196        run.go("#line notanumber\n");
2197        assert_eq!(run.messages(), vec!["`#line` needs a decimal line number".to_owned()]);
2198    }
2199
2200    #[test]
2201    fn a_pragma_passes_through_unchanged() {
2202        assert_eq!(clean("#pragma pack(1)\nint x;\n"), "#pragma pack(1) int x;");
2203    }
2204
2205    /// glibc indents a directive inside a nest of conditionals, one space per level, so
2206    /// `regex.h` writes `# pragma GCC diagnostic push`. gcc prints it back with the space
2207    /// gone, and a header preprocessed two ways that differ only there is a difference
2208    /// somebody has to read before deciding it does not matter.
2209    #[test]
2210    fn the_space_between_the_hash_and_the_word_comes_off_a_pragma_that_is_indented() {
2211        assert_eq!(clean("#if 1\n# pragma pack(1)\n#endif\n"), "#pragma pack(1)");
2212        assert_eq!(clean("#  pragma  pack( 1 )\n"), "#pragma pack( 1 )", "the rest is kept");
2213    }
2214
2215    #[test]
2216    fn the_pragma_operator_becomes_a_pragma() {
2217        assert_eq!(
2218            clean("_Pragma(\"GCC visibility push(default)\")\nint x;\n"),
2219            "#pragma GCC visibility push(default) int x;"
2220        );
2221    }
2222
2223    #[test]
2224    fn the_pragma_operator_works_from_inside_a_macro() {
2225        // This is the entire reason `_Pragma` exists: a `#pragma` cannot be written in a macro
2226        // body, so a header that wants to wrap one has no other option.
2227        let src = "#define PUSH _Pragma(\"pack(push)\")\nPUSH\nint x;\n";
2228        assert_eq!(clean(src), "#pragma pack(push) int x;");
2229    }
2230
2231    /// A pragma is a line even when it was written as an expression, so whatever follows one
2232    /// has to start a line. The phase that turns these back into a record takes the line as
2233    /// its unit, and without this the `int` would be read as part of the pragma.
2234    #[test]
2235    fn what_follows_a_pragma_operator_starts_a_line() {
2236        let mut run = Run::new();
2237        let out = run.raw("int x; _Pragma(\"pack(1)\") int y;\n");
2238        let starts: Vec<_> =
2239            out.iter().map(|tok| tok.flags.has(TokenFlags::START_OF_LINE)).collect();
2240        // `int x ;` then the six the pragma became, then `int y ;`. Only the first `int` was
2241        // at the start of a line in the source, and the second one is now.
2242        assert_eq!(
2243            starts,
2244            vec![true, false, false, true, false, false, false, false, false, true, false, false]
2245        );
2246    }
2247
2248    #[test]
2249    fn a_pragma_operator_that_is_not_given_a_string_is_reported() {
2250        let mut run = Run::new();
2251        run.go("_Pragma(x)\n");
2252        assert_eq!(run.messages(), vec!["`_Pragma` takes a single string literal".to_owned()]);
2253    }
2254
2255    #[test]
2256    fn an_include_reads_the_file_it_names() {
2257        let mut run = Run::new();
2258        run.file("/dir/one.h", "int from_the_header;\n");
2259        run.dir("/dir");
2260        assert_eq!(run.go("#include <one.h>\nint after;\n"), "int from_the_header; int after;");
2261        assert!(run.messages().is_empty());
2262    }
2263
2264    #[test]
2265    fn a_quoted_include_looks_next_to_the_including_file_first() {
2266        let mut run = Run::new();
2267        run.file("/local.h", "beside\n");
2268        run.file("/dir/local.h", "on the path\n");
2269        run.dir("/dir");
2270        assert_eq!(run.go("#include \"local.h\"\n"), "beside");
2271        assert!(run.messages().is_empty());
2272    }
2273
2274    #[test]
2275    fn an_angled_include_does_not_look_next_to_the_including_file() {
2276        let mut run = Run::new();
2277        run.file("/local.h", "beside\n");
2278        run.file("/dir/local.h", "on the path\n");
2279        run.dir("/dir");
2280        assert_eq!(run.go("#include <local.h>\n"), "on the path");
2281    }
2282
2283    #[test]
2284    fn a_macro_defined_in_a_header_is_visible_after_the_include() {
2285        let mut run = Run::new();
2286        run.file("/dir/defs.h", "#define N 42\n");
2287        run.dir("/dir");
2288        assert_eq!(run.go("#include <defs.h>\nint a = N;\n"), "int a = 42;");
2289        assert!(run.messages().is_empty());
2290    }
2291
2292    #[test]
2293    fn an_include_guard_keeps_the_second_read_empty() {
2294        let mut run = Run::new();
2295        run.file("/dir/g.h", "#ifndef G\n#define G\nonce\n#endif\n");
2296        run.dir("/dir");
2297        assert_eq!(run.go("#include <g.h>\n#include <g.h>\n"), "once");
2298        assert!(run.messages().is_empty());
2299        assert_eq!(run.files(), 2, "the second include is not opened at all");
2300    }
2301
2302    #[test]
2303    fn the_other_spelling_of_a_guard_is_recognised_too() {
2304        for guard in ["#if !defined(G)", "#if !defined G"] {
2305            let mut run = Run::new();
2306            run.file("/dir/g.h", &format!("{guard}\n#define G\nonce\n#endif\n"));
2307            run.dir("/dir");
2308            assert_eq!(run.go("#include <g.h>\n#include <g.h>\n"), "once");
2309            assert_eq!(run.files(), 2, "{guard} should be a guard");
2310        }
2311    }
2312
2313    #[test]
2314    fn a_conditional_that_is_not_a_guard_does_not_skip_anything() {
2315        // Nothing defines the macro, so the second read is not the same as the first and the
2316        // file has to be opened again.
2317        let mut run = Run::new();
2318        run.file("/dir/g.h", "#ifndef G\ntwice\n#endif\n");
2319        run.dir("/dir");
2320        assert_eq!(run.go("#include <g.h>\n#include <g.h>\n"), "twice twice");
2321        assert_eq!(run.files(), 3);
2322    }
2323
2324    #[test]
2325    fn a_token_outside_the_guard_stops_it_being_a_guard() {
2326        let mut run = Run::new();
2327        run.file("/dir/g.h", "#ifndef G\n#define G\n#endif\nalways\n");
2328        run.dir("/dir");
2329        assert_eq!(run.go("#include <g.h>\n#include <g.h>\n"), "always always");
2330        assert_eq!(run.files(), 3);
2331    }
2332
2333    #[test]
2334    fn pragma_once_skips_the_second_read_and_does_not_reach_the_output() {
2335        let mut run = Run::new();
2336        run.file("/dir/o.h", "#pragma once\nonce\n");
2337        run.dir("/dir");
2338        assert_eq!(run.go("#include <o.h>\n#include <o.h>\n"), "once");
2339        assert!(run.messages().is_empty());
2340        assert_eq!(run.files(), 2);
2341    }
2342
2343    #[test]
2344    fn pragma_once_in_the_main_file_is_a_warning_and_is_still_applied() {
2345        // The warning is about the usual case, a main file that meant to be a header. The
2346        // line is applied anyway, because the file that includes itself is the case where it
2347        // does work in a main file, and without it this is an infinite include.
2348        let mut run = Run::new();
2349        let src = "#pragma once\n#include <s.c>\nbody\n";
2350        run.file("/dir/s.c", src);
2351        run.dir("/dir");
2352        assert_eq!(run.go_named("/dir/s.c", src), "body");
2353        assert_eq!(run.severities(), vec![Severity::Warning]);
2354        assert_eq!(run.messages(), vec!["`#pragma once` in the main file".to_owned()]);
2355        assert_eq!(run.files(), 1);
2356    }
2357
2358    #[test]
2359    fn pragma_once_holds_across_two_spellings_of_the_one_path() {
2360        // `-I .` puts a `./` in front of everything it finds, and the file that asked to be
2361        // read once was named without one. Comparing the text as written would read it twice.
2362        let mut run = Run::new();
2363        run.file("dir/s.c", "#pragma once\nbody\n");
2364        run.dir(".");
2365        assert_eq!(run.go("#include <dir/s.c>\n#include <dir/s.c>\n"), "body");
2366        assert!(run.messages().is_empty());
2367        assert_eq!(run.files(), 2);
2368    }
2369
2370    #[test]
2371    fn any_other_pragma_still_passes_through() {
2372        assert_eq!(clean("#pragma once_upon_a_time\n"), "#pragma once_upon_a_time");
2373    }
2374
2375    /// clang's own `__clang_cuda_complex_builtins.h` opens with this, and a header that pushes a
2376    /// name, defines it for its own use and pops it at the end is the whole idiom.
2377    #[test]
2378    fn push_macro_and_pop_macro_put_a_definition_aside_and_bring_it_back() {
2379        let src = "#define X 1\n#pragma push_macro(\"X\")\n#undef X\n#define X 2\n                   a X\n#pragma pop_macro(\"X\")\nb X\n";
2380        assert_eq!(clean(src), "a 2 b 1");
2381    }
2382
2383    #[test]
2384    fn a_name_with_no_definition_pushes_and_pops_the_absence() {
2385        // The pragma is about the state, and "not defined" is a state. A header that pushes a
2386        // name it does not know about has to get an undefined name back, not the one it made.
2387        let src = "#pragma push_macro(\"X\")\n#define X 1\na X\n#pragma pop_macro(\"X\")\nb X\n";
2388        assert_eq!(clean(src), "a 1 b X");
2389    }
2390
2391    #[test]
2392    fn the_pushes_nest() {
2393        let src = "#define X 1\n#pragma push_macro(\"X\")\n#undef X\n#define X 2\n                   #pragma push_macro(\"X\")\n#undef X\n#define X 3\n                   a X\n#pragma pop_macro(\"X\")\nb X\n#pragma pop_macro(\"X\")\nc X\n";
2394        assert_eq!(clean(src), "a 3 b 2 c 1");
2395    }
2396
2397    #[test]
2398    fn a_pop_with_nothing_pushed_says_nothing() {
2399        // The two are written in pairs across headers that do not know about each other, so a
2400        // diagnostic here would fire on code that is not wrong. gcc is silent as well.
2401        assert_eq!(clean("#define X 1\n#pragma pop_macro(\"X\")\nX\n"), "1");
2402        assert_eq!(clean("#pragma pop_macro(\"Never\")\nx\n"), "x");
2403    }
2404
2405    #[test]
2406    fn the_pragma_operator_spelling_works_and_takes_effect_where_it_is_written() {
2407        // A `#pragma` cannot come out of a macro body, so a macro that wants to save a name has
2408        // only this spelling. The lines around it are one run of text to the expander, and the
2409        // pop has to be answered before the line after it is expanded or that line still sees
2410        // the definition the pop was there to undo.
2411        let src = "#define X 1\n_Pragma(\"push_macro(\\\"X\\\")\")\n#undef X\n#define X 2\n                   a X\n_Pragma(\"pop_macro(\\\"X\\\")\")\nb X\n";
2412        assert_eq!(clean(src), "a 2 b 1");
2413    }
2414
2415    #[test]
2416    fn the_gcc_spelling_is_not_one_of_these_and_passes_through() {
2417        // `#pragma GCC push_macro("X")` does nothing in gcc and is printed back, unlike the
2418        // namespaced spellings of the pragmas the compiler proper reads. Answering it here
2419        // would be a difference from gcc dressed up as a courtesy.
2420        let src = "#define X 1\n#pragma GCC push_macro(\"X\")\n#undef X\n#define X 2\nX\n";
2421        assert_eq!(clean(src), "#pragma GCC push_macro(\"X\") 2");
2422    }
2423
2424    #[test]
2425    fn a_push_macro_that_is_not_the_shape_is_an_error() {
2426        for src in ["#pragma push_macro\n", "#pragma push_macro(X)\n", "#pragma pop_macro()\n"] {
2427            let mut run = Run::new();
2428            run.go(src);
2429            let word = if src.contains("push") { "push" } else { "pop" };
2430            assert_eq!(
2431                run.messages(),
2432                vec![format!("invalid `#pragma {word}_macro` directive")],
2433                "from {src:?}"
2434            );
2435        }
2436    }
2437
2438    #[test]
2439    fn a_string_that_does_not_spell_one_identifier_names_no_macro() {
2440        // gcc neither complains about these nor does anything with them, and matching that is
2441        // worth more than improving on it: a header that has one has been building for years.
2442        assert_eq!(clean("#pragma push_macro(\"a b\")\nx\n"), "x");
2443        assert_eq!(clean("#pragma push_macro(\"2\")\nx\n"), "x");
2444    }
2445
2446    #[test]
2447    fn what_follows_the_closing_parenthesis_is_the_usual_warning() {
2448        let mut run = Run::new();
2449        assert_eq!(run.go("#define X 1\n#pragma push_macro(\"X\") junk\nX\n"), "1");
2450        assert_eq!(run.severities(), vec![Severity::Warning]);
2451        assert_eq!(run.messages(), vec!["extra tokens after `#pragma`".to_owned()]);
2452    }
2453
2454    #[test]
2455    fn has_include_answers_from_the_search_path() {
2456        let mut run = Run::new();
2457        run.file("/dir/there.h", "");
2458        run.dir("/dir");
2459        let src = "#if __has_include(<there.h>)\nyes\n#endif\n\
2460                   #if __has_include(<gone.h>)\nno\n#endif\n";
2461        assert_eq!(run.go(src), "yes");
2462        assert!(run.messages().is_empty(), "a header that is not there is an answer, not an error");
2463    }
2464
2465    #[test]
2466    fn has_include_asks_the_question_the_include_on_the_same_line_would() {
2467        // The quoted form looks next to the file that wrote it, so the two spellings answer
2468        // differently about the same header. A `__has_include` that did not agree with the
2469        // `#include` it guards would be worse than not having one.
2470        let mut run = Run::new();
2471        run.file("/beside.h", "");
2472        let src = "#if __has_include(\"beside.h\")\nquoted\n#endif\n\
2473                   #if __has_include(<beside.h>)\nangled\n#endif\n";
2474        assert_eq!(run.go(src), "quoted");
2475    }
2476
2477    #[test]
2478    fn has_include_next_starts_where_include_next_would() {
2479        let mut run = Run::new();
2480        run.file("/a/both.h", "#if __has_include_next(<both.h>)\nmore\n#endif\n");
2481        run.file("/b/both.h", "last\n");
2482        run.file("/a/only.h", "#if __has_include_next(<only.h>)\nmore\n#endif\n");
2483        run.dir("/a");
2484        run.dir("/b");
2485        assert_eq!(run.go("#include <both.h>\n"), "more");
2486        assert_eq!(run.go("#include <only.h>\n"), "", "there is nothing after /a to find it in");
2487    }
2488
2489    #[test]
2490    fn the_operand_of_has_include_is_not_macro_expanded() {
2491        // `linux` is a predefined macro on a Linux target, and `<linux/version.h>` is a real
2492        // header. Expanding the operand would ask about `<1/version.h>`.
2493        let mut run = Run::new();
2494        run.file("/dir/linux/version.h", "");
2495        run.dir("/dir");
2496        let src = "#define linux 1\n#if __has_include(<linux/version.h>)\nyes\n#endif\n";
2497        assert_eq!(run.go(src), "yes");
2498    }
2499
2500    #[test]
2501    fn a_macro_may_expand_to_a_has_include() {
2502        // Which is why the operators are resolved after expansion as well as before it.
2503        let mut run = Run::new();
2504        run.file("/dir/there.h", "");
2505        run.dir("/dir");
2506        let src = "#define HAVE __has_include(<there.h>)\n#if HAVE\nyes\n#endif\n";
2507        assert_eq!(run.go(src), "yes");
2508    }
2509
2510    #[test]
2511    fn defined_says_the_has_operators_are_there() {
2512        // The shape every header that uses them is written in, because they are newer than
2513        // some of the compilers it has to build under.
2514        let src = "#if defined(__has_include) && defined __has_builtin\nyes\n#endif\n";
2515        assert_eq!(clean(src), "yes");
2516        assert_eq!(clean("#ifdef __has_attribute\nyes\n#endif\n"), "yes");
2517    }
2518
2519    #[test]
2520    fn has_attribute_answers_out_of_the_matrix() {
2521        // No attribute is implemented until the parser lands, and the table saying so is the
2522        // whole point: a yes here would send a header down a path that then fails to compile.
2523        assert_eq!(clean("#if __has_attribute(packed)\nyes\n#endif\n"), "");
2524        assert_eq!(clean("#if __has_attribute(no_such_attribute)\nyes\n#endif\n"), "");
2525        assert_eq!(clean("#if !__has_attribute(packed)\nno\n#endif\n"), "no");
2526    }
2527
2528    #[test]
2529    fn the_scoped_spelling_of_an_attribute_is_the_same_question() {
2530        // `[[gnu::packed]]` and `__attribute__((packed))` are one attribute, and
2531        // `__has_c_attribute` answers with the value the standard gives it rather than with
2532        // one. Both answer zero today because the table says the attribute is unimplemented.
2533        assert_eq!(clean("#if __has_c_attribute(gnu::packed)\nyes\n#endif\n"), "");
2534        assert_eq!(clean("#if __has_c_attribute(deprecated)\nyes\n#endif\n"), "");
2535    }
2536
2537    #[test]
2538    fn has_builtin_answers_no_until_the_builtin_is_real() {
2539        assert_eq!(clean("#if __has_builtin(__builtin_expect)\nyes\n#endif\n"), "");
2540        assert_eq!(clean("#if __has_builtin(__builtin_nonesuch)\nyes\n#endif\n"), "");
2541    }
2542
2543    #[test]
2544    fn has_feature_and_has_extension_read_the_same_table() {
2545        // The preprocessor features are the ones that are real today, so they are the ones
2546        // that answer yes, and `__has_extension` answers yes wherever `__has_feature` does.
2547        assert_eq!(clean("#if __has_feature(pragma_once)\nyes\n#endif\n"), "yes");
2548        assert_eq!(clean("#if __has_extension(pragma_once)\nyes\n#endif\n"), "yes");
2549        assert_eq!(clean("#if __has_extension(include_next)\nyes\n#endif\n"), "yes");
2550        assert_eq!(clean("#if __has_feature(include_next)\nyes\n#endif\n"), "");
2551        assert_eq!(clean("#if __has_feature(statement_expressions)\nyes\n#endif\n"), "");
2552    }
2553
2554    #[test]
2555    fn building_module_is_always_no_and_is_recognised_so_that_the_line_parses() {
2556        // Clang's own stddef.h writes this, and the whole point of knowing the name is that
2557        // the operand disappears with it. An unknown identifier would leave `(m)` behind and
2558        // the `#if` would fail to parse rather than answering no.
2559        assert_eq!(clean("#if __building_module(m)\nyes\n#endif\n"), "");
2560        assert_eq!(clean("#if !__building_module(m)\nyes\n#endif\n"), "yes");
2561        assert_eq!(
2562            clean(
2563                "#if !defined(offsetof) || (__has_feature(modules) && !__building_module(x))\nyes\n#endif\n"
2564            ),
2565            "yes"
2566        );
2567        // Defined, the same as the rest of the family: a header asks before it uses one.
2568        assert_eq!(clean("#ifdef __building_module\nyes\n#endif\n"), "yes");
2569        assert_eq!(clean("#if defined(__building_module)\nyes\n#endif\n"), "yes");
2570    }
2571
2572    #[test]
2573    fn a_has_operator_without_an_operand_is_reported() {
2574        let mut run = Run::new();
2575        run.go("#if __has_include\nyes\n#endif\n");
2576        assert_eq!(run.messages(), ["expected `(` after `__has_include`"]);
2577        let mut run = Run::new();
2578        run.go("#if __has_include(1)\nyes\n#endif\n");
2579        assert_eq!(run.messages(), ["expected a file name in `<>` or `\"\"`"]);
2580        let mut run = Run::new();
2581        run.go("#if __has_attribute(\"packed\")\nyes\n#endif\n");
2582        assert_eq!(run.messages(), ["expected an identifier as the operand of `__has_attribute`"]);
2583    }
2584
2585    #[test]
2586    fn the_has_operators_answer_in_ordinary_text_too() {
2587        // GCC and clang both make these builtin macros rather than something only the
2588        // conditional parser knows, so a program may write one in a declaration. Real headers
2589        // do: an attribute macro is often written as the answer rather than as a `#if`.
2590        assert_eq!(clean("f __has_feature(pragma_once)\n"), "f 1");
2591        assert_eq!(clean("b __has_builtin(__builtin_expect)\n"), "b 0");
2592        assert_eq!(clean("a __has_attribute(packed)\n"), "a 0");
2593        assert_eq!(clean("c __has_c_attribute(deprecated)\n"), "c 0");
2594        assert_eq!(clean("m __building_module(foo)\n"), "m 0");
2595    }
2596
2597    #[test]
2598    fn a_macro_that_expands_to_a_has_operator_is_answered_where_it_is_used() {
2599        // The awkward half of the same feature. The answer is deferred to wherever the macro
2600        // lands, so the sweep has to run after expansion and not only before it.
2601        assert_eq!(clean("#define HAVE __has_feature(pragma_once)\nx HAVE\n"), "x 1");
2602        assert_eq!(clean("#define HAVE(x) __has_attribute(x)\ny HAVE(packed)\n"), "y 0");
2603    }
2604
2605    #[test]
2606    fn a_has_operator_in_text_still_needs_its_operand() {
2607        let mut run = Run::new();
2608        run.go("tail __has_attribute;\n");
2609        assert_eq!(run.messages(), ["expected `(` after `__has_attribute`"]);
2610    }
2611
2612    #[test]
2613    fn the_header_operators_are_refused_in_ordinary_text() {
2614        // `<stdio.h>` in a text line was scanned as a run of comparisons, so there is no
2615        // header name left to ask about. GCC and clang both say the same thing here.
2616        let mut run = Run::new();
2617        run.file("/dir/there.h", "");
2618        run.dir("/dir");
2619        run.go("a __has_include(<there.h>)\n");
2620        assert_eq!(run.messages(), ["`__has_include` used outside of a preprocessing directive"]);
2621        let mut run = Run::new();
2622        run.go("b __has_include_next(\"x.h\")\n");
2623        assert_eq!(
2624            run.messages(),
2625            ["`__has_include_next` used outside of a preprocessing directive"]
2626        );
2627    }
2628
2629    #[test]
2630    fn the_predefined_set_is_visible_to_the_source_file() {
2631        let mut run = Run::new();
2632        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2633        let src = "#if defined(__x86_64__) && defined(__linux__) && __SIZEOF_LONG__ == 8\n\
2634                   yes\n#endif\n";
2635        assert_eq!(run.go(src), "yes");
2636        assert!(run.messages().is_empty());
2637    }
2638
2639    #[test]
2640    fn the_predefined_set_follows_the_target_and_not_the_host() {
2641        let mut run = Run::new();
2642        run.predefine("aarch64-unknown-linux-gnu", &Predef::new());
2643        assert_eq!(
2644            run.go("#ifdef __x86_64__\nno\n#endif\n#ifdef __aarch64__\nyes\n#endif\n"),
2645            "yes"
2646        );
2647    }
2648
2649    #[test]
2650    fn a_predefined_macro_expands_where_it_is_used() {
2651        let mut run = Run::new();
2652        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2653        assert_eq!(run.go("__SIZE_TYPE__ n;\n"), "long unsigned int n;");
2654    }
2655
2656    #[test]
2657    fn a_command_line_define_is_a_definition_like_any_other() {
2658        let mut opts = Predef::new();
2659        opts.defines = vec!["FOO".to_owned(), "BAR=3".to_owned()];
2660        opts.undefines = vec!["__linux__".to_owned()];
2661        let mut run = Run::new();
2662        run.predefine("x86_64-unknown-linux-gnu", &opts);
2663        let src = "#if FOO && BAR == 3 && !defined(__linux__)\nyes\n#endif\n";
2664        assert_eq!(run.go(src), "yes");
2665        assert!(run.messages().is_empty());
2666    }
2667
2668    #[test]
2669    fn the_predefined_set_produces_no_tokens_of_its_own() {
2670        // It is a file of directives, so the output of the compilation is the source file
2671        // and nothing else. A stray token here would appear at the top of every `-E` run.
2672        let mut run = Run::new();
2673        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2674        assert_eq!(run.go("alone\n"), "alone");
2675    }
2676
2677    #[test]
2678    fn the_predefined_files_are_named_the_way_gcc_names_them() {
2679        let mut run = Run::new();
2680        let mut opts = Predef::new();
2681        opts.defines = vec!["FOO=1".to_owned()];
2682        run.predefine("x86_64-unknown-linux-gnu", &opts);
2683        let names: Vec<&str> = run.sources.files().iter().map(|f| f.name.as_str()).collect();
2684        assert_eq!(names, ["<built-in>", "<command-line>"]);
2685    }
2686
2687    #[test]
2688    fn a_dialect_without_the_gnu_extensions_says_so() {
2689        let mut opts = Predef::new();
2690        opts.gnu_extensions = false;
2691        opts.std = Std::C99;
2692        let mut run = Run::new();
2693        run.predefine("x86_64-unknown-linux-gnu", &opts);
2694        let src = "#if defined(__STRICT_ANSI__) && __STDC_VERSION__ == 199901L && !defined(linux)\n\
2695                   yes\n#endif\n";
2696        assert_eq!(run.go(src), "yes");
2697    }
2698
2699    #[test]
2700    fn the_date_and_time_are_the_same_for_the_whole_translation_unit() {
2701        let mut opts = Predef::new();
2702        opts.timestamp = Timestamp::from_unix(0);
2703        let mut run = Run::new();
2704        run.predefine("x86_64-unknown-linux-gnu", &opts);
2705        assert_eq!(run.go("__DATE__ __TIME__\n"), "\"Jan  1 1970\" \"00:00:00\"");
2706    }
2707
2708    #[test]
2709    fn a_has_operator_in_a_dead_branch_is_not_asked_about() {
2710        // The line is not evaluated at all, so a malformed one inside `#if 0` is text.
2711        assert_eq!(clean("#if 0\n#if __has_include\n#endif\n#endif\nafter\n"), "after");
2712    }
2713
2714    #[test]
2715    fn a_conditional_may_not_span_an_include() {
2716        // GCC and Clang both refuse this, and the reason is that a header which opens a
2717        // conditional it does not close leaves the file that included it in a state nothing
2718        // downstream can reason about.
2719        let mut run = Run::new();
2720        run.file("/dir/open.h", "#if 1\n");
2721        run.dir("/dir");
2722        run.go("#include <open.h>\nkept\n#endif\n");
2723        let messages = run.messages();
2724        assert_eq!(messages.len(), 2);
2725        assert!(messages[0].contains("unterminated"));
2726        assert!(messages[1].contains("without"));
2727    }
2728
2729    #[test]
2730    fn include_next_continues_after_the_directory_the_file_came_from() {
2731        // The wrapper header trick: `/a` has a `limits.h` that pulls in the real one from
2732        // `/b`, and the two have the same name on purpose.
2733        let mut run = Run::new();
2734        run.file("/a/limits.h", "wrapper\n#include_next <limits.h>\n");
2735        run.file("/b/limits.h", "real\n");
2736        run.dir("/a");
2737        run.dir("/b");
2738        assert_eq!(run.go("#include <limits.h>\n"), "wrapper real");
2739        assert!(run.messages().is_empty());
2740    }
2741
2742    #[test]
2743    fn a_computed_include_is_expanded_first() {
2744        let mut run = Run::new();
2745        run.file("/dir/sub/thing.h", "computed\n");
2746        run.dir("/dir");
2747        let src = "#define HEADER <sub/thing.h>\n#include HEADER\n";
2748        assert_eq!(run.go(src), "computed");
2749        assert!(run.messages().is_empty());
2750        // The string literal form goes through the same path and keeps its delimiters.
2751        let mut run = Run::new();
2752        run.file("/dir/sub/thing.h", "computed\n");
2753        run.dir("/dir");
2754        assert_eq!(run.go("#define H \"sub/thing.h\"\n#include H\n"), "computed");
2755    }
2756
2757    #[test]
2758    fn a_header_that_is_not_there_says_where_it_looked() {
2759        let mut run = Run::new();
2760        run.dir("/dir");
2761        run.go("#include <nope.h>\n");
2762        let diagnostics = run.pp.take_diagnostics();
2763        assert_eq!(diagnostics.len(), 1);
2764        assert_eq!(diagnostics[0].code, Some("E0341"));
2765        assert_eq!(diagnostics[0].message, "`nope.h` file not found");
2766        assert!(diagnostics[0].children[0].message.contains("/dir"));
2767    }
2768
2769    #[test]
2770    fn an_include_that_is_not_a_header_name_is_reported() {
2771        let mut run = Run::new();
2772        run.go("#include 3\n");
2773        let diagnostics = run.pp.take_diagnostics();
2774        assert_eq!(diagnostics[0].code, Some("E0343"));
2775    }
2776
2777    #[test]
2778    fn a_header_that_includes_itself_stops() {
2779        let mut run = Run::new();
2780        run.file("/dir/loop.h", "#include <loop.h>\n");
2781        run.dir("/dir");
2782        run.go("#include <loop.h>\n");
2783        let diagnostics = run.pp.take_diagnostics();
2784        assert_eq!(diagnostics.len(), 1, "one complaint, not one per level");
2785        assert_eq!(diagnostics[0].code, Some("E0342"));
2786    }
2787
2788    #[test]
2789    fn an_include_in_a_dead_branch_is_not_read() {
2790        let mut run = Run::new();
2791        assert_eq!(run.go("#if 0\n#include <nothing.h>\n#endif\nafter\n"), "after");
2792        assert!(run.messages().is_empty(), "a skipped include is not resolved");
2793    }
2794
2795    #[test]
2796    fn embed_writes_the_bytes_of_the_resource() {
2797        let mut run = Run::new();
2798        run.bytes("/logo.bin", &[0, 1, 127, 128, 255]);
2799        assert_eq!(run.go("#embed \"logo.bin\"\n"), "0, 1, 127, 128, 255");
2800        assert!(run.messages().is_empty());
2801    }
2802
2803    #[test]
2804    fn an_embed_is_a_valid_initializer_on_both_sides_of_empty() {
2805        // The reason `prefix` and `suffix` exist. An empty resource is `if_empty` alone, with
2806        // neither of them, so the same three lines are a well formed array whether the file
2807        // has bytes in it or not. Emitting `prefix` and `suffix` around nothing would leave a
2808        // trailing comma inside the braces and turn an empty file into a syntax error.
2809        let mut run = Run::new();
2810        run.bytes("/some.bin", &[7, 8]);
2811        run.bytes("/none.bin", &[]);
2812        let line = |name: &str| {
2813            format!("{{\n#embed \"{name}\" prefix(0xEF,) suffix(,0xFE) if_empty(0)\n}}\n")
2814        };
2815        assert_eq!(run.go(&line("some.bin")), "{ 0xEF,7, 8 ,0xFE }");
2816        assert_eq!(run.go_named("/other.c", &line("none.bin")), "{ 0 }");
2817        assert!(run.messages().is_empty());
2818    }
2819
2820    #[test]
2821    fn the_limit_and_the_offset_choose_a_window_of_the_resource() {
2822        let mut run = Run::new();
2823        run.bytes("/eight.bin", &[1, 2, 3, 4, 5, 6, 7, 8]);
2824        assert_eq!(run.go("#embed \"eight.bin\" limit(3)\n"), "1, 2, 3");
2825        assert_eq!(
2826            run.go_named("/b.c", "#embed \"eight.bin\" gnu::offset(4) limit(3)\n"),
2827            "5, 6, 7"
2828        );
2829        // A limit of zero is an empty embed, not an unlimited one, and an offset past the end
2830        // is empty rather than an error.
2831        assert_eq!(run.go_named("/c.c", "#embed \"eight.bin\" limit(0) if_empty(9)\n"), "9");
2832        assert_eq!(run.go_named("/d.c", "#embed \"eight.bin\" gnu::offset(99)\n"), "");
2833        assert!(run.messages().is_empty());
2834    }
2835
2836    #[test]
2837    fn the_limit_is_a_constant_expression_and_not_just_a_number() {
2838        // It is the `#if` language, so a macro and arithmetic both work. A header that writes
2839        // `limit(CHUNK * 2)` is doing the ordinary thing.
2840        let mut run = Run::new();
2841        run.bytes("/eight.bin", &[1, 2, 3, 4, 5, 6, 7, 8]);
2842        assert_eq!(
2843            run.go("#define CHUNK 2\n#embed \"eight.bin\" limit(CHUNK * 2)\n"),
2844            "1, 2, 3, 4"
2845        );
2846        assert!(run.messages().is_empty());
2847    }
2848
2849    #[test]
2850    fn a_misspelled_embed_parameter_is_refused_rather_than_ignored() {
2851        // Carrying on without it would produce an array with the wrong contents and no
2852        // message, which is the worst outcome available.
2853        let mut run = Run::new();
2854        run.bytes("/eight.bin", &[1, 2]);
2855        assert_eq!(run.go("#embed \"eight.bin\" limits(1)\n"), "");
2856        assert_eq!(run.messages(), vec!["unknown `#embed` parameter `limits`".to_owned()]);
2857        let mut vendor = Run::new();
2858        vendor.bytes("/eight.bin", &[1, 2]);
2859        assert_eq!(vendor.go("#embed \"eight.bin\" clang::offset(1)\n"), "");
2860        assert_eq!(
2861            vendor.messages(),
2862            vec!["unknown `#embed` parameter `clang::offset`".to_owned()]
2863        );
2864    }
2865
2866    #[test]
2867    fn a_missing_embed_resource_is_reported_as_a_resource() {
2868        let mut run = Run::new();
2869        run.go("#embed <nothing.bin>\n");
2870        assert_eq!(run.messages(), vec!["`nothing.bin` resource not found".to_owned()]);
2871    }
2872
2873    #[test]
2874    fn has_embed_tells_missing_from_present_from_empty() {
2875        // Three answers, which is the reason the operator is not `__has_include` with a
2876        // different name. A present but empty resource needs its `if_empty` written and a
2877        // missing one needs a fallback, and a yes or no cannot tell the two apart.
2878        let mut run = Run::new();
2879        run.bytes("/some.bin", &[1]);
2880        run.bytes("/none.bin", &[]);
2881        let src = "#if __has_embed(\"none.bin\") == __STDC_EMBED_EMPTY__\nempty\n#endif\n\
2882                   #if __has_embed(\"some.bin\") == __STDC_EMBED_FOUND__\nfound\n#endif\n\
2883                   #if __has_embed(\"gone.bin\") == __STDC_EMBED_NOT_FOUND__\ngone\n#endif\n";
2884        run.predefine("x86_64-unknown-linux-gnu", &Predef::default());
2885        assert_eq!(run.go(src), "empty found gone");
2886        assert!(run.messages().is_empty());
2887    }
2888
2889    #[test]
2890    fn has_embed_takes_the_limit_into_account() {
2891        // The guard has to answer the question the directive it guards will ask. A resource
2892        // that exists but has nothing left after `limit(0)` is empty to both of them.
2893        let mut run = Run::new();
2894        run.bytes("/some.bin", &[1, 2, 3]);
2895        run.predefine("x86_64-unknown-linux-gnu", &Predef::default());
2896        let src = "#if __has_embed(\"some.bin\" limit(0)) == __STDC_EMBED_EMPTY__\nempty\n#endif\n";
2897        assert_eq!(run.go(src), "empty");
2898        assert!(run.messages().is_empty());
2899    }
2900
2901    #[test]
2902    fn a_directive_may_have_space_before_the_hash_and_after_it() {
2903        assert_eq!(clean("  #  define F 1\n#ifdef F\nyes\n#endif\n"), "yes");
2904    }
2905
2906    #[test]
2907    fn a_definition_survives_across_a_conditional() {
2908        assert_eq!(clean("#if 1\n#define F 7\n#endif\nF\n"), "7");
2909    }
2910
2911    #[test]
2912    fn an_empty_if_expression_is_reported() {
2913        let mut run = Run::new();
2914        run.go("#if\n#endif\n");
2915        assert_eq!(run.messages(), vec!["`#if` with no expression".to_owned()]);
2916    }
2917
2918    #[test]
2919    fn the_file_and_the_line_say_where_the_use_is() {
2920        let mut run = Run::new();
2921        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2922        assert_eq!(run.go("__FILE__ __LINE__\n__LINE__\n"), "\"/main.c\" 1 2");
2923        assert!(run.messages().is_empty());
2924    }
2925
2926    #[test]
2927    fn a_macro_that_mentions_the_line_answers_with_the_call() {
2928        let mut run = Run::new();
2929        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2930        run.file("/where.h", "#define WHERE __FILE__ __LINE__\n");
2931        // The point of the whole arrangement. `assert` is this macro, and a version that
2932        // answered with the header the macro was written in would name a file the user has
2933        // never opened and a line that means nothing.
2934        assert_eq!(run.go("#include \"where.h\"\n\n\nWHERE\n"), "\"/main.c\" 4");
2935        assert!(run.messages().is_empty());
2936    }
2937
2938    #[test]
2939    fn the_file_name_is_the_file_without_the_directories() {
2940        let mut run = Run::new();
2941        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2942        assert_eq!(run.go_named("/deep/down/main.c", "__FILE_NAME__\n"), "\"main.c\"");
2943    }
2944
2945    #[test]
2946    fn a_backslash_in_the_name_is_escaped() {
2947        let mut run = Run::new();
2948        run.predefine("x86_64-pc-windows-msvc", &Predef::new());
2949        // The literal has to mean the path, so the separators are escaped. Getting this wrong
2950        // turns `\src` into an unknown escape and `\a` into a bell character.
2951        let text = run.go_named("C:\\src\\main.c", "__FILE__ __FILE_NAME__\n");
2952        assert_eq!(text, "\"C:\\\\src\\\\main.c\" \"main.c\"");
2953    }
2954
2955    #[test]
2956    fn the_base_file_is_the_one_named_on_the_command_line() {
2957        let mut run = Run::new();
2958        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2959        run.file("/deep.h", "__FILE__ __BASE_FILE__\n");
2960        assert_eq!(run.go("#include \"deep.h\"\n"), "\"/deep.h\" \"/main.c\"");
2961        assert!(run.messages().is_empty());
2962    }
2963
2964    #[test]
2965    fn the_include_level_counts_the_headers_above_it() {
2966        let mut run = Run::new();
2967        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2968        run.file("/one.h", "__INCLUDE_LEVEL__\n#include \"two.h\"\n");
2969        run.file("/two.h", "__INCLUDE_LEVEL__\n");
2970        assert_eq!(run.go("__INCLUDE_LEVEL__\n#include \"one.h\"\n"), "0 1 2");
2971        assert!(run.messages().is_empty());
2972    }
2973
2974    #[test]
2975    fn the_counter_is_a_different_number_every_time() {
2976        let mut run = Run::new();
2977        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2978        assert_eq!(run.go("__COUNTER__ __COUNTER__ __COUNTER__\n"), "0 1 2");
2979    }
2980
2981    #[test]
2982    fn the_counter_advances_once_per_argument_rather_than_once_per_use() {
2983        let mut run = Run::new();
2984        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2985        // An argument is expanded once however many times the body names it, so `TWICE`
2986        // produces the same number twice. That is what GCC does, and the reason for it is
2987        // that expanding an argument twice would report anything wrong inside it twice.
2988        assert_eq!(run.go("#define TWICE(x) x x\nTWICE(__COUNTER__) __COUNTER__\n"), "0 0 1");
2989    }
2990
2991    #[test]
2992    fn the_line_is_a_number_an_if_can_use() {
2993        let mut run = Run::new();
2994        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
2995        assert_eq!(run.go("#if __LINE__ == 1 && __INCLUDE_LEVEL__ == 0\nyes\n#endif\n"), "yes");
2996        assert!(run.messages().is_empty());
2997    }
2998
2999    #[test]
3000    fn the_dynamic_macros_are_defined_like_any_others() {
3001        let mut run = Run::new();
3002        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
3003        let src = "#ifdef __FILE__\nyes\n#endif\n#undef __LINE__\n#ifndef __LINE__\ngone\n#endif\n";
3004        assert_eq!(run.go(src), "yes gone");
3005        assert!(run.messages().is_empty(), "`#undef` of a builtin is allowed, as it is in GCC");
3006    }
3007
3008    #[test]
3009    fn redefining_a_dynamic_macro_warns_and_points_at_the_built_in_file() {
3010        let mut run = Run::new();
3011        run.predefine("x86_64-unknown-linux-gnu", &Predef::new());
3012        assert_eq!(run.go("#define __FILE__ \"mine.c\"\n__FILE__\n"), "\"mine.c\"");
3013        let complaints = run.pp.take_diagnostics();
3014        assert_eq!(complaints.len(), 1);
3015        assert_eq!(complaints[0].code, Some("W0301"));
3016        let previous = complaints[0].children.first().expect("a note saying where it was");
3017        assert_eq!(run.sources.lookup(previous.span.lo).map(|loc| loc.file), {
3018            let built_in = run.sources.files().iter().find(|f| f.name == BUILT_IN);
3019            built_in.map(|f| f.id)
3020        });
3021    }
3022
3023    #[test]
3024    fn destringizing_undoes_what_stringizing_did() {
3025        assert_eq!(destringize(r#""a \"b\" c""#), r#"a "b" c"#);
3026        assert_eq!(destringize(r#""a \\ b""#), r"a \ b");
3027        assert_eq!(destringize(r#"L"wide""#), "wide");
3028    }
3029}