xref: /freebsd/contrib/bmake/unit-tests/directive-for-if.mk (revision d5e0a182cf153f8993a633b93d9220c99a89e760)
1# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
2#
3# Test for a .for directive that contains an .if directive.
4#
5# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for
6# loop, their values were placed verbatim in the expanded body.  Since then,
7# each variable value expands to an expression of the form ${:Uvalue}.
8#
9# Before that change, the following adventurous code was possible:
10#
11#	.for directive in if ifdef ifndef
12#	.  ${directive} "1" != "0"
13#	.  endif
14#	.endfor
15#
16# A more practical usage of the .for loop that often led to surprises was the
17# following:
18#
19#	.for var in VAR1 VAR2 VAR3
20#	.  if ${var} != "VAR2"
21#	.  endif
22#	.endfor
23#
24# The .for loop body expanded to this string:
25#
26#	.  if VAR1 != "VAR2"
27#	.  endif
28#
29# Since bare words were not allowed at the left-hand side of a condition,
30# make complained about a "Malformed conditional", which was surprising since
31# the code before expanding the .for loop body looked quite well.
32#
33# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for
34# loops changed from plain textual value to using expressions of the form
35# ${:Uvalue}, this surprising behavior was documented in the code, and a
36# workaround was implemented that allowed bare words when they are followed
37# by either '!' or '=', as part of the operators '!=' or '=='.
38#
39# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand
40# side of a condition, but that applies only to expression of the form
41# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if
42# directives.
43
44# The following snippet worked in 2005, when the variables from the .for loop
45# expanded to their bare textual value.
46.for directive in if ifdef ifndef
47.  ${directive} "1" != "0"
48# expect+3: if-less endif
49# expect+2: if-less endif
50# expect+1: if-less endif
51.  endif
52.endfor
53# In 2021, the above code does not generate an error message, even though the
54# code looks clearly malformed.  This is due to the '!', which is interpreted
55# as a dependency operator, similar to ':' and '::'.  The parser turns this
56# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
57# '=' and '"0"'.  Since that line is not interpreted as an '.if' directive,
58# the error message 'if-less endif' makes sense.
59
60# In 2005, make complained:
61#
62#	.if line:	Malformed conditional (VAR1 != "VAR2")
63#	.endif line:	if-less endif
64#	.endif line:	Need an operator
65#
66# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
67.for var in VAR1 VAR2 VAR3
68.  if ${var} != "VAR2"
69_!=	echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
70.  endif
71.endfor
72
73# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
74# expression from the .for loop with '"'.  Such a string literal
75# has been allowed since cond.c 1.23 from 2004-04-13.  Between that commit and
76# the one from 2008, the parser would still get confused if the value from the
77# .for loop contained '"', which was effectively a code injection.
78#
79# Surrounding ${var} with quotes disabled the check for typos though.  For
80# ordinary variables, referring to an undefined variable on the left-hand side
81# of the comparison resulted in a "Malformed conditional".  Since the .for
82# loop was usually close to the .if clause, this was not a problem in
83# practice.
84.for var in VAR1 VAR2 VAR3
85.  if "${var}" != "VAR2"
86.  endif
87.endfor
88
89all:
90