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 fn name_of(&self, at: BytePos) -> &str {
270 match self.sources.lookup_file(at) {
271 Some(file) => &self.sources.file(file).name,
272 None => UNKNOWN,
273 }
274 }
275
276 /// The line `at` is on, counting from one.
277 ///
278 /// Zero for a position in no file, which is a token the preprocessor made up rather than
279 /// read. Nothing in a real translation unit gets there, and answering zero is better than
280 /// answering with some other file's line.
281 fn line_of(&self, at: BytePos) -> u32 {
282 self.sources.lookup(at).map_or(0, |loc| loc.line)
283 }
284
285 /// The file at the bottom of the include stack, which is the one on the command line.
286 fn base_file(&self, at: BytePos) -> &str {
287 match self.sources.include_stack(at).last() {
288 Some(outermost) => self.name_of(outermost.lo),
289 None => self.name_of(at),
290 }
291 }
292
293 /// Reads an argument list, `pending` positioned on the opening parenthesis.
294 ///
295 /// Returns the arguments and the closing parenthesis token, whose hide set the caller
296 /// needs. Returns `None` after reporting a problem, in which case the macro name is
297 /// emitted unexpanded and the argument tokens are dropped, which is what GCC and Clang
298 /// both do: an argument list that does not fit the macro has no useful reading and
299 /// putting it back only produces a second error from the parser.
300 fn collect_args(
301 &mut self,
302 def: &MacroDef,
303 pending: &mut Vec<Tok>,
304 name: Tok,
305 ) -> Option<(Vec<Vec<Tok>>, Tok)> {
306 let open = pending.pop().expect("the caller checked for an opening parenthesis");
307 let mut args: Vec<Vec<Tok>> = Vec::with_capacity(def.arity() + 1);
308 let mut current: Vec<Tok> = Vec::new();
309 let mut depth = 1usize;
310 let rparen = loop {
311 let Some(tok) = pending.pop() else {
312 let d = Diagnostic::error("unterminated macro argument list", open.report_span())
313 .with_code("E0311")
314 .note("this macro was invoked here", name.report_span());
315 let d = self.in_expansions(d, name.trace, name.span);
316 self.diagnostics.push(d);
317 return None;
318 };
319 match tok.punct() {
320 Some(Punct::LParen) => {
321 depth += 1;
322 current.push(tok);
323 }
324 Some(Punct::RParen) => {
325 depth -= 1;
326 if depth == 0 {
327 break tok;
328 }
329 current.push(tok);
330 }
331 // Once the named parameters are filled, a variadic macro's remaining commas
332 // are part of the last argument rather than separators.
333 Some(Punct::Comma)
334 if depth == 1 && !(def.is_variadic() && args.len() >= def.arity()) =>
335 {
336 args.push(std::mem::take(&mut current));
337 }
338 _ => current.push(tok),
339 }
340 };
341
342 // `F()` on a macro that takes nothing is no arguments. On a macro that takes one, the
343 // same text is one empty argument, which is why this cannot be decided by looking at
344 // the tokens alone.
345 let empty_invocation = args.is_empty() && current.is_empty();
346 if !(empty_invocation && def.arity() == 0 && !def.is_variadic()) {
347 args.push(current);
348 }
349 if def.is_variadic() && args.len() == def.arity() {
350 args.push(Vec::new());
351 }
352
353 let expected = def.arity() + usize::from(def.is_variadic());
354 if args.len() != expected {
355 let word = if args.len() < expected { "few" } else { "many" };
356 let d = Diagnostic::error(
357 format!(
358 "too {word} arguments to macro `{}`, expected {}{}, got {}",
359 self.interner.resolve(def.name),
360 def.arity(),
361 if def.is_variadic() { " or more" } else { "" },
362 args.len()
363 ),
364 name.report_span(),
365 )
366 .with_code("E0312")
367 .note("defined here", def.span);
368 let d = self.in_expansions(d, name.trace, name.span);
369 self.diagnostics.push(d);
370 return None;
371 }
372 Some((args, rparen))
373 }
374
375 /// Appends the chain of macros `trace` records to `d`, outermost first.
376 ///
377 /// The diagnostic itself points at the outermost invocation, because that is the line the
378 /// user wrote. Each note then names one macro and points at where the next thing in was
379 /// written, so a reader walks from their own code into the header that surprised them
380 /// rather than being handed both ends and left to guess the middle. The last note points
381 /// at `innermost`, which is where inside the innermost macro's body the trouble is.
382 ///
383 /// A token the user wrote has an empty chain and gets nothing added, which is the common
384 /// case and is why this is cheap to call unconditionally.
385 fn in_expansions(&self, mut d: Diagnostic, trace: TraceId, innermost: Span) -> Diagnostic {
386 let chain = self.traces.chain(trace);
387 for (i, step) in chain.iter().enumerate() {
388 let at = chain.get(i + 1).map_or(innermost, |next| next.at);
389 let name = self.interner.resolve(step.macro_name);
390 d = d.note(format!("expanded from macro `{name}`"), at);
391 }
392 d
393 }
394
395 /// Argument substitution over a replacement list.
396 ///
397 /// The order of the cases matters and each one of them is a known source of bugs, so
398 /// they are written out separately rather than folded together.
399 fn subst(&mut self, def: &MacroDef, args: &mut Args, hs: HideSet, invocation: Tok) -> Vec<Tok> {
400 // The name is always there: `subst` is only reached through an identifier that looked
401 // a macro up. The fallback keeps the trace merely incomplete rather than making this a
402 // panic on a path the compiler is not supposed to be able to take.
403 let name = invocation.ident();
404 // The chain for everything this expansion produces, known before the body is walked so
405 // that a diagnostic raised while walking it can say which macro it is inside. The
406 // invocation's own trace is the chain above, which is right whether it came from the
407 // user's file or from three macros further out.
408 let here = match name {
409 Some(name) => self.traces.push(name, invocation.span, invocation.trace),
410 None => invocation.trace,
411 };
412 let outer = std::mem::replace(&mut self.current, here);
413 // Body tokens start with the chain of the invocation rather than with none, so that a
414 // token written in this body comes out with the macros above this one on it. An
415 // argument token already has that chain, having been substituted from the call site.
416 let body: Vec<Tok> =
417 def.body.iter().map(|&t| Tok { trace: invocation.trace, ..Tok::new(t) }).collect();
418 let substituted = self.subst_list(def, args, &body, invocation);
419 self.current = outer;
420 let mut os = drop_placemarkers(substituted);
421 for tok in &mut os {
422 tok.hides = self.hides.union(tok.hides, hs);
423 // The outermost invocation wins, because substitution of the outer macro runs
424 // after substitution of the inner ones, and the outer call is the line the user
425 // wrote and the line a diagnostic should point at.
426 tok.expansion = invocation.report_span();
427 // The trace keeps what `expansion` throws away. Every token here already carries
428 // the chain above this macro, so this records one step inside it, and the interning
429 // means the whole replacement list usually shares one node.
430 if let Some(name) = name {
431 tok.trace = self.traces.push(name, invocation.span, tok.trace);
432 }
433 }
434 if let Some(first) = os.first_mut() {
435 first.flags = carried_spacing(invocation.flags);
436 }
437 os
438 }
439
440 /// The recursive half of substitution, which `__VA_OPT__` re-enters for its contents.
441 fn subst_list(
442 &mut self,
443 def: &MacroDef,
444 args: &mut Args,
445 is: &[Tok],
446 invocation: Tok,
447 ) -> Vec<Tok> {
448 let mut os: Vec<Tok> = Vec::with_capacity(is.len());
449 let mut at = 0;
450 // Whitespace owed to the output because the thing that carried it substituted to
451 // nothing. `#define f(a, ...) [a __VA_ARGS__]` invoked as `f(1)` produces `[1 ]`, not
452 // `[1]`, and matching that is part of what makes `-E` output diffable against GCC's,
453 // per `spec/05-preprocessor.md` section 5.6.
454 let mut owed = false;
455 while at < is.len() {
456 let tok = is[at];
457
458 // `# parameter`, and the C23 `# __VA_OPT__(...)`.
459 if def.function_like && tok.is(Punct::Hash) {
460 if let Some(next) = is.get(at + 1) {
461 if let Some(idx) = next.ident().and_then(|s| def.param_index(s)) {
462 let text = self.stringize(args.raw(idx));
463 let string = self.string_token(&text, tok.span.to(next.span));
464 emit(&mut os, &[string], tok, &mut owed);
465 at += 2;
466 continue;
467 }
468 if next.ident() == Some(self.va_opt) {
469 if let Some(inner) = va_opt_group(is, at + 1) {
470 let close = inner.end;
471 let raw = if args.raw(def.arity()).is_empty() {
472 Vec::new()
473 } else {
474 self.subst_raw(def, args, &is[inner])
475 };
476 let text = self.stringize(&raw);
477 let string = self.string_token(&text, tok.span.to(is[close].span));
478 emit(&mut os, &[string], tok, &mut owed);
479 at = close + 1;
480 continue;
481 }
482 }
483 }
484 }
485
486 // `## operand`. The definition check guarantees there is an operand. A paste
487 // clears any owed whitespace, because the point of it is that the two operands
488 // become one token with nothing between them.
489 if tok.is(Punct::HashHash) {
490 let next = is[at + 1];
491 owed = false;
492 let param = next.ident().and_then(|s| def.param_index(s).map(|idx| (s, idx)));
493 if let Some((name, idx)) = param {
494 let raw = args.raw(idx).to_vec();
495 // The GNU extension: in `, ## __VA_ARGS__` the paste is not a paste at
496 // all. It drops the comma when there are no variable arguments and does
497 // nothing at all when there are. An enormous amount of existing code
498 // depends on it and will for another decade.
499 let comma_variadic = def.is_variadic_param(name)
500 && os.last().is_some_and(|l| l.is(Punct::Comma));
501 if comma_variadic {
502 if raw.is_empty() {
503 os.pop();
504 } else {
505 emit(&mut os, &raw, next, &mut owed);
506 }
507 } else {
508 self.glue(&mut os, &raw, next.span, tok.span);
509 }
510 at += 2;
511 continue;
512 }
513 if next.ident() == Some(self.va_opt) {
514 if let Some(inner) = va_opt_group(is, at + 1) {
515 let close = inner.end;
516 let rhs = self.va_opt_value(def, args, &is[inner], invocation, next.span);
517 self.glue(&mut os, &rhs, next.span, tok.span);
518 at = close + 1;
519 continue;
520 }
521 }
522 self.glue(&mut os, &[next], next.span, tok.span);
523 at += 2;
524 continue;
525 }
526
527 // `__VA_OPT__(...)` in an ordinary position.
528 if tok.ident() == Some(self.va_opt) {
529 if let Some(inner) = va_opt_group(is, at) {
530 let close = inner.end;
531 let value = self.va_opt_value(def, args, &is[inner], invocation, tok.span);
532 emit(&mut os, &value, tok, &mut owed);
533 at = close + 1;
534 continue;
535 }
536 }
537
538 // A parameter. Pasted with what follows it means the raw argument; otherwise the
539 // fully expanded one.
540 if let Some(idx) = tok.ident().and_then(|s| def.param_index(s)) {
541 if is.get(at + 1).is_some_and(|n| n.is(Punct::HashHash)) {
542 let raw = args.raw(idx).to_vec();
543 let placemarker = [Tok::placemarker_at(tok.span)];
544 let value = if raw.is_empty() { &placemarker[..] } else { &raw[..] };
545 emit(&mut os, value, tok, &mut owed);
546 } else {
547 let expanded = args.expanded(idx, self).to_vec();
548 emit(&mut os, &expanded, tok, &mut owed);
549 }
550 at += 1;
551 continue;
552 }
553
554 emit_plain(&mut os, tok, &mut owed);
555 at += 1;
556 }
557 os
558 }
559
560 /// What a `__VA_OPT__(...)` group stands for: its substituted contents when the variadic
561 /// argument has tokens, and a placemarker when it does not.
562 fn va_opt_value(
563 &mut self,
564 def: &MacroDef,
565 args: &mut Args,
566 inner: &[Tok],
567 invocation: Tok,
568 span: Span,
569 ) -> Vec<Tok> {
570 if args.raw(def.arity()).is_empty() {
571 return vec![Tok::placemarker_at(span)];
572 }
573 let value = self.subst_list(def, args, inner, invocation);
574 if value.is_empty() { vec![Tok::placemarker_at(span)] } else { value }
575 }
576
577 /// Substitution with parameters replaced by their unexpanded arguments, which is what
578 /// stringizing a `__VA_OPT__` group needs.
579 fn subst_raw(&mut self, def: &MacroDef, args: &mut Args, inner: &[Tok]) -> Vec<Tok> {
580 let mut out = Vec::with_capacity(inner.len());
581 for &tok in inner {
582 match tok.ident().and_then(|s| def.param_index(s)) {
583 Some(idx) => out.extend_from_slice(args.raw(idx)),
584 None => out.push(tok),
585 }
586 }
587 out
588 }
589
590 /// Pastes `rhs` onto the last token of `os`.
591 ///
592 /// An empty `rhs` is a placemarker, and pasting anything onto a placemarker or a
593 /// placemarker onto anything leaves the anything, which is what makes `a ## b` with an
594 /// empty `b` produce `a` instead of an error.
595 fn glue(&mut self, os: &mut Vec<Tok>, rhs: &[Tok], span: Span, op: Span) {
596 let placemarker = [Tok::placemarker_at(span)];
597 let rhs = if rhs.is_empty() { &placemarker[..] } else { rhs };
598 let Some(lhs) = os.pop() else {
599 os.extend_from_slice(rhs);
600 return;
601 };
602 let first = rhs[0];
603 if lhs.is_placemarker() {
604 os.extend_from_slice(rhs);
605 return;
606 }
607 if first.is_placemarker() {
608 os.push(lhs);
609 os.extend_from_slice(&rhs[1..]);
610 return;
611 }
612 match self.paste(lhs, first, op) {
613 Some(joined) => os.push(joined),
614 None => {
615 // The two were meant to be one token, so they are printed with nothing
616 // between them even though the paste failed. GCC and Clang both do this.
617 let mut first = first;
618 first.flags = TokenFlags::EMPTY;
619 os.push(lhs);
620 os.push(first);
621 }
622 }
623 os.extend_from_slice(&rhs[1..]);
624 }
625
626 /// Concatenates two spellings and re-lexes the result.
627 ///
628 /// A result that is not exactly one preprocessing token is a constraint violation. GCC
629 /// diagnoses it and keeps both tokens, and we do the same, because rejecting the
630 /// translation unit here would stop a build over something that in practice never
631 /// reaches the parser.
632 fn paste(&mut self, lhs: Tok, rhs: Tok, op: Span) -> Option<Tok> {
633 let mut text = String::new();
634 self.spell(lhs, &mut text);
635 let split = text.len();
636 self.spell(rhs, &mut text);
637
638 let (tokens, _) = tokenize(text.as_bytes(), 0, Options::new(), self.interner);
639 let single = tokens.len() == 2
640 && tokens[0].kind != PpTokenKind::Eof
641 && tokens[1].kind == PpTokenKind::Eof
642 && tokens[0].span.lo == 0
643 && tokens[0].span.hi as usize == text.len();
644 if !single {
645 let d = Diagnostic::error(
646 format!(
647 "pasting `{}` and `{}` does not give a valid preprocessing token",
648 &text[..split],
649 &text[split..]
650 ),
651 lhs.report_span().to(rhs.report_span()),
652 )
653 .with_code("E0313")
654 .note("the left operand is here", lhs.span)
655 .note("the right operand is here", rhs.span);
656 let d = self.in_expansions(d, self.current, op);
657 self.diagnostics.push(d);
658 return None;
659 }
660 Some(Tok {
661 kind: tokens[0].kind,
662 flags: lhs.flags,
663 value: tokens[0].value,
664 span: lhs.span.to(rhs.span),
665 expansion: lhs.expansion,
666 trace: lhs.trace,
667 hides: self.hides.union(lhs.hides, rhs.hides),
668 placemarker: false,
669 })
670 }
671
672 /// Builds the string literal `#` produces.
673 ///
674 /// Internal whitespace runs collapse to one space, leading and trailing space is
675 /// dropped, and a backslash or double quote inside a string or character literal is
676 /// escaped, per `spec/05-preprocessor.md` section 5.3.
677 fn stringize(&self, toks: &[Tok]) -> String {
678 let mut out = String::from("\"");
679 let mut first = true;
680 for &tok in toks.iter().filter(|t| !t.is_placemarker()) {
681 if !first && tok.flags.has(TokenFlags::LEADING_SPACE) {
682 out.push(' ');
683 }
684 first = false;
685 let mut spelled = String::new();
686 self.spell(tok, &mut spelled);
687 if matches!(tok.kind, PpTokenKind::StringLit | PpTokenKind::CharConst) {
688 for ch in spelled.chars() {
689 if ch == '\\' || ch == '"' {
690 out.push('\\');
691 }
692 out.push(ch);
693 }
694 } else {
695 out.push_str(&spelled);
696 }
697 }
698 out.push('"');
699 out
700 }
701
702 /// Wraps stringized text as a token.
703 fn string_token(&mut self, text: &str, span: Span) -> Tok {
704 Tok {
705 kind: PpTokenKind::StringLit,
706 flags: TokenFlags::EMPTY,
707 value: Some(self.interner.intern(text)),
708 span,
709 expansion: Span::DUMMY,
710 trace: TraceId::NONE,
711 hides: HideSet::EMPTY,
712 placemarker: false,
713 }
714 }
715
716 /// Appends a token's spelling.
717 fn spell(&self, tok: Tok, out: &mut String) {
718 if tok.is_placemarker() {
719 return;
720 }
721 match (tok.kind, tok.value) {
722 (PpTokenKind::Punct(p), _) => out.push_str(p.as_str()),
723 (_, Some(sym)) => out.push_str(self.interner.resolve(sym)),
724 (_, None) => {}
725 }
726 }
727}
728
729/// Appends what a body token substituted to.
730///
731/// The first token of the result takes the spacing of the token it replaced, so that
732/// `#define f(x) (x + x)` prints as `(1 + 1)` rather than `(1 +1)`. A group that substituted
733/// to nothing leaves its spacing owed to whatever comes next.
734fn emit(os: &mut Vec<Tok>, value: &[Tok], source: Tok, owed: &mut bool) {
735 let Some((&first, rest)) = value.split_first() else {
736 *owed = *owed || source.flags.has(TokenFlags::LEADING_SPACE);
737 return;
738 };
739 let mut first = first;
740 first.flags = carried_spacing(source.flags);
741 if *owed {
742 first.flags = first.flags.with(TokenFlags::LEADING_SPACE);
743 *owed = false;
744 }
745 os.push(first);
746 os.extend_from_slice(rest);
747}
748
749/// Appends a token that stands for itself, which is every token of a replacement list that
750/// is not a parameter or an operator.
751fn emit_plain(os: &mut Vec<Tok>, tok: Tok, owed: &mut bool) {
752 let mut tok = tok;
753 if *owed {
754 tok.flags = tok.flags.with(TokenFlags::LEADING_SPACE);
755 *owed = false;
756 }
757 os.push(tok);
758}
759
760/// Removes placemarkers, handing any whitespace they carried to the next real token.
761fn drop_placemarkers(toks: Vec<Tok>) -> Vec<Tok> {
762 let mut out = Vec::with_capacity(toks.len());
763 let mut owed = false;
764 for tok in toks {
765 if tok.is_placemarker() {
766 owed = owed || tok.flags.has(TokenFlags::LEADING_SPACE);
767 continue;
768 }
769 emit_plain(&mut out, tok, &mut owed);
770 }
771 out
772}
773
774/// Finds the parenthesised group belonging to a `__VA_OPT__` at `at`.
775///
776/// Returns the range of the contents. The closing parenthesis is at `range.end`, so the
777/// group ends at `range.end + 1`, which is what every caller wants next.
778fn va_opt_group(is: &[Tok], at: usize) -> Option<std::ops::Range<usize>> {
779 if !is.get(at + 1).is_some_and(|t| t.is(Punct::LParen)) {
780 return None;
781 }
782 let start = at + 2;
783 let mut depth = 1usize;
784 let mut end = start;
785 while end < is.len() {
786 match is[end].punct() {
787 Some(Punct::LParen) => depth += 1,
788 Some(Punct::RParen) => {
789 depth -= 1;
790 if depth == 0 {
791 return Some(start..end);
792 }
793 }
794 _ => {}
795 }
796 end += 1;
797 }
798 None
799}
800
801/// Pushes a replacement onto the front of the pushback stack, preserving its order.
802fn push_front(pending: &mut Vec<Tok>, mut replacement: Vec<Tok>, invocation: Tok) {
803 // An expansion that came to nothing still leaves its spacing behind. `#define E` used as
804 // `int a E;` preprocesses to `int a ;` and not to `int a;`, in GCC and in clang both. On
805 // the glibc headers that is most of the difference between agreeing with the reference and
806 // not, because `__THROW` and the rest of the attribute macros expand to nothing on a
807 // non-GNU dialect and sit next to a `;` or a `,` several hundred times per header.
808 //
809 // The space is handed to whatever gets rescanned next, which may itself be a macro that
810 // vanishes, so `a E E E b` walks the debt along until something real takes it. Only the
811 // space carries: a vanished macro cannot start a line that its own replacement did not.
812 if replacement.is_empty() {
813 if invocation.flags.has(TokenFlags::LEADING_SPACE) {
814 if let Some(next) = pending.last_mut() {
815 next.flags = next.flags.with(TokenFlags::LEADING_SPACE);
816 }
817 }
818 return;
819 }
820 replacement.reverse();
821 pending.append(&mut replacement);
822}
823
824/// The flags a replacement's first token inherits from the invocation.
825///
826/// Only spacing carries over. A macro that expanded from a spliced or digraph token did not
827/// itself come from one, and saying it did would put the wrong thing in `-E` output.
828fn carried_spacing(flags: TokenFlags) -> TokenFlags {
829 let mut carried = TokenFlags::EMPTY;
830 if flags.has(TokenFlags::START_OF_LINE) {
831 carried = carried.with(TokenFlags::START_OF_LINE);
832 }
833 if flags.has(TokenFlags::LEADING_SPACE) {
834 carried = carried.with(TokenFlags::LEADING_SPACE);
835 }
836 carried
837}
838
839/// The arguments of one invocation, raw and expanded.
840///
841/// An argument used twice in a replacement list is expanded once. That is not just a saving:
842/// `spec/02-the-goal.md` wants the same input to produce the same diagnostics, and expanding
843/// an argument twice would report anything wrong inside it twice.
844struct Args {
845 raw: Vec<Vec<Tok>>,
846 expanded: Vec<Option<Vec<Tok>>>,
847 /// The chain the invocation itself came out of, which is the chain the argument text is
848 /// in as well, since the caller wrote it and the macro being called did not.
849 outer: TraceId,
850}
851
852impl Args {
853 fn new(raw: Vec<Vec<Tok>>, outer: TraceId) -> Args {
854 let count = raw.len();
855 Args { raw, expanded: vec![None; count], outer }
856 }
857
858 /// The argument list of an object-like macro, which has none.
859 fn none() -> Args {
860 Args { raw: Vec::new(), expanded: Vec::new(), outer: TraceId::NONE }
861 }
862
863 fn raw(&self, idx: usize) -> &[Tok] {
864 self.raw.get(idx).map_or(&[][..], |a| a.as_slice())
865 }
866
867 fn expanded(&mut self, idx: usize, run: &mut Run<'_>) -> &[Tok] {
868 let Some(slot) = self.expanded.get(idx) else {
869 return &[];
870 };
871 if slot.is_none() {
872 let saved = std::mem::replace(&mut run.current, self.outer);
873 let expanded = run.expand(self.raw[idx].clone());
874 run.current = saved;
875 self.expanded[idx] = Some(expanded);
876 }
877 self.expanded[idx].as_deref().expect("just filled in")
878 }
879}