Home | History | Annotate | Line # | Download | only in unit-tests
      1 # $NetBSD: cond-token-plain.mk,v 1.23 2025/07/06 07:56:16 rillig Exp $
      2 #
      3 # Tests for plain tokens (that is, string literals without quotes)
      4 # in .if conditions.  These are also called bare words.
      5 
      6 .MAKEFLAGS: -dc
      7 
      8 # The word 'value' after the '!=' is a bare word.
      9 .if ${:Uvalue} != value
     10 .  error
     11 .endif
     12 
     13 # Using a '#' in a string literal in a condition leads to a malformed
     14 # condition since comment parsing is done in an early phase and removes the
     15 # '#' and everything after it long before the condition parser gets to see it.
     16 #
     17 #
     18 # expect+1: Unfinished string literal """
     19 .if ${:U} != "#hash"
     20 .  error
     21 .endif
     22 
     23 # To get a '#' into a condition, it has to be escaped using a backslash.
     24 # This prevents the comment parser from removing it, and in turn, it becomes
     25 # visible to CondParser_String.
     26 .if ${:U\#hash} != "\#hash"
     27 .  error
     28 .endif
     29 
     30 # Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles
     31 # the '#' specially, even though at this point, there should be no need for
     32 # comment handling anymore.  The comments are supposed to be stripped off
     33 # in a very early parsing phase.
     34 #
     35 # See https://gnats.netbsd.org/19596 for example makefiles demonstrating the
     36 # original problems.  At that time, the parser didn't recognize the comment in
     37 # the line '.else # comment3'.  This workaround is not needed anymore since
     38 # comments are stripped in an earlier phase.  See "case '#'" in
     39 # CondParser_Token.
     40 #
     41 #
     42 # expect+1: Unfinished string literal ""\\"
     43 .if ${:U\\} != "\\#hash"
     44 .  error
     45 .endif
     46 
     47 # The right-hand side of a comparison is not parsed as a token, therefore
     48 # the code from CondParser_Token does not apply to it.
     49 # TODO: Explain the consequences.
     50 # TODO: Does this mean that more syntactic variants are allowed here?
     51 .if ${:U\#hash} != \#hash
     52 .  error
     53 .endif
     54 
     55 # XXX: What is the purpose of treating an escaped '#' in the following
     56 # condition as a comment?  And why only at the beginning of a token,
     57 # just as in the shell?
     58 .if 0 \# This is treated as a comment, but why?
     59 .  error
     60 .endif
     61 
     62 # Ah, ok, this can be used to add an end-of-condition comment.  But does
     63 # anybody really use this?  This is neither documented nor obvious since
     64 # the '#' is escaped.  It's much clearer to write a comment in the line
     65 # above the condition.
     66 .if ${0 \# comment:?yes:no} != no
     67 .  error
     68 .endif
     69 .if ${1 \# comment:?yes:no} != yes
     70 .  error
     71 .endif
     72 
     73 # Usually there is whitespace around the comparison operator, but this is
     74 # not required.
     75 .if ${UNDEF:Uundefined}!=undefined
     76 .  error
     77 .endif
     78 .if ${UNDEF:U12345}>12345
     79 .  error
     80 .endif
     81 .if ${UNDEF:U12345}<12345
     82 .  error
     83 .endif
     84 .if (${UNDEF:U0})||0
     85 .  error
     86 .endif
     87 
     88 # Only the comparison operator terminates the comparison operand, and it's
     89 # a coincidence that the '!' is both used in the '!=' comparison operator
     90 # as well as for negating a comparison result.
     91 #
     92 # The characters '&' and '|' are part of the comparison operand.
     93 .if ${:Uvar}&&name != "var&&name"
     94 .  error
     95 .endif
     96 .if ${:Uvar}||name != "var||name"
     97 .  error
     98 .endif
     99 
    100 # A bare word may occur alone in a condition, without any comparison
    101 # operator.  It is interpreted as the function call 'defined(bare)'.
    102 .if bare
    103 .  error
    104 .else
    105 # expect+1: A bare word is treated like defined(...), and the variable 'bare' is not defined.
    106 .  info A bare word is treated like defined(...), and the variable $\
    107 	'bare' is not defined.
    108 .endif
    109 
    110 VAR=	defined
    111 .if VAR
    112 # expect+1: A bare word is treated like defined(...).
    113 .  info A bare word is treated like defined(...).
    114 .else
    115 .  error
    116 .endif
    117 
    118 # Bare words may be intermixed with expressions.
    119 .if V${:UA}R
    120 # expect+1: ok
    121 .  info ok
    122 .else
    123 .  error
    124 .endif
    125 
    126 # In bare words, even undefined variables are allowed.  Without the bare
    127 # words, undefined variables are not allowed.  That feels inconsistent.
    128 .if V${UNDEF}AR
    129 # expect+1: Undefined variables in bare words expand to an empty string.
    130 .  info Undefined variables in bare words expand to an empty string.
    131 .else
    132 .  error
    133 .endif
    134 
    135 .if 0${:Ux00}
    136 .  error
    137 .else
    138 # expect+1: Numbers can be composed from literals and expressions.
    139 .  info Numbers can be composed from literals and expressions.
    140 .endif
    141 
    142 .if 0${:Ux01}
    143 # expect+1: Numbers can be composed from literals and expressions.
    144 .  info Numbers can be composed from literals and expressions.
    145 .else
    146 .  error
    147 .endif
    148 
    149 # If the right-hand side is missing, it's a parse error.
    150 # expect+1: Missing right-hand side of operator "=="
    151 .if "" ==
    152 .  error
    153 .else
    154 .  error
    155 .endif
    156 
    157 # If the left-hand side is missing, it's a parse error as well, but without
    158 # a specific error message.
    159 # expect+1: Malformed conditional "== """
    160 .if == ""
    161 .  error
    162 .else
    163 .  error
    164 .endif
    165 
    166 # The '\\' is not a line continuation.  Neither is it an unquoted string
    167 # literal.  Instead, it is parsed as a bare word (ParseWord),
    168 # and in that context, the backslash is just an ordinary character. The
    169 # function argument thus stays '\\' (2 backslashes).  This string is passed
    170 # to FuncDefined, and since there is no variable named '\\', the condition
    171 # evaluates to false.
    172 .if \\
    173 .  error
    174 .else
    175 # expect+1: The variable '\\' is not defined.
    176 .  info The variable '\\' is not defined.
    177 .endif
    178 
    179 ${:U\\\\}=	backslash
    180 .if \\
    181 # expect+1: Now the variable '\\' is defined.
    182 .  info Now the variable '\\' is defined.
    183 .else
    184 .  error
    185 .endif
    186 
    187 # Anything that doesn't start with a double quote is considered a "bare word".
    188 # Strangely, a bare word may contain double quotes inside.  Nobody should ever
    189 # depend on this since it may well be unintended.  See CondParser_String.
    190 .if "unquoted\"quoted" != unquoted"quoted
    191 .  error
    192 .endif
    193 
    194 # FIXME: In CondParser_String, Var_Parse returns var_Error without a
    195 # corresponding error message.
    196 # expect+1: Malformed conditional "$$$$$$$$ != """
    197 .if $$$$$$$$ != ""
    198 .  error
    199 .else
    200 .  error
    201 .endif
    202 
    203 # In a condition in an .if directive, the left-hand side must not be an
    204 # unquoted string literal.
    205 # expect+1: Malformed conditional "left == right"
    206 .if left == right
    207 .endif
    208 # Before cond.c 1.276 from 2021-09-21, an expression containing the
    209 # modifier ':?:' allowed unquoted string literals for the rest of the
    210 # condition.  This was an unintended implementation mistake.
    211 # expect+1: Malformed conditional "${0:?:} || left == right"
    212 .if ${0:?:} || left == right
    213 .endif
    214 # This affected only the comparisons after the expression, so the following
    215 # was still a syntax error.
    216 # expect+1: Malformed conditional "left == right || ${0:?:}"
    217 .if left == right || ${0:?:}
    218 .endif
    219 
    220 # See cond-token-string.mk for similar tests where the condition is enclosed
    221 # in "quotes".
    222 
    223 .MAKEFLAGS: -d0
    224 
    225 
    226 # As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf
    227 # looks suspicious of evaluating the expression twice: first for parsing a
    228 # bare word and second for parsing the left-hand side of a comparison.
    229 #
    230 # In '.if' directives, the left-hand side of a comparison must not be a bare
    231 # word though, and this keeps CondParser_Leaf from evaluating the expression
    232 # for the second time.  The right-hand side of a comparison may be a bare
    233 # word, but that side has no risk of being parsed more than once.
    234 #
    235 # expect+1: Malformed conditional "VAR.${IF_COUNT::+=1} != """
    236 .if VAR.${IF_COUNT::+=1} != ""
    237 .  error
    238 .else
    239 .  error
    240 .endif
    241 .if ${IF_COUNT} != "1"
    242 .  error
    243 .endif
    244 
    245 # A different situation is when CondParser.leftUnquotedOK is true.  This
    246 # situation arises in expressions of the form ${cond:?yes:no}.  As of
    247 # 2021-12-30, the condition in such an expression is evaluated before parsing
    248 # the condition, see varmod-ifelse.mk.  To pass an expression to the
    249 # condition parser, it needs to be escaped.  This rarely happens in practice,
    250 # in most cases the conditions are simple enough that it doesn't matter
    251 # whether the condition is first evaluated and then parsed, or vice versa.
    252 # A half-baked attempt at hiding this implementation detail is
    253 # CondParser.leftUnquotedOK, but that is a rather leaky abstraction.
    254 
    255 #.MAKEFLAGS: -dcv
    256 COND=	VAR.$${MOD_COUNT::+=1}
    257 .if ${${COND} == "VAR.":?yes:no} != "yes"
    258 .  error
    259 .endif
    260 
    261 # The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was
    262 # evaluated twice.  In practice, expressions that occur in conditions do not
    263 # have side effects, making this problem rather academic, but it is there.
    264 .if ${MOD_COUNT} != "1 1"
    265 .  error
    266 .endif
    267 #.MAKEFLAGS: -d0
    268 
    269 
    270 # A trailing backslash in a bare word does not escape anything.
    271 # expect+1: Unfinished backslash escape sequence
    272 .if ${${:U str == str\\}:?yes:no}
    273 .  error
    274 .else
    275 .  error
    276 .endif
    277 
    278 # A trailing backslash in an unfinished string literal word does not escape
    279 # anything.
    280 # expect+2: Unfinished backslash escape sequence
    281 # expect+1: Unfinished string literal ""str\"
    282 .if ${${:U str == "str\\}:?yes:no}
    283 .  error
    284 .else
    285 .  error
    286 .endif
    287 
    288 # expect+1: Unfinished string literal ""str"
    289 .if ${${:U str == "str}:?yes:no}
    290 .  error
    291 .else
    292 .  error
    293 .endif
    294