@@ -1,4 +1,4 @@
-# $NetBSD: varmod-defined.mk,v 1.3 2020/08/25 21:58:08 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.4 2020/09/03 18:52:36 rillig Exp $
#
# Tests for the :D variable modifier, which returns the given string
# if the variable is defined. It is closely related to the :U modifier.
@@ -23,6 +23,45 @@
.if ${UNDEF:Dvalue} != ""
.error
.endif
+
+# The modifier text may contain plain text as well as expressions.
+#
+.if ${DEF:D<${DEF}>} != "<defined>"
+. error
+.endif
+
+# Special characters that would be interpreted differently can be escaped.
+# These are '}' (the closing character of the expression), ':', '$' and '\'.
+# Any other backslash sequences are preserved.
+#
+# The escaping rules for string literals in conditions are completely
+# different though. There, any character may be escaped using a backslash.
+#
+.if ${DEF:D \} \: \$ \\ \) \n } != " } : \$ \\ \\) \\n "
+. error
+.endif
+
+# Like in several other places in variable expressions, when
+# ApplyModifier_Defined calls Var_Parse, double dollars lead to a parse
+# error that is silently ignored. This makes all dollar signs disappear,
+# except for the last, which is a well-formed variable expression.
+#
+.if ${DEF:D$$$$$${DEF}} != "defined"
+. error
+.endif
+
+# Any other text is written without any further escaping. In contrast
+# to the :M modifier, parentheses and braces do not need to be nested.
+# Instead, the :D modifier is implemented sanely by parsing nested
+# expressions as such, without trying any shortcuts. See ApplyModifier_Match
+# for an inferior variant.
+#
+.if ${DEF:D!&((((} != "!&(((("
+. error
+.endif
+
+# TODO: Add more tests for parsing the plain text part, to cover each branch
+# of ApplyModifier_Defined.
all:
@:;
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-undefined.mk,v 1.3 2020/08/23 20:49:33 rillig Exp $
+# $NetBSD: varmod-undefined.mk,v 1.4 2020/09/03 18:52:36 rillig Exp $
#
# Tests for the :U variable modifier, which returns the given string
# if the variable is undefined.
@@ -46,9 +46,19 @@
# the .newline variable is for.
#
# Whitespace at the edges is preserved, on both sides of the comparison.
-
+#
.if ${:U \: \} \$ \\ \a \b \n } != " : } \$ \\ \\a \\b \\n "
.error
+.endif
+
+# Even after the :U modifier has been applied, the expression still remembers
+# that it originated from an undefined variable, and the :U modifier can
+# be used to overwrite the value of the expression.
+#
+.if ${UNDEF:Uvalue:S,a,X,} != "vXlue"
+. error
+.elif ${UNDEF:Uvalue:S,a,X,:Uwas undefined} != "was undefined"
+. error
.endif
all: