summaryrefslogtreecommitdiff
path: root/tests/t0059-va-opt.sh
blob: 052df6c8f1c8c5fa387b5354066d0456c79ea06e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/bin/sh
set -eu
base="${TMPDIR-/tmp}/mcpu-cpp-va-opt-$$"
mkdir -p "$base"
trap 'rm -rf "$base"' EXIT HUP INT TERM

cat > "$base/main.c" <<'EOT'
#define EMPTY
#define X 123
#define xy RESCANNED
#define HAS(...) __VA_OPT__(yes)
#define COMMA(format, ...) call(format __VA_OPT__(,) __VA_ARGS__)
#define STR(...) #__VA_OPT__(__VA_ARGS__)
#define STRFIX(a, ...) #__VA_OPT__(a __VA_ARGS__)
#define STRPASTE(a, ...) #__VA_OPT__(a ## z)
#define LEFT(...) pre ## __VA_OPT__(__VA_ARGS__)
#define RIGHT(...) __VA_OPT__(__VA_ARGS__) ## post
#define INNER(a, ...) __VA_OPT__(a ## y)
#define BALANCED(...) __VA_OPT__((a, (b, c)))
#define EMPTY_LEFT(...) x ## __VA_OPT__()
#define EMPTY_RIGHT(...) __VA_OPT__() ## y
#define TEXT(...) "__VA_OPT__(not syntax)" __VA_OPT__(ok)

[HAS()]
[HAS(EMPTY)]
[HAS(token)]
COMMA("zero")
COMMA("two", X, 7)
STR()
STR(X)
STRFIX(X, y)
STRPASTE(X, y)
LEFT()
LEFT(X)
RIGHT()
RIGHT(X)
INNER(x, token)
BALANCED(token)
EMPTY_LEFT(token)
EMPTY_RIGHT(token)
TEXT()
TEXT(token)
EOT

"$MCPU_CPP" --no-config "$base/main.c" -o "$base/out"
sed '/^#/d; /^[[:space:]]*$/d; s/[[:space:]]//g' "$base/out" > "$base/norm"

cat > "$base/expected" <<'EOT'
[]
[]
[yes]
call("zero")
call("two",123,7)
""
"123"
"123y"
"Xz"
pre
pre123
post
123post
RESCANNED
(a,(b,c))
x
y
"__VA_OPT__(notsyntax)"
"__VA_OPT__(notsyntax)"ok
EOT

diff -u "$base/expected" "$base/norm"

# __VA_OPT__ remains visible in macro dumps as part of the replacement list.
"$MCPU_CPP" --no-config -dM "$base/main.c" > "$base/dump"
grep -Fx '#define HAS(...) __VA_OPT__(yes)' "$base/dump" >/dev/null
grep -Fx '#define COMMA(format,...) call(format __VA_OPT__(,) __VA_ARGS__)' "$base/dump" >/dev/null
grep -Fx '#define STR(...) #__VA_OPT__(__VA_ARGS__)' "$base/dump" >/dev/null

# __VA_OPT__ is structural syntax, not a general identifier-like extension.
cat > "$base/nonvariadic.c" <<'EOT'
#define BAD(x) __VA_OPT__(x)
EOT
if "$MCPU_CPP" --no-config "$base/nonvariadic.c" -o "$base/nonvariadic.out" 2>"$base/nonvariadic.err"; then
  echo '__VA_OPT__ unexpectedly accepted in a non-variadic macro' >&2
  exit 1
fi
grep "'__VA_OPT__' may appear only in a variadic macro replacement list" "$base/nonvariadic.err" >/dev/null

cat > "$base/object.c" <<'EOT'
#define BAD __VA_OPT__(x)
EOT
if "$MCPU_CPP" --no-config "$base/object.c" -o "$base/object.out" 2>"$base/object.err"; then
  echo '__VA_OPT__ unexpectedly accepted in an object-like macro' >&2
  exit 1
fi
grep "'__VA_OPT__' may appear only in a variadic macro replacement list" "$base/object.err" >/dev/null

cat > "$base/nested.c" <<'EOT'
#define BAD(...) __VA_OPT__(a __VA_OPT__(b))
EOT
if "$MCPU_CPP" --no-config "$base/nested.c" -o "$base/nested.out" 2>"$base/nested.err"; then
  echo 'nested __VA_OPT__ unexpectedly accepted' >&2
  exit 1
fi
grep "'__VA_OPT__' may not appear inside another '__VA_OPT__'" "$base/nested.err" >/dev/null

cat > "$base/no-open.c" <<'EOT'
#define BAD(...) __VA_OPT__ x
EOT
if "$MCPU_CPP" --no-config "$base/no-open.c" -o "$base/no-open.out" 2>"$base/no-open.err"; then
  echo '__VA_OPT__ without opening parenthesis unexpectedly accepted' >&2
  exit 1
fi
grep "'__VA_OPT__' must be followed by '('" "$base/no-open.err" >/dev/null

cat > "$base/unterminated.c" <<'EOT'
#define BAD(...) __VA_OPT__((x)
EOT
if "$MCPU_CPP" --no-config "$base/unterminated.c" -o "$base/unterminated.out" 2>"$base/unterminated.err"; then
  echo 'unterminated __VA_OPT__ unexpectedly accepted' >&2
  exit 1
fi
grep "unterminated '__VA_OPT__'" "$base/unterminated.err" >/dev/null

cat > "$base/paste-first.c" <<'EOT'
#define BAD(...) __VA_OPT__(## x)
EOT
if "$MCPU_CPP" --no-config "$base/paste-first.c" -o "$base/paste-first.out" 2>"$base/paste-first.err"; then
  echo 'leading ## inside __VA_OPT__ unexpectedly accepted' >&2
  exit 1
fi
grep "'##' cannot appear at the beginning of '__VA_OPT__'" "$base/paste-first.err" >/dev/null

cat > "$base/paste-last.c" <<'EOT'
#define BAD(...) __VA_OPT__(x ##)
EOT
if "$MCPU_CPP" --no-config "$base/paste-last.c" -o "$base/paste-last.out" 2>"$base/paste-last.err"; then
  echo 'trailing ## inside __VA_OPT__ unexpectedly accepted' >&2
  exit 1
fi
grep "'##' cannot appear at the end of '__VA_OPT__'" "$base/paste-last.err" >/dev/null

# The historical GNU comma-swallow extension remains deliberately absent.
# __VA_OPT__ is the supported way to make a separator conditional.
cat > "$base/no-gnu-comma.c" <<'EOT'
#define OLD(format, ...) call(format, ## __VA_ARGS__)
OLD("x")
EOT
"$MCPU_CPP" --no-config "$base/no-gnu-comma.c" -o "$base/no-gnu-comma.out" 2>"$base/no-gnu-comma.err"
sed '/^#/d; /^[[:space:]]*$/d; s/[[:space:]]//g' "$base/no-gnu-comma.out" > "$base/no-gnu-comma.norm"
grep -Fx 'call("x",)' "$base/no-gnu-comma.norm" >/dev/null