Skip to main content

rucc_pp/
directive.rs

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