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