xref: /freebsd/contrib/bmake/unit-tests/cond-token-plain.mk (revision a90b9d0159070121c221b966469c3e36d912bf82)
1# $NetBSD: cond-token-plain.mk,v 1.19 2023/11/19 21:47:52 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# XXX: The error message is missing for this malformed condition.
18# The right-hand side of the comparison is just a '"', before unescaping.
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# XXX: Missing error message for the malformed condition. The right-hand
42# side before unescaping is double-quotes, backslash, backslash.
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
110VAR=	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
256COND=	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