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}