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