xref: /freebsd/contrib/bmake/unit-tests/cond-token-plain.mk (revision 18054d0220cfc8df9c9568c437bd6fbb59d53c3c)
1# $NetBSD: cond-token-plain.mk,v 1.15 2021/12/30 02:14:55 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 boolean operators '&' and '|' don't terminate a 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 appear alone in a condition, without any comparison
101# operator.  It is implicitly converted into defined(bare).
102.if bare
103.  error
104.else
105.  info A bare word is treated like defined(...), and the variable $\
106	'bare' is not defined.
107.endif
108
109VAR=	defined
110.if VAR
111.  info A bare word is treated like defined(...).
112.else
113.  error
114.endif
115
116# Bare words may be intermixed with variable expressions.
117.if V${:UA}R
118.  info ok
119.else
120.  error
121.endif
122
123# In bare words, even undefined variables are allowed.  Without the bare
124# words, undefined variables are not allowed.  That feels inconsistent.
125.if V${UNDEF}AR
126.  info Undefined variables in bare words expand to an empty string.
127.else
128.  error
129.endif
130
131.if 0${:Ux00}
132.  error
133.else
134.  info Numbers can be composed from literals and variable expressions.
135.endif
136
137.if 0${:Ux01}
138.  info Numbers can be composed from literals and variable expressions.
139.else
140.  error
141.endif
142
143# If the right-hand side is missing, it's a parse error.
144.if "" ==
145.  error
146.else
147.  error
148.endif
149
150# If the left-hand side is missing, it's a parse error as well, but without
151# a specific error message.
152.if == ""
153.  error
154.else
155.  error
156.endif
157
158# The '\\' is not a line continuation.  Neither is it an unquoted string
159# literal.  Instead, it is parsed as a bare word (ParseWord),
160# and in that context, the backslash is just an ordinary character. The
161# function argument thus stays '\\' (2 backslashes).  This string is passed
162# to FuncDefined, and since there is no variable named '\\', the condition
163# evaluates to false.
164.if \\
165.  error
166.else
167.  info The variable '\\' is not defined.
168.endif
169
170${:U\\\\}=	backslash
171.if \\
172.  info Now the variable '\\' is defined.
173.else
174.  error
175.endif
176
177# Anything that doesn't start with a double quote is considered a "bare word".
178# Strangely, a bare word may contain double quotes inside.  Nobody should ever
179# depend on this since it may well be unintended.  See CondParser_String.
180.if "unquoted\"quoted" != unquoted"quoted
181.  error
182.endif
183
184# FIXME: In CondParser_String, Var_Parse returns var_Error without a
185# corresponding error message.
186.if $$$$$$$$ != ""
187.  error
188.else
189.  error
190.endif
191
192# In a condition in an .if directive, the left-hand side must not be an
193# unquoted string literal.
194# expect+1: Malformed conditional (left == right)
195.if left == right
196.endif
197# Before cond.c 1.276 from 2021-09-21, a variable expression containing the
198# modifier ':?:' allowed unquoted string literals for the rest of the
199# condition.  This was an unintended implementation mistake.
200# expect+1: Malformed conditional (${0:?:} || left == right)
201.if ${0:?:} || left == right
202.endif
203# This affected only the comparisons after the expression, so the following
204# was still a syntax error.
205# expect+1: Malformed conditional (left == right || ${0:?:})
206.if left == right || ${0:?:}
207.endif
208
209# See cond-token-string.mk for similar tests where the condition is enclosed
210# in "quotes".
211
212.MAKEFLAGS: -d0
213
214
215# As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf
216# looks suspicious of evaluating the expression twice: first for parsing a
217# bare word and second for parsing the left-hand side of a comparison.
218#
219# In '.if' directives, the left-hand side of a comparison must not be a bare
220# word though, and this keeps CondParser_Leaf from evaluating the expression
221# for the second time.  The right-hand side of a comparison may be a bare
222# word, but that side has no risk of being parsed more than once.
223#
224# expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "")
225.if VAR.${IF_COUNT::+=1} != ""
226.  error
227.else
228.  error
229.endif
230.if ${IF_COUNT} != "1"
231.  error
232.endif
233
234# A different situation is when CondParser.leftUnquotedOK is true.  This
235# situation arises in expressions of the form ${cond:?yes:no}.  As of
236# 2021-12-30, the condition in such an expression is evaluated before parsing
237# the condition, see varmod-ifelse.mk.  To pass a variable expression to the
238# condition parser, it needs to be escaped.  This rarely happens in practice,
239# in most cases the conditions are simple enough that it doesn't matter
240# whether the condition is first evaluated and then parsed, or vice versa.
241# A half-baked attempt at hiding this implementation detail is
242# CondParser.leftUnquotedOK, but that is a rather leaky abstraction.
243
244#.MAKEFLAGS: -dcv
245COND=	VAR.$${MOD_COUNT::+=1}
246.if ${${COND} == "VAR.":?yes:no} != "yes"
247.  error
248.endif
249
250# The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was
251# evaluated twice.  In practice, expressions that occur in conditions do not
252# have side effects, making this problem rather academic, but it is there.
253.if ${MOD_COUNT} != "1 1"
254.  error
255.endif
256#.MAKEFLAGS: -d0
257