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        let mut run = Run {
128            hides: &mut self.hides,
129            traces: &mut self.traces,
130            current: TraceId::NONE,
131            diagnostics: &mut self.diagnostics,
132            macros,
133            va_opt: interner.intern("__VA_OPT__"),
134            interner,
135            sources,
136            counter: &mut self.counter,
137            prefix_map: &self.prefix_map,
138            steps: 0,
139        };
140        run.expand(tokens)
141    }
142}
143
144/// One expansion, holding the pieces borrowed for its duration.
145struct Run<'a> {
146    hides: &'a mut HideSets,
147    traces: &'a mut Traces,
148    /// The expansion being substituted right now, or [`TraceId::NONE`] at the top level.
149    ///
150    /// A token records its own chain once substitution has finished with it, which is too
151    /// late for a diagnostic raised in the middle of that substitution: at the moment `a ## b`
152    /// fails, the macro whose body wrote the `##` has not been recorded yet. So the chain is
153    /// also kept here, where it is correct while the body is being walked. Saved and restored
154    /// around the call, because pre-expanding an argument re-enters expansion.
155    current: TraceId,
156    diagnostics: &'a mut Vec<Diagnostic>,
157    interner: &'a mut Interner,
158    macros: &'a MacroTable,
159    /// Where a token is, which is what the builtin macros are answered from.
160    sources: &'a SourceMap,
161    /// `__VA_OPT__`, interned once rather than looked up per body token.
162    va_opt: Symbol,
163    /// The translation unit's `__COUNTER__`, borrowed so that it survives this expansion.
164    counter: &'a mut u32,
165    /// What `__FILE__` is rewritten by. See [`Expander::prefix_map`].
166    prefix_map: &'a PrefixMap,
167    steps: usize,
168}
169
170impl<'a> Run<'a> {
171    /// The main loop.
172    ///
173    /// There is deliberately no "already decided not to expand" flag here. Whether a token
174    /// may expand is entirely a question of its hide set, and hide sets only ever grow as a
175    /// token is carried outwards, so a name that was hidden stays hidden. A function-like
176    /// macro name left alone because no parenthesis followed it is a different matter: it may
177    /// well be invoked later, once the tokens after it have been expanded and the parenthesis
178    /// has appeared. `t(t(g)(0) + t)(1)` in the standard's own example depends on that.
179    fn expand(&mut self, input: Vec<Tok>) -> Vec<Tok> {
180        let macros = self.macros;
181        let mut pending = input;
182        pending.reverse();
183        let mut out: Vec<Tok> = Vec::with_capacity(pending.len());
184
185        while let Some(tok) = pending.pop() {
186            self.steps += 1;
187            if self.steps > MAX_STEPS {
188                let d = Diagnostic::error("macro expansion is too large", tok.report_span())
189                    .with_code("E0310")
190                    .note("expansion stopped here, the rest of the line is not expanded", tok.span);
191                let d = self.in_expansions(d, tok.trace, tok.span);
192                self.diagnostics.push(d);
193                out.push(tok);
194                pending.reverse();
195                out.append(&mut pending);
196                return out;
197            }
198
199            let Some(name) = tok.ident() else {
200                out.push(tok);
201                continue;
202            };
203            if self.hides.contains(tok.hides, name) {
204                out.push(tok);
205                continue;
206            }
207            let Some(def) = macros.lookup(name) else {
208                out.push(tok);
209                continue;
210            };
211
212            // A builtin stands for one token and that token can never expand into anything,
213            // so it goes straight to the output rather than back onto the stack to be
214            // rescanned. `__LINE__` is the most frequently expanded macro in a real build
215            // after the assert family, and this is the short path it deserves.
216            if let Some(builtin) = def.builtin {
217                let value = self.builtin_value(builtin, tok);
218                out.push(value);
219                continue;
220            }
221
222            if !def.function_like {
223                let hs = self.hides.add(tok.hides, name);
224                let mut args = Args::none();
225                let replacement = self.subst(def, &mut args, hs, tok);
226                push_front(&mut pending, replacement, tok);
227                continue;
228            }
229
230            // A function-like macro is only invoked when a parenthesis follows. `#define f(x)`
231            // followed by a bare `f` is an ordinary identifier and a great deal of code relies
232            // on that, `errno` and `assert` among them.
233            if !pending.last().is_some_and(|t| t.is(Punct::LParen)) {
234                out.push(tok);
235                continue;
236            }
237
238            let Some((raw, rparen)) = self.collect_args(def, &mut pending, tok) else {
239                out.push(tok);
240                continue;
241            };
242            let shared = self.hides.intersect(tok.hides, rparen.hides);
243            let hs = self.hides.add(shared, name);
244            let mut args = Args::new(raw, tok.trace);
245            let replacement = self.subst(def, &mut args, hs, tok);
246            push_front(&mut pending, replacement, tok);
247        }
248        out
249    }
250
251    /// What one of the builtin macros stands for at the place it was used.
252    ///
253    /// The position asked about is [`Tok::report_span`], the outermost invocation, rather than
254    /// where the token is spelled. `#define WHERE __FILE__ ":" __LINE__` written in a header
255    /// has to answer with the file and the line of the code that used it, and a version of
256    /// this that answered with the header would be worse than not having the macros at all.
257    fn builtin_value(&mut self, which: Builtin, tok: Tok) -> Tok {
258        let at = tok.report_span().lo;
259        let (kind, text) = match which {
260            Builtin::File => (PpTokenKind::StringLit, quoted(&self.mapped(self.name_of(at)))),
261            // The unmapped name, because a mapping rewrites the front of a path and this is the
262            // part of it after the last separator. gcc leaves this macro alone for that reason
263            // and so does this: a build asking for a name with no directories in it has already
264            // got what a prefix map is for.
265            Builtin::FileName => (PpTokenKind::StringLit, quoted(base_name(self.name_of(at)))),
266            Builtin::BaseFile => (PpTokenKind::StringLit, quoted(&self.mapped(self.base_file(at)))),
267            Builtin::Line => (PpTokenKind::Number, self.line_of(at).to_string()),
268            Builtin::IncludeLevel => {
269                (PpTokenKind::Number, self.sources.include_stack(at).len().to_string())
270            }
271            Builtin::Counter => {
272                let value = *self.counter;
273                // Saturating rather than wrapping. A translation unit that expanded this four
274                // billion times has other problems, and repeating a number that was promised
275                // to be unique is a miscompile rather than an error.
276                *self.counter = self.counter.saturating_add(1);
277                (PpTokenKind::Number, value.to_string())
278            }
279        };
280        Tok {
281            kind,
282            flags: tok.flags,
283            value: Some(self.interner.intern(&text)),
284            span: tok.span,
285            expansion: tok.expansion,
286            trace: tok.trace,
287            hides: tok.hides,
288            placemarker: false,
289        }
290    }
291
292    /// The name of the file `at` is in, as a diagnostic would print it.
293    ///
294    /// The presented name rather than the real one, so that a `#line` moves `__FILE__` with
295    /// it. That is the whole point of the directive: a generator writes the name of the file
296    /// it was given, and the error a user reads has to name that file rather than the
297    /// generated one they have never seen.
298    fn name_of(&self, at: BytePos) -> &str {
299        self.sources.presumed(at).map_or(UNKNOWN, |loc| loc.name)
300    }
301
302    /// That name as the program is to be told it, which is with `-fmacro-prefix-map=` applied.
303    ///
304    /// Separate from [`Run::name_of`] rather than folded into it, because the two answers are
305    /// wanted in different places: this one goes into the binary and the other goes to the person
306    /// at the machine the file is on.
307    fn mapped<'n>(&self, name: &'n str) -> Cow<'n, str> {
308        self.prefix_map.apply(name)
309    }
310
311    /// The line `at` is on, counting from one, and presented rather than real for the same
312    /// reason the name is.
313    ///
314    /// Zero for a position in no file, which is a token the preprocessor made up rather than
315    /// read. Nothing in a real translation unit gets there, and answering zero is better than
316    /// answering with some other file's line.
317    fn line_of(&self, at: BytePos) -> u32 {
318        self.sources.presumed(at).map_or(0, |loc| loc.line)
319    }
320
321    /// The file at the bottom of the include stack, which is the one on the command line.
322    fn base_file(&self, at: BytePos) -> &str {
323        match self.sources.include_stack(at).last() {
324            Some(outermost) => self.name_of(outermost.lo),
325            None => self.name_of(at),
326        }
327    }
328
329    /// Reads an argument list, `pending` positioned on the opening parenthesis.
330    ///
331    /// Returns the arguments and the closing parenthesis token, whose hide set the caller
332    /// needs. Returns `None` after reporting a problem, in which case the macro name is
333    /// emitted unexpanded and the argument tokens are dropped, which is what GCC and Clang
334    /// both do: an argument list that does not fit the macro has no useful reading and
335    /// putting it back only produces a second error from the parser.
336    fn collect_args(
337        &mut self,
338        def: &MacroDef,
339        pending: &mut Vec<Tok>,
340        name: Tok,
341    ) -> Option<(Vec<Vec<Tok>>, Tok)> {
342        let open = pending.pop().expect("the caller checked for an opening parenthesis");
343        let mut args: Vec<Vec<Tok>> = Vec::with_capacity(def.arity() + 1);
344        let mut current: Vec<Tok> = Vec::new();
345        let mut depth = 1usize;
346        let rparen = loop {
347            let Some(tok) = pending.pop() else {
348                let d = Diagnostic::error("unterminated macro argument list", open.report_span())
349                    .with_code("E0311")
350                    .note("this macro was invoked here", name.report_span());
351                let d = self.in_expansions(d, name.trace, name.span);
352                self.diagnostics.push(d);
353                return None;
354            };
355            match tok.punct() {
356                Some(Punct::LParen) => {
357                    depth += 1;
358                    current.push(tok);
359                }
360                Some(Punct::RParen) => {
361                    depth -= 1;
362                    if depth == 0 {
363                        break tok;
364                    }
365                    current.push(tok);
366                }
367                // Once the named parameters are filled, a variadic macro's remaining commas
368                // are part of the last argument rather than separators.
369                Some(Punct::Comma)
370                    if depth == 1 && !(def.is_variadic() && args.len() >= def.arity()) =>
371                {
372                    args.push(std::mem::take(&mut current));
373                }
374                _ => current.push(tok),
375            }
376        };
377
378        // `F()` on a macro that takes nothing is no arguments. On a macro that takes one, the
379        // same text is one empty argument, which is why this cannot be decided by looking at
380        // the tokens alone.
381        let empty_invocation = args.is_empty() && current.is_empty();
382        if !(empty_invocation && def.arity() == 0 && !def.is_variadic()) {
383            args.push(current);
384        }
385        if def.is_variadic() && args.len() == def.arity() {
386            args.push(Vec::new());
387        }
388
389        let expected = def.arity() + usize::from(def.is_variadic());
390        if args.len() != expected {
391            let word = if args.len() < expected { "few" } else { "many" };
392            let d = Diagnostic::error(
393                format!(
394                    "too {word} arguments to macro `{}`, expected {}{}, got {}",
395                    self.interner.resolve(def.name),
396                    def.arity(),
397                    if def.is_variadic() { " or more" } else { "" },
398                    args.len()
399                ),
400                name.report_span(),
401            )
402            .with_code("E0312")
403            .note("defined here", def.span);
404            let d = self.in_expansions(d, name.trace, name.span);
405            self.diagnostics.push(d);
406            return None;
407        }
408        Some((args, rparen))
409    }
410
411    /// Appends the chain of macros `trace` records to `d`, outermost first.
412    ///
413    /// The diagnostic itself points at the outermost invocation, because that is the line the
414    /// user wrote. Each note then names one macro and points at where the next thing in was
415    /// written, so a reader walks from their own code into the header that surprised them
416    /// rather than being handed both ends and left to guess the middle. The last note points
417    /// at `innermost`, which is where inside the innermost macro's body the trouble is.
418    ///
419    /// A token the user wrote has an empty chain and gets nothing added, which is the common
420    /// case and is why this is cheap to call unconditionally.
421    fn in_expansions(&self, mut d: Diagnostic, trace: TraceId, innermost: Span) -> Diagnostic {
422        let chain = self.traces.chain(trace);
423        for (i, step) in chain.iter().enumerate() {
424            let at = chain.get(i + 1).map_or(innermost, |next| next.at);
425            let name = self.interner.resolve(step.macro_name);
426            d = d.note(format!("expanded from macro `{name}`"), at);
427        }
428        d
429    }
430
431    /// Argument substitution over a replacement list.
432    ///
433    /// The order of the cases matters and each one of them is a known source of bugs, so
434    /// they are written out separately rather than folded together.
435    fn subst(&mut self, def: &MacroDef, args: &mut Args, hs: HideSet, invocation: Tok) -> Vec<Tok> {
436        // The name is always there: `subst` is only reached through an identifier that looked
437        // a macro up. The fallback keeps the trace merely incomplete rather than making this a
438        // panic on a path the compiler is not supposed to be able to take.
439        let name = invocation.ident();
440        // The chain for everything this expansion produces, known before the body is walked so
441        // that a diagnostic raised while walking it can say which macro it is inside. The
442        // invocation's own trace is the chain above, which is right whether it came from the
443        // user's file or from three macros further out.
444        let here = match name {
445            Some(name) => self.traces.push(name, invocation.span, invocation.trace),
446            None => invocation.trace,
447        };
448        let outer = std::mem::replace(&mut self.current, here);
449        // Body tokens start with the chain of the invocation rather than with none, so that a
450        // token written in this body comes out with the macros above this one on it. An
451        // argument token already has that chain, having been substituted from the call site.
452        let body: Vec<Tok> =
453            def.body.iter().map(|&t| Tok { trace: invocation.trace, ..Tok::new(t) }).collect();
454        let substituted = self.subst_list(def, args, &body, invocation);
455        self.current = outer;
456        let mut os = drop_placemarkers(substituted);
457        for tok in &mut os {
458            tok.hides = self.hides.union(tok.hides, hs);
459            // The outermost invocation wins, because substitution of the outer macro runs
460            // after substitution of the inner ones, and the outer call is the line the user
461            // wrote and the line a diagnostic should point at.
462            tok.expansion = invocation.report_span();
463            // The trace keeps what `expansion` throws away. Every token here already carries
464            // the chain above this macro, so this records one step inside it, and the interning
465            // means the whole replacement list usually shares one node.
466            if let Some(name) = name {
467                tok.trace = self.traces.push(name, invocation.span, tok.trace);
468            }
469        }
470        if let Some(first) = os.first_mut() {
471            first.flags = carried_spacing(invocation.flags);
472        }
473        os
474    }
475
476    /// The recursive half of substitution, which `__VA_OPT__` re-enters for its contents.
477    fn subst_list(
478        &mut self,
479        def: &MacroDef,
480        args: &mut Args,
481        is: &[Tok],
482        invocation: Tok,
483    ) -> Vec<Tok> {
484        let mut os: Vec<Tok> = Vec::with_capacity(is.len());
485        let mut at = 0;
486        // Whitespace owed to the output because the thing that carried it substituted to
487        // nothing. `#define f(a, ...) [a __VA_ARGS__]` invoked as `f(1)` produces `[1 ]`, not
488        // `[1]`, and matching that is part of what makes `-E` output diffable against GCC's,
489        // per `spec/05-preprocessor.md` section 5.6.
490        let mut owed = false;
491        while at < is.len() {
492            let tok = is[at];
493
494            // `# parameter`, and the C23 `# __VA_OPT__(...)`.
495            if def.function_like && tok.is(Punct::Hash) {
496                if let Some(next) = is.get(at + 1) {
497                    if let Some(idx) = next.ident().and_then(|s| def.param_index(s)) {
498                        let text = self.stringize(args.raw(idx));
499                        let string = self.string_token(&text, tok.span.to(next.span));
500                        emit(&mut os, &[string], tok, &mut owed);
501                        at += 2;
502                        continue;
503                    }
504                    if next.ident() == Some(self.va_opt) {
505                        if let Some(inner) = va_opt_group(is, at + 1) {
506                            let close = inner.end;
507                            let raw = if args.raw(def.arity()).is_empty() {
508                                Vec::new()
509                            } else {
510                                self.subst_raw(def, args, &is[inner])
511                            };
512                            let text = self.stringize(&raw);
513                            let string = self.string_token(&text, tok.span.to(is[close].span));
514                            emit(&mut os, &[string], tok, &mut owed);
515                            at = close + 1;
516                            continue;
517                        }
518                    }
519                }
520            }
521
522            // `## operand`. The definition check guarantees there is an operand. A paste
523            // clears any owed whitespace, because the point of it is that the two operands
524            // become one token with nothing between them.
525            if tok.is(Punct::HashHash) {
526                let next = is[at + 1];
527                owed = false;
528                let param = next.ident().and_then(|s| def.param_index(s).map(|idx| (s, idx)));
529                if let Some((name, idx)) = param {
530                    let raw = args.raw(idx).to_vec();
531                    // The GNU extension: in `, ## __VA_ARGS__` the paste is not a paste at
532                    // all. It drops the comma when there are no variable arguments and does
533                    // nothing at all when there are. An enormous amount of existing code
534                    // depends on it and will for another decade.
535                    let comma_variadic = def.is_variadic_param(name)
536                        && os.last().is_some_and(|l| l.is(Punct::Comma));
537                    if comma_variadic {
538                        if raw.is_empty() {
539                            os.pop();
540                        } else {
541                            emit(&mut os, &raw, next, &mut owed);
542                        }
543                    } else {
544                        self.glue(&mut os, &raw, next.span, tok.span);
545                    }
546                    at += 2;
547                    continue;
548                }
549                if next.ident() == Some(self.va_opt) {
550                    if let Some(inner) = va_opt_group(is, at + 1) {
551                        let close = inner.end;
552                        let rhs = self.va_opt_value(def, args, &is[inner], invocation, next.span);
553                        self.glue(&mut os, &rhs, next.span, tok.span);
554                        at = close + 1;
555                        continue;
556                    }
557                }
558                self.glue(&mut os, &[next], next.span, tok.span);
559                at += 2;
560                continue;
561            }
562
563            // `__VA_OPT__(...)` in an ordinary position.
564            if tok.ident() == Some(self.va_opt) {
565                if let Some(inner) = va_opt_group(is, at) {
566                    let close = inner.end;
567                    let value = self.va_opt_value(def, args, &is[inner], invocation, tok.span);
568                    emit(&mut os, &value, tok, &mut owed);
569                    at = close + 1;
570                    continue;
571                }
572            }
573
574            // A parameter. Pasted with what follows it means the raw argument; otherwise the
575            // fully expanded one.
576            if let Some(idx) = tok.ident().and_then(|s| def.param_index(s)) {
577                if is.get(at + 1).is_some_and(|n| n.is(Punct::HashHash)) {
578                    let raw = args.raw(idx).to_vec();
579                    let placemarker = [Tok::placemarker_at(tok.span)];
580                    let value = if raw.is_empty() { &placemarker[..] } else { &raw[..] };
581                    emit(&mut os, value, tok, &mut owed);
582                } else {
583                    let expanded = args.expanded(idx, self).to_vec();
584                    emit(&mut os, &expanded, tok, &mut owed);
585                }
586                at += 1;
587                continue;
588            }
589
590            emit_plain(&mut os, tok, &mut owed);
591            at += 1;
592        }
593        os
594    }
595
596    /// What a `__VA_OPT__(...)` group stands for: its substituted contents when the variadic
597    /// argument has tokens, and a placemarker when it does not.
598    fn va_opt_value(
599        &mut self,
600        def: &MacroDef,
601        args: &mut Args,
602        inner: &[Tok],
603        invocation: Tok,
604        span: Span,
605    ) -> Vec<Tok> {
606        if args.raw(def.arity()).is_empty() {
607            return vec![Tok::placemarker_at(span)];
608        }
609        let value = self.subst_list(def, args, inner, invocation);
610        if value.is_empty() { vec![Tok::placemarker_at(span)] } else { value }
611    }
612
613    /// Substitution with parameters replaced by their unexpanded arguments, which is what
614    /// stringizing a `__VA_OPT__` group needs.
615    fn subst_raw(&mut self, def: &MacroDef, args: &mut Args, inner: &[Tok]) -> Vec<Tok> {
616        let mut out = Vec::with_capacity(inner.len());
617        for &tok in inner {
618            match tok.ident().and_then(|s| def.param_index(s)) {
619                Some(idx) => out.extend_from_slice(args.raw(idx)),
620                None => out.push(tok),
621            }
622        }
623        out
624    }
625
626    /// Pastes `rhs` onto the last token of `os`.
627    ///
628    /// An empty `rhs` is a placemarker, and pasting anything onto a placemarker or a
629    /// placemarker onto anything leaves the anything, which is what makes `a ## b` with an
630    /// empty `b` produce `a` instead of an error.
631    fn glue(&mut self, os: &mut Vec<Tok>, rhs: &[Tok], span: Span, op: Span) {
632        let placemarker = [Tok::placemarker_at(span)];
633        let rhs = if rhs.is_empty() { &placemarker[..] } else { rhs };
634        let Some(lhs) = os.pop() else {
635            os.extend_from_slice(rhs);
636            return;
637        };
638        let first = rhs[0];
639        if lhs.is_placemarker() {
640            os.extend_from_slice(rhs);
641            return;
642        }
643        if first.is_placemarker() {
644            os.push(lhs);
645            os.extend_from_slice(&rhs[1..]);
646            return;
647        }
648        match self.paste(lhs, first, op) {
649            Some(joined) => os.push(joined),
650            None => {
651                // The two were meant to be one token, so they are printed with nothing
652                // between them even though the paste failed. GCC and Clang both do this.
653                let mut first = first;
654                first.flags = TokenFlags::EMPTY;
655                os.push(lhs);
656                os.push(first);
657            }
658        }
659        os.extend_from_slice(&rhs[1..]);
660    }
661
662    /// Concatenates two spellings and re-lexes the result.
663    ///
664    /// A result that is not exactly one preprocessing token is a constraint violation. GCC
665    /// diagnoses it and keeps both tokens, and we do the same, because rejecting the
666    /// translation unit here would stop a build over something that in practice never
667    /// reaches the parser.
668    fn paste(&mut self, lhs: Tok, rhs: Tok, op: Span) -> Option<Tok> {
669        let mut text = String::new();
670        self.spell(lhs, &mut text);
671        let split = text.len();
672        self.spell(rhs, &mut text);
673
674        let (tokens, _) = tokenize(text.as_bytes(), 0, Options::new(), self.interner);
675        let single = tokens.len() == 2
676            && tokens[0].kind != PpTokenKind::Eof
677            && tokens[1].kind == PpTokenKind::Eof
678            && tokens[0].span.lo == 0
679            && tokens[0].span.hi as usize == text.len();
680        if !single {
681            let d = Diagnostic::error(
682                format!(
683                    "pasting `{}` and `{}` does not give a valid preprocessing token",
684                    &text[..split],
685                    &text[split..]
686                ),
687                lhs.report_span().to(rhs.report_span()),
688            )
689            .with_code("E0313")
690            .note("the left operand is here", lhs.span)
691            .note("the right operand is here", rhs.span);
692            let d = self.in_expansions(d, self.current, op);
693            self.diagnostics.push(d);
694            return None;
695        }
696        Some(Tok {
697            kind: tokens[0].kind,
698            flags: lhs.flags,
699            value: tokens[0].value,
700            span: lhs.span.to(rhs.span),
701            expansion: lhs.expansion,
702            trace: lhs.trace,
703            hides: self.hides.union(lhs.hides, rhs.hides),
704            placemarker: false,
705        })
706    }
707
708    /// Builds the string literal `#` produces.
709    ///
710    /// Internal whitespace runs collapse to one space, leading and trailing space is
711    /// dropped, and a backslash or double quote inside a string or character literal is
712    /// escaped, per `spec/05-preprocessor.md` section 5.3.
713    fn stringize(&self, toks: &[Tok]) -> String {
714        let mut out = String::from("\"");
715        let mut first = true;
716        for &tok in toks.iter().filter(|t| !t.is_placemarker()) {
717            if !first && tok.flags.has(TokenFlags::LEADING_SPACE) {
718                out.push(' ');
719            }
720            first = false;
721            let mut spelled = String::new();
722            self.spell(tok, &mut spelled);
723            if matches!(tok.kind, PpTokenKind::StringLit | PpTokenKind::CharConst) {
724                for ch in spelled.chars() {
725                    if ch == '\\' || ch == '"' {
726                        out.push('\\');
727                    }
728                    out.push(ch);
729                }
730            } else {
731                out.push_str(&spelled);
732            }
733        }
734        out.push('"');
735        out
736    }
737
738    /// Wraps stringized text as a token.
739    fn string_token(&mut self, text: &str, span: Span) -> Tok {
740        Tok {
741            kind: PpTokenKind::StringLit,
742            flags: TokenFlags::EMPTY,
743            value: Some(self.interner.intern(text)),
744            span,
745            expansion: Span::DUMMY,
746            trace: TraceId::NONE,
747            hides: HideSet::EMPTY,
748            placemarker: false,
749        }
750    }
751
752    /// Appends a token's spelling.
753    fn spell(&self, tok: Tok, out: &mut String) {
754        if tok.is_placemarker() {
755            return;
756        }
757        match (tok.kind, tok.value) {
758            (PpTokenKind::Punct(p), _) => out.push_str(p.as_str()),
759            (_, Some(sym)) => out.push_str(self.interner.resolve(sym)),
760            (_, None) => {}
761        }
762    }
763}
764
765/// Appends what a body token substituted to.
766///
767/// The first token of the result takes the spacing of the token it replaced, so that
768/// `#define f(x) (x + x)` prints as `(1 + 1)` rather than `(1 +1)`. A group that substituted
769/// to nothing leaves its spacing owed to whatever comes next.
770fn emit(os: &mut Vec<Tok>, value: &[Tok], source: Tok, owed: &mut bool) {
771    let Some((&first, rest)) = value.split_first() else {
772        *owed = *owed || source.flags.has(TokenFlags::LEADING_SPACE);
773        return;
774    };
775    let mut first = first;
776    first.flags = carried_spacing(source.flags);
777    if *owed {
778        first.flags = first.flags.with(TokenFlags::LEADING_SPACE);
779        *owed = false;
780    }
781    os.push(first);
782    os.extend_from_slice(rest);
783}
784
785/// Appends a token that stands for itself, which is every token of a replacement list that
786/// is not a parameter or an operator.
787fn emit_plain(os: &mut Vec<Tok>, tok: Tok, owed: &mut bool) {
788    let mut tok = tok;
789    if *owed {
790        tok.flags = tok.flags.with(TokenFlags::LEADING_SPACE);
791        *owed = false;
792    }
793    os.push(tok);
794}
795
796/// Removes placemarkers, handing any whitespace they carried to the next real token.
797fn drop_placemarkers(toks: Vec<Tok>) -> Vec<Tok> {
798    let mut out = Vec::with_capacity(toks.len());
799    let mut owed = false;
800    for tok in toks {
801        if tok.is_placemarker() {
802            owed = owed || tok.flags.has(TokenFlags::LEADING_SPACE);
803            continue;
804        }
805        emit_plain(&mut out, tok, &mut owed);
806    }
807    out
808}
809
810/// Finds the parenthesised group belonging to a `__VA_OPT__` at `at`.
811///
812/// Returns the range of the contents. The closing parenthesis is at `range.end`, so the
813/// group ends at `range.end + 1`, which is what every caller wants next.
814fn va_opt_group(is: &[Tok], at: usize) -> Option<std::ops::Range<usize>> {
815    if !is.get(at + 1).is_some_and(|t| t.is(Punct::LParen)) {
816        return None;
817    }
818    let start = at + 2;
819    let mut depth = 1usize;
820    let mut end = start;
821    while end < is.len() {
822        match is[end].punct() {
823            Some(Punct::LParen) => depth += 1,
824            Some(Punct::RParen) => {
825                depth -= 1;
826                if depth == 0 {
827                    return Some(start..end);
828                }
829            }
830            _ => {}
831        }
832        end += 1;
833    }
834    None
835}
836
837/// Pushes a replacement onto the front of the pushback stack, preserving its order.
838fn push_front(pending: &mut Vec<Tok>, mut replacement: Vec<Tok>, invocation: Tok) {
839    // An expansion that came to nothing still leaves its spacing behind. `#define E` used as
840    // `int a E;` preprocesses to `int a ;` and not to `int a;`, in GCC and in clang both. On
841    // the glibc headers that is most of the difference between agreeing with the reference and
842    // not, because `__THROW` and the rest of the attribute macros expand to nothing on a
843    // non-GNU dialect and sit next to a `;` or a `,` several hundred times per header.
844    //
845    // The space is handed to whatever gets rescanned next, which may itself be a macro that
846    // vanishes, so `a E E E b` walks the debt along until something real takes it. Only the
847    // space carries: a vanished macro cannot start a line that its own replacement did not.
848    if replacement.is_empty() {
849        if invocation.flags.has(TokenFlags::LEADING_SPACE) {
850            if let Some(next) = pending.last_mut() {
851                next.flags = next.flags.with(TokenFlags::LEADING_SPACE);
852            }
853        }
854        return;
855    }
856    replacement.reverse();
857    pending.append(&mut replacement);
858}
859
860/// The flags a replacement's first token inherits from the invocation.
861///
862/// Only spacing carries over. A macro that expanded from a spliced or digraph token did not
863/// itself come from one, and saying it did would put the wrong thing in `-E` output.
864fn carried_spacing(flags: TokenFlags) -> TokenFlags {
865    let mut carried = TokenFlags::EMPTY;
866    if flags.has(TokenFlags::START_OF_LINE) {
867        carried = carried.with(TokenFlags::START_OF_LINE);
868    }
869    if flags.has(TokenFlags::LEADING_SPACE) {
870        carried = carried.with(TokenFlags::LEADING_SPACE);
871    }
872    carried
873}
874
875/// The arguments of one invocation, raw and expanded.
876///
877/// An argument used twice in a replacement list is expanded once. That is not just a saving:
878/// `spec/02-the-goal.md` wants the same input to produce the same diagnostics, and expanding
879/// an argument twice would report anything wrong inside it twice.
880struct Args {
881    raw: Vec<Vec<Tok>>,
882    expanded: Vec<Option<Vec<Tok>>>,
883    /// The chain the invocation itself came out of, which is the chain the argument text is
884    /// in as well, since the caller wrote it and the macro being called did not.
885    outer: TraceId,
886}
887
888impl Args {
889    fn new(raw: Vec<Vec<Tok>>, outer: TraceId) -> Args {
890        let count = raw.len();
891        Args { raw, expanded: vec![None; count], outer }
892    }
893
894    /// The argument list of an object-like macro, which has none.
895    fn none() -> Args {
896        Args { raw: Vec::new(), expanded: Vec::new(), outer: TraceId::NONE }
897    }
898
899    fn raw(&self, idx: usize) -> &[Tok] {
900        self.raw.get(idx).map_or(&[][..], |a| a.as_slice())
901    }
902
903    fn expanded(&mut self, idx: usize, run: &mut Run<'_>) -> &[Tok] {
904        let Some(slot) = self.expanded.get(idx) else {
905            return &[];
906        };
907        if slot.is_none() {
908            let saved = std::mem::replace(&mut run.current, self.outer);
909            let expanded = run.expand(self.raw[idx].clone());
910            run.current = saved;
911            self.expanded[idx] = Some(expanded);
912        }
913        self.expanded[idx].as_deref().expect("just filled in")
914    }
915}