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