Skip to main content

rucc_pp/
expand.rs

1//! Macro expansion.
2//!
3//! Design: `spec/05-preprocessor.md` section 5.3.
4//!
5//! This is Prosser's algorithm with hide sets, not the expansion-depth approximation. The
6//! two agree on everything anybody writes on purpose and disagree on mutually recursive
7//! macros, which appear in real headers more often than they should and where being wrong is
8//! invisible until it is catastrophic.
9//!
10//! The shape of it: `expand` walks a stream of tokens with pushback, and when it finds a
11//! macro invocation it replaces it with `subst` of the replacement list and pushes that back
12//! onto the front of the stream to be rescanned. Rescanning from the front rather than
13//! recursing is what lets a replacement consume tokens that follow the invocation, which is
14//! required and which is the reason a `Vec` used as a stack shows up here instead of an
15//! iterator chain.
16
17use std::borrow::Cow;
18
19use rucc_base::{Interner, Symbol};
20use rucc_diag::{BytePos, Diagnostic, SourceMap, Span};
21use rucc_lex::{Options, PpToken, PpTokenKind, Punct, TokenFlags, tokenize};
22use rucc_session::PrefixMap;
23
24use crate::hide::{HideSet, HideSets};
25use crate::include::{UNKNOWN, base_name, quoted};
26use crate::macros::{Builtin, MacroDef, MacroTable};
27use crate::token::Tok;
28use crate::trace::{TraceId, Traces};
29
30/// A backstop against a replacement list that grows without bound.
31///
32/// Hide sets guarantee that expansion terminates, but they say nothing about how large the
33/// result gets, and a short chain of macros that each mention the next one twice produces a
34/// megabyte from four lines. Real code never comes near this; input designed to hang the
35/// compiler does, and `spec/19-risks.md` asks for a bound rather than a hang.
36const MAX_STEPS: usize = 1 << 24;
37
38/// Macro expansion state that outlives a single expansion.
39///
40/// Hide sets are interned for the whole translation unit, because the same set is produced
41/// over and over by the same nest of headers and re-interning it is free while re-allocating
42/// it is not.
43#[derive(Debug, Default)]
44pub struct Expander {
45    hides: HideSets,
46    /// Every macro traversed by every expansion in this translation unit, interned. Kept next
47    /// to the hide sets and for the same reason: one table per translation unit, so an index
48    /// stays meaningful for as long as any token carrying it does.
49    traces: Traces,
50    diagnostics: Vec<Diagnostic>,
51    /// What `__COUNTER__` says next. Per translation unit, because that is the scope the
52    /// macro promises to be unique over and the scope a header that builds a name out of it
53    /// relies on.
54    counter: u32,
55    /// What `__FILE__` and `__BASE_FILE__` are rewritten by, which is `-fmacro-prefix-map=`.
56    ///
57    /// Here rather than on the source map because it is not a fact about where anything is: a
58    /// diagnostic still names the real file, a line marker still writes the real name, and this
59    /// changes only what the program is told when it asks. That is gcc's division and it is the
60    /// useful one, since the person reading an error is at the machine the file is on and the
61    /// string in the binary is going somewhere else.
62    prefix_map: PrefixMap,
63}
64
65impl Expander {
66    /// A fresh expander, whose `__FILE__` is the name the file was found under.
67    pub fn new() -> Expander {
68        Expander {
69            hides: HideSets::new(),
70            traces: Traces::new(),
71            diagnostics: Vec::new(),
72            counter: 0,
73            prefix_map: PrefixMap::new(),
74        }
75    }
76
77    /// The same, with `__FILE__` rewritten by `map`.
78    pub fn with_prefix_map(map: PrefixMap) -> Expander {
79        Expander { prefix_map: map, ..Expander::new() }
80    }
81
82    /// Everything reported so far.
83    pub fn diagnostics(&self) -> &[Diagnostic] {
84        &self.diagnostics
85    }
86
87    /// Takes the diagnostics, leaving the expander empty.
88    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
89        std::mem::take(&mut self.diagnostics)
90    }
91
92    /// How many distinct hide sets have been interned, which is the number to watch when
93    /// this starts costing memory.
94    pub fn hide_sets(&self) -> usize {
95        self.hides.len()
96    }
97
98    /// Expands a run of lexed tokens.
99    ///
100    /// The input is a directive-free stretch of the file. An `Eof` token is ignored rather
101    /// than passed through, because the caller decides where the stream ends.
102    pub fn expand(
103        &mut self,
104        tokens: &[PpToken],
105        macros: &MacroTable,
106        interner: &mut Interner,
107        sources: &SourceMap,
108    ) -> Vec<Tok> {
109        let input: Vec<Tok> =
110            tokens.iter().filter(|t| t.kind != PpTokenKind::Eof).map(|&t| Tok::new(t)).collect();
111        self.expand_toks(input, macros, interner, sources)
112    }
113
114    /// Expands tokens that already carry hide sets, for a caller that is splicing streams
115    /// together itself.
116    ///
117    /// The source map is needed rather than merely useful: `__FILE__` and `__LINE__` are
118    /// answered from where the token turned out to be, and the map is the only thing that
119    /// knows that once a token has come out of three nested macros in two headers.
120    pub fn expand_toks(
121        &mut self,
122        tokens: Vec<Tok>,
123        macros: &MacroTable,
124        interner: &mut Interner,
125        sources: &SourceMap,
126    ) -> Vec<Tok> {
127        self.run(tokens, macros, interner, sources, None)
128    }
129
130    /// Expands the tokens of a `#if` or `#elif` line.
131    ///
132    /// The one difference from [`Expander::expand_toks`] is `defined`, which on such a line is an
133    /// operator that happens to be spelled like an identifier, and whose operand is not expanded.
134    /// A line that says `defined(X)` where the caller has already dealt with it never reaches
135    /// here, so what this is for is the `defined` a macro body wrote.
136    pub fn expand_condition(
137        &mut self,
138        tokens: Vec<Tok>,
139        macros: &MacroTable,
140        interner: &mut Interner,
141        sources: &SourceMap,
142        condition: Condition,
143    ) -> Vec<Tok> {
144        self.run(tokens, macros, interner, sources, Some(condition))
145    }
146
147    fn run(
148        &mut self,
149        tokens: Vec<Tok>,
150        macros: &MacroTable,
151        interner: &mut Interner,
152        sources: &SourceMap,
153        condition: Option<Condition>,
154    ) -> Vec<Tok> {
155        let mut run = Run {
156            hides: &mut self.hides,
157            traces: &mut self.traces,
158            current: TraceId::NONE,
159            diagnostics: &mut self.diagnostics,
160            macros,
161            va_opt: interner.intern("__VA_OPT__"),
162            interner,
163            sources,
164            counter: &mut self.counter,
165            prefix_map: &self.prefix_map,
166            condition,
167            steps: 0,
168        };
169        run.expand(tokens)
170    }
171}
172
173/// What a `#if` line asks of an expansion that an ordinary stretch of a file does not.
174///
175/// Only `defined` is in here, and it carries its own name because the expander has nothing else
176/// to recognise an operator by: by the time a line reaches expansion it is a run of tokens like
177/// any other, and `defined` is an identifier in every one of them.
178#[derive(Debug, Clone, Copy)]
179pub struct Condition {
180    /// What `defined` is called, interned.
181    pub defined: Symbol,
182    /// Whether to report each `defined` an expansion produced, which is `-Wpedantic`.
183    pub report: bool,
184}
185
186/// One expansion, holding the pieces borrowed for its duration.
187struct Run<'a> {
188    hides: &'a mut HideSets,
189    traces: &'a mut Traces,
190    /// The expansion being substituted right now, or [`TraceId::NONE`] at the top level.
191    ///
192    /// A token records its own chain once substitution has finished with it, which is too
193    /// late for a diagnostic raised in the middle of that substitution: at the moment `a ## b`
194    /// fails, the macro whose body wrote the `##` has not been recorded yet. So the chain is
195    /// also kept here, where it is correct while the body is being walked. Saved and restored
196    /// around the call, because pre-expanding an argument re-enters expansion.
197    current: TraceId,
198    diagnostics: &'a mut Vec<Diagnostic>,
199    interner: &'a mut Interner,
200    macros: &'a MacroTable,
201    /// Where a token is, which is what the builtin macros are answered from.
202    sources: &'a SourceMap,
203    /// `__VA_OPT__`, interned once rather than looked up per body token.
204    va_opt: Symbol,
205    /// The translation unit's `__COUNTER__`, borrowed so that it survives this expansion.
206    counter: &'a mut u32,
207    /// What `__FILE__` is rewritten by. See [`Expander::prefix_map`].
208    prefix_map: &'a PrefixMap,
209    /// What the line being expanded is, when it is a `#if` line and not an ordinary one.
210    condition: Option<Condition>,
211    steps: usize,
212}
213
214impl<'a> Run<'a> {
215    /// The main loop.
216    ///
217    /// There is deliberately no "already decided not to expand" flag here. Whether a token
218    /// may expand is entirely a question of its hide set, and hide sets only ever grow as a
219    /// token is carried outwards, so a name that was hidden stays hidden. A function-like
220    /// macro name left alone because no parenthesis followed it is a different matter: it may
221    /// well be invoked later, once the tokens after it have been expanded and the parenthesis
222    /// has appeared. `t(t(g)(0) + t)(1)` in the standard's own example depends on that.
223    fn expand(&mut self, input: Vec<Tok>) -> Vec<Tok> {
224        let macros = self.macros;
225        let mut pending = input;
226        pending.reverse();
227        let mut out: Vec<Tok> = Vec::with_capacity(pending.len());
228
229        while let Some(tok) = pending.pop() {
230            self.steps += 1;
231            if self.steps > MAX_STEPS {
232                let d = Diagnostic::error("macro expansion is too large", tok.report_span())
233                    .with_code("E0310")
234                    .note("expansion stopped here, the rest of the line is not expanded", tok.span);
235                let d = self.in_expansions(d, tok.trace, tok.span);
236                self.diagnostics.push(d);
237                out.push(tok);
238                pending.reverse();
239                out.append(&mut pending);
240                return out;
241            }
242
243            let Some(name) = tok.ident() else {
244                out.push(tok);
245                continue;
246            };
247            // On a `#if` line `defined` is an operator, and neither it nor the name it asks
248            // about is expanded, however the two of them got here. This is in the loop rather
249            // than done to the line before expansion begins because a macro body is allowed to
250            // write the operator: once the expansion of `defined(X)` has finished, `X` has been
251            // replaced by whatever it is defined as and the question is about the wrong thing,
252            // or about nothing at all when the macro is defined as empty.
253            if self.condition.is_some_and(|c| c.defined == name) {
254                self.defined_operator(tok, &mut pending, &mut out);
255                continue;
256            }
257            if self.hides.contains(tok.hides, name) {
258                out.push(tok);
259                continue;
260            }
261            let Some(def) = macros.lookup(name) else {
262                out.push(tok);
263                continue;
264            };
265
266            // A builtin stands for one token and that token can never expand into anything,
267            // so it goes straight to the output rather than back onto the stack to be
268            // rescanned. `__LINE__` is the most frequently expanded macro in a real build
269            // after the assert family, and this is the short path it deserves.
270            if let Some(builtin) = def.builtin {
271                let value = self.builtin_value(builtin, tok);
272                out.push(value);
273                continue;
274            }
275
276            if !def.function_like {
277                let hs = self.hides.add(tok.hides, name);
278                let mut args = Args::none();
279                let replacement = self.subst(def, &mut args, hs, tok);
280                push_front(&mut pending, replacement, tok);
281                continue;
282            }
283
284            // A function-like macro is only invoked when a parenthesis follows. `#define f(x)`
285            // followed by a bare `f` is an ordinary identifier and a great deal of code relies
286            // on that, `errno` and `assert` among them.
287            if !pending.last().is_some_and(|t| t.is(Punct::LParen)) {
288                out.push(tok);
289                continue;
290            }
291
292            let Some((raw, rparen)) = self.collect_args(def, &mut pending, tok) else {
293                out.push(tok);
294                continue;
295            };
296            let shared = self.hides.intersect(tok.hides, rparen.hides);
297            let hs = self.hides.add(shared, name);
298            let mut args = Args::new(raw, tok.trace);
299            let replacement = self.subst(def, &mut args, hs, tok);
300            push_front(&mut pending, replacement, tok);
301        }
302        out
303    }
304
305    /// Copies a `defined` and the name it asks about through untouched.
306    ///
307    /// The tokens come out in the order they went in and with nothing added, so the line the
308    /// caller resolves `defined` on afterwards is the line the operator was written on. A
309    /// malformed one is therefore still malformed there and the error reported is the error for
310    /// what the user wrote, rather than one about whatever the expansion made of it.
311    ///
312    /// The parentheses are optional and `defined X` is as good as `defined(X)`, so the closing
313    /// one is only taken when an opening one was. Anything else that follows is left where it
314    /// is, since this is not the place that decides the operator is wrong.
315    fn defined_operator(&mut self, tok: Tok, pending: &mut Vec<Tok>, out: &mut Vec<Tok>) {
316        // A macro that expands to `defined` is legal in every compiler and undefined behaviour
317        // in the standard, because the order the two operators run in was never written down.
318        // What the code means here is what it means to GCC and to Clang, and the warning is for
319        // the person who has to build the same file with something else.
320        //
321        // The wording is GCC's, so that a build log filtered on it reads the same either way.
322        // The notes under it are not: they name the macros the operator came out of, which is
323        // the thing the reader has to find and which GCC leaves them to look for.
324        if self.condition.is_some_and(|c| c.report) && tok.trace != TraceId::NONE {
325            let what = "this use of `defined` may not be portable";
326            let d = Diagnostic::warning(what, tok.report_span()).with_code("W0334");
327            let d = self.in_expansions(d, tok.trace, tok.span);
328            self.diagnostics.push(d);
329        }
330        out.push(tok);
331        let parenthesised = pending.last().is_some_and(|t| t.is(Punct::LParen));
332        if parenthesised {
333            out.push(pending.pop().expect("the parenthesis just looked at"));
334        }
335        if let Some(operand) = pending.pop() {
336            out.push(operand);
337        }
338        if parenthesised && pending.last().is_some_and(|t| t.is(Punct::RParen)) {
339            out.push(pending.pop().expect("the parenthesis just looked at"));
340        }
341    }
342
343    /// What one of the builtin macros stands for at the place it was used.
344    ///
345    /// The position asked about is [`Tok::report_span`], the outermost invocation, rather than
346    /// where the token is spelled. `#define WHERE __FILE__ ":" __LINE__` written in a header
347    /// has to answer with the file and the line of the code that used it, and a version of
348    /// this that answered with the header would be worse than not having the macros at all.
349    fn builtin_value(&mut self, which: Builtin, tok: Tok) -> Tok {
350        let at = tok.report_span().lo;
351        let (kind, text) = match which {
352            Builtin::File => (PpTokenKind::StringLit, quoted(&self.mapped(self.name_of(at)))),
353            // The unmapped name, because a mapping rewrites the front of a path and this is the
354            // part of it after the last separator. gcc leaves this macro alone for that reason
355            // and so does this: a build asking for a name with no directories in it has already
356            // got what a prefix map is for.
357            Builtin::FileName => (PpTokenKind::StringLit, quoted(base_name(self.name_of(at)))),
358            Builtin::BaseFile => (PpTokenKind::StringLit, quoted(&self.mapped(self.base_file(at)))),
359            Builtin::Line => (PpTokenKind::Number, self.line_of(at).to_string()),
360            Builtin::IncludeLevel => {
361                (PpTokenKind::Number, self.sources.include_stack(at).len().to_string())
362            }
363            Builtin::Counter => {
364                let value = *self.counter;
365                // Saturating rather than wrapping. A translation unit that expanded this four
366                // billion times has other problems, and repeating a number that was promised
367                // to be unique is a miscompile rather than an error.
368                *self.counter = self.counter.saturating_add(1);
369                (PpTokenKind::Number, value.to_string())
370            }
371        };
372        Tok {
373            kind,
374            flags: tok.flags,
375            value: Some(self.interner.intern(&text)),
376            span: tok.span,
377            expansion: tok.expansion,
378            trace: tok.trace,
379            hides: tok.hides,
380            placemarker: false,
381        }
382    }
383
384    /// The name of the file `at` is in, as a diagnostic would print it.
385    ///
386    /// The presented name rather than the real one, so that a `#line` moves `__FILE__` with
387    /// it. That is the whole point of the directive: a generator writes the name of the file
388    /// it was given, and the error a user reads has to name that file rather than the
389    /// generated one they have never seen.
390    fn name_of(&self, at: BytePos) -> &str {
391        self.sources.presumed(at).map_or(UNKNOWN, |loc| loc.name)
392    }
393
394    /// That name as the program is to be told it, which is with `-fmacro-prefix-map=` applied.
395    ///
396    /// Separate from [`Run::name_of`] rather than folded into it, because the two answers are
397    /// wanted in different places: this one goes into the binary and the other goes to the person
398    /// at the machine the file is on.
399    fn mapped<'n>(&self, name: &'n str) -> Cow<'n, str> {
400        self.prefix_map.apply(name)
401    }
402
403    /// The line `at` is on, counting from one, and presented rather than real for the same
404    /// reason the name is.
405    ///
406    /// Zero for a position in no file, which is a token the preprocessor made up rather than
407    /// read. Nothing in a real translation unit gets there, and answering zero is better than
408    /// answering with some other file's line.
409    fn line_of(&self, at: BytePos) -> u32 {
410        self.sources.presumed(at).map_or(0, |loc| loc.line)
411    }
412
413    /// The file at the bottom of the include stack, which is the one on the command line.
414    fn base_file(&self, at: BytePos) -> &str {
415        match self.sources.include_stack(at).last() {
416            Some(outermost) => self.name_of(outermost.lo),
417            None => self.name_of(at),
418        }
419    }
420
421    /// Reads an argument list, `pending` positioned on the opening parenthesis.
422    ///
423    /// Returns the arguments and the closing parenthesis token, whose hide set the caller
424    /// needs. Returns `None` after reporting a problem, in which case the macro name is
425    /// emitted unexpanded and the argument tokens are dropped, which is what GCC and Clang
426    /// both do: an argument list that does not fit the macro has no useful reading and
427    /// putting it back only produces a second error from the parser.
428    fn collect_args(
429        &mut self,
430        def: &MacroDef,
431        pending: &mut Vec<Tok>,
432        name: Tok,
433    ) -> Option<(Vec<Vec<Tok>>, Tok)> {
434        let open = pending.pop().expect("the caller checked for an opening parenthesis");
435        let mut args: Vec<Vec<Tok>> = Vec::with_capacity(def.arity() + 1);
436        let mut current: Vec<Tok> = Vec::new();
437        let mut depth = 1usize;
438        let rparen = loop {
439            let Some(tok) = pending.pop() else {
440                let d = Diagnostic::error("unterminated macro argument list", open.report_span())
441                    .with_code("E0311")
442                    .note("this macro was invoked here", name.report_span());
443                let d = self.in_expansions(d, name.trace, name.span);
444                self.diagnostics.push(d);
445                return None;
446            };
447            match tok.punct() {
448                Some(Punct::LParen) => {
449                    depth += 1;
450                    current.push(tok);
451                }
452                Some(Punct::RParen) => {
453                    depth -= 1;
454                    if depth == 0 {
455                        break tok;
456                    }
457                    current.push(tok);
458                }
459                // Once the named parameters are filled, a variadic macro's remaining commas
460                // are part of the last argument rather than separators.
461                Some(Punct::Comma)
462                    if depth == 1 && !(def.is_variadic() && args.len() >= def.arity()) =>
463                {
464                    args.push(std::mem::take(&mut current));
465                }
466                _ => current.push(tok),
467            }
468        };
469
470        // `F()` on a macro that takes nothing is no arguments. On a macro that takes one, the
471        // same text is one empty argument, which is why this cannot be decided by looking at
472        // the tokens alone.
473        let empty_invocation = args.is_empty() && current.is_empty();
474        if !(empty_invocation && def.arity() == 0 && !def.is_variadic()) {
475            args.push(current);
476        }
477        if def.is_variadic() && args.len() == def.arity() {
478            args.push(Vec::new());
479        }
480
481        let expected = def.arity() + usize::from(def.is_variadic());
482        if args.len() != expected {
483            let word = if args.len() < expected { "few" } else { "many" };
484            let d = Diagnostic::error(
485                format!(
486                    "too {word} arguments to macro `{}`, expected {}{}, got {}",
487                    self.interner.resolve(def.name),
488                    def.arity(),
489                    if def.is_variadic() { " or more" } else { "" },
490                    args.len()
491                ),
492                name.report_span(),
493            )
494            .with_code("E0312")
495            .note("defined here", def.span);
496            let d = self.in_expansions(d, name.trace, name.span);
497            self.diagnostics.push(d);
498            return None;
499        }
500        Some((args, rparen))
501    }
502
503    /// Appends the chain of macros `trace` records to `d`, outermost first.
504    ///
505    /// The diagnostic itself points at the outermost invocation, because that is the line the
506    /// user wrote. Each note then names one macro and points at where the next thing in was
507    /// written, so a reader walks from their own code into the header that surprised them
508    /// rather than being handed both ends and left to guess the middle. The last note points
509    /// at `innermost`, which is where inside the innermost macro's body the trouble is.
510    ///
511    /// A token the user wrote has an empty chain and gets nothing added, which is the common
512    /// case and is why this is cheap to call unconditionally.
513    fn in_expansions(&self, mut d: Diagnostic, trace: TraceId, innermost: Span) -> Diagnostic {
514        let chain = self.traces.chain(trace);
515        for (i, step) in chain.iter().enumerate() {
516            let at = chain.get(i + 1).map_or(innermost, |next| next.at);
517            let name = self.interner.resolve(step.macro_name);
518            d = d.note(format!("expanded from macro `{name}`"), at);
519        }
520        d
521    }
522
523    /// Argument substitution over a replacement list.
524    ///
525    /// The order of the cases matters and each one of them is a known source of bugs, so
526    /// they are written out separately rather than folded together.
527    fn subst(&mut self, def: &MacroDef, args: &mut Args, hs: HideSet, invocation: Tok) -> Vec<Tok> {
528        // The name is always there: `subst` is only reached through an identifier that looked
529        // a macro up. The fallback keeps the trace merely incomplete rather than making this a
530        // panic on a path the compiler is not supposed to be able to take.
531        let name = invocation.ident();
532        // The chain for everything this expansion produces, known before the body is walked so
533        // that a diagnostic raised while walking it can say which macro it is inside. The
534        // invocation's own trace is the chain above, which is right whether it came from the
535        // user's file or from three macros further out.
536        let here = match name {
537            Some(name) => self.traces.push(name, invocation.span, invocation.trace),
538            None => invocation.trace,
539        };
540        let outer = std::mem::replace(&mut self.current, here);
541        // Body tokens start with the chain of the invocation rather than with none, so that a
542        // token written in this body comes out with the macros above this one on it. An
543        // argument token already has that chain, having been substituted from the call site.
544        let body: Vec<Tok> =
545            def.body.iter().map(|&t| Tok { trace: invocation.trace, ..Tok::new(t) }).collect();
546        let substituted = self.subst_list(def, args, &body, invocation);
547        self.current = outer;
548        let mut os = drop_placemarkers(substituted);
549        for tok in &mut os {
550            tok.hides = self.hides.union(tok.hides, hs);
551            // The outermost invocation wins, because substitution of the outer macro runs
552            // after substitution of the inner ones, and the outer call is the line the user
553            // wrote and the line a diagnostic should point at.
554            tok.expansion = invocation.report_span();
555            // The trace keeps what `expansion` throws away. Every token here already carries
556            // the chain above this macro, so this records one step inside it, and the interning
557            // means the whole replacement list usually shares one node.
558            if let Some(name) = name {
559                tok.trace = self.traces.push(name, invocation.span, tok.trace);
560            }
561        }
562        if let Some(first) = os.first_mut() {
563            first.flags = carried_spacing(invocation.flags);
564        }
565        os
566    }
567
568    /// The recursive half of substitution, which `__VA_OPT__` re-enters for its contents.
569    fn subst_list(
570        &mut self,
571        def: &MacroDef,
572        args: &mut Args,
573        is: &[Tok],
574        invocation: Tok,
575    ) -> Vec<Tok> {
576        let mut os: Vec<Tok> = Vec::with_capacity(is.len());
577        let mut at = 0;
578        // Whitespace owed to the output because the thing that carried it substituted to
579        // nothing. `#define f(a, ...) [a __VA_ARGS__]` invoked as `f(1)` produces `[1 ]`, not
580        // `[1]`, and matching that is part of what makes `-E` output diffable against GCC's,
581        // per `spec/05-preprocessor.md` section 5.6.
582        let mut owed = false;
583        while at < is.len() {
584            let tok = is[at];
585
586            // `# parameter`, and the C23 `# __VA_OPT__(...)`.
587            if def.function_like && tok.is(Punct::Hash) {
588                if let Some(next) = is.get(at + 1) {
589                    if let Some(idx) = next.ident().and_then(|s| def.param_index(s)) {
590                        let text = self.stringize(args.raw(idx));
591                        let string = self.string_token(&text, tok.span.to(next.span));
592                        emit(&mut os, &[string], tok, &mut owed);
593                        at += 2;
594                        continue;
595                    }
596                    if next.ident() == Some(self.va_opt) {
597                        if let Some(inner) = va_opt_group(is, at + 1) {
598                            let close = inner.end;
599                            let raw = if args.raw(def.arity()).is_empty() {
600                                Vec::new()
601                            } else {
602                                self.subst_raw(def, args, &is[inner])
603                            };
604                            let text = self.stringize(&raw);
605                            let string = self.string_token(&text, tok.span.to(is[close].span));
606                            emit(&mut os, &[string], tok, &mut owed);
607                            at = close + 1;
608                            continue;
609                        }
610                    }
611                }
612            }
613
614            // `## operand`. The definition check guarantees there is an operand. A paste
615            // clears any owed whitespace, because the point of it is that the two operands
616            // become one token with nothing between them.
617            if tok.is(Punct::HashHash) {
618                let next = is[at + 1];
619                owed = false;
620                let param = next.ident().and_then(|s| def.param_index(s).map(|idx| (s, idx)));
621                if let Some((name, idx)) = param {
622                    let raw = args.raw(idx).to_vec();
623                    // The GNU extension: in `, ## __VA_ARGS__` the paste is not a paste at
624                    // all. It drops the comma when there are no variable arguments and does
625                    // nothing at all when there are. An enormous amount of existing code
626                    // depends on it and will for another decade.
627                    let comma_variadic = def.is_variadic_param(name)
628                        && os.last().is_some_and(|l| l.is(Punct::Comma));
629                    if comma_variadic {
630                        if raw.is_empty() {
631                            os.pop();
632                        } else {
633                            emit(&mut os, &raw, next, &mut owed);
634                        }
635                    } else {
636                        self.glue(&mut os, &raw, next.span, tok.span);
637                    }
638                    at += 2;
639                    continue;
640                }
641                if next.ident() == Some(self.va_opt) {
642                    if let Some(inner) = va_opt_group(is, at + 1) {
643                        let close = inner.end;
644                        let rhs = self.va_opt_value(def, args, &is[inner], invocation, next.span);
645                        self.glue(&mut os, &rhs, next.span, tok.span);
646                        at = close + 1;
647                        continue;
648                    }
649                }
650                self.glue(&mut os, &[next], next.span, tok.span);
651                at += 2;
652                continue;
653            }
654
655            // `__VA_OPT__(...)` in an ordinary position.
656            if tok.ident() == Some(self.va_opt) {
657                if let Some(inner) = va_opt_group(is, at) {
658                    let close = inner.end;
659                    let value = self.va_opt_value(def, args, &is[inner], invocation, tok.span);
660                    emit(&mut os, &value, tok, &mut owed);
661                    at = close + 1;
662                    continue;
663                }
664            }
665
666            // A parameter. Pasted with what follows it means the raw argument; otherwise the
667            // fully expanded one.
668            if let Some(idx) = tok.ident().and_then(|s| def.param_index(s)) {
669                if is.get(at + 1).is_some_and(|n| n.is(Punct::HashHash)) {
670                    let raw = args.raw(idx).to_vec();
671                    let placemarker = [Tok::placemarker_at(tok.span)];
672                    let value = if raw.is_empty() { &placemarker[..] } else { &raw[..] };
673                    emit(&mut os, value, tok, &mut owed);
674                } else {
675                    let expanded = args.expanded(idx, self).to_vec();
676                    emit(&mut os, &expanded, tok, &mut owed);
677                }
678                at += 1;
679                continue;
680            }
681
682            emit_plain(&mut os, tok, &mut owed);
683            at += 1;
684        }
685        os
686    }
687
688    /// What a `__VA_OPT__(...)` group stands for: its substituted contents when the variadic
689    /// argument has tokens, and a placemarker when it does not.
690    fn va_opt_value(
691        &mut self,
692        def: &MacroDef,
693        args: &mut Args,
694        inner: &[Tok],
695        invocation: Tok,
696        span: Span,
697    ) -> Vec<Tok> {
698        if args.raw(def.arity()).is_empty() {
699            return vec![Tok::placemarker_at(span)];
700        }
701        let value = self.subst_list(def, args, inner, invocation);
702        if value.is_empty() { vec![Tok::placemarker_at(span)] } else { value }
703    }
704
705    /// Substitution with parameters replaced by their unexpanded arguments, which is what
706    /// stringizing a `__VA_OPT__` group needs.
707    fn subst_raw(&mut self, def: &MacroDef, args: &mut Args, inner: &[Tok]) -> Vec<Tok> {
708        let mut out = Vec::with_capacity(inner.len());
709        for &tok in inner {
710            match tok.ident().and_then(|s| def.param_index(s)) {
711                Some(idx) => out.extend_from_slice(args.raw(idx)),
712                None => out.push(tok),
713            }
714        }
715        out
716    }
717
718    /// Pastes `rhs` onto the last token of `os`.
719    ///
720    /// An empty `rhs` is a placemarker, and pasting anything onto a placemarker or a
721    /// placemarker onto anything leaves the anything, which is what makes `a ## b` with an
722    /// empty `b` produce `a` instead of an error.
723    fn glue(&mut self, os: &mut Vec<Tok>, rhs: &[Tok], span: Span, op: Span) {
724        let placemarker = [Tok::placemarker_at(span)];
725        let rhs = if rhs.is_empty() { &placemarker[..] } else { rhs };
726        let Some(lhs) = os.pop() else {
727            os.extend_from_slice(rhs);
728            return;
729        };
730        let first = rhs[0];
731        if lhs.is_placemarker() {
732            os.extend_from_slice(rhs);
733            return;
734        }
735        if first.is_placemarker() {
736            os.push(lhs);
737            os.extend_from_slice(&rhs[1..]);
738            return;
739        }
740        match self.paste(lhs, first, op) {
741            Some(joined) => os.push(joined),
742            None => {
743                // The two were meant to be one token, so they are printed with nothing
744                // between them even though the paste failed. GCC and Clang both do this.
745                let mut first = first;
746                first.flags = TokenFlags::EMPTY;
747                os.push(lhs);
748                os.push(first);
749            }
750        }
751        os.extend_from_slice(&rhs[1..]);
752    }
753
754    /// Concatenates two spellings and re-lexes the result.
755    ///
756    /// A result that is not exactly one preprocessing token is a constraint violation. GCC
757    /// diagnoses it and keeps both tokens, and we do the same, because rejecting the
758    /// translation unit here would stop a build over something that in practice never
759    /// reaches the parser.
760    fn paste(&mut self, lhs: Tok, rhs: Tok, op: Span) -> Option<Tok> {
761        let mut text = String::new();
762        self.spell(lhs, &mut text);
763        let split = text.len();
764        self.spell(rhs, &mut text);
765
766        let (tokens, _) = tokenize(text.as_bytes(), 0, Options::new(), self.interner);
767        let single = tokens.len() == 2
768            && tokens[0].kind != PpTokenKind::Eof
769            && tokens[1].kind == PpTokenKind::Eof
770            && tokens[0].span.lo == 0
771            && tokens[0].span.hi as usize == text.len();
772        if !single {
773            let d = Diagnostic::error(
774                format!(
775                    "pasting `{}` and `{}` does not give a valid preprocessing token",
776                    &text[..split],
777                    &text[split..]
778                ),
779                lhs.report_span().to(rhs.report_span()),
780            )
781            .with_code("E0313")
782            .note("the left operand is here", lhs.span)
783            .note("the right operand is here", rhs.span);
784            let d = self.in_expansions(d, self.current, op);
785            self.diagnostics.push(d);
786            return None;
787        }
788        Some(Tok {
789            kind: tokens[0].kind,
790            flags: lhs.flags,
791            value: tokens[0].value,
792            span: lhs.span.to(rhs.span),
793            expansion: lhs.expansion,
794            trace: lhs.trace,
795            hides: self.hides.union(lhs.hides, rhs.hides),
796            placemarker: false,
797        })
798    }
799
800    /// Builds the string literal `#` produces.
801    ///
802    /// Internal whitespace runs collapse to one space, leading and trailing space is
803    /// dropped, and a backslash or double quote inside a string or character literal is
804    /// escaped, per `spec/05-preprocessor.md` section 5.3.
805    fn stringize(&self, toks: &[Tok]) -> String {
806        let mut out = String::from("\"");
807        let mut first = true;
808        for &tok in toks.iter().filter(|t| !t.is_placemarker()) {
809            if !first && tok.flags.has(TokenFlags::LEADING_SPACE) {
810                out.push(' ');
811            }
812            first = false;
813            let mut spelled = String::new();
814            self.spell(tok, &mut spelled);
815            if matches!(tok.kind, PpTokenKind::StringLit | PpTokenKind::CharConst) {
816                for ch in spelled.chars() {
817                    if ch == '\\' || ch == '"' {
818                        out.push('\\');
819                    }
820                    out.push(ch);
821                }
822            } else {
823                out.push_str(&spelled);
824            }
825        }
826        out.push('"');
827        out
828    }
829
830    /// Wraps stringized text as a token.
831    fn string_token(&mut self, text: &str, span: Span) -> Tok {
832        Tok {
833            kind: PpTokenKind::StringLit,
834            flags: TokenFlags::EMPTY,
835            value: Some(self.interner.intern(text)),
836            span,
837            expansion: Span::DUMMY,
838            trace: TraceId::NONE,
839            hides: HideSet::EMPTY,
840            placemarker: false,
841        }
842    }
843
844    /// Appends a token's spelling.
845    fn spell(&self, tok: Tok, out: &mut String) {
846        if tok.is_placemarker() {
847            return;
848        }
849        match (tok.kind, tok.value) {
850            (PpTokenKind::Punct(p), _) => out.push_str(p.as_str()),
851            (_, Some(sym)) => out.push_str(self.interner.resolve(sym)),
852            (_, None) => {}
853        }
854    }
855}
856
857/// Appends what a body token substituted to.
858///
859/// The first token of the result takes the spacing of the token it replaced, so that
860/// `#define f(x) (x + x)` prints as `(1 + 1)` rather than `(1 +1)`. A group that substituted
861/// to nothing leaves its spacing owed to whatever comes next.
862fn emit(os: &mut Vec<Tok>, value: &[Tok], source: Tok, owed: &mut bool) {
863    let Some((&first, rest)) = value.split_first() else {
864        *owed = *owed || source.flags.has(TokenFlags::LEADING_SPACE);
865        return;
866    };
867    let mut first = first;
868    first.flags = carried_spacing(source.flags);
869    if *owed {
870        first.flags = first.flags.with(TokenFlags::LEADING_SPACE);
871        *owed = false;
872    }
873    os.push(first);
874    os.extend_from_slice(rest);
875}
876
877/// Appends a token that stands for itself, which is every token of a replacement list that
878/// is not a parameter or an operator.
879fn emit_plain(os: &mut Vec<Tok>, tok: Tok, owed: &mut bool) {
880    let mut tok = tok;
881    if *owed {
882        tok.flags = tok.flags.with(TokenFlags::LEADING_SPACE);
883        *owed = false;
884    }
885    os.push(tok);
886}
887
888/// Removes placemarkers, handing any whitespace they carried to the next real token.
889fn drop_placemarkers(toks: Vec<Tok>) -> Vec<Tok> {
890    let mut out = Vec::with_capacity(toks.len());
891    let mut owed = false;
892    for tok in toks {
893        if tok.is_placemarker() {
894            owed = owed || tok.flags.has(TokenFlags::LEADING_SPACE);
895            continue;
896        }
897        emit_plain(&mut out, tok, &mut owed);
898    }
899    out
900}
901
902/// Finds the parenthesised group belonging to a `__VA_OPT__` at `at`.
903///
904/// Returns the range of the contents. The closing parenthesis is at `range.end`, so the
905/// group ends at `range.end + 1`, which is what every caller wants next.
906fn va_opt_group(is: &[Tok], at: usize) -> Option<std::ops::Range<usize>> {
907    if !is.get(at + 1).is_some_and(|t| t.is(Punct::LParen)) {
908        return None;
909    }
910    let start = at + 2;
911    let mut depth = 1usize;
912    let mut end = start;
913    while end < is.len() {
914        match is[end].punct() {
915            Some(Punct::LParen) => depth += 1,
916            Some(Punct::RParen) => {
917                depth -= 1;
918                if depth == 0 {
919                    return Some(start..end);
920                }
921            }
922            _ => {}
923        }
924        end += 1;
925    }
926    None
927}
928
929/// Pushes a replacement onto the front of the pushback stack, preserving its order.
930fn push_front(pending: &mut Vec<Tok>, mut replacement: Vec<Tok>, invocation: Tok) {
931    // An expansion that came to nothing still leaves its spacing behind. `#define E` used as
932    // `int a E;` preprocesses to `int a ;` and not to `int a;`, in GCC and in clang both. On
933    // the glibc headers that is most of the difference between agreeing with the reference and
934    // not, because `__THROW` and the rest of the attribute macros expand to nothing on a
935    // non-GNU dialect and sit next to a `;` or a `,` several hundred times per header.
936    //
937    // The space is handed to whatever gets rescanned next, which may itself be a macro that
938    // vanishes, so `a E E E b` walks the debt along until something real takes it.
939    //
940    // Standing at the front of a line is handed along the same way. The line belongs to the
941    // file rather than to the macro, so when the macro that was written first on one leaves
942    // nothing behind, the token that ends up first there is the one that starts the line.
943    // Losing it is not cosmetic: a `#pragma` line is read as the tokens between the `pragma`
944    // and the next one that starts a line, so a declaration written as `SQLITE_API const char
945    // sqlite3_version[] = ...` after a `#pragma pack` gets eaten by the pragma instead, and the
946    // program is left without the declaration and with a complaint about junk on the pragma.
947    if replacement.is_empty() {
948        if let Some(next) = pending.last_mut() {
949            next.flags = next.flags.with(carried_spacing(invocation.flags));
950        }
951        return;
952    }
953    replacement.reverse();
954    pending.append(&mut replacement);
955}
956
957/// The flags a replacement's first token inherits from the invocation.
958///
959/// Only spacing carries over. A macro that expanded from a spliced or digraph token did not
960/// itself come from one, and saying it did would put the wrong thing in `-E` output.
961fn carried_spacing(flags: TokenFlags) -> TokenFlags {
962    let mut carried = TokenFlags::EMPTY;
963    if flags.has(TokenFlags::START_OF_LINE) {
964        carried = carried.with(TokenFlags::START_OF_LINE);
965    }
966    if flags.has(TokenFlags::LEADING_SPACE) {
967        carried = carried.with(TokenFlags::LEADING_SPACE);
968    }
969    carried
970}
971
972/// The arguments of one invocation, raw and expanded.
973///
974/// An argument used twice in a replacement list is expanded once. That is not just a saving:
975/// `spec/02-the-goal.md` wants the same input to produce the same diagnostics, and expanding
976/// an argument twice would report anything wrong inside it twice.
977struct Args {
978    raw: Vec<Vec<Tok>>,
979    expanded: Vec<Option<Vec<Tok>>>,
980    /// The chain the invocation itself came out of, which is the chain the argument text is
981    /// in as well, since the caller wrote it and the macro being called did not.
982    outer: TraceId,
983}
984
985impl Args {
986    fn new(raw: Vec<Vec<Tok>>, outer: TraceId) -> Args {
987        let count = raw.len();
988        Args { raw, expanded: vec![None; count], outer }
989    }
990
991    /// The argument list of an object-like macro, which has none.
992    fn none() -> Args {
993        Args { raw: Vec::new(), expanded: Vec::new(), outer: TraceId::NONE }
994    }
995
996    fn raw(&self, idx: usize) -> &[Tok] {
997        self.raw.get(idx).map_or(&[][..], |a| a.as_slice())
998    }
999
1000    fn expanded(&mut self, idx: usize, run: &mut Run<'_>) -> &[Tok] {
1001        let Some(slot) = self.expanded.get(idx) else {
1002            return &[];
1003        };
1004        if slot.is_none() {
1005            let saved = std::mem::replace(&mut run.current, self.outer);
1006            let expanded = run.expand(self.raw[idx].clone());
1007            run.current = saved;
1008            self.expanded[idx] = Some(expanded);
1009        }
1010        self.expanded[idx].as_deref().expect("just filled in")
1011    }
1012}