xref: /freebsd/contrib/bmake/unit-tests/varmod-edge.mk (revision 49caa483b3fafffd9cf5197eb30e8bb235aa7410)
1*49caa483SSimon J. Gerraty# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $
2*49caa483SSimon J. Gerraty#
3*49caa483SSimon J. Gerraty# Tests for edge cases in variable modifiers.
4*49caa483SSimon J. Gerraty#
5*49caa483SSimon J. Gerraty# These tests demonstrate the current implementation in small examples.
6*49caa483SSimon J. Gerraty# They may contain surprising behavior.
7*49caa483SSimon J. Gerraty#
8*49caa483SSimon J. Gerraty# Each test consists of:
9*49caa483SSimon J. Gerraty# - INP, the input to the test
10*49caa483SSimon J. Gerraty# - MOD, the expression for testing the modifier
11*49caa483SSimon J. Gerraty# - EXP, the expected output
12*49caa483SSimon J. Gerraty
13*49caa483SSimon J. GerratyTESTS+=		M-paren
14*49caa483SSimon J. GerratyINP.M-paren=	(parentheses) {braces} (opening closing) ()
15*49caa483SSimon J. GerratyMOD.M-paren=	${INP.M-paren:M(*)}
16*49caa483SSimon J. GerratyEXP.M-paren=	(parentheses) ()
17*49caa483SSimon J. Gerraty
18*49caa483SSimon J. Gerraty# The first closing brace matches the opening parenthesis.
19*49caa483SSimon J. Gerraty# The second closing brace actually ends the variable expression.
20*49caa483SSimon J. Gerraty#
21*49caa483SSimon J. Gerraty# XXX: This is unexpected but rarely occurs in practice.
22*49caa483SSimon J. GerratyTESTS+=		M-mixed
23*49caa483SSimon J. GerratyINP.M-mixed=	(paren-brace} (
24*49caa483SSimon J. GerratyMOD.M-mixed=	${INP.M-mixed:M(*}}
25*49caa483SSimon J. GerratyEXP.M-mixed=	(paren-brace}
26*49caa483SSimon J. Gerraty
27*49caa483SSimon J. Gerraty# After the :M modifier has parsed the pattern, only the closing brace
28*49caa483SSimon J. Gerraty# and the colon are unescaped. The other characters are left as-is.
29*49caa483SSimon J. Gerraty# To actually see this effect, the backslashes in the :M modifier need
30*49caa483SSimon J. Gerraty# to be doubled since single backslashes would simply be unescaped by
31*49caa483SSimon J. Gerraty# Str_Match.
32*49caa483SSimon J. Gerraty#
33*49caa483SSimon J. Gerraty# XXX: This is unexpected. The opening brace should also be unescaped.
34*49caa483SSimon J. GerratyTESTS+=		M-unescape
35*49caa483SSimon J. GerratyINP.M-unescape=	({}): \(\{\}\)\: \(\{}\):
36*49caa483SSimon J. GerratyMOD.M-unescape=	${INP.M-unescape:M\\(\\{\\}\\)\\:}
37*49caa483SSimon J. GerratyEXP.M-unescape=	\(\{}\):
38*49caa483SSimon J. Gerraty
39*49caa483SSimon J. Gerraty# When the :M and :N modifiers are parsed, the pattern finishes as soon
40*49caa483SSimon J. Gerraty# as open_parens + open_braces == closing_parens + closing_braces. This
41*49caa483SSimon J. Gerraty# means that ( and } form a matching pair.
42*49caa483SSimon J. Gerraty#
43*49caa483SSimon J. Gerraty# Nested variable expressions are not parsed as such. Instead, only the
44*49caa483SSimon J. Gerraty# parentheses and braces are counted. This leads to a parse error since
45*49caa483SSimon J. Gerraty# the nested expression is not "${:U*)}" but only "${:U*)", which is
46*49caa483SSimon J. Gerraty# missing the closing brace. The expression is evaluated anyway.
47*49caa483SSimon J. Gerraty# The final brace in the output comes from the end of M.nest-mix.
48*49caa483SSimon J. Gerraty#
49*49caa483SSimon J. Gerraty# XXX: This is unexpected but rarely occurs in practice.
50*49caa483SSimon J. GerratyTESTS+=		M-nest-mix
51*49caa483SSimon J. GerratyINP.M-nest-mix=	(parentheses)
52*49caa483SSimon J. GerratyMOD.M-nest-mix=	${INP.M-nest-mix:M${:U*)}}
53*49caa483SSimon J. GerratyEXP.M-nest-mix=	(parentheses)}
54*49caa483SSimon J. Gerraty# make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
55*49caa483SSimon J. Gerraty
56*49caa483SSimon J. Gerraty# In contrast to parentheses and braces, the brackets are not counted
57*49caa483SSimon J. Gerraty# when the :M modifier is parsed since Makefile variables only take the
58*49caa483SSimon J. Gerraty# ${VAR} or $(VAR) forms, but not $[VAR].
59*49caa483SSimon J. Gerraty#
60*49caa483SSimon J. Gerraty# The final ] in the pattern is needed to close the character class.
61*49caa483SSimon J. GerratyTESTS+=		M-nest-brk
62*49caa483SSimon J. GerratyINP.M-nest-brk=	[ [[ [[[
63*49caa483SSimon J. GerratyMOD.M-nest-brk=	${INP.M-nest-brk:M${:U[[[[[]}}
64*49caa483SSimon J. GerratyEXP.M-nest-brk=	[
65*49caa483SSimon J. Gerraty
66*49caa483SSimon J. Gerraty# The pattern in the nested variable has an unclosed character class.
67*49caa483SSimon J. Gerraty# No error is reported though, and the pattern is closed implicitly.
68*49caa483SSimon J. Gerraty#
69*49caa483SSimon J. Gerraty# XXX: It is unexpected that no error is reported.
70*49caa483SSimon J. Gerraty# See str.c, function Str_Match.
71*49caa483SSimon J. Gerraty#
72*49caa483SSimon J. Gerraty# Before 2019-12-02, this test case triggered an out-of-bounds read
73*49caa483SSimon J. Gerraty# in Str_Match.
74*49caa483SSimon J. GerratyTESTS+=		M-pat-err
75*49caa483SSimon J. GerratyINP.M-pat-err=	[ [[ [[[
76*49caa483SSimon J. GerratyMOD.M-pat-err=	${INP.M-pat-err:M${:U[[}}
77*49caa483SSimon J. GerratyEXP.M-pat-err=	[
78*49caa483SSimon J. Gerraty
79*49caa483SSimon J. Gerraty# The first backslash does not escape the second backslash.
80*49caa483SSimon J. Gerraty# Therefore, the second backslash escapes the parenthesis.
81*49caa483SSimon J. Gerraty# This means that the pattern ends there.
82*49caa483SSimon J. Gerraty# The final } in the output comes from the end of MOD.M-bsbs.
83*49caa483SSimon J. Gerraty#
84*49caa483SSimon J. Gerraty# If the first backslash were to escape the second backslash, the first
85*49caa483SSimon J. Gerraty# closing brace would match the opening parenthesis (see M-mixed), and
86*49caa483SSimon J. Gerraty# the second closing brace would be needed to close the variable.
87*49caa483SSimon J. Gerraty# After that, the remaining backslash would escape the parenthesis in
88*49caa483SSimon J. Gerraty# the pattern, therefore (} would match.
89*49caa483SSimon J. GerratyTESTS+=		M-bsbs
90*49caa483SSimon J. GerratyINP.M-bsbs=	(} \( \(}
91*49caa483SSimon J. GerratyMOD.M-bsbs=	${INP.M-bsbs:M\\(}}
92*49caa483SSimon J. GerratyEXP.M-bsbs=	\(}
93*49caa483SSimon J. Gerraty#EXP.M-bsbs=	(}	# If the first backslash were to escape ...
94*49caa483SSimon J. Gerraty
95*49caa483SSimon J. Gerraty# The backslash in \( does not escape the parenthesis, therefore it
96*49caa483SSimon J. Gerraty# counts for the nesting level and matches with the first closing brace.
97*49caa483SSimon J. Gerraty# The second closing brace closes the variable, and the third is copied
98*49caa483SSimon J. Gerraty# literally.
99*49caa483SSimon J. Gerraty#
100*49caa483SSimon J. Gerraty# The second :M in the pattern is nested between ( and }, therefore it
101*49caa483SSimon J. Gerraty# does not start a new modifier.
102*49caa483SSimon J. GerratyTESTS+=		M-bs1-par
103*49caa483SSimon J. GerratyINP.M-bs1-par=	( (:M (:M} \( \(:M \(:M}
104*49caa483SSimon J. GerratyMOD.M-bs1-par=	${INP.M-bs1-par:M\(:M*}}}
105*49caa483SSimon J. GerratyEXP.M-bs1-par=	(:M}}
106*49caa483SSimon J. Gerraty
107*49caa483SSimon J. Gerraty# The double backslash is passed verbatim to the pattern matcher.
108*49caa483SSimon J. Gerraty# The Str_Match pattern is \\(:M*}, and there the backslash is unescaped.
109*49caa483SSimon J. Gerraty# Again, the ( takes place in the nesting level, and there is no way to
110*49caa483SSimon J. Gerraty# prevent this, no matter how many backslashes are used.
111*49caa483SSimon J. GerratyTESTS+=		M-bs2-par
112*49caa483SSimon J. GerratyINP.M-bs2-par=	( (:M (:M} \( \(:M \(:M}
113*49caa483SSimon J. GerratyMOD.M-bs2-par=	${INP.M-bs2-par:M\\(:M*}}}
114*49caa483SSimon J. GerratyEXP.M-bs2-par=	\(:M}}
115*49caa483SSimon J. Gerraty
116*49caa483SSimon J. Gerraty# Str_Match uses a recursive algorithm for matching the * patterns.
117*49caa483SSimon J. Gerraty# Make sure that it survives patterns with 128 asterisks.
118*49caa483SSimon J. Gerraty# That should be enough for all practical purposes.
119*49caa483SSimon J. Gerraty# To produce a stack overflow, just add more :Qs below.
120*49caa483SSimon J. GerratyTESTS+=		M-128
121*49caa483SSimon J. GerratyINP.M-128=	${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,x,g}
122*49caa483SSimon J. GerratyPAT.M-128=	${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,*,g}
123*49caa483SSimon J. GerratyMOD.M-128=	${INP.M-128:M${PAT.M-128}}
124*49caa483SSimon J. GerratyEXP.M-128=	${INP.M-128}
125*49caa483SSimon J. Gerraty
126*49caa483SSimon J. Gerraty# This is the normal SysV substitution. Nothing surprising here.
127*49caa483SSimon J. GerratyTESTS+=		eq-ext
128*49caa483SSimon J. GerratyINP.eq-ext=	file.c file.cc
129*49caa483SSimon J. GerratyMOD.eq-ext=	${INP.eq-ext:%.c=%.o}
130*49caa483SSimon J. GerratyEXP.eq-ext=	file.o file.cc
131*49caa483SSimon J. Gerraty
132*49caa483SSimon J. Gerraty# The SysV := modifier is greedy and consumes all the modifier text
133*49caa483SSimon J. Gerraty# up until the closing brace or parenthesis. The :Q may look like a
134*49caa483SSimon J. Gerraty# modifier, but it really isn't, that's why it appears in the output.
135*49caa483SSimon J. GerratyTESTS+=		eq-q
136*49caa483SSimon J. GerratyINP.eq-q=	file.c file.cc
137*49caa483SSimon J. GerratyMOD.eq-q=	${INP.eq-q:%.c=%.o:Q}
138*49caa483SSimon J. GerratyEXP.eq-q=	file.o:Q file.cc
139*49caa483SSimon J. Gerraty
140*49caa483SSimon J. Gerraty# The = in the := modifier can be escaped.
141*49caa483SSimon J. GerratyTESTS+=		eq-bs
142*49caa483SSimon J. GerratyINP.eq-bs=	file.c file.c=%.o
143*49caa483SSimon J. GerratyMOD.eq-bs=	${INP.eq-bs:%.c\=%.o=%.ext}
144*49caa483SSimon J. GerratyEXP.eq-bs=	file.c file.ext
145*49caa483SSimon J. Gerraty
146*49caa483SSimon J. Gerraty# Having only an escaped = results in a parse error.
147*49caa483SSimon J. Gerraty# The call to "pattern.lhs = VarGetPattern" fails.
148*49caa483SSimon J. GerratyTESTS+=		eq-esc
149*49caa483SSimon J. GerratyINP.eq-esc=	file.c file...
150*49caa483SSimon J. GerratyMOD.eq-esc=	${INP.eq-esc:a\=b}
151*49caa483SSimon J. GerratyEXP.eq-esc=	# empty
152*49caa483SSimon J. Gerraty# make: Unclosed substitution for INP.eq-esc (= missing)
153*49caa483SSimon J. Gerraty
154*49caa483SSimon J. Gerratyall:
155*49caa483SSimon J. Gerraty.for test in ${TESTS}
156*49caa483SSimon J. Gerraty.  if ${MOD.${test}} == ${EXP.${test}}
157*49caa483SSimon J. Gerraty	@printf 'ok %s\n' ${test:Q}''
158*49caa483SSimon J. Gerraty.  else
159*49caa483SSimon J. Gerraty	@printf 'error in %s: expected %s, got %s\n' \
160*49caa483SSimon J. Gerraty		${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}''
161*49caa483SSimon J. Gerraty.  endif
162*49caa483SSimon J. Gerraty.endfor
163