varmod-edge.mk revision 1.26 1 1.26 rillig # $NetBSD: varmod-edge.mk,v 1.26 2024/07/06 10:14:35 rillig Exp $
2 1.1 rillig #
3 1.1 rillig # Tests for edge cases in variable modifiers.
4 1.1 rillig #
5 1.1 rillig # These tests demonstrate the current implementation in small examples.
6 1.1 rillig # They may contain surprising behavior.
7 1.1 rillig #
8 1.1 rillig # Each test consists of:
9 1.1 rillig # - INP, the input to the test
10 1.1 rillig # - MOD, the expression for testing the modifier
11 1.1 rillig # - EXP, the expected output
12 1.1 rillig
13 1.26 rillig INP= (parentheses) {braces} (opening closing) ()
14 1.26 rillig MOD= ${INP:M(*)}
15 1.26 rillig EXP= (parentheses) ()
16 1.26 rillig .if ${MOD} != ${EXP}
17 1.26 rillig . warning expected "${EXP}", got "${MOD}"
18 1.26 rillig .endif
19 1.1 rillig
20 1.1 rillig # The first closing brace matches the opening parenthesis.
21 1.18 rillig # The second closing brace actually ends the expression.
22 1.1 rillig #
23 1.1 rillig # XXX: This is unexpected but rarely occurs in practice.
24 1.26 rillig INP= (paren-brace} (
25 1.26 rillig MOD= ${INP:M(*}}
26 1.26 rillig EXP= (paren-brace}
27 1.26 rillig .if ${MOD} != ${EXP}
28 1.26 rillig . warning expected "${EXP}", got "${MOD}"
29 1.26 rillig .endif
30 1.1 rillig
31 1.4 rillig # After the :M modifier has parsed the pattern, only the closing brace
32 1.4 rillig # and the colon are unescaped. The other characters are left as-is.
33 1.4 rillig # To actually see this effect, the backslashes in the :M modifier need
34 1.4 rillig # to be doubled since single backslashes would simply be unescaped by
35 1.4 rillig # Str_Match.
36 1.4 rillig #
37 1.4 rillig # XXX: This is unexpected. The opening brace should also be unescaped.
38 1.26 rillig INP= ({}): \(\{\}\)\: \(\{}\):
39 1.26 rillig MOD= ${INP:M\\(\\{\\}\\)\\:}
40 1.26 rillig EXP= \(\{}\):
41 1.26 rillig .if ${MOD} != ${EXP}
42 1.26 rillig . warning expected "${EXP}", got "${MOD}"
43 1.26 rillig .endif
44 1.4 rillig
45 1.1 rillig # When the :M and :N modifiers are parsed, the pattern finishes as soon
46 1.1 rillig # as open_parens + open_braces == closing_parens + closing_braces. This
47 1.1 rillig # means that ( and } form a matching pair.
48 1.1 rillig #
49 1.18 rillig # Nested expressions are not parsed as such. Instead, only the
50 1.1 rillig # parentheses and braces are counted. This leads to a parse error since
51 1.1 rillig # the nested expression is not "${:U*)}" but only "${:U*)", which is
52 1.1 rillig # missing the closing brace. The expression is evaluated anyway.
53 1.1 rillig # The final brace in the output comes from the end of M.nest-mix.
54 1.1 rillig #
55 1.1 rillig # XXX: This is unexpected but rarely occurs in practice.
56 1.26 rillig INP= (parentheses)
57 1.26 rillig MOD= ${INP:M${:U*)}}
58 1.26 rillig EXP= (parentheses)}
59 1.26 rillig # expect+1: while evaluating variable "MOD" with value "${INP:M${:U*)}}": while evaluating variable "INP" with value "(parentheses)": while evaluating "${:U*)" with value "*)": Unclosed expression, expecting '}' for modifier "U*)"
60 1.26 rillig .if ${MOD} != ${EXP}
61 1.26 rillig . warning expected "${EXP}", got "${MOD}"
62 1.26 rillig .endif
63 1.24 rillig
64 1.1 rillig
65 1.1 rillig # In contrast to parentheses and braces, the brackets are not counted
66 1.26 rillig # when the :M modifier is parsed since Makefile expressions only take the
67 1.1 rillig # ${VAR} or $(VAR) forms, but not $[VAR].
68 1.1 rillig #
69 1.1 rillig # The final ] in the pattern is needed to close the character class.
70 1.26 rillig INP= [ [[ [[[
71 1.26 rillig MOD= ${INP:M${:U[[[[[]}}
72 1.26 rillig EXP= [
73 1.26 rillig .if ${MOD} != ${EXP}
74 1.26 rillig . warning expected "${EXP}", got "${MOD}"
75 1.26 rillig .endif
76 1.1 rillig
77 1.1 rillig # The pattern in the nested variable has an unclosed character class.
78 1.1 rillig # No error is reported though, and the pattern is closed implicitly.
79 1.1 rillig #
80 1.1 rillig # XXX: It is unexpected that no error is reported.
81 1.2 rillig # See str.c, function Str_Match.
82 1.5 rillig #
83 1.5 rillig # Before 2019-12-02, this test case triggered an out-of-bounds read
84 1.5 rillig # in Str_Match.
85 1.26 rillig INP= [ [[ [[[
86 1.26 rillig MOD= ${INP:M${:U[[}}
87 1.26 rillig EXP= [
88 1.26 rillig .if ${MOD} != ${EXP}
89 1.26 rillig . warning expected "${EXP}", got "${MOD}"
90 1.26 rillig .endif
91 1.1 rillig
92 1.2 rillig # The first backslash does not escape the second backslash.
93 1.2 rillig # Therefore, the second backslash escapes the parenthesis.
94 1.2 rillig # This means that the pattern ends there.
95 1.26 rillig # The final } in the output comes from the end of MOD.
96 1.2 rillig #
97 1.2 rillig # If the first backslash were to escape the second backslash, the first
98 1.26 rillig # closing brace would match the opening parenthesis (see paren-brace), and
99 1.2 rillig # the second closing brace would be needed to close the variable.
100 1.3 rillig # After that, the remaining backslash would escape the parenthesis in
101 1.3 rillig # the pattern, therefore (} would match.
102 1.26 rillig INP= (} \( \(}
103 1.26 rillig MOD= ${INP:M\\(}}
104 1.26 rillig EXP= \(}
105 1.26 rillig #EXP= (} # If the first backslash were to escape ...
106 1.26 rillig .if ${MOD} != ${EXP}
107 1.26 rillig . warning expected "${EXP}", got "${MOD}"
108 1.26 rillig .endif
109 1.2 rillig
110 1.4 rillig # The backslash in \( does not escape the parenthesis, therefore it
111 1.4 rillig # counts for the nesting level and matches with the first closing brace.
112 1.4 rillig # The second closing brace closes the variable, and the third is copied
113 1.4 rillig # literally.
114 1.4 rillig #
115 1.4 rillig # The second :M in the pattern is nested between ( and }, therefore it
116 1.4 rillig # does not start a new modifier.
117 1.26 rillig INP= ( (:M (:M} \( \(:M \(:M}
118 1.26 rillig MOD= ${INP:M\(:M*}}}
119 1.26 rillig EXP= (:M}}
120 1.26 rillig .if ${MOD} != ${EXP}
121 1.26 rillig . warning expected "${EXP}", got "${MOD}"
122 1.26 rillig .endif
123 1.4 rillig
124 1.4 rillig # The double backslash is passed verbatim to the pattern matcher.
125 1.4 rillig # The Str_Match pattern is \\(:M*}, and there the backslash is unescaped.
126 1.4 rillig # Again, the ( takes place in the nesting level, and there is no way to
127 1.4 rillig # prevent this, no matter how many backslashes are used.
128 1.26 rillig INP= ( (:M (:M} \( \(:M \(:M}
129 1.26 rillig MOD= ${INP:M\\(:M*}}}
130 1.26 rillig EXP= \(:M}}
131 1.26 rillig .if ${MOD} != ${EXP}
132 1.26 rillig . warning expected "${EXP}", got "${MOD}"
133 1.26 rillig .endif
134 1.26 rillig
135 1.26 rillig # Before str.c 1.48 from 2020-06-15, Str_Match used a recursive algorithm for
136 1.26 rillig # matching the '*' patterns and did not optimize for multiple '*' in a row.
137 1.26 rillig # Test a pattern with 65536 asterisks.
138 1.26 rillig INP= ${:U\\:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:S,\\,x,g}
139 1.26 rillig PAT= ${:U\\:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:S,\\,*,g}
140 1.26 rillig MOD= ${INP:M${PAT}}
141 1.26 rillig EXP= ${INP}
142 1.26 rillig .if ${MOD} != ${EXP}
143 1.26 rillig . warning expected "${EXP}", got "${MOD}"
144 1.26 rillig .endif
145 1.6 rillig
146 1.6 rillig # This is the normal SysV substitution. Nothing surprising here.
147 1.26 rillig INP= file.c file.cc
148 1.26 rillig MOD= ${INP:%.c=%.o}
149 1.26 rillig EXP= file.o file.cc
150 1.26 rillig .if ${MOD} != ${EXP}
151 1.26 rillig . warning expected "${EXP}", got "${MOD}"
152 1.26 rillig .endif
153 1.6 rillig
154 1.6 rillig # The SysV := modifier is greedy and consumes all the modifier text
155 1.6 rillig # up until the closing brace or parenthesis. The :Q may look like a
156 1.6 rillig # modifier, but it really isn't, that's why it appears in the output.
157 1.26 rillig INP= file.c file.cc
158 1.26 rillig MOD= ${INP:%.c=%.o:Q}
159 1.26 rillig EXP= file.o:Q file.cc
160 1.26 rillig .if ${MOD} != ${EXP}
161 1.26 rillig . warning expected "${EXP}", got "${MOD}"
162 1.26 rillig .endif
163 1.6 rillig
164 1.6 rillig # The = in the := modifier can be escaped.
165 1.26 rillig INP= file.c file.c=%.o
166 1.26 rillig MOD= ${INP:%.c\=%.o=%.ext}
167 1.26 rillig EXP= file.c file.ext
168 1.26 rillig .if ${MOD} != ${EXP}
169 1.26 rillig . warning expected "${EXP}", got "${MOD}"
170 1.26 rillig .endif
171 1.6 rillig
172 1.8 rillig # Having only an escaped '=' results in a parse error.
173 1.8 rillig # The call to "pattern.lhs = ParseModifierPart" fails.
174 1.26 rillig INP= file.c file...
175 1.26 rillig MOD= ${INP:a\=b}
176 1.26 rillig EXP= # empty
177 1.26 rillig # expect+1: while evaluating variable "MOD" with value "${INP:a\=b}": while evaluating variable "INP" with value "file.c file...": Unfinished modifier ('=' missing)
178 1.26 rillig .if ${MOD} != ${EXP}
179 1.26 rillig . warning expected "${EXP}", got "${MOD}"
180 1.26 rillig .endif
181 1.26 rillig
182 1.26 rillig INP= value
183 1.26 rillig MOD= ${INP:}
184 1.26 rillig EXP= value
185 1.26 rillig .if ${MOD} != ${EXP}
186 1.26 rillig . warning expected "${EXP}", got "${MOD}"
187 1.26 rillig .endif
188 1.26 rillig
189 1.26 rillig INP= value
190 1.26 rillig MOD= ${INP::::}
191 1.26 rillig EXP= # empty
192 1.26 rillig # expect+2: while evaluating variable "MOD" with value "${INP::::}": while evaluating variable "INP" with value "value": Unknown modifier ":"
193 1.26 rillig # expect+1: while evaluating variable "MOD" with value "${INP::::}": while evaluating variable "INP" with value "": Unknown modifier ":"
194 1.26 rillig .if ${MOD} != ${EXP}
195 1.26 rillig . warning expected "${EXP}", got "${MOD}"
196 1.26 rillig .endif
197 1.11 rillig
198 1.14 rillig # Even in expressions based on an unnamed variable, there may be errors.
199 1.14 rillig # XXX: The error message should mention the variable name of the expression,
200 1.14 rillig # even though that name is empty in this case.
201 1.17 rillig # expect+2: Malformed conditional (${:Z})
202 1.25 rillig # expect+1: while evaluating "${:Z}" with value "": Unknown modifier "Z"
203 1.14 rillig .if ${:Z}
204 1.14 rillig . error
205 1.14 rillig .else
206 1.14 rillig . error
207 1.14 rillig .endif
208 1.14 rillig
209 1.14 rillig # Even in expressions based on an unnamed variable, there may be errors.
210 1.15 rillig #
211 1.15 rillig # Before var.c 1.842 from 2021-02-23, the error message did not surround the
212 1.15 rillig # variable name with quotes, leading to the rather confusing "Unfinished
213 1.15 rillig # modifier for (',' missing)", having two spaces in a row.
214 1.15 rillig #
215 1.25 rillig # expect+2: while evaluating "${:S,}" with value "": Unfinished modifier (',' missing)
216 1.17 rillig # expect+1: Malformed conditional (${:S,})
217 1.14 rillig .if ${:S,}
218 1.14 rillig . error
219 1.14 rillig .else
220 1.14 rillig . error
221 1.14 rillig .endif
222