aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/unit-tests/varmod.mk
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/unit-tests/varmod.mk')
-rw-r--r--contrib/bmake/unit-tests/varmod.mk227
1 files changed, 221 insertions, 6 deletions
diff --git a/contrib/bmake/unit-tests/varmod.mk b/contrib/bmake/unit-tests/varmod.mk
index 21ddf9103251..af976f9d0086 100644
--- a/contrib/bmake/unit-tests/varmod.mk
+++ b/contrib/bmake/unit-tests/varmod.mk
@@ -1,11 +1,78 @@
-# $NetBSD: varmod.mk,v 1.5 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: varmod.mk,v 1.30 2025/06/29 11:27:21 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
+#
+# See also:
+# varparse-errors.mk
+
+# As of 2024-06-05, the possible behaviors during parsing are:
+#
+# * `strict`: the parsing style used by most modifiers:
+# * either uses `ParseModifierPart` or parses the modifier literal
+# * other modifiers may follow, separated by a ':'
+#
+# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means
+# that no further modifiers are parsed in that expression.
+#
+# * `no-colon`: after parsing this modifier, the following modifier
+# does not need to be separated by a colon.
+# Omitting this colon is bad style.
+#
+# * `individual`: parsing this modifier does not follow the common
+# pattern of calling `ParseModifierPart`.
+#
+# The SysV column says whether a modifier falls back trying the `:from=to`
+# System V modifier. Remarks:
+#
+# In the assignment modifiers `::=` and its variants, the `=` is part of
+# the modifier name, so they never fall back to the `:from=to` modifier.
+#
+# All no-colon modifiers get a "no", as the modifier name would be
+# trimmed off before the `:from=to` modifier could see them, for
+# example, ${VAR:LAR=ALUE} and ${VAR:L:AR=ALUE} behave the same.
+#
+# | **Modifier** | **Behavior** | **Remarks** | **SysV** |
+# |--------------|--------------|--------------------|----------|
+# | ! | no-colon | | no |
+# | := | greedy | | no |
+# | :?= | greedy | | no |
+# | :+= | greedy | | no |
+# | :!= | greedy | | no |
+# | ?: | greedy | | no |
+# | @ | no-colon | | no |
+# | C | no-colon | | no |
+# | D | individual | custom parser | no |
+# | E | strict | | yes |
+# | H | strict | | yes |
+# | L | no-colon | | no |
+# | M | individual | custom parser | no |
+# | N | individual | custom parser | no |
+# | O | strict | only literal value | yes |
+# | P | no-colon | | no |
+# | Q | strict | | yes |
+# | R | strict | | yes |
+# | S | no-colon | | no |
+# | T | strict | | yes |
+# | U | individual | custom parser | no |
+# | [ | strict | | no |
+# | _ | individual | strcspn | no |
+# | gmtime | strict | | no |
+# | hash | strict | | yes |
+# | localtime | strict | | no |
+# | q | strict | | yes |
+# | range | strict | | no |
+# | sh | strict | | yes |
+# | t | strict | | yes |
+# | u | strict | | yes |
+# | from=to | greedy | SysV, fallback | --- |
+
+# These tests assume
+.MAKE.SAVE_DOLLARS = yes
DOLLAR1= $$
DOLLAR2= ${:U\$}
-# To get a single '$' sign in the value of a variable expression, it has to
+# To get a single '$' sign in the value of an expression, it has to
# be written as '$$' in a literal variable value.
#
# See Var_Parse, where it calls Var_Subst.
@@ -39,22 +106,170 @@ DOLLAR2= ${:U\$}
# For compatibility, make does not print these error messages in normal mode.
# Should it?
.MAKEFLAGS: -dL
+# expect+2: To escape a dollar, use \$, not $$, at "$$:L} != """
+# expect+1: Invalid variable name ":", at "$:L} != """
.if ${$$:L} != ""
. error
.endif
# A '$' followed by nothing is an error as well.
+# expect+1: Dollar followed by nothing
.if ${:Uword:@word@${word}$@} != "word"
. error
.endif
-# The variable modifier :P does not fall back to the SysV modifier.
+# The modifier :P does not fall back to the SysV modifier.
# Therefore the modifier :P=RE generates a parse error.
-# XXX: The .error should not be reached since the variable expression is
-# malformed, and this error should be propagated up to Cond_EvalLine.
VAR= STOP
+# expect+1: Missing delimiter ":" after modifier "P"
.if ${VAR:P=RE} != "STORE"
. error
+.else
+. error
+.endif
+
+# Test the word selection modifier ':[n]' with a very large number that is
+# larger than ULONG_MAX for any supported platform.
+# expect+1: Invalid modifier ":[99333000222000111000]"
+.if ${word:L:[99333000222000111000]}
+.endif
+# expect+1: Invalid modifier ":[2147483648]"
+.if ${word:L:[2147483648]}
+.endif
+
+# Test the range generation modifier ':range=n' with a very large number that
+# is larger than SIZE_MAX for any supported platform.
+# expect+1: Invalid number "99333000222000111000}" for modifier ":range"
+.if ${word:L:range=99333000222000111000}
+.endif
+
+# In an indirect modifier, the delimiter is '\0', which at the same time marks
+# the end of the string. The sequence '\\' '\0' is not an escaped delimiter,
+# as it would be wrong to skip past the end of the string.
+# expect+1: Invalid time value "\"
+.if ${:${:Ugmtime=\\}}
+. error
+.endif
+
+# Test a '$' at the end of a modifier part, for all modifiers in the order
+# listed in ApplyModifier.
+#
+# The only modifier parts where an unescaped '$' makes sense at the end are
+# the 'from' parts of the ':S' and ':C' modifiers. In all other modifier
+# parts, an unescaped '$' is an undocumented and discouraged edge case, as it
+# means the same as an escaped '$'.
+.if ${:U:!printf '%s\n' $!} != "\$"
+. error
+.endif
+# expect+1: Dollar followed by nothing
+.if ${VAR::=value$} != "" || ${VAR} != "value"
+. error
+.endif
+${:U }= <space>
+# expect+2: Dollar followed by nothing
+# expect+1: Dollar followed by nothing
+.if ${VAR::+=appended$} != "" || ${VAR} != "value<space>appended"
+. error
+.endif
+.if ${1:?then$:else$} != "then\$"
+. error
+.endif
+.if ${0:?then$:else$} != "else\$"
+. error
+.endif
+# expect+1: Dollar followed by nothing
+.if ${word:L:@w@$w$@} != "word"
+. error
+.endif
+# expect+1: Invalid modifier ":[$]"
+.if ${word:[$]}
+. error
+.else
+. error
+.endif
+VAR_DOLLAR= VAR$$
+.if ${word:L:_=VAR$} != "word" || ${${VAR_DOLLAR}} != "word"
+. error
+.endif
+.if ${word:L:C,d$,m,} != "worm"
+. error
+.endif
+.if ${word:L:C,d,$,} != "wor\$"
+. error
+.endif
+# expect+2: Dollar followed by nothing
+# expect+1: Invalid variable name "}", at "$} != "set""
+.if ${VAR:Dset$} != "set"
+. error
+.endif
+# expect+1: Invalid variable name "}", at "$} != "fallback""
+.if ${:Ufallback$} != "fallback"
+. error
+.endif
+# expect+1: Invalid time value "1000$"
+.if ${%y:L:gmtime=1000$}
+. error
+.else
+. error
+.endif
+# expect+1: Invalid time value "1000$"
+.if ${%y:L:localtime=1000$}
+. error
+.else
+. error
+.endif
+# expect+1: Dollar followed by nothing
+.if ${word:L:Mw*$} != "word"
+. error
+.endif
+# expect+1: Dollar followed by nothing
+.if ${word:L:NX*$} != "word"
+. error
+.endif
+# expect+1: Invalid argument "fallback$" for modifier ":mtime"
+.if ${.:L:mtime=fallback$}
+. error
+.else
+. error
+.endif
+.if ${word:L:S,d$,m,} != "worm"
+. error
+.endif
+.if ${word:L:S,d,m$,} != "worm\$"
+. error
+.endif
+
+.undef VAR
+# expect+1: Missing delimiter ":" after modifier "L"
+.if ${VAR:LAR=ALUE} != "VALUE"
+. error
+.endif
+.if ${VAR:L:AR=ALUE} != "VALUE"
+. error
.endif
-all: # nothing
+
+# When an expression has the usual form ${...} with braces,
+# in the part of a modifier, ":}\$" can be escaped using a backslash.
+# All other characters are passed through unmodified.
+# expect+1: Invalid time value " : } \ $ ) \) ( "
+.if ${%Y:L:localtime= \: \} \\ \$ ) \) ( :M*} != ": } \\ \$ ) \\) ("
+. error
+.endif
+# When an expression has the unusual form $(...) with parentheses,
+# in the part of a modifier, ":)\$" can be escaped using a backslash.
+# All other characters are passed through unmodified.
+# expect+1: Invalid time value " : \) \ $ "
+.if ${%Y:L:localtime= \: \) \\ \$ } \} { :M*} != ": ) \\ \$ } \\} {"
+. error
+.endif
+# Same when the modifier is the last modifier in an expression.
+# expect+1: Invalid time value " : } \ $ ) \) ( "
+.if ${%Y:L:localtime= \: \} \\ \$ ) \) ( } != " : } \\ \$ ) \\) ( "
+. error
+.endif
+# Same when the modifier is the last modifier in an expression.
+# expect+1: Invalid time value " : \) \ $ "
+.if ${%Y:L:localtime= \: \) \\ \$ } \} { } != " : ) \\ \$ } \\} { "
+. error
+.endif