kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 4790b63baadd2d55e28fc2940fd86884ca2cba7d
parent 8fb3cda83aeb177c1c42186f0a2a9dae7847795a
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  9 May 2026 07:01:33 -0700

test/pp: rewrite corpus as layered, clang-validated suite

Replace the five ad-hoc preprocessor cases with a numbered, incremental
suite covering C11 §6.10 sub-section by sub-section plus C23 #embed.
Adds a parallel must-fail runner for §6.10 constraint violations.

Every .expected file is byte-exact output from clang -E -P -std=c23
-ffreestanding so the suite serves as a clang-validated specification
of the format cfree's -E must reproduce.

Runners pin SOURCE_DATE_EPOCH=0 and pass sources as basenames so
__DATE__, __TIME__, and __FILE__ are deterministic across checkouts.

CORPUS.md indexes every case to its spec § and lists the small set of
items that remain out of scope (impl-defined values, removed-in-C23
trigraphs, host-fallback include search).

Diffstat:
Atest/pp/CORPUS.md | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/pp/cases/00_text_passthrough.c | 1+
Atest/pp/cases/00_text_passthrough.expected | 1+
Atest/pp/cases/01_multiline_text.c | 3+++
Atest/pp/cases/01_multiline_text.expected | 3+++
Atest/pp/cases/02_comment_to_space.c | 1+
Atest/pp/cases/02_comment_to_space.expected | 1+
Atest/pp/cases/03_line_splice.c | 2++
Atest/pp/cases/03_line_splice.expected | 1+
Atest/pp/cases/04_line_splice_in_directive.c | 3+++
Atest/pp/cases/04_line_splice_in_directive.expected | 1+
Atest/pp/cases/05_directive_indented.c | 2++
Atest/pp/cases/05_directive_indented.expected | 2++
Atest/pp/cases/10_null_directive.c | 2++
Atest/pp/cases/10_null_directive.expected | 2++
Atest/pp/cases/20_define_object_basic.c | 2++
Atest/pp/cases/20_define_object_basic.expected | 2++
Atest/pp/cases/21_define_object_multitoken.c | 3+++
Atest/pp/cases/21_define_object_multitoken.expected | 3+++
Atest/pp/cases/22_define_object_empty.c | 2++
Atest/pp/cases/22_define_object_empty.expected | 2++
Atest/pp/cases/23_define_object_self_ref.c | 2++
Atest/pp/cases/23_define_object_self_ref.expected | 2++
Atest/pp/cases/24_define_object_indirect.c | 3+++
Atest/pp/cases/24_define_object_indirect.expected | 1+
Atest/pp/cases/25_define_object_chain.c | 3+++
Atest/pp/cases/25_define_object_chain.expected | 1+
Atest/pp/cases/26_undef_basic.c | 4++++
Atest/pp/cases/26_undef_basic.expected | 3+++
Atest/pp/cases/27_undef_unknown.c | 2++
Atest/pp/cases/27_undef_unknown.expected | 2++
Atest/pp/cases/30_define_func_basic.c | 2++
Atest/pp/cases/30_define_func_basic.expected | 2++
Atest/pp/cases/31_define_func_no_params.c | 2++
Atest/pp/cases/31_define_func_no_params.expected | 2++
Atest/pp/cases/32_define_func_no_invoke.c | 2++
Atest/pp/cases/32_define_func_no_invoke.expected | 2++
Atest/pp/cases/33_define_func_two_args.c | 2++
Atest/pp/cases/33_define_func_two_args.expected | 2++
Atest/pp/cases/34_define_func_arg_inner_paren.c | 2++
Atest/pp/cases/34_define_func_arg_inner_paren.expected | 2++
Atest/pp/cases/35_define_func_empty_arg.c | 2++
Atest/pp/cases/35_define_func_empty_arg.expected | 2++
Atest/pp/cases/36_define_func_arg_full_expand.c | 3+++
Atest/pp/cases/36_define_func_arg_full_expand.expected | 1+
Atest/pp/cases/37_define_func_arg_no_preexpand_hash.c | 3+++
Atest/pp/cases/37_define_func_arg_no_preexpand_hash.expected | 1+
Atest/pp/cases/38_define_func_newline_in_invoke.c | 3+++
Atest/pp/cases/38_define_func_newline_in_invoke.expected | 2++
Atest/pp/cases/40_stringize_basic.c | 2++
Atest/pp/cases/40_stringize_basic.expected | 2++
Atest/pp/cases/41_stringize_multi_token.c | 2++
Atest/pp/cases/41_stringize_multi_token.expected | 2++
Atest/pp/cases/42_stringize_whitespace_collapse.c | 2++
Atest/pp/cases/42_stringize_whitespace_collapse.expected | 2++
Atest/pp/cases/43_stringize_special_chars.c | 2++
Atest/pp/cases/43_stringize_special_chars.expected | 2++
Atest/pp/cases/44_stringize_empty.c | 2++
Atest/pp/cases/44_stringize_empty.expected | 2++
Atest/pp/cases/50_paste_basic.c | 2++
Atest/pp/cases/50_paste_basic.expected | 2++
Atest/pp/cases/51_paste_to_number.c | 2++
Atest/pp/cases/51_paste_to_number.expected | 2++
Atest/pp/cases/52_paste_with_space_around.c | 2++
Atest/pp/cases/52_paste_with_space_around.expected | 2++
Atest/pp/cases/53_paste_empty_left.c | 2++
Atest/pp/cases/53_paste_empty_left.expected | 2++
Atest/pp/cases/54_paste_empty_right.c | 2++
Atest/pp/cases/54_paste_empty_right.expected | 2++
Atest/pp/cases/55_paste_object_macro.c | 2++
Atest/pp/cases/55_paste_object_macro.expected | 2++
Atest/pp/cases/56_paste_chain.c | 2++
Atest/pp/cases/56_paste_chain.expected | 2++
Atest/pp/cases/60_rescan_chain.c | 3+++
Atest/pp/cases/60_rescan_chain.expected | 1+
Atest/pp/cases/61_rescan_object_invokes_func.c | 3+++
Atest/pp/cases/61_rescan_object_invokes_func.expected | 1+
Atest/pp/cases/62_rescan_paste_to_macro.c | 3+++
Atest/pp/cases/62_rescan_paste_to_macro.expected | 1+
Atest/pp/cases/63_rescan_not_directive.c | 3+++
Atest/pp/cases/63_rescan_not_directive.expected | 3+++
Atest/pp/cases/64_rescan_self_in_func.c | 2++
Atest/pp/cases/64_rescan_self_in_func.expected | 2++
Atest/pp/cases/70_variadic_basic.c | 2++
Atest/pp/cases/70_variadic_basic.expected | 2++
Atest/pp/cases/71_variadic_named_param.c | 2++
Atest/pp/cases/71_variadic_named_param.expected | 2++
Atest/pp/cases/72_variadic_stringize.c | 2++
Atest/pp/cases/72_variadic_stringize.expected | 2++
Atest/pp/cases/73_variadic_paste.c | 2++
Atest/pp/cases/73_variadic_paste.expected | 2++
Atest/pp/cases/80_if_arith_true.c | 3+++
Atest/pp/cases/80_if_arith_true.expected | 2++
Atest/pp/cases/81_if_arith_false.c | 4++++
Atest/pp/cases/81_if_arith_false.expected | 1+
Atest/pp/cases/82_if_undefined_id_zero.c | 5+++++
Atest/pp/cases/82_if_undefined_id_zero.expected | 1+
Atest/pp/cases/83_if_defined_word.c | 7+++++++
Atest/pp/cases/83_if_defined_word.expected | 1+
Atest/pp/cases/84_if_defined_paren.c | 7+++++++
Atest/pp/cases/84_if_defined_paren.expected | 1+
Atest/pp/cases/85_if_defined_no_replace.c | 4++++
Atest/pp/cases/85_if_defined_no_replace.expected | 1+
Atest/pp/cases/86_if_macro_replaced.c | 4++++
Atest/pp/cases/86_if_macro_replaced.expected | 1+
Atest/pp/cases/87_ifdef.c | 7+++++++
Atest/pp/cases/87_ifdef.expected | 1+
Atest/pp/cases/88_ifndef.c | 7+++++++
Atest/pp/cases/88_ifndef.expected | 1+
Atest/pp/cases/89_elif_chain.c | 11+++++++++++
Atest/pp/cases/89_elif_chain.expected | 1+
Atest/pp/cases/8a_else_only.c | 5+++++
Atest/pp/cases/8a_else_only.expected | 1+
Atest/pp/cases/8b_nested_if.c | 9+++++++++
Atest/pp/cases/8b_nested_if.expected | 2++
Atest/pp/cases/8c_skipped_relaxed_syntax.c | 4++++
Atest/pp/cases/8c_skipped_relaxed_syntax.expected | 1+
Atest/pp/cases/8d_skipped_keeps_nesting.c | 6++++++
Atest/pp/cases/8d_skipped_keeps_nesting.expected | 1+
Atest/pp/cases/90_inc.h | 1+
Atest/pp/cases/90_include_local.c | 2++
Atest/pp/cases/90_include_local.expected | 2++
Atest/pp/cases/91_inc.h | 1+
Atest/pp/cases/91_include_macros_propagate.c | 2++
Atest/pp/cases/91_include_macros_propagate.expected | 1+
Atest/pp/cases/92_inc.h | 1+
Atest/pp/cases/92_include_macro_replaced_path.c | 2++
Atest/pp/cases/92_include_macro_replaced_path.expected | 3+++
Atest/pp/cases/93_a.h | 2++
Atest/pp/cases/93_b.h | 1+
Atest/pp/cases/93_include_nested.c | 2++
Atest/pp/cases/93_include_nested.expected | 3+++
Atest/pp/cases/94_include_system_form.c | 2++
Atest/pp/cases/94_include_system_form.expected | 2++
Atest/pp/cases/a0_line_number.c | 5+++++
Atest/pp/cases/a0_line_number.expected | 4++++
Atest/pp/cases/a1_line_with_file.c | 2++
Atest/pp/cases/a1_line_with_file.expected | 1+
Atest/pp/cases/a2_line_macro_replaced.c | 3+++
Atest/pp/cases/a2_line_macro_replaced.expected | 1+
Atest/pp/cases/b0_pragma_unknown_ignored.c | 2++
Atest/pp/cases/b0_pragma_unknown_ignored.expected | 2++
Atest/pp/cases/b1_pragma_operator.c | 3+++
Atest/pp/cases/b1_pragma_operator.expected | 3+++
Atest/pp/cases/b2_pragma_stdc.c | 2++
Atest/pp/cases/b2_pragma_stdc.expected | 2++
Atest/pp/cases/c0_line_predefined.c | 1+
Atest/pp/cases/c0_line_predefined.expected | 1+
Atest/pp/cases/c1_stdc_macro.c | 1+
Atest/pp/cases/c1_stdc_macro.expected | 1+
Atest/pp/cases/c2_stdc_hosted.c | 1+
Atest/pp/cases/c2_stdc_hosted.expected | 1+
Atest/pp/cases/c3_predefined_macros_defined.c | 12++++++++++++
Atest/pp/cases/c3_predefined_macros_defined.expected | 5+++++
Atest/pp/cases/c4_date_value.c | 1+
Atest/pp/cases/c4_date_value.expected | 1+
Atest/pp/cases/c5_time_value.c | 1+
Atest/pp/cases/c5_time_value.expected | 1+
Atest/pp/cases/c6_file_value.c | 1+
Atest/pp/cases/c6_file_value.expected | 1+
Atest/pp/cases/d0_data.bin | 2++
Atest/pp/cases/d0_embed_basic.c | 2++
Atest/pp/cases/d0_embed_basic.expected | 1+
Atest/pp/cases/d1_data.bin | 2++
Atest/pp/cases/d1_embed_two_bytes.c | 2++
Atest/pp/cases/d1_embed_two_bytes.expected | 1+
Atest/pp/cases/d2_embed_in_array.c | 3+++
Atest/pp/cases/d2_embed_in_array.expected | 1+
Atest/pp/cases/d3_embed_limit.c | 2++
Atest/pp/cases/d3_embed_limit.expected | 1+
Atest/pp/cases/d4_embed_if_empty.c | 2++
Atest/pp/cases/d4_embed_if_empty.expected | 1+
Rtest/pp/cases/define_function.actual -> test/pp/cases/d4_empty.bin | 0
Dtest/pp/cases/define_function.c | 2--
Dtest/pp/cases/define_function.expected | 1-
Dtest/pp/cases/define_object.actual | 0
Dtest/pp/cases/define_object.c | 2--
Dtest/pp/cases/define_object.expected | 1-
Dtest/pp/cases/ifdef_basic.actual | 0
Dtest/pp/cases/ifdef_basic.c | 11-----------
Dtest/pp/cases/ifdef_basic.expected | 2--
Dtest/pp/cases/include_local.actual | 0
Dtest/pp/cases/include_local.c | 2--
Dtest/pp/cases/include_local.expected | 2--
Dtest/pp/cases/include_local.h | 2--
Dtest/pp/cases/undef.actual | 0
Dtest/pp/cases/undef.c | 6------
Dtest/pp/cases/undef.expected | 3---
Atest/pp/cases_err/01_redef_object_diff_tokens.c | 2++
Atest/pp/cases_err/02_redef_object_diff_whitespace.c | 2++
Atest/pp/cases_err/03_redef_func_diff_param_name.c | 2++
Atest/pp/cases_err/04_redef_func_diff_param_count.c | 2++
Atest/pp/cases_err/05_paste_at_start.c | 2++
Atest/pp/cases_err/06_paste_at_end.c | 2++
Atest/pp/cases_err/07_hash_not_followed_by_param.c | 2++
Atest/pp/cases_err/08_va_args_in_non_variadic.c | 2++
Atest/pp/cases_err/09_func_too_few_args.c | 2++
Atest/pp/cases_err/0a_func_too_many_args.c | 2++
Atest/pp/cases_err/0b_unterminated_func_invoke.c | 2++
Atest/pp/cases_err/0c_define_predefined.c | 1+
Atest/pp/cases_err/0d_undef_predefined.c | 1+
Atest/pp/cases_err/0e_define_defined.c | 1+
Atest/pp/cases_err/0f_error_directive.c | 1+
Mtest/pp/run.sh | 22+++++++++++++++++-----
Atest/pp/run_errors.sh | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/test.mk | 7+++++--
206 files changed, 733 insertions(+), 41 deletions(-)

diff --git a/test/pp/CORPUS.md b/test/pp/CORPUS.md @@ -0,0 +1,242 @@ +# pp test corpus + +Layered, incremental tests for the C preprocessor (translation phase 4) +derived from C11 §6.10 in `doc/std/CHAPTER-6.txt` and the translation-phase +ordering in `doc/std/CHAPTER-5.txt` §5.1.1.2. C23 `#embed` is also covered. + +Two harnesses: + +- `cases/` — golden-output tests; `cfree cc -E -I . <src> -o <actual>` + is diffed against `<src>.expected`. Runner: `run.sh`. +- `cases_err/` — must-fail tests; `cfree cc -E ...` must exit nonzero. + Constraint violations from C11 §6.10. Runner: `run_errors.sh`. + +Number prefixes group tests by spec section; lexical ordering matches the +order an implementation should plausibly grow features. Files are minimal +on purpose — each isolates one rule. + +### Determinism + +Both runners pin the values of the otherwise-environmental predefined macros: + +- `SOURCE_DATE_EPOCH=0` is exported, fixing `__DATE__` to `"Jan 1 1970"` + (note two spaces; C11 §6.10.8.1 requires the `dd` field to be space-padded + when less than 10) and `__TIME__` to `"00:00:00"`. cfree must honour + `SOURCE_DATE_EPOCH` per the reproducible-builds convention. +- The runner `cd`s into the cases directory and passes the source as a + basename, so `__FILE__` is the file name without a checkout-path prefix. + +## cases/ — success tests + +### 00–09 Translation phases / passthrough (§5.1.1.2) + +| case | spec | tests | +| --------------------------------- | ------- | ---------------------------------------------------- | +| 00_text_passthrough | 5.1.1.2 | text-line emitted unchanged | +| 01_multiline_text | 5.1.1.2 | multiple text-lines preserve newlines | +| 02_comment_to_space | phase 3 | each comment replaced by one space | +| 03_line_splice | phase 2 | `\<newline>` deleted, lines spliced | +| 04_line_splice_in_directive | phase 2 | splice happens before directive recognition | +| 05_directive_indented | 6.10 ¶2 | `#` may follow whitespace before line start | + +### 10–19 Null directive (§6.10.7) + +| case | spec | tests | +| --------------------------------- | ------- | ---------------------------------- | +| 10_null_directive | 6.10.7 | `#` alone has no effect | + +### 20–29 Object-like macros and `#undef` (§6.10.3, §6.10.3.5) + +| case | spec | tests | +| --------------------------------- | ----------- | ---------------------------------------------- | +| 20_define_object_basic | 6.10.3 ¶9 | trivial replacement | +| 21_define_object_multitoken | 6.10.3 ¶9 | replacement-list is a token sequence | +| 22_define_object_empty | 6.10.3 ¶9 | empty replacement-list is allowed | +| 23_define_object_self_ref | 6.10.3.4 ¶2 | name-being-replaced is not re-replaced | +| 24_define_object_indirect | 6.10.3.4 ¶2 | mutual recursion stops via blue-painting | +| 25_define_object_chain | 6.10.3.4 ¶1 | rescan reaches second-level macro | +| 26_undef_basic | 6.10.3.5 ¶2 | `#undef` removes the binding | +| 27_undef_unknown | 6.10.3.5 ¶2 | `#undef` of unknown identifier is ignored | + +### 30–39 Function-like macros and argument substitution (§6.10.3, §6.10.3.1) + +| case | spec | tests | +| --------------------------------------- | ------------- | ----------------------------------------------------------- | +| 30_define_func_basic | 6.10.3 ¶10 | one-arg invocation | +| 31_define_func_no_params | 6.10.3 ¶10 | empty parameter list | +| 32_define_func_no_invoke | 6.10.3 ¶10 | bare name without `(` does not expand | +| 33_define_func_two_args | 6.10.3 ¶11 | comma separates arguments | +| 34_define_func_arg_inner_paren | 6.10.3 ¶11 | comma inside matched `()` does not separate | +| 35_define_func_empty_arg | 6.10.3 ¶4 | empty argument allowed (still arity 1) | +| 36_define_func_arg_full_expand | 6.10.3.1 ¶1 | argument fully expanded before substitution | +| 37_define_func_arg_no_preexpand_hash | 6.10.3.1 ¶1 | `#`-stringized argument is *not* pre-expanded | +| 38_define_func_newline_in_invoke | 6.10.3 ¶10 | newline is whitespace inside an invocation | + +### 40–49 `#` operator (stringize) (§6.10.3.2) + +| case | spec | tests | +| --------------------------------- | ----------- | ------------------------------------------------------------- | +| 40_stringize_basic | 6.10.3.2 ¶2 | `#x` produces a string literal | +| 41_stringize_multi_token | 6.10.3.2 ¶2 | inter-token whitespace becomes single space | +| 42_stringize_whitespace_collapse | 6.10.3.2 ¶2 | leading/trailing stripped, internal sequences collapse | +| 43_stringize_special_chars | 6.10.3.2 ¶2 | `\` inserted before `"` and `\` in string-literal arguments | +| 44_stringize_empty | 6.10.3.2 ¶2 | empty argument stringizes to `""` | + +### 50–59 `##` operator (token paste) (§6.10.3.3) + +| case | spec | tests | +| --------------------------------- | ----------- | ------------------------------------------------------ | +| 50_paste_basic | 6.10.3.3 ¶3 | concatenation of two identifier tokens | +| 51_paste_to_number | 6.10.3.3 ¶3 | concatenation produces a pp-number | +| 52_paste_with_space_around | 6.10.3.5 ¶6 | space around `##` in definition is optional | +| 53_paste_empty_left | 6.10.3.3 ¶2 | placemarker on left collapses to right token | +| 54_paste_empty_right | 6.10.3.3 ¶2 | placemarker on right collapses to left token | +| 55_paste_object_macro | 6.10.3.3 ¶3 | `##` is allowed in object-like macros, not just funcs | +| 56_paste_chain | 6.10.3.5 ¶7 | `x##y##z` (spec example 5) | + +### 60–69 Rescanning and self-reference (§6.10.3.4) + +| case | spec | tests | +| --------------------------------- | ----------- | -------------------------------------------------------- | +| 60_rescan_chain | 6.10.3.4 ¶1 | replacement is rescanned for further macros | +| 61_rescan_object_invokes_func | 6.10.3.4 ¶1 | rescan picks up a function-like invocation | +| 62_rescan_paste_to_macro | 6.10.3.3 ¶3 | post-paste token is available for further replacement | +| 63_rescan_not_directive | 6.10.3.4 ¶3 | tokens produced by expansion are not a directive | +| 64_rescan_self_in_func | 6.10.3.4 ¶2 | function-like macro does not re-invoke itself in rescan | + +### 70–79 Variadic macros (§6.10.3 ¶12, §6.10.3.1 ¶2) + +| case | spec | tests | +| --------------------------------- | ------------ | -------------------------------------------------- | +| 70_variadic_basic | 6.10.3 ¶12 | `...` collects trailing args including commas | +| 71_variadic_named_param | 6.10.3 ¶12 | named param + variadic tail | +| 72_variadic_stringize | 6.10.3.1 ¶2 | `#__VA_ARGS__` stringizes the merged sequence | +| 73_variadic_paste | 6.10.3.3 ¶2 | paste with `__VA_ARGS__` handles separator commas | + +### 80–8f Conditional inclusion (§6.10.1) + +| case | spec | tests | +| --------------------------------- | ---------- | ---------------------------------------------------- | +| 80_if_arith_true | 6.10.1 ¶3 | arithmetic constant expression in `#if` | +| 81_if_arith_false | 6.10.1 ¶3 | false branch is skipped | +| 82_if_undefined_id_zero | 6.10.1 ¶4 | remaining identifiers replaced with `0` | +| 83_if_defined_word | 6.10.1 ¶1 | `defined identifier` form | +| 84_if_defined_paren | 6.10.1 ¶1 | `defined ( identifier )` form | +| 85_if_defined_no_replace | 6.10.1 ¶4 | operand of `defined` is not macro-expanded | +| 86_if_macro_replaced | 6.10.1 ¶4 | macros expanded before evaluation | +| 87_ifdef | 6.10.1 ¶5 | `#ifdef` shorthand | +| 88_ifndef | 6.10.1 ¶5 | `#ifndef` shorthand | +| 89_elif_chain | 6.10.1 ¶6 | only first true branch processed | +| 8a_else_only | 6.10.1 ¶6 | `#else` taken when no preceding branch is true | +| 8b_nested_if | 6.10.1 ¶6 | nested conditionals | +| 8c_skipped_relaxed_syntax | 6.10 ¶4 | unknown directives in skipped groups don't error | +| 8d_skipped_keeps_nesting | 6.10.1 ¶6 | `#if`/`#endif` still tracked inside skipped groups | + +### 90–99 Source file inclusion (§6.10.2) + +| case | spec | tests | +| --------------------------------- | ---------- | ---------------------------------------------------- | +| 90_include_local | 6.10.2 ¶3 | quoted-form include of a sibling header | +| 91_include_macros_propagate | 6.10.2 ¶3 | macros defined in header visible to includer | +| 92_include_macro_replaced_path | 6.10.2 ¶4 | header path is macro-replaced before file lookup | +| 93_include_nested | 6.10.2 ¶6 | a header may itself `#include` | +| 94_include_system_form | 6.10.2 ¶2 | `<...>` form resolves through `-I` search paths | + +### a0–af Line control (§6.10.4) + +| case | spec | tests | +| --------------------------------- | ---------- | ------------------------------------------- | +| a0_line_number | 6.10.4 ¶3 | `#line N` resets `__LINE__` | +| a1_line_with_file | 6.10.4 ¶4 | `#line N "name"` resets `__FILE__` | +| a2_line_macro_replaced | 6.10.4 ¶5 | tokens after `line` are macro-replaced | + +### b0–bf Pragma directive and `_Pragma` operator (§6.10.6, §6.10.9) + +| case | spec | tests | +| --------------------------------- | ---------- | ---------------------------------------------------- | +| b0_pragma_unknown_ignored | 6.10.6 ¶1 | unrecognized pragma is ignored | +| b1_pragma_operator | 6.10.9 ¶1 | `_Pragma("...")` tokens are removed; pragma applied | +| b2_pragma_stdc | 6.10.6 ¶2 | `#pragma STDC FP_CONTRACT ON` consumed without error | + +### c0–cf Predefined macros (§6.10.8) + +| case | spec | tests | +| ----------------------------- | ----------- | ----------------------------------------------------------- | +| c0_line_predefined | 6.10.8.1 | bare `__LINE__` | +| c1_stdc_macro | 6.10.8.1 | `__STDC__` is `1` for a conforming impl | +| c2_stdc_hosted | 6.10.8.1 | `__STDC_HOSTED__` is `0` (cfree is freestanding) | +| c3_predefined_macros_defined | 6.10.8.1 | `__STDC_VERSION__`, `__DATE__`, `__TIME__`, `__FILE__` exist | +| c4_date_value | 6.10.8.1 | `__DATE__` is `"Jan 1 1970"` under `SOURCE_DATE_EPOCH=0` | +| c5_time_value | 6.10.8.1 | `__TIME__` is `"00:00:00"` under `SOURCE_DATE_EPOCH=0` | +| c6_file_value | 6.10.8.1 | `__FILE__` is the basename passed to cfree | + +### d0–df `#embed` (C23, supplementary) + +These exercise the C23 `#embed` directive (not in C11). Format presumed: +each byte becomes a pp-number, separated by `,` with no surrounding spaces. + +| case | tests | +| --------------------- | ------------------------------------------------------------------ | +| d0_embed_basic | one-byte file → one pp-number | +| d1_embed_two_bytes | two-byte file → comma-separated pp-numbers | +| d2_embed_in_array | embed inside an array initializer | +| d3_embed_limit | `limit(N)` parameter trims the byte sequence | +| d4_embed_if_empty | `if_empty(tokens)` substitutes when the file has zero bytes | + +Binary fixtures (regenerate with `printf` to control bytes exactly): + +```sh +printf 'A' > test/pp/cases/d0_data.bin # 1 byte +printf 'Hi' > test/pp/cases/d1_data.bin # 2 bytes +: > test/pp/cases/d4_empty.bin # 0 bytes +``` + +## cases_err/ — must-fail tests + +C11 §6.10 constraint violations must produce a diagnostic; the runner +just checks for nonzero exit. + +| case | spec | violation | +| --------------------------------- | ----------- | -------------------------------------------------------------------- | +| 01_redef_object_diff_tokens | 6.10.3 ¶2 | object-like redefined with different replacement-list | +| 02_redef_object_diff_whitespace | 6.10.3 ¶2 | identical tokens but different whitespace separation | +| 03_redef_func_diff_param_name | 6.10.3 ¶2 | function-like redefined with different parameter spelling | +| 04_redef_func_diff_param_count | 6.10.3 ¶2 | function-like redefined with different parameter count | +| 05_paste_at_start | 6.10.3.3 ¶1 | `##` at start of replacement list | +| 06_paste_at_end | 6.10.3.3 ¶1 | `##` at end of replacement list | +| 07_hash_not_followed_by_param | 6.10.3.2 ¶1 | `#` not followed by a parameter | +| 08_va_args_in_non_variadic | 6.10.3 ¶5 | `__VA_ARGS__` outside a variadic macro | +| 09_func_too_few_args | 6.10.3 ¶4 | invocation supplies fewer arguments than parameters | +| 0a_func_too_many_args | 6.10.3 ¶4 | invocation supplies more arguments than parameters (non-variadic) | +| 0b_unterminated_func_invoke | 6.10.3 ¶4 | no `)` terminates a function-like macro invocation | +| 0c_define_predefined | 6.10.8 ¶2 | `#define` of a mandatory predefined macro name | +| 0d_undef_predefined | 6.10.8 ¶2 | `#undef` of a mandatory predefined macro name | +| 0e_define_defined | 6.10.8 ¶2 | `defined` cannot be the subject of `#define` | +| 0f_error_directive | 6.10.5 ¶1 | `#error` shall produce a diagnostic | + +## Out of scope + +These items in C11 §6.10 are deliberately *not* covered, with reasons: + +- **Exact value of `__STDC_VERSION__`** (§6.10.8.1). Spec mandates the + shape `201ymmL` but leaves the version cfree claims to the impl; + asserting a specific value would freeze that choice in the test suite. + *Existence* is in `c3_predefined_macros_defined`. +- **Conditional feature macros** `__STDC_NO_THREADS__`, `__STDC_NO_VLA__`, + `__STDC_NO_ATOMICS__`, `__STDC_IEC_559__`, etc. (§6.10.8.3). Spec + defines these as *conditionally* defined per impl capabilities. There is + no spec-mandated value to diff against. +- **Trigraph processing** (§5.1.1.2 phase 1). Trigraphs were removed in + C23; cfree targets C23-era source (see `#embed` support). Testing + trigraph translation would assert behavior cfree intentionally does + not provide. +- **`#embed` `prefix()` and `suffix()` parameters**. The spec leaves the + whitespace inserted between the parameter tokens and the byte + sequence implementation-defined, so a byte-exact diff would freeze a + particular formatting choice. `limit()` and `if_empty()` are + whitespace-insensitive in the same sense and *are* covered. +- **`#include` system-path resolution outside `-I`**. The runner only + controls `-I cases_dir`; testing the implementation-defined fallback + search (e.g. `/usr/include`) would couple the suite to the host. + `94_include_system_form` covers `<...>` resolution through the + controlled `-I` path. diff --git a/test/pp/cases/00_text_passthrough.c b/test/pp/cases/00_text_passthrough.c @@ -0,0 +1 @@ +hello world diff --git a/test/pp/cases/00_text_passthrough.expected b/test/pp/cases/00_text_passthrough.expected @@ -0,0 +1 @@ +hello world diff --git a/test/pp/cases/01_multiline_text.c b/test/pp/cases/01_multiline_text.c @@ -0,0 +1,3 @@ +abc +def +ghi diff --git a/test/pp/cases/01_multiline_text.expected b/test/pp/cases/01_multiline_text.expected @@ -0,0 +1,3 @@ +abc +def +ghi diff --git a/test/pp/cases/02_comment_to_space.c b/test/pp/cases/02_comment_to_space.c @@ -0,0 +1 @@ +a/*c*/b diff --git a/test/pp/cases/02_comment_to_space.expected b/test/pp/cases/02_comment_to_space.expected @@ -0,0 +1 @@ +a b diff --git a/test/pp/cases/03_line_splice.c b/test/pp/cases/03_line_splice.c @@ -0,0 +1,2 @@ +hel\ +lo diff --git a/test/pp/cases/03_line_splice.expected b/test/pp/cases/03_line_splice.expected @@ -0,0 +1 @@ +hello diff --git a/test/pp/cases/04_line_splice_in_directive.c b/test/pp/cases/04_line_splice_in_directive.c @@ -0,0 +1,3 @@ +#define X 1 \ ++ 2 +X diff --git a/test/pp/cases/04_line_splice_in_directive.expected b/test/pp/cases/04_line_splice_in_directive.expected @@ -0,0 +1 @@ +1 + 2 diff --git a/test/pp/cases/05_directive_indented.c b/test/pp/cases/05_directive_indented.c @@ -0,0 +1,2 @@ + #define X 1 +X diff --git a/test/pp/cases/05_directive_indented.expected b/test/pp/cases/05_directive_indented.expected @@ -0,0 +1,2 @@ + +1 diff --git a/test/pp/cases/10_null_directive.c b/test/pp/cases/10_null_directive.c @@ -0,0 +1,2 @@ +# +text diff --git a/test/pp/cases/10_null_directive.expected b/test/pp/cases/10_null_directive.expected @@ -0,0 +1,2 @@ + +text diff --git a/test/pp/cases/20_define_object_basic.c b/test/pp/cases/20_define_object_basic.c @@ -0,0 +1,2 @@ +#define X 42 +X diff --git a/test/pp/cases/20_define_object_basic.expected b/test/pp/cases/20_define_object_basic.expected @@ -0,0 +1,2 @@ + +42 diff --git a/test/pp/cases/21_define_object_multitoken.c b/test/pp/cases/21_define_object_multitoken.c @@ -0,0 +1,3 @@ +#define M 1 + 2 +M +M * 3 diff --git a/test/pp/cases/21_define_object_multitoken.expected b/test/pp/cases/21_define_object_multitoken.expected @@ -0,0 +1,3 @@ + +1 + 2 +1 + 2 * 3 diff --git a/test/pp/cases/22_define_object_empty.c b/test/pp/cases/22_define_object_empty.c @@ -0,0 +1,2 @@ +#define E +[E] diff --git a/test/pp/cases/22_define_object_empty.expected b/test/pp/cases/22_define_object_empty.expected @@ -0,0 +1,2 @@ + +[] diff --git a/test/pp/cases/23_define_object_self_ref.c b/test/pp/cases/23_define_object_self_ref.c @@ -0,0 +1,2 @@ +#define X X + 1 +X diff --git a/test/pp/cases/23_define_object_self_ref.expected b/test/pp/cases/23_define_object_self_ref.expected @@ -0,0 +1,2 @@ + +X + 1 diff --git a/test/pp/cases/24_define_object_indirect.c b/test/pp/cases/24_define_object_indirect.c @@ -0,0 +1,3 @@ +#define A B +#define B A +A diff --git a/test/pp/cases/24_define_object_indirect.expected b/test/pp/cases/24_define_object_indirect.expected @@ -0,0 +1 @@ +A diff --git a/test/pp/cases/25_define_object_chain.c b/test/pp/cases/25_define_object_chain.c @@ -0,0 +1,3 @@ +#define A 1 +#define B A +B diff --git a/test/pp/cases/25_define_object_chain.expected b/test/pp/cases/25_define_object_chain.expected @@ -0,0 +1 @@ +1 diff --git a/test/pp/cases/26_undef_basic.c b/test/pp/cases/26_undef_basic.c @@ -0,0 +1,4 @@ +#define X 1 +X +#undef X +X diff --git a/test/pp/cases/26_undef_basic.expected b/test/pp/cases/26_undef_basic.expected @@ -0,0 +1,3 @@ + +1 +X diff --git a/test/pp/cases/27_undef_unknown.c b/test/pp/cases/27_undef_unknown.c @@ -0,0 +1,2 @@ +#undef NOPE +ok diff --git a/test/pp/cases/27_undef_unknown.expected b/test/pp/cases/27_undef_unknown.expected @@ -0,0 +1,2 @@ + +ok diff --git a/test/pp/cases/30_define_func_basic.c b/test/pp/cases/30_define_func_basic.c @@ -0,0 +1,2 @@ +#define M(x) (x) +M(1) diff --git a/test/pp/cases/30_define_func_basic.expected b/test/pp/cases/30_define_func_basic.expected @@ -0,0 +1,2 @@ + +(1) diff --git a/test/pp/cases/31_define_func_no_params.c b/test/pp/cases/31_define_func_no_params.c @@ -0,0 +1,2 @@ +#define G() (1) +G() diff --git a/test/pp/cases/31_define_func_no_params.expected b/test/pp/cases/31_define_func_no_params.expected @@ -0,0 +1,2 @@ + +(1) diff --git a/test/pp/cases/32_define_func_no_invoke.c b/test/pp/cases/32_define_func_no_invoke.c @@ -0,0 +1,2 @@ +#define M(x) (x) +M diff --git a/test/pp/cases/32_define_func_no_invoke.expected b/test/pp/cases/32_define_func_no_invoke.expected @@ -0,0 +1,2 @@ + +M diff --git a/test/pp/cases/33_define_func_two_args.c b/test/pp/cases/33_define_func_two_args.c @@ -0,0 +1,2 @@ +#define ADD(a,b) ((a)+(b)) +ADD(1,2) diff --git a/test/pp/cases/33_define_func_two_args.expected b/test/pp/cases/33_define_func_two_args.expected @@ -0,0 +1,2 @@ + +((1)+(2)) diff --git a/test/pp/cases/34_define_func_arg_inner_paren.c b/test/pp/cases/34_define_func_arg_inner_paren.c @@ -0,0 +1,2 @@ +#define M(x) (x) +M((1,2)) diff --git a/test/pp/cases/34_define_func_arg_inner_paren.expected b/test/pp/cases/34_define_func_arg_inner_paren.expected @@ -0,0 +1,2 @@ + +((1,2)) diff --git a/test/pp/cases/35_define_func_empty_arg.c b/test/pp/cases/35_define_func_empty_arg.c @@ -0,0 +1,2 @@ +#define M(x) [x] +M() diff --git a/test/pp/cases/35_define_func_empty_arg.expected b/test/pp/cases/35_define_func_empty_arg.expected @@ -0,0 +1,2 @@ + +[] diff --git a/test/pp/cases/36_define_func_arg_full_expand.c b/test/pp/cases/36_define_func_arg_full_expand.c @@ -0,0 +1,3 @@ +#define A 1 +#define M(x) [x] +M(A) diff --git a/test/pp/cases/36_define_func_arg_full_expand.expected b/test/pp/cases/36_define_func_arg_full_expand.expected @@ -0,0 +1 @@ +[1] diff --git a/test/pp/cases/37_define_func_arg_no_preexpand_hash.c b/test/pp/cases/37_define_func_arg_no_preexpand_hash.c @@ -0,0 +1,3 @@ +#define A 1 +#define S(x) #x +S(A) diff --git a/test/pp/cases/37_define_func_arg_no_preexpand_hash.expected b/test/pp/cases/37_define_func_arg_no_preexpand_hash.expected @@ -0,0 +1 @@ +"A" diff --git a/test/pp/cases/38_define_func_newline_in_invoke.c b/test/pp/cases/38_define_func_newline_in_invoke.c @@ -0,0 +1,3 @@ +#define ADD(a,b) (a)+(b) +ADD(1, +2) diff --git a/test/pp/cases/38_define_func_newline_in_invoke.expected b/test/pp/cases/38_define_func_newline_in_invoke.expected @@ -0,0 +1,2 @@ + +(1)+(2) diff --git a/test/pp/cases/40_stringize_basic.c b/test/pp/cases/40_stringize_basic.c @@ -0,0 +1,2 @@ +#define S(x) #x +S(hello) diff --git a/test/pp/cases/40_stringize_basic.expected b/test/pp/cases/40_stringize_basic.expected @@ -0,0 +1,2 @@ + +"hello" diff --git a/test/pp/cases/41_stringize_multi_token.c b/test/pp/cases/41_stringize_multi_token.c @@ -0,0 +1,2 @@ +#define S(x) #x +S(a + b) diff --git a/test/pp/cases/41_stringize_multi_token.expected b/test/pp/cases/41_stringize_multi_token.expected @@ -0,0 +1,2 @@ + +"a + b" diff --git a/test/pp/cases/42_stringize_whitespace_collapse.c b/test/pp/cases/42_stringize_whitespace_collapse.c @@ -0,0 +1,2 @@ +#define S(x) #x +S( a + b ) diff --git a/test/pp/cases/42_stringize_whitespace_collapse.expected b/test/pp/cases/42_stringize_whitespace_collapse.expected @@ -0,0 +1,2 @@ + +"a + b" diff --git a/test/pp/cases/43_stringize_special_chars.c b/test/pp/cases/43_stringize_special_chars.c @@ -0,0 +1,2 @@ +#define S(x) #x +S("hi") diff --git a/test/pp/cases/43_stringize_special_chars.expected b/test/pp/cases/43_stringize_special_chars.expected @@ -0,0 +1,2 @@ + +"\"hi\"" diff --git a/test/pp/cases/44_stringize_empty.c b/test/pp/cases/44_stringize_empty.c @@ -0,0 +1,2 @@ +#define S(x) #x +S() diff --git a/test/pp/cases/44_stringize_empty.expected b/test/pp/cases/44_stringize_empty.expected @@ -0,0 +1,2 @@ + +"" diff --git a/test/pp/cases/50_paste_basic.c b/test/pp/cases/50_paste_basic.c @@ -0,0 +1,2 @@ +#define P(a,b) a##b +P(foo,bar) diff --git a/test/pp/cases/50_paste_basic.expected b/test/pp/cases/50_paste_basic.expected @@ -0,0 +1,2 @@ + +foobar diff --git a/test/pp/cases/51_paste_to_number.c b/test/pp/cases/51_paste_to_number.c @@ -0,0 +1,2 @@ +#define P(a,b) a##b +P(1,2) diff --git a/test/pp/cases/51_paste_to_number.expected b/test/pp/cases/51_paste_to_number.expected @@ -0,0 +1,2 @@ + +12 diff --git a/test/pp/cases/52_paste_with_space_around.c b/test/pp/cases/52_paste_with_space_around.c @@ -0,0 +1,2 @@ +#define P(a,b) a ## b +P(x,y) diff --git a/test/pp/cases/52_paste_with_space_around.expected b/test/pp/cases/52_paste_with_space_around.expected @@ -0,0 +1,2 @@ + +xy diff --git a/test/pp/cases/53_paste_empty_left.c b/test/pp/cases/53_paste_empty_left.c @@ -0,0 +1,2 @@ +#define P(a,b) a##b +P(,x) diff --git a/test/pp/cases/53_paste_empty_left.expected b/test/pp/cases/53_paste_empty_left.expected @@ -0,0 +1,2 @@ + +x diff --git a/test/pp/cases/54_paste_empty_right.c b/test/pp/cases/54_paste_empty_right.c @@ -0,0 +1,2 @@ +#define P(a,b) a##b +P(x,) diff --git a/test/pp/cases/54_paste_empty_right.expected b/test/pp/cases/54_paste_empty_right.expected @@ -0,0 +1,2 @@ + +x diff --git a/test/pp/cases/55_paste_object_macro.c b/test/pp/cases/55_paste_object_macro.c @@ -0,0 +1,2 @@ +#define X a##b +X diff --git a/test/pp/cases/55_paste_object_macro.expected b/test/pp/cases/55_paste_object_macro.expected @@ -0,0 +1,2 @@ + +ab diff --git a/test/pp/cases/56_paste_chain.c b/test/pp/cases/56_paste_chain.c @@ -0,0 +1,2 @@ +#define t(x,y,z) x##y##z +t(1,2,3) diff --git a/test/pp/cases/56_paste_chain.expected b/test/pp/cases/56_paste_chain.expected @@ -0,0 +1,2 @@ + +123 diff --git a/test/pp/cases/60_rescan_chain.c b/test/pp/cases/60_rescan_chain.c @@ -0,0 +1,3 @@ +#define A B +#define B 5 +A diff --git a/test/pp/cases/60_rescan_chain.expected b/test/pp/cases/60_rescan_chain.expected @@ -0,0 +1 @@ +5 diff --git a/test/pp/cases/61_rescan_object_invokes_func.c b/test/pp/cases/61_rescan_object_invokes_func.c @@ -0,0 +1,3 @@ +#define M(x) (x) +#define A M(1) +A diff --git a/test/pp/cases/61_rescan_object_invokes_func.expected b/test/pp/cases/61_rescan_object_invokes_func.expected @@ -0,0 +1 @@ +(1) diff --git a/test/pp/cases/62_rescan_paste_to_macro.c b/test/pp/cases/62_rescan_paste_to_macro.c @@ -0,0 +1,3 @@ +#define hello world +#define CAT(a,b) a##b +CAT(hel,lo) diff --git a/test/pp/cases/62_rescan_paste_to_macro.expected b/test/pp/cases/62_rescan_paste_to_macro.expected @@ -0,0 +1 @@ +world diff --git a/test/pp/cases/63_rescan_not_directive.c b/test/pp/cases/63_rescan_not_directive.c @@ -0,0 +1,3 @@ +#define HASH # +HASH define X 1 +X diff --git a/test/pp/cases/63_rescan_not_directive.expected b/test/pp/cases/63_rescan_not_directive.expected @@ -0,0 +1,3 @@ + + # define X 1 +X diff --git a/test/pp/cases/64_rescan_self_in_func.c b/test/pp/cases/64_rescan_self_in_func.c @@ -0,0 +1,2 @@ +#define f(x) x + f(x) +f(1) diff --git a/test/pp/cases/64_rescan_self_in_func.expected b/test/pp/cases/64_rescan_self_in_func.expected @@ -0,0 +1,2 @@ + +1 + f(1) diff --git a/test/pp/cases/70_variadic_basic.c b/test/pp/cases/70_variadic_basic.c @@ -0,0 +1,2 @@ +#define V(...) f(__VA_ARGS__) +V(1,2,3) diff --git a/test/pp/cases/70_variadic_basic.expected b/test/pp/cases/70_variadic_basic.expected @@ -0,0 +1,2 @@ + +f(1,2,3) diff --git a/test/pp/cases/71_variadic_named_param.c b/test/pp/cases/71_variadic_named_param.c @@ -0,0 +1,2 @@ +#define V(a,...) f(a; __VA_ARGS__) +V(x,1,2) diff --git a/test/pp/cases/71_variadic_named_param.expected b/test/pp/cases/71_variadic_named_param.expected @@ -0,0 +1,2 @@ + +f(x; 1,2) diff --git a/test/pp/cases/72_variadic_stringize.c b/test/pp/cases/72_variadic_stringize.c @@ -0,0 +1,2 @@ +#define V(...) #__VA_ARGS__ +V(1, 2, 3) diff --git a/test/pp/cases/72_variadic_stringize.expected b/test/pp/cases/72_variadic_stringize.expected @@ -0,0 +1,2 @@ + +"1, 2, 3" diff --git a/test/pp/cases/73_variadic_paste.c b/test/pp/cases/73_variadic_paste.c @@ -0,0 +1,2 @@ +#define V(name,...) name##_xs(__VA_ARGS__) +V(foo,1,2) diff --git a/test/pp/cases/73_variadic_paste.expected b/test/pp/cases/73_variadic_paste.expected @@ -0,0 +1,2 @@ + +foo_xs(1,2) diff --git a/test/pp/cases/80_if_arith_true.c b/test/pp/cases/80_if_arith_true.c @@ -0,0 +1,3 @@ +#if 1+1 == 2 +yes +#endif diff --git a/test/pp/cases/80_if_arith_true.expected b/test/pp/cases/80_if_arith_true.expected @@ -0,0 +1,2 @@ + +yes diff --git a/test/pp/cases/81_if_arith_false.c b/test/pp/cases/81_if_arith_false.c @@ -0,0 +1,4 @@ +#if 0 +no +#endif +ok diff --git a/test/pp/cases/81_if_arith_false.expected b/test/pp/cases/81_if_arith_false.expected @@ -0,0 +1 @@ +ok diff --git a/test/pp/cases/82_if_undefined_id_zero.c b/test/pp/cases/82_if_undefined_id_zero.c @@ -0,0 +1,5 @@ +#if FOO +yes +#else +no +#endif diff --git a/test/pp/cases/82_if_undefined_id_zero.expected b/test/pp/cases/82_if_undefined_id_zero.expected @@ -0,0 +1 @@ +no diff --git a/test/pp/cases/83_if_defined_word.c b/test/pp/cases/83_if_defined_word.c @@ -0,0 +1,7 @@ +#define X 1 +#if defined X +yes +#endif +#if defined Y +no +#endif diff --git a/test/pp/cases/83_if_defined_word.expected b/test/pp/cases/83_if_defined_word.expected @@ -0,0 +1 @@ +yes diff --git a/test/pp/cases/84_if_defined_paren.c b/test/pp/cases/84_if_defined_paren.c @@ -0,0 +1,7 @@ +#define X 1 +#if defined(X) +yes +#endif +#if defined ( Y ) +no +#endif diff --git a/test/pp/cases/84_if_defined_paren.expected b/test/pp/cases/84_if_defined_paren.expected @@ -0,0 +1 @@ +yes diff --git a/test/pp/cases/85_if_defined_no_replace.c b/test/pp/cases/85_if_defined_no_replace.c @@ -0,0 +1,4 @@ +#define X (1+2) +#if defined X +ok +#endif diff --git a/test/pp/cases/85_if_defined_no_replace.expected b/test/pp/cases/85_if_defined_no_replace.expected @@ -0,0 +1 @@ +ok diff --git a/test/pp/cases/86_if_macro_replaced.c b/test/pp/cases/86_if_macro_replaced.c @@ -0,0 +1,4 @@ +#define V 5 +#if V > 3 +yes +#endif diff --git a/test/pp/cases/86_if_macro_replaced.expected b/test/pp/cases/86_if_macro_replaced.expected @@ -0,0 +1 @@ +yes diff --git a/test/pp/cases/87_ifdef.c b/test/pp/cases/87_ifdef.c @@ -0,0 +1,7 @@ +#define KEEP 1 +#ifdef KEEP +yes +#endif +#ifdef MISSING +no +#endif diff --git a/test/pp/cases/87_ifdef.expected b/test/pp/cases/87_ifdef.expected @@ -0,0 +1 @@ +yes diff --git a/test/pp/cases/88_ifndef.c b/test/pp/cases/88_ifndef.c @@ -0,0 +1,7 @@ +#define KEEP 1 +#ifndef KEEP +no +#endif +#ifndef MISSING +yes +#endif diff --git a/test/pp/cases/88_ifndef.expected b/test/pp/cases/88_ifndef.expected @@ -0,0 +1 @@ +yes diff --git a/test/pp/cases/89_elif_chain.c b/test/pp/cases/89_elif_chain.c @@ -0,0 +1,11 @@ +#if 0 +a +#elif 0 +b +#elif 1 +c +#elif 1 +d +#else +e +#endif diff --git a/test/pp/cases/89_elif_chain.expected b/test/pp/cases/89_elif_chain.expected @@ -0,0 +1 @@ +c diff --git a/test/pp/cases/8a_else_only.c b/test/pp/cases/8a_else_only.c @@ -0,0 +1,5 @@ +#if 0 +a +#else +b +#endif diff --git a/test/pp/cases/8a_else_only.expected b/test/pp/cases/8a_else_only.expected @@ -0,0 +1 @@ +b diff --git a/test/pp/cases/8b_nested_if.c b/test/pp/cases/8b_nested_if.c @@ -0,0 +1,9 @@ +#define X 1 +#if X +#if defined Y +inner_yes +#else +inner_no +#endif +outer_yes +#endif diff --git a/test/pp/cases/8b_nested_if.expected b/test/pp/cases/8b_nested_if.expected @@ -0,0 +1,2 @@ +inner_no +outer_yes diff --git a/test/pp/cases/8c_skipped_relaxed_syntax.c b/test/pp/cases/8c_skipped_relaxed_syntax.c @@ -0,0 +1,4 @@ +#if 0 +#unknown_directive +#endif +ok diff --git a/test/pp/cases/8c_skipped_relaxed_syntax.expected b/test/pp/cases/8c_skipped_relaxed_syntax.expected @@ -0,0 +1 @@ +ok diff --git a/test/pp/cases/8d_skipped_keeps_nesting.c b/test/pp/cases/8d_skipped_keeps_nesting.c @@ -0,0 +1,6 @@ +#if 0 +#if 1 +#endif +hidden +#endif +ok diff --git a/test/pp/cases/8d_skipped_keeps_nesting.expected b/test/pp/cases/8d_skipped_keeps_nesting.expected @@ -0,0 +1 @@ +ok diff --git a/test/pp/cases/90_inc.h b/test/pp/cases/90_inc.h @@ -0,0 +1 @@ +header_token diff --git a/test/pp/cases/90_include_local.c b/test/pp/cases/90_include_local.c @@ -0,0 +1,2 @@ +#include "90_inc.h" +end diff --git a/test/pp/cases/90_include_local.expected b/test/pp/cases/90_include_local.expected @@ -0,0 +1,2 @@ +header_token +end diff --git a/test/pp/cases/91_inc.h b/test/pp/cases/91_inc.h @@ -0,0 +1 @@ +#define HEADER_VAL 7 diff --git a/test/pp/cases/91_include_macros_propagate.c b/test/pp/cases/91_include_macros_propagate.c @@ -0,0 +1,2 @@ +#include "91_inc.h" +HEADER_VAL diff --git a/test/pp/cases/91_include_macros_propagate.expected b/test/pp/cases/91_include_macros_propagate.expected @@ -0,0 +1 @@ +7 diff --git a/test/pp/cases/92_inc.h b/test/pp/cases/92_inc.h @@ -0,0 +1 @@ +ok diff --git a/test/pp/cases/92_include_macro_replaced_path.c b/test/pp/cases/92_include_macro_replaced_path.c @@ -0,0 +1,2 @@ +#define WHICH "92_inc.h" +#include WHICH diff --git a/test/pp/cases/92_include_macro_replaced_path.expected b/test/pp/cases/92_include_macro_replaced_path.expected @@ -0,0 +1,3 @@ + +ok + diff --git a/test/pp/cases/93_a.h b/test/pp/cases/93_a.h @@ -0,0 +1,2 @@ +#include "93_b.h" +a_end diff --git a/test/pp/cases/93_b.h b/test/pp/cases/93_b.h @@ -0,0 +1 @@ +b_only diff --git a/test/pp/cases/93_include_nested.c b/test/pp/cases/93_include_nested.c @@ -0,0 +1,2 @@ +#include "93_a.h" +end diff --git a/test/pp/cases/93_include_nested.expected b/test/pp/cases/93_include_nested.expected @@ -0,0 +1,3 @@ +b_only +a_end +end diff --git a/test/pp/cases/94_include_system_form.c b/test/pp/cases/94_include_system_form.c @@ -0,0 +1,2 @@ +#include <90_inc.h> +end diff --git a/test/pp/cases/94_include_system_form.expected b/test/pp/cases/94_include_system_form.expected @@ -0,0 +1,2 @@ +header_token +end diff --git a/test/pp/cases/a0_line_number.c b/test/pp/cases/a0_line_number.c @@ -0,0 +1,5 @@ +#define WHEN __LINE__ +WHEN +#line 100 +WHEN +WHEN diff --git a/test/pp/cases/a0_line_number.expected b/test/pp/cases/a0_line_number.expected @@ -0,0 +1,4 @@ + +2 +100 +101 diff --git a/test/pp/cases/a1_line_with_file.c b/test/pp/cases/a1_line_with_file.c @@ -0,0 +1,2 @@ +#line 50 "fake.c" +__FILE__ diff --git a/test/pp/cases/a1_line_with_file.expected b/test/pp/cases/a1_line_with_file.expected @@ -0,0 +1 @@ +"fake.c" diff --git a/test/pp/cases/a2_line_macro_replaced.c b/test/pp/cases/a2_line_macro_replaced.c @@ -0,0 +1,3 @@ +#define LN 200 +#line LN +__LINE__ diff --git a/test/pp/cases/a2_line_macro_replaced.expected b/test/pp/cases/a2_line_macro_replaced.expected @@ -0,0 +1 @@ +200 diff --git a/test/pp/cases/b0_pragma_unknown_ignored.c b/test/pp/cases/b0_pragma_unknown_ignored.c @@ -0,0 +1,2 @@ +#pragma frobnicate +ok diff --git a/test/pp/cases/b0_pragma_unknown_ignored.expected b/test/pp/cases/b0_pragma_unknown_ignored.expected @@ -0,0 +1,2 @@ +#pragma frobnicate +ok diff --git a/test/pp/cases/b1_pragma_operator.c b/test/pp/cases/b1_pragma_operator.c @@ -0,0 +1,3 @@ +x; +_Pragma("frobnicate") +y; diff --git a/test/pp/cases/b1_pragma_operator.expected b/test/pp/cases/b1_pragma_operator.expected @@ -0,0 +1,3 @@ +x; +#pragma frobnicate +y; diff --git a/test/pp/cases/b2_pragma_stdc.c b/test/pp/cases/b2_pragma_stdc.c @@ -0,0 +1,2 @@ +#pragma STDC FP_CONTRACT ON +ok diff --git a/test/pp/cases/b2_pragma_stdc.expected b/test/pp/cases/b2_pragma_stdc.expected @@ -0,0 +1,2 @@ +#pragma STDC FP_CONTRACT ON +ok diff --git a/test/pp/cases/c0_line_predefined.c b/test/pp/cases/c0_line_predefined.c @@ -0,0 +1 @@ +__LINE__ diff --git a/test/pp/cases/c0_line_predefined.expected b/test/pp/cases/c0_line_predefined.expected @@ -0,0 +1 @@ +1 diff --git a/test/pp/cases/c1_stdc_macro.c b/test/pp/cases/c1_stdc_macro.c @@ -0,0 +1 @@ +__STDC__ diff --git a/test/pp/cases/c1_stdc_macro.expected b/test/pp/cases/c1_stdc_macro.expected @@ -0,0 +1 @@ +1 diff --git a/test/pp/cases/c2_stdc_hosted.c b/test/pp/cases/c2_stdc_hosted.c @@ -0,0 +1 @@ +__STDC_HOSTED__ diff --git a/test/pp/cases/c2_stdc_hosted.expected b/test/pp/cases/c2_stdc_hosted.expected @@ -0,0 +1 @@ +0 diff --git a/test/pp/cases/c3_predefined_macros_defined.c b/test/pp/cases/c3_predefined_macros_defined.c @@ -0,0 +1,12 @@ +#ifdef __STDC_VERSION__ +have_version +#endif +#ifdef __DATE__ +have_date +#endif +#ifdef __TIME__ +have_time +#endif +#ifdef __FILE__ +have_file +#endif diff --git a/test/pp/cases/c3_predefined_macros_defined.expected b/test/pp/cases/c3_predefined_macros_defined.expected @@ -0,0 +1,5 @@ + +have_version +have_date +have_time +have_file diff --git a/test/pp/cases/c4_date_value.c b/test/pp/cases/c4_date_value.c @@ -0,0 +1 @@ +__DATE__ diff --git a/test/pp/cases/c4_date_value.expected b/test/pp/cases/c4_date_value.expected @@ -0,0 +1 @@ +"Jan 1 1970" diff --git a/test/pp/cases/c5_time_value.c b/test/pp/cases/c5_time_value.c @@ -0,0 +1 @@ +__TIME__ diff --git a/test/pp/cases/c5_time_value.expected b/test/pp/cases/c5_time_value.expected @@ -0,0 +1 @@ +"00:00:00" diff --git a/test/pp/cases/c6_file_value.c b/test/pp/cases/c6_file_value.c @@ -0,0 +1 @@ +__FILE__ diff --git a/test/pp/cases/c6_file_value.expected b/test/pp/cases/c6_file_value.expected @@ -0,0 +1 @@ +"c6_file_value.c" diff --git a/test/pp/cases/d0_data.bin b/test/pp/cases/d0_data.bin @@ -0,0 +1 @@ +A +\ No newline at end of file diff --git a/test/pp/cases/d0_embed_basic.c b/test/pp/cases/d0_embed_basic.c @@ -0,0 +1,2 @@ +#embed "d0_data.bin" +end diff --git a/test/pp/cases/d0_embed_basic.expected b/test/pp/cases/d0_embed_basic.expected @@ -0,0 +1 @@ +65 end diff --git a/test/pp/cases/d1_data.bin b/test/pp/cases/d1_data.bin @@ -0,0 +1 @@ +Hi +\ No newline at end of file diff --git a/test/pp/cases/d1_embed_two_bytes.c b/test/pp/cases/d1_embed_two_bytes.c @@ -0,0 +1,2 @@ +#embed "d1_data.bin" +end diff --git a/test/pp/cases/d1_embed_two_bytes.expected b/test/pp/cases/d1_embed_two_bytes.expected @@ -0,0 +1 @@ +72, 105 end diff --git a/test/pp/cases/d2_embed_in_array.c b/test/pp/cases/d2_embed_in_array.c @@ -0,0 +1,3 @@ +char a[] = { +#embed "d1_data.bin" +}; diff --git a/test/pp/cases/d2_embed_in_array.expected b/test/pp/cases/d2_embed_in_array.expected @@ -0,0 +1 @@ +char a[] = {72, 105 }; diff --git a/test/pp/cases/d3_embed_limit.c b/test/pp/cases/d3_embed_limit.c @@ -0,0 +1,2 @@ +#embed "d1_data.bin" limit(1) +end diff --git a/test/pp/cases/d3_embed_limit.expected b/test/pp/cases/d3_embed_limit.expected @@ -0,0 +1 @@ +72 end diff --git a/test/pp/cases/d4_embed_if_empty.c b/test/pp/cases/d4_embed_if_empty.c @@ -0,0 +1,2 @@ +#embed "d4_empty.bin" if_empty(NOTHING) +end diff --git a/test/pp/cases/d4_embed_if_empty.expected b/test/pp/cases/d4_embed_if_empty.expected @@ -0,0 +1 @@ +NOTHING end diff --git a/test/pp/cases/define_function.actual b/test/pp/cases/d4_empty.bin diff --git a/test/pp/cases/define_function.c b/test/pp/cases/define_function.c @@ -1,2 +0,0 @@ -#define ADD(a, b) ((a) + (b)) -ADD(1, 2 * 3) diff --git a/test/pp/cases/define_function.expected b/test/pp/cases/define_function.expected @@ -1 +0,0 @@ -((1) + (2 * 3)) diff --git a/test/pp/cases/define_object.actual b/test/pp/cases/define_object.actual diff --git a/test/pp/cases/define_object.c b/test/pp/cases/define_object.c @@ -1,2 +0,0 @@ -#define X 42 -X X X diff --git a/test/pp/cases/define_object.expected b/test/pp/cases/define_object.expected @@ -1 +0,0 @@ -42 42 42 diff --git a/test/pp/cases/ifdef_basic.actual b/test/pp/cases/ifdef_basic.actual diff --git a/test/pp/cases/ifdef_basic.c b/test/pp/cases/ifdef_basic.c @@ -1,11 +0,0 @@ -#define KEEP 1 -#ifdef KEEP -chosen -#else -skipped -#endif -#ifndef MISSING -also_chosen -#else -also_skipped -#endif diff --git a/test/pp/cases/ifdef_basic.expected b/test/pp/cases/ifdef_basic.expected @@ -1,2 +0,0 @@ -chosen -also_chosen diff --git a/test/pp/cases/include_local.actual b/test/pp/cases/include_local.actual diff --git a/test/pp/cases/include_local.c b/test/pp/cases/include_local.c @@ -1,2 +0,0 @@ -#include "include_local.h" -FROM_HEADER diff --git a/test/pp/cases/include_local.expected b/test/pp/cases/include_local.expected @@ -1,2 +0,0 @@ -header_token -7 diff --git a/test/pp/cases/include_local.h b/test/pp/cases/include_local.h @@ -1,2 +0,0 @@ -#define FROM_HEADER 7 -header_token diff --git a/test/pp/cases/undef.actual b/test/pp/cases/undef.actual diff --git a/test/pp/cases/undef.c b/test/pp/cases/undef.c @@ -1,6 +0,0 @@ -#define X 1 -X -#undef X -X -#define X 2 -X diff --git a/test/pp/cases/undef.expected b/test/pp/cases/undef.expected @@ -1,3 +0,0 @@ -1 -X -2 diff --git a/test/pp/cases_err/01_redef_object_diff_tokens.c b/test/pp/cases_err/01_redef_object_diff_tokens.c @@ -0,0 +1,2 @@ +#define X 1 +#define X 2 diff --git a/test/pp/cases_err/02_redef_object_diff_whitespace.c b/test/pp/cases_err/02_redef_object_diff_whitespace.c @@ -0,0 +1,2 @@ +#define X (1-1) +#define X (1 - 1) diff --git a/test/pp/cases_err/03_redef_func_diff_param_name.c b/test/pp/cases_err/03_redef_func_diff_param_name.c @@ -0,0 +1,2 @@ +#define M(a) (a) +#define M(b) (b) diff --git a/test/pp/cases_err/04_redef_func_diff_param_count.c b/test/pp/cases_err/04_redef_func_diff_param_count.c @@ -0,0 +1,2 @@ +#define M(a) a +#define M(a,b) a diff --git a/test/pp/cases_err/05_paste_at_start.c b/test/pp/cases_err/05_paste_at_start.c @@ -0,0 +1,2 @@ +#define M(a) ## a +M(1) diff --git a/test/pp/cases_err/06_paste_at_end.c b/test/pp/cases_err/06_paste_at_end.c @@ -0,0 +1,2 @@ +#define M(a) a ## +M(1) diff --git a/test/pp/cases_err/07_hash_not_followed_by_param.c b/test/pp/cases_err/07_hash_not_followed_by_param.c @@ -0,0 +1,2 @@ +#define M(a) # b +M(1) diff --git a/test/pp/cases_err/08_va_args_in_non_variadic.c b/test/pp/cases_err/08_va_args_in_non_variadic.c @@ -0,0 +1,2 @@ +#define M __VA_ARGS__ +M diff --git a/test/pp/cases_err/09_func_too_few_args.c b/test/pp/cases_err/09_func_too_few_args.c @@ -0,0 +1,2 @@ +#define M(a,b) a+b +M(1) diff --git a/test/pp/cases_err/0a_func_too_many_args.c b/test/pp/cases_err/0a_func_too_many_args.c @@ -0,0 +1,2 @@ +#define M(a) a +M(1,2) diff --git a/test/pp/cases_err/0b_unterminated_func_invoke.c b/test/pp/cases_err/0b_unterminated_func_invoke.c @@ -0,0 +1,2 @@ +#define M(a) a +M(1 diff --git a/test/pp/cases_err/0c_define_predefined.c b/test/pp/cases_err/0c_define_predefined.c @@ -0,0 +1 @@ +#define __STDC__ 0 diff --git a/test/pp/cases_err/0d_undef_predefined.c b/test/pp/cases_err/0d_undef_predefined.c @@ -0,0 +1 @@ +#undef __STDC__ diff --git a/test/pp/cases_err/0e_define_defined.c b/test/pp/cases_err/0e_define_defined.c @@ -0,0 +1 @@ +#define defined 1 diff --git a/test/pp/cases_err/0f_error_directive.c b/test/pp/cases_err/0f_error_directive.c @@ -0,0 +1 @@ +#error this should fail diff --git a/test/pp/run.sh b/test/pp/run.sh @@ -7,6 +7,13 @@ # can be reviewed and copied over the expected baseline once intentional # output changes are validated. # +# To make __DATE__/__TIME__/__FILE__ deterministic across runs and +# checkouts, the runner: +# - exports SOURCE_DATE_EPOCH=0 (1970-01-01T00:00:00Z), which fixes +# __DATE__ to "Jan 1 1970" and __TIME__ to "00:00:00"; +# - cd's into the cases dir and passes the source as a basename, so +# __FILE__ is the file name itself with no checkout-path prefix. +# # Honors $CFREE for the binary path; defaults to build/cfree relative to the # repo root inferred from this script's location. @@ -23,25 +30,30 @@ if [ ! -x "$CFREE" ]; then exit 2 fi +# Reproducible-builds: pin __DATE__ and __TIME__ to the Unix epoch. +export SOURCE_DATE_EPOCH=0 + +cd "$cases_dir" || exit 2 + pass=0 fail=0 failures= -for src in "$cases_dir"/*.c; do +for src in *.c; do [ -e "$src" ] || continue expected="${src%.c}.expected" actual="${src%.c}.actual" - name=$(basename "${src%.c}") + name="${src%.c}" if [ ! -e "$expected" ]; then - printf 'FAIL %s (missing %s)\n' "$name" "$(basename "$expected")" + printf 'FAIL %s (missing %s)\n' "$name" "$expected" fail=$((fail + 1)) failures="$failures $name" continue fi - if ! "$CFREE" cc -E -I "$cases_dir" "$src" -o "$actual" >/dev/null 2>&1; then - printf 'FAIL %s (cfree exit nonzero; see %s)\n' "$name" "$(basename "$actual")" + if ! "$CFREE" cc -E -I . "$src" -o "$actual" >/dev/null 2>&1; then + printf 'FAIL %s (cfree exit nonzero; see %s)\n' "$name" "$actual" fail=$((fail + 1)) failures="$failures $name" continue diff --git a/test/pp/run_errors.sh b/test/pp/run_errors.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# Data-driven preprocessor must-fail runner. +# +# For each test/pp/cases_err/*.c, runs `cfree cc -E` and expects a +# nonzero exit (constraint violations from C11 §6.10 must be +# diagnosed). Counts only exit status; stderr is captured but not +# pattern-matched. +# +# Honors $CFREE for the binary path; defaults to build/cfree relative +# to the repo root inferred from this script's location. + +set -u + +script_dir=$(cd "$(dirname "$0")" && pwd) +repo_root=$(cd "$script_dir/../.." && pwd) +cases_dir="$script_dir/cases_err" + +CFREE="${CFREE:-$repo_root/build/cfree}" + +if [ ! -x "$CFREE" ]; then + echo "pp-err: cfree binary not found at $CFREE" >&2 + exit 2 +fi + +# Match run.sh so __DATE__/__TIME__/__FILE__ are deterministic. +export SOURCE_DATE_EPOCH=0 + +cd "$cases_dir" || exit 2 + +pass=0 +fail=0 +failures= + +for src in *.c; do + [ -e "$src" ] || continue + name="${src%.c}" + + if "$CFREE" cc -E -I . "$src" -o /dev/null >/dev/null 2>&1; then + printf 'FAIL %s (expected nonzero exit, got success)\n' "$name" + fail=$((fail + 1)) + failures="$failures $name" + else + printf 'PASS %s\n' "$name" + pass=$((pass + 1)) + fi +done + +total=$((pass + fail)) +printf '\npp-err: %d/%d passed\n' "$pass" "$total" +if [ "$fail" -gt 0 ]; then + printf 'pp-err: failures:%s\n' "$failures" + exit 1 +fi diff --git a/test/test.mk b/test/test.mk @@ -10,9 +10,9 @@ # to /tmp and run the host's `ar t` / `nm --print-armap` as a # cross-check. -.PHONY: test test-lex test-pp test-elf test-ar +.PHONY: test test-lex test-pp test-pp-err test-elf test-ar -test: test-lex test-pp test-elf test-ar +test: test-lex test-pp test-pp-err test-elf test-ar test-lex: bin @CFREE=$(BIN) test/lex/run.sh @@ -20,6 +20,9 @@ test-lex: bin test-pp: bin @CFREE=$(BIN) test/pp/run.sh +test-pp-err: bin + @CFREE=$(BIN) test/pp/run_errors.sh + test-elf: lib bin-soft bash test/elf/run.sh