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