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