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