Home | History | Annotate | Line # | Download | only in unit-tests
      1 # $NetBSD: varmod-match-escape.mk,v 1.20 2025/06/28 22:39:29 rillig Exp $
      2 #
      3 # As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
      4 # depending on whether there was an expression somewhere before the
      5 # first backslash or not.  See ParseModifier_Match, "copy = true".
      6 #
      7 # Apart from the different and possibly confusing debug output, there is no
      8 # difference in behavior.  When parsing the modifier text, only \{, \} and \:
      9 # are unescaped, and in the pattern matching these have the same meaning as
     10 # their plain variants '{', '}' and ':'.  In the pattern matching from
     11 # Str_Match, only \*, \? or \[ would make a noticeable difference.
     12 
     13 .MAKEFLAGS: -dcv
     14 
     15 SPECIALS=	\: : \\ * \*
     16 .if ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}}
     17 .  warning unexpected
     18 .endif
     19 
     20 # And now both cases combined: A single modifier with both an escaped ':'
     21 # as well as an expression that expands to a ':'.
     22 #
     23 # XXX: As of 2020-11-01, when an escaped ':' occurs before the
     24 # expression, the whole modifier text is subject to unescaping '\:' to ':',
     25 # before the expression is expanded.  This means that the '\:' in
     26 # the expression is expanded as well, turning ${:U\:} into a simple
     27 # ${:U:}, which silently expands to an empty string, instead of generating
     28 # an error message.
     29 #
     30 # XXX: As of 2020-11-01, the modifier on the right-hand side of the
     31 # comparison is parsed differently though.  First, the expression
     32 # is parsed, resulting in ':' and needSubst=true.  After that, the escaped
     33 # ':' is seen, and this time, copy=true is not executed but stays copy=false.
     34 # Therefore the escaped ':' is kept as-is, and the final pattern becomes
     35 # ':\:'.
     36 #
     37 # If ParseModifier_Match had used the same parsing algorithm as Var_Subst,
     38 # both patterns would end up as '::'.
     39 #
     40 VALUES=		: :: :\:
     41 .if ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:}
     42 # expect+1: warning: XXX: Oops
     43 .  warning XXX: Oops
     44 .endif
     45 
     46 .MAKEFLAGS: -d0
     47 
     48 # XXX: As of 2020-11-01, unlike all other variable modifiers, a '$' in the
     49 # :M and :N modifiers is written as '$$', not as '\$'.  This is confusing,
     50 # undocumented and hopefully not used in practice.
     51 .if ${:U\$:M$$} != "\$"
     52 .  error
     53 .endif
     54 
     55 # XXX: As of 2020-11-01, unlike all other variable modifiers, '\$' is not
     56 # parsed as an escaped '$'.  Instead, ParseModifier_Match first scans for
     57 # the ':' at the end of the modifier, which results in the pattern '\$'.
     58 # No unescaping takes place since the pattern neither contained '\:' nor
     59 # '\{' nor '\}'.  But the text is expanded, and a lonely '$' at the end
     60 # is silently discarded.  The resulting expanded pattern is thus '\', that
     61 # is a single backslash.
     62 # expect+1: Unfinished backslash at the end in pattern "\" of modifier ":M"
     63 .if ${:U\$:M\$} != ""
     64 .  error
     65 .endif
     66 
     67 # In lint mode, the case of a lonely '$' is covered with an error message.
     68 .MAKEFLAGS: -dL
     69 # expect+2: Dollar followed by nothing
     70 # expect+1: Unfinished backslash at the end in pattern "\" of modifier ":M"
     71 .if ${:U\$:M\$} != ""
     72 .  error
     73 .endif
     74 
     75 # The control flow of the pattern parser depends on the actual string that
     76 # is being matched.  There needs to be either a test that shows a difference
     77 # in behavior, or a proof that the behavior does not depend on the actual
     78 # string.
     79 #
     80 # TODO: Str_Match("a-z]", "[a-z]")
     81 # TODO: Str_Match("012", "[0-]]")
     82 # TODO: Str_Match("[", "[[]")
     83 # TODO: Str_Match("]", "[]")
     84 # TODO: Str_Match("]", "[[-]]")
     85 
     86 # Demonstrate an inconsistency between positive and negative character lists
     87 # when the range ends with the character ']'.
     88 #
     89 # 'A' begins the range, 'B' is in the middle of the range, ']' ends the range,
     90 # 'a' is outside the range.
     91 WORDS=		A A] A]] B B] B]] ] ]] ]]] a a] a]]
     92 # The ']' is part of the character range and at the same time ends the
     93 # character list.
     94 EXP.[A-]=	A B ]
     95 # The first ']' is part of the character range and at the same time ends the
     96 # character list.
     97 EXP.[A-]]=	A] B] ]]
     98 # The first ']' is part of the character range and at the same time ends the
     99 # character list.
    100 EXP.[A-]]]=	A]] B]] ]]]
    101 # For negative character lists, the ']' ends the character range but does not
    102 # end the character list.
    103 # XXX: This is unnecessarily inconsistent but irrelevant in practice as there
    104 # is no practical need for a character range that ends at ']'.
    105 EXP.[^A-]=	a
    106 EXP.[^A-]]=	a
    107 EXP.[^A-]]]=	a]
    108 
    109 .for pattern in [A-] [A-]] [A-]]] [^A-] [^A-]] [^A-]]]
    110 # expect+2: Unfinished character list in pattern "[A-]" of modifier ":M"
    111 # expect+1: Unfinished character list in pattern "[^A-]" of modifier ":M"
    112 .  if ${WORDS:M${pattern}} != ${EXP.${pattern}}
    113 .    warning ${pattern}: ${WORDS:M${pattern}} != ${EXP.${pattern}}
    114 .  endif
    115 .endfor
    116 
    117 # In brackets, the backslash is just an ordinary character.
    118 # Outside brackets, it is an escape character for a few special characters.
    119 # TODO: Str_Match("\\", "[\\-]]")
    120 # TODO: Str_Match("-]", "[\\-]]")
    121 
    122 all:
    123 	@:;
    124