Skip to main content

rucc_pp/
directive.rs

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