cond-token-plain.mk revision 1.23 1 # $NetBSD: cond-token-plain.mk,v 1.23 2025/07/06 07:56:16 rillig Exp $
2 #
3 # Tests for plain tokens (that is, string literals without quotes)
4 # in .if conditions. These are also called bare words.
5
6 .MAKEFLAGS: -dc
7
8 # The word 'value' after the '!=' is a bare word.
9 .if ${:Uvalue} != value
10 . error
11 .endif
12
13 # Using a '#' in a string literal in a condition leads to a malformed
14 # condition since comment parsing is done in an early phase and removes the
15 # '#' and everything after it long before the condition parser gets to see it.
16 #
17 #
18 # expect+1: Unfinished string literal """
19 .if ${:U} != "#hash"
20 . error
21 .endif
22
23 # To get a '#' into a condition, it has to be escaped using a backslash.
24 # This prevents the comment parser from removing it, and in turn, it becomes
25 # visible to CondParser_String.
26 .if ${:U\#hash} != "\#hash"
27 . error
28 .endif
29
30 # Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles
31 # the '#' specially, even though at this point, there should be no need for
32 # comment handling anymore. The comments are supposed to be stripped off
33 # in a very early parsing phase.
34 #
35 # See https://gnats.netbsd.org/19596 for example makefiles demonstrating the
36 # original problems. At that time, the parser didn't recognize the comment in
37 # the line '.else # comment3'. This workaround is not needed anymore since
38 # comments are stripped in an earlier phase. See "case '#'" in
39 # CondParser_Token.
40 #
41 #
42 # expect+1: Unfinished string literal ""\\"
43 .if ${:U\\} != "\\#hash"
44 . error
45 .endif
46
47 # The right-hand side of a comparison is not parsed as a token, therefore
48 # the code from CondParser_Token does not apply to it.
49 # TODO: Explain the consequences.
50 # TODO: Does this mean that more syntactic variants are allowed here?
51 .if ${:U\#hash} != \#hash
52 . error
53 .endif
54
55 # XXX: What is the purpose of treating an escaped '#' in the following
56 # condition as a comment? And why only at the beginning of a token,
57 # just as in the shell?
58 .if 0 \# This is treated as a comment, but why?
59 . error
60 .endif
61
62 # Ah, ok, this can be used to add an end-of-condition comment. But does
63 # anybody really use this? This is neither documented nor obvious since
64 # the '#' is escaped. It's much clearer to write a comment in the line
65 # above the condition.
66 .if ${0 \# comment:?yes:no} != no
67 . error
68 .endif
69 .if ${1 \# comment:?yes:no} != yes
70 . error
71 .endif
72
73 # Usually there is whitespace around the comparison operator, but this is
74 # not required.
75 .if ${UNDEF:Uundefined}!=undefined
76 . error
77 .endif
78 .if ${UNDEF:U12345}>12345
79 . error
80 .endif
81 .if ${UNDEF:U12345}<12345
82 . error
83 .endif
84 .if (${UNDEF:U0})||0
85 . error
86 .endif
87
88 # Only the comparison operator terminates the comparison operand, and it's
89 # a coincidence that the '!' is both used in the '!=' comparison operator
90 # as well as for negating a comparison result.
91 #
92 # The characters '&' and '|' are part of the comparison operand.
93 .if ${:Uvar}&&name != "var&&name"
94 . error
95 .endif
96 .if ${:Uvar}||name != "var||name"
97 . error
98 .endif
99
100 # A bare word may occur alone in a condition, without any comparison
101 # operator. It is interpreted as the function call 'defined(bare)'.
102 .if bare
103 . error
104 .else
105 # expect+1: A bare word is treated like defined(...), and the variable 'bare' is not defined.
106 . info A bare word is treated like defined(...), and the variable $\
107 'bare' is not defined.
108 .endif
109
110 VAR= defined
111 .if VAR
112 # expect+1: A bare word is treated like defined(...).
113 . info A bare word is treated like defined(...).
114 .else
115 . error
116 .endif
117
118 # Bare words may be intermixed with expressions.
119 .if V${:UA}R
120 # expect+1: ok
121 . info ok
122 .else
123 . error
124 .endif
125
126 # In bare words, even undefined variables are allowed. Without the bare
127 # words, undefined variables are not allowed. That feels inconsistent.
128 .if V${UNDEF}AR
129 # expect+1: Undefined variables in bare words expand to an empty string.
130 . info Undefined variables in bare words expand to an empty string.
131 .else
132 . error
133 .endif
134
135 .if 0${:Ux00}
136 . error
137 .else
138 # expect+1: Numbers can be composed from literals and expressions.
139 . info Numbers can be composed from literals and expressions.
140 .endif
141
142 .if 0${:Ux01}
143 # expect+1: Numbers can be composed from literals and expressions.
144 . info Numbers can be composed from literals and expressions.
145 .else
146 . error
147 .endif
148
149 # If the right-hand side is missing, it's a parse error.
150 # expect+1: Missing right-hand side of operator "=="
151 .if "" ==
152 . error
153 .else
154 . error
155 .endif
156
157 # If the left-hand side is missing, it's a parse error as well, but without
158 # a specific error message.
159 # expect+1: Malformed conditional "== """
160 .if == ""
161 . error
162 .else
163 . error
164 .endif
165
166 # The '\\' is not a line continuation. Neither is it an unquoted string
167 # literal. Instead, it is parsed as a bare word (ParseWord),
168 # and in that context, the backslash is just an ordinary character. The
169 # function argument thus stays '\\' (2 backslashes). This string is passed
170 # to FuncDefined, and since there is no variable named '\\', the condition
171 # evaluates to false.
172 .if \\
173 . error
174 .else
175 # expect+1: The variable '\\' is not defined.
176 . info The variable '\\' is not defined.
177 .endif
178
179 ${:U\\\\}= backslash
180 .if \\
181 # expect+1: Now the variable '\\' is defined.
182 . info Now the variable '\\' is defined.
183 .else
184 . error
185 .endif
186
187 # Anything that doesn't start with a double quote is considered a "bare word".
188 # Strangely, a bare word may contain double quotes inside. Nobody should ever
189 # depend on this since it may well be unintended. See CondParser_String.
190 .if "unquoted\"quoted" != unquoted"quoted
191 . error
192 .endif
193
194 # FIXME: In CondParser_String, Var_Parse returns var_Error without a
195 # corresponding error message.
196 # expect+1: Malformed conditional "$$$$$$$$ != """
197 .if $$$$$$$$ != ""
198 . error
199 .else
200 . error
201 .endif
202
203 # In a condition in an .if directive, the left-hand side must not be an
204 # unquoted string literal.
205 # expect+1: Malformed conditional "left == right"
206 .if left == right
207 .endif
208 # Before cond.c 1.276 from 2021-09-21, an expression containing the
209 # modifier ':?:' allowed unquoted string literals for the rest of the
210 # condition. This was an unintended implementation mistake.
211 # expect+1: Malformed conditional "${0:?:} || left == right"
212 .if ${0:?:} || left == right
213 .endif
214 # This affected only the comparisons after the expression, so the following
215 # was still a syntax error.
216 # expect+1: Malformed conditional "left == right || ${0:?:}"
217 .if left == right || ${0:?:}
218 .endif
219
220 # See cond-token-string.mk for similar tests where the condition is enclosed
221 # in "quotes".
222
223 .MAKEFLAGS: -d0
224
225
226 # As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf
227 # looks suspicious of evaluating the expression twice: first for parsing a
228 # bare word and second for parsing the left-hand side of a comparison.
229 #
230 # In '.if' directives, the left-hand side of a comparison must not be a bare
231 # word though, and this keeps CondParser_Leaf from evaluating the expression
232 # for the second time. The right-hand side of a comparison may be a bare
233 # word, but that side has no risk of being parsed more than once.
234 #
235 # expect+1: Malformed conditional "VAR.${IF_COUNT::+=1} != """
236 .if VAR.${IF_COUNT::+=1} != ""
237 . error
238 .else
239 . error
240 .endif
241 .if ${IF_COUNT} != "1"
242 . error
243 .endif
244
245 # A different situation is when CondParser.leftUnquotedOK is true. This
246 # situation arises in expressions of the form ${cond:?yes:no}. As of
247 # 2021-12-30, the condition in such an expression is evaluated before parsing
248 # the condition, see varmod-ifelse.mk. To pass an expression to the
249 # condition parser, it needs to be escaped. This rarely happens in practice,
250 # in most cases the conditions are simple enough that it doesn't matter
251 # whether the condition is first evaluated and then parsed, or vice versa.
252 # A half-baked attempt at hiding this implementation detail is
253 # CondParser.leftUnquotedOK, but that is a rather leaky abstraction.
254
255 #.MAKEFLAGS: -dcv
256 COND= VAR.$${MOD_COUNT::+=1}
257 .if ${${COND} == "VAR.":?yes:no} != "yes"
258 . error
259 .endif
260
261 # The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was
262 # evaluated twice. In practice, expressions that occur in conditions do not
263 # have side effects, making this problem rather academic, but it is there.
264 .if ${MOD_COUNT} != "1 1"
265 . error
266 .endif
267 #.MAKEFLAGS: -d0
268
269
270 # A trailing backslash in a bare word does not escape anything.
271 # expect+1: Unfinished backslash escape sequence
272 .if ${${:U str == str\\}:?yes:no}
273 . error
274 .else
275 . error
276 .endif
277
278 # A trailing backslash in an unfinished string literal word does not escape
279 # anything.
280 # expect+2: Unfinished backslash escape sequence
281 # expect+1: Unfinished string literal ""str\"
282 .if ${${:U str == "str\\}:?yes:no}
283 . error
284 .else
285 . error
286 .endif
287
288 # expect+1: Unfinished string literal ""str"
289 .if ${${:U str == "str}:?yes:no}
290 . error
291 .else
292 . error
293 .endif
294