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}