1#![doc(html_root_url = "https://docs.rs/rucc-pp/0.2.0")]
78
79mod cond;
80mod directive;
81mod dump;
82mod embed;
83mod expand;
84mod hide;
85mod include;
86mod macros;
87mod predef;
88mod print;
89mod token;
90mod trace;
91
92pub use crate::directive::{LineDirective, Preprocessor};
93pub use crate::dump::macros as dump_macros;
94pub use crate::expand::Expander;
95pub use crate::hide::{HideSet, HideSets};
96pub use crate::include::Context;
97pub use crate::macros::{Builtin, MacroDef, MacroTable, parse_define};
98pub use crate::predef::{BUILT_IN, COMMAND_LINE, Predef, Timestamp};
99pub use crate::print::{PrintOptions, print};
102pub use crate::token::Tok;
103pub use crate::trace::{Step, TraceId, Traces};
104pub use rucc_session::GnucVersion;
105
106pub const MILESTONE: &str = "M1";
108
109#[cfg(test)]
110mod tests {
111 use rucc_base::Interner;
112 use rucc_diag::{Diagnostic, SourceMap, Span};
113 use rucc_lex::{Options, PpToken, PpTokenKind, TokenFlags, tokenize};
114
115 use super::*;
116
117 struct Pp {
119 interner: Interner,
120 macros: MacroTable,
121 expander: Expander,
122 sources: SourceMap,
126 }
127
128 impl Pp {
129 fn new() -> Pp {
130 Pp {
131 interner: Interner::new(),
132 macros: MacroTable::new(),
133 expander: Expander::new(),
134 sources: SourceMap::new(),
135 }
136 }
137
138 fn lex(&mut self, src: &str) -> Vec<PpToken> {
139 let (tokens, errors) = tokenize(src.as_bytes(), 0, Options::new(), &mut self.interner);
140 assert!(errors.is_empty(), "test input should lex cleanly: {errors:?}");
141 tokens.into_iter().filter(|t| t.kind != PpTokenKind::Eof).collect()
142 }
143
144 fn define(&mut self, line: &str) {
146 let tokens = self.lex(line);
147 let (def, errors) = parse_define(&tokens, &mut self.interner);
148 assert!(errors.is_empty(), "definition should be clean: {errors:?}");
149 self.macros.define(def.expect("should parse"), &self.interner);
150 }
151
152 fn undef(&mut self, name: &str) {
153 let sym = self.interner.intern(name);
154 self.macros.undef(sym);
155 }
156
157 fn expand(&mut self, src: &str) -> Vec<Tok> {
158 let tokens = self.lex(src);
159 self.expander.expand(&tokens, &self.macros, &mut self.interner, &self.sources)
160 }
161
162 fn text(&mut self, src: &str) -> String {
166 let out = self.expand(src);
167 let mut text = String::new();
168 for (at, tok) in out.iter().enumerate() {
169 if at > 0 && tok.flags.has(TokenFlags::LEADING_SPACE) {
170 text.push(' ');
171 }
172 match tok.kind {
173 PpTokenKind::Punct(p) => text.push_str(p.as_str()),
174 _ => text.push_str(
175 self.interner.resolve(tok.value.expect("every non-punctuator interns")),
176 ),
177 }
178 }
179 text
180 }
181
182 fn errors(&mut self) -> Vec<Diagnostic> {
183 self.expander.take_diagnostics()
184 }
185 }
186
187 #[test]
188 fn an_object_like_macro_is_replaced_by_its_body() {
189 let mut pp = Pp::new();
190 pp.define("N 42");
191 assert_eq!(pp.text("int a = N;"), "int a = 42;");
192 }
193
194 #[test]
195 fn an_undefined_identifier_is_left_alone() {
196 let mut pp = Pp::new();
197 assert_eq!(pp.text("int a = N;"), "int a = N;");
198 }
199
200 #[test]
201 fn a_function_like_macro_needs_a_parenthesis_to_be_invoked() {
202 let mut pp = Pp::new();
203 pp.define("f(x) x");
204 assert_eq!(pp.text("f"), "f", "a bare name is an ordinary identifier");
205 assert_eq!(pp.text("f (1)"), "1", "whitespace before the parenthesis is fine");
206 }
207
208 #[test]
209 fn arguments_are_expanded_before_they_are_substituted() {
210 let mut pp = Pp::new();
211 pp.define("ONE 1");
212 pp.define("f(x) (x + x)");
213 assert_eq!(pp.text("f(ONE)"), "(1 + 1)");
214 }
215
216 #[test]
217 fn an_empty_argument_is_an_argument() {
218 let mut pp = Pp::new();
219 pp.define("f(x) [x]");
220 assert_eq!(pp.text("f()"), "[]");
221 }
222
223 #[test]
224 fn a_macro_with_no_parameters_takes_no_arguments() {
225 let mut pp = Pp::new();
226 pp.define("f() nothing");
227 assert_eq!(pp.text("f()"), "nothing");
228 }
229
230 #[test]
231 fn a_comma_inside_parentheses_does_not_split_an_argument() {
232 let mut pp = Pp::new();
233 pp.define("f(x) [x]");
234 assert_eq!(pp.text("f((1, 2))"), "[(1, 2)]");
235 }
236
237 #[test]
238 fn an_invocation_may_span_lines() {
239 let mut pp = Pp::new();
240 pp.define("f(a, b) a b");
241 assert_eq!(pp.text("f(1,\n 2)"), "1 2");
242 }
243
244 #[test]
245 fn a_replacement_may_consume_tokens_that_follow_the_invocation() {
246 let mut pp = Pp::new();
247 pp.define("f(x) [x]");
248 pp.define("g f");
249 assert_eq!(pp.text("g(1)"), "[1]");
253 }
254
255 #[test]
256 fn a_parenthesis_that_has_not_expanded_yet_does_not_count() {
257 let mut pp = Pp::new();
258 pp.define("lparen (");
259 pp.define("f(x) [x]");
260 pp.define("g f lparen 1 )");
261 assert_eq!(pp.text("g"), "f ( 1 )");
266 }
267
268 #[test]
269 fn a_macro_does_not_expand_inside_its_own_expansion() {
270 let mut pp = Pp::new();
271 pp.define("A A + 1");
272 assert_eq!(pp.text("A"), "A + 1");
273 }
274
275 #[test]
276 fn mutually_recursive_macros_terminate_with_both_names_left() {
277 let mut pp = Pp::new();
278 pp.define("A B");
279 pp.define("B A");
280 assert_eq!(pp.text("A"), "A");
281 assert_eq!(pp.text("B"), "B");
282 }
283
284 #[test]
285 fn a_hidden_name_stays_hidden_when_it_is_carried_outwards() {
286 let mut pp = Pp::new();
290 pp.define("f(x) x");
291 pp.define("g(y) [y]");
292 pp.define("h f(h)");
293 assert_eq!(pp.text("g(h)"), "[h]");
294 }
295
296 #[test]
297 fn stringify_puts_the_argument_in_quotes() {
298 let mut pp = Pp::new();
299 pp.define("str(x) #x");
300 assert_eq!(pp.text("str(hello)"), "\"hello\"");
301 }
302
303 #[test]
304 fn stringify_uses_the_unexpanded_argument() {
305 let mut pp = Pp::new();
306 pp.define("N 42");
307 pp.define("str(x) #x");
308 pp.define("xstr(x) str(x)");
309 assert_eq!(pp.text("str(N)"), "\"N\"");
310 assert_eq!(pp.text("xstr(N)"), "\"42\"", "one level of indirection expands first");
311 }
312
313 #[test]
314 fn stringify_collapses_whitespace_and_drops_it_at_the_edges() {
315 let mut pp = Pp::new();
316 pp.define("str(x) #x");
317 assert_eq!(pp.text("str( a + b )"), "\"a + b\"");
318 }
319
320 #[test]
321 fn stringify_escapes_quotes_and_backslashes_inside_literals() {
322 let mut pp = Pp::new();
323 pp.define("str(x) #x");
324 assert_eq!(pp.text(r#"str("a\n")"#), r#""\"a\\n\"""#);
325 }
326
327 #[test]
328 fn paste_joins_two_tokens_into_one() {
329 let mut pp = Pp::new();
330 pp.define("cat(a, b) a ## b");
331 assert_eq!(pp.text("cat(foo, bar)"), "foobar");
332 assert_eq!(pp.text("cat(1, 2)"), "12");
333 assert_eq!(pp.text("cat(+, =)"), "+=");
334 }
335
336 #[test]
337 fn paste_uses_the_unexpanded_arguments() {
338 let mut pp = Pp::new();
339 pp.define("N 42");
340 pp.define("cat(a, b) a ## b");
341 assert_eq!(pp.text("cat(N, N)"), "NN");
342 }
343
344 #[test]
345 fn the_result_of_a_paste_is_rescanned() {
346 let mut pp = Pp::new();
347 pp.define("foobar yes");
348 pp.define("cat(a, b) a ## b");
349 assert_eq!(pp.text("cat(foo, bar)"), "yes");
350 }
351
352 #[test]
353 fn pasting_an_empty_argument_leaves_the_other_side() {
354 let mut pp = Pp::new();
355 pp.define("cat(a, b) a ## b");
356 assert_eq!(pp.text("cat(foo,)"), "foo");
357 assert_eq!(pp.text("cat(, bar)"), "bar");
358 assert_eq!(pp.text("cat(,)"), "");
359 }
360
361 #[test]
362 fn a_paste_that_does_not_make_a_token_is_an_error_and_both_tokens_survive() {
363 let mut pp = Pp::new();
364 pp.define("cat(a, b) a ## b");
365 assert_eq!(pp.text("cat(+, foo)"), "+foo");
366 let errors = pp.errors();
367 assert_eq!(errors.len(), 1);
368 assert_eq!(errors[0].code, Some("E0313"));
369 assert!(errors[0].message.contains("pasting `+` and `foo`"));
370 }
371
372 fn note_texts(d: &Diagnostic) -> Vec<&str> {
374 d.children.iter().map(|c| c.message.as_str()).collect()
375 }
376
377 #[test]
378 fn a_paste_error_says_which_macro_wrote_the_paste() {
379 let mut pp = Pp::new();
382 pp.define("cat(a, b) a ## b");
383 assert_eq!(pp.text("cat(+, foo)"), "+foo");
384 let errors = pp.errors();
385 let notes = note_texts(&errors[0]);
386 assert_eq!(notes[2], "expanded from macro `cat`");
387 assert_eq!(errors[0].children[2].span, Span::new(12, 14));
390 }
391
392 #[test]
393 fn a_paste_error_names_every_macro_it_came_out_of_outermost_first() {
394 let mut pp = Pp::new();
397 pp.define("cat(a, b) a ## b");
398 pp.define("outer(y) cat(y, +)");
399 assert_eq!(pp.text("outer(z)"), "z+");
400 let errors = pp.errors();
401 assert_eq!(errors.len(), 1);
402 assert_eq!(errors[0].code, Some("E0313"));
403 let notes = note_texts(&errors[0]);
404 assert_eq!(¬es[2..], ["expanded from macro `outer`", "expanded from macro `cat`"]);
405 assert_eq!(errors[0].children[2].span, Span::new(9, 12));
407 assert_eq!(errors[0].children[3].span, Span::new(12, 14));
408 }
409
410 #[test]
411 fn a_macro_called_wrongly_from_another_macro_says_where_it_was_called() {
412 let mut pp = Pp::new();
413 pp.define("two(a, b) a b");
414 pp.define("wrap(x) two(x)");
415 pp.text("wrap(1)");
416 let errors = pp.errors();
417 assert_eq!(errors.len(), 1);
418 assert_eq!(errors[0].code, Some("E0312"));
419 assert_eq!(note_texts(&errors[0])[1], "expanded from macro `wrap`");
420 assert_eq!(errors[0].children[1].span, Span::new(8, 11));
421 }
422
423 #[test]
424 fn a_macro_used_in_an_argument_is_not_blamed_on_the_macro_it_is_passed_to() {
425 let mut pp = Pp::new();
428 pp.define("cat(a, b) a ## b");
429 pp.define("id(x) x");
430 pp.text("id(cat(+, foo))");
431 let errors = pp.errors();
432 assert_eq!(errors.len(), 1);
433 assert_eq!(note_texts(&errors[0])[2..], ["expanded from macro `cat`"]);
434 }
435
436 #[test]
437 fn variadic_arguments_arrive_as_one_argument_with_the_commas_intact() {
438 let mut pp = Pp::new();
439 pp.define("f(fmt, ...) g(fmt, __VA_ARGS__)");
440 assert_eq!(pp.text("f(\"%d %d\", 1, 2)"), "g(\"%d %d\", 1, 2)");
441 }
442
443 #[test]
444 fn the_gnu_named_variadic_form_works_the_same_way() {
445 let mut pp = Pp::new();
446 pp.define("f(fmt, rest...) g(fmt, rest)");
447 assert_eq!(pp.text("f(a, b, c)"), "g(a, b, c)");
448 }
449
450 #[test]
451 fn a_variadic_macro_may_be_called_with_nothing_for_the_variadic_part() {
452 let mut pp = Pp::new();
453 pp.define("f(a, ...) [a __VA_ARGS__]");
454 assert_eq!(pp.text("f(1)"), "[1 ]");
455 }
456
457 #[test]
458 fn the_gnu_comma_swallowing_extension_drops_the_comma() {
459 let mut pp = Pp::new();
460 pp.define("log(fmt, ...) printf(fmt, ## __VA_ARGS__)");
461 assert_eq!(pp.text("log(\"hi\")"), "printf(\"hi\")");
462 assert_eq!(pp.text("log(\"%d\", 1)"), "printf(\"%d\", 1)");
463 }
464
465 #[test]
466 fn va_opt_appears_only_when_there_are_variable_arguments() {
467 let mut pp = Pp::new();
468 pp.define("log(fmt, ...) printf(fmt __VA_OPT__(,) __VA_ARGS__)");
469 assert_eq!(pp.text("log(\"hi\")"), "printf(\"hi\" )");
470 assert_eq!(pp.text("log(\"%d\", 1)"), "printf(\"%d\" , 1)");
471 }
472
473 #[test]
474 fn va_opt_contents_are_substituted_like_any_other_replacement() {
475 let mut pp = Pp::new();
476 pp.define("f(a, ...) [a __VA_OPT__(and __VA_ARGS__ done)]");
477 assert_eq!(pp.text("f(1)"), "[1 ]");
478 assert_eq!(pp.text("f(1, 2)"), "[1 and 2 done]");
479 }
480
481 #[test]
482 fn va_opt_pastes_as_a_unit() {
483 let mut pp = Pp::new();
484 pp.define("f(a, ...) a ## __VA_OPT__(x)");
485 assert_eq!(pp.text("f(y)"), "y", "with no variable arguments it is a placemarker");
486 assert_eq!(pp.text("f(y, 1)"), "yx");
487 }
488
489 #[test]
490 fn too_few_arguments_are_reported_against_the_definition() {
491 let mut pp = Pp::new();
492 pp.define("f(a, b) a b");
493 assert_eq!(pp.text("f(1)"), "f", "the arguments are consumed, as GCC and Clang do");
494 let errors = pp.errors();
495 assert_eq!(errors.len(), 1);
496 assert_eq!(errors[0].code, Some("E0312"));
497 assert_eq!(errors[0].children.len(), 1, "the definition is worth pointing at");
498 }
499
500 #[test]
501 fn an_unterminated_argument_list_is_reported_at_the_parenthesis() {
502 let mut pp = Pp::new();
503 pp.define("f(a) a");
504 assert_eq!(pp.text("f(1"), "f");
505 let errors = pp.errors();
506 assert_eq!(errors[0].code, Some("E0311"));
507 }
508
509 #[test]
510 fn a_token_from_a_macro_is_reported_at_the_invocation() {
511 let mut pp = Pp::new();
512 pp.define("N 42");
513 let out = pp.expand(" N");
514 assert_eq!(out.len(), 1);
515 assert_eq!(out[0].expansion, out[0].report_span());
516 assert_eq!(out[0].expansion.lo, 2, "the invocation is at offset 2 in the input");
517 assert_ne!(out[0].span, out[0].expansion, "the spelling is in the macro body");
518 }
519
520 #[test]
521 fn hide_sets_are_shared_rather_than_rebuilt() {
522 let mut pp = Pp::new();
523 pp.define("A 1");
524 pp.define("B 2");
525 for _ in 0..100 {
526 pp.text("A B A B");
527 }
528 assert!(
529 pp.expander.hide_sets() <= 3,
530 "the empty set plus one per macro, however many times they are used"
531 );
532 }
533
534 #[test]
535 fn expansion_is_the_same_every_time() {
536 let mut first = Pp::new();
537 let mut second = Pp::new();
538 for pp in [&mut first, &mut second] {
539 pp.define("N 42");
540 pp.define("cat(a, b) a ## b");
541 pp.define("log(fmt, ...) printf(fmt, ## __VA_ARGS__)");
542 }
543 let src = "cat(x, y) N log(\"a\") log(\"b\", 1)";
544 assert_eq!(first.text(src), second.text(src));
545 }
546
547 #[test]
552 fn the_standards_rescanning_example() {
553 let mut pp = Pp::new();
554 pp.define("x 3");
555 pp.define("f(a) f(x * (a))");
556 pp.undef("x");
557 pp.define("x 2");
558 pp.define("g f");
559 pp.define("z z[0]");
560 pp.define("h g(~");
561 pp.define("m(a) a(w)");
562 pp.define("w 0,1");
563 pp.define("t(a) a");
564 pp.define("p() int");
565 pp.define("q(x) x");
566 pp.define("r(x,y) x ## y");
567 pp.define("str(x) # x");
568
569 assert_eq!(
570 pp.text("f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);"),
571 "f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);"
572 );
573 assert_eq!(
577 pp.text("g(x+(3,4)-w) | h 5) & m\n(f)^m(m);"),
578 "f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);"
579 );
580 assert_eq!(
581 pp.text("p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };"),
582 "int i[] = { 1, 23, 4, 5, };"
583 );
584 assert_eq!(
585 pp.text("char c[2][6] = { str(hello), str() };"),
586 "char c[2][6] = { \"hello\", \"\" };"
587 );
588 assert!(pp.errors().is_empty(), "the standard's example is well formed");
589 }
590
591 #[test]
593 fn the_standards_variadic_example() {
594 let mut pp = Pp::new();
595 pp.define("debug(...) fprintf(stderr, __VA_ARGS__)");
596 pp.define("showlist(...) puts(#__VA_ARGS__)");
597 pp.define("report(test, ...) ((test)?puts(#test): printf(__VA_ARGS__))");
598
599 assert_eq!(pp.text("debug(\"Flag\");"), "fprintf(stderr, \"Flag\");");
600 assert_eq!(pp.text("debug(\"X = %d\\n\", x);"), "fprintf(stderr, \"X = %d\\n\", x);");
601 assert_eq!(
602 pp.text("showlist(The first, second, and third items.);"),
603 "puts(\"The first, second, and third items.\");"
604 );
605 assert_eq!(
606 pp.text("report(x>y, \"x is %d but y is %d\", x, y);"),
607 "((x>y)?puts(\"x>y\"): printf(\"x is %d but y is %d\", x, y));"
608 );
609 assert!(pp.errors().is_empty(), "the standard's example is well formed");
610 }
611
612 #[test]
613 fn milestone_is_recorded() {
614 assert!(MILESTONE.starts_with('M'));
615 }
616}