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