#!/bin/sh set -eu base="${TMPDIR-/tmp}/mcpu-cpp-variadic-$$" mkdir -p "$base" trap 'rm -rf "$base"' EXIT HUP INT TERM cat > "$base/main.c" <<'EOT' #define A 7 #define SUM(X, Y) X + Y #define V(...) <__VA_ARGS__> #define F(first, ...) first | __VA_ARGS__ #define STRV(...) #__VA_ARGS__ #define LEFT(...) pre ## __VA_ARGS__ #define RIGHT(...) __VA_ARGS__ ## post #define CALL(FN, ...) FN(__VA_ARGS__) #define WRAP(...) V(__VA_ARGS__) #define ID(X) X #define РУССКИЙ(первый, ...) первый : __VA_ARGS__ V(alpha, beta, gamma) V() F(one, two, three) F(one) F(one,) F(A, SUM(1, 2)) STRV(A, b + c) LEFT(fix) LEFT() RIGHT(fix) RIGHT() CALL(ID, A) WRAP(A, SUM(3, 4)) РУССКИЙ(один, два, три) EOT "$MCPU_CPP" --no-config "$base/main.c" -o "$base/out" grep -Fx '' "$base/out" >/dev/null grep -Fx '<>' "$base/out" >/dev/null grep -Fx 'one | two, three' "$base/out" >/dev/null # Both an omitted variadic tail and an explicitly empty one expand to no tokens. test "$(grep -Fxc 'one | ' "$base/out")" -eq 2 grep -Fx '7 | 1 + 2' "$base/out" >/dev/null grep -Fx '"A, b + c"' "$base/out" >/dev/null grep -Fx 'prefix' "$base/out" >/dev/null grep -Fx 'pre' "$base/out" >/dev/null grep -Fx 'fixpost' "$base/out" >/dev/null grep -Fx 'post' "$base/out" >/dev/null grep -Fx '7' "$base/out" >/dev/null grep -Fx '<7, 3 + 4>' "$base/out" >/dev/null grep -Fx 'один : два, три' "$base/out" >/dev/null # The variable argument is preserved as such when macro definitions are dumped. "$MCPU_CPP" --no-config -dM "$base/main.c" > "$base/dump" grep -Fx '#define V(...) <__VA_ARGS__>' "$base/dump" >/dev/null grep -Fx '#define F(first,...) first | __VA_ARGS__' "$base/dump" >/dev/null grep -Fx '#define STRV(...) #__VA_ARGS__' "$base/dump" >/dev/null grep -Fx '#define РУССКИЙ(первый,...) первый : __VA_ARGS__' "$base/dump" >/dev/null # Variadic-ness is part of a macro definition. Repeating the same variadic # definition is harmless, while changing a non-variadic definition into a # variadic one is a real redefinition and therefore participates in -Werror. cat > "$base/redefine-same.c" <<'EOT' #define SAME(x, ...) x | __VA_ARGS__ #define SAME(x, ...) x | __VA_ARGS__ SAME(1, 2) EOT "$MCPU_CPP" --no-config "$base/redefine-same.c" \ -o "$base/redefine-same.out" 2>"$base/redefine-same.err" test ! -s "$base/redefine-same.err" grep -Fx '1 | 2' "$base/redefine-same.out" >/dev/null cat > "$base/redefine-kind.c" <<'EOT' #define CHANGE(x) x #define CHANGE(x, ...) x | __VA_ARGS__ EOT if "$MCPU_CPP" --no-config -Werror "$base/redefine-kind.c" \ -o "$base/redefine-kind.out" 2>"$base/redefine-kind.err"; then echo 'variadic/non-variadic redefinition escaped -Werror' >&2 exit 1 fi grep "error: macro 'CHANGE' redefined" "$base/redefine-kind.err" >/dev/null # A variadic macro may have fixed parameters, but those fixed parameters are # still required. An empty spelling counts as an argument, just as it does # for ordinary function-like macros. cat > "$base/few.c" <<'EOT' #define NEEDS_TWO(a, b, ...) a + b + __VA_ARGS__ NEEDS_TWO(1) EOT if "$MCPU_CPP" --no-config "$base/few.c" -o "$base/few.out" 2>"$base/few.err"; then echo 'variadic macro accepted too few fixed arguments' >&2 exit 1 fi grep "macro 'NEEDS_TWO' used with too few arguments" "$base/few.err" >/dev/null # The old GNU named variadic form is deliberately outside the 0.0.46 # contract. Only the C99-style ... + __VA_ARGS__ form is accepted. cat > "$base/named.c" <<'EOT' #define OLD(args...) args EOT if "$MCPU_CPP" --no-config "$base/named.c" -o "$base/named.out" 2>"$base/named.err"; then echo 'GNU named variadic macro form unexpectedly accepted' >&2 exit 1 fi grep 'badly punctuated parameter list in #define' "$base/named.err" >/dev/null cat > "$base/malformed.c" <<'EOT' #define BAD(..., x) x EOT if "$MCPU_CPP" --no-config "$base/malformed.c" -o "$base/malformed.out" 2>"$base/malformed.err"; then echo 'parameters after ... unexpectedly accepted' >&2 exit 1 fi grep 'badly punctuated parameter list in #define' "$base/malformed.err" >/dev/null cat > "$base/reserved.c" <<'EOT' #define BAD(__VA_ARGS__) __VA_ARGS__ EOT if "$MCPU_CPP" --no-config "$base/reserved.c" -o "$base/reserved.out" 2>"$base/reserved.err"; then echo '__VA_ARGS__ unexpectedly accepted as an ordinary parameter name' >&2 exit 1 fi grep "'__VA_ARGS__' cannot be used as a macro parameter name" "$base/reserved.err" >/dev/null # A replacement using #__VA_ARGS__ is valid only for a variadic macro. cat > "$base/not-variadic.c" <<'EOT' #define BAD(x) #__VA_ARGS__ EOT if "$MCPU_CPP" --no-config "$base/not-variadic.c" -o "$base/not-variadic.out" 2>"$base/not-variadic.err"; then echo '#__VA_ARGS__ unexpectedly accepted in a non-variadic macro' >&2 exit 1 fi grep "'#' operator should be followed by a macro argument name" "$base/not-variadic.err" >/dev/null