Home | History | Annotate | Line # | Download | only in unit-tests
varmod-ifelse.mk revision 1.12
      1  1.12  rillig # $NetBSD: varmod-ifelse.mk,v 1.12 2021/04/19 22:05:29 rillig Exp $
      2   1.1  rillig #
      3   1.2  rillig # Tests for the ${cond:?then:else} variable modifier, which evaluates either
      4   1.2  rillig # the then-expression or the else-expression, depending on the condition.
      5   1.5  rillig #
      6   1.5  rillig # The modifier was added on 1998-04-01.
      7   1.5  rillig #
      8   1.5  rillig # Until 2015-10-11, the modifier always evaluated both the "then" and the
      9   1.5  rillig # "else" expressions.
     10   1.1  rillig 
     11   1.1  rillig # TODO: Implementation
     12   1.1  rillig 
     13   1.5  rillig # The variable name of the expression is expanded and then taken as the
     14   1.5  rillig # condition.  In this case it becomes:
     15   1.5  rillig #
     16   1.5  rillig #	variable expression == "variable expression"
     17   1.5  rillig #
     18   1.5  rillig # This confuses the parser, which expects an operator instead of the bare
     19   1.5  rillig # word "expression".  If the name were expanded lazily, everything would be
     20   1.5  rillig # fine since the condition would be:
     21   1.5  rillig #
     22   1.5  rillig #	${:Uvariable expression} == "literal"
     23   1.5  rillig #
     24   1.5  rillig # Evaluating the variable name lazily would require additional code in
     25   1.5  rillig # Var_Parse and ParseVarname, it would be more useful and predictable
     26   1.5  rillig # though.
     27   1.5  rillig .if ${${:Uvariable expression} == "literal":?bad:bad}
     28   1.5  rillig .  error
     29   1.5  rillig .else
     30   1.5  rillig .  error
     31   1.5  rillig .endif
     32   1.5  rillig 
     33   1.5  rillig # In a variable assignment, undefined variables are not an error.
     34   1.5  rillig # Because of the early expansion, the whole condition evaluates to
     35   1.5  rillig # ' == ""' though, which cannot be parsed because the left-hand side looks
     36   1.5  rillig # empty.
     37   1.5  rillig COND:=	${${UNDEF} == "":?bad-assign:bad-assign}
     38   1.5  rillig 
     39   1.5  rillig # In a condition, undefined variables generate a "Malformed conditional"
     40   1.5  rillig # error.  That error message is wrong though.  In lint mode, the correct
     41   1.5  rillig # "Undefined variable" error message is generated.
     42   1.5  rillig # The difference to the ':=' variable assignment is the additional
     43   1.5  rillig # "Malformed conditional" error message.
     44   1.5  rillig .if ${${UNDEF} == "":?bad-cond:bad-cond}
     45   1.5  rillig .  error
     46   1.5  rillig .else
     47   1.5  rillig .  error
     48   1.5  rillig .endif
     49   1.5  rillig 
     50   1.4  rillig # When the :? is parsed, it is greedy.  The else branch spans all the
     51   1.4  rillig # text, up until the closing character '}', even if the text looks like
     52   1.4  rillig # another modifier.
     53   1.4  rillig .if ${1:?then:else:Q} != "then"
     54   1.4  rillig .  error
     55   1.4  rillig .endif
     56   1.4  rillig .if ${0:?then:else:Q} != "else:Q"
     57   1.4  rillig .  error
     58   1.4  rillig .endif
     59   1.3  rillig 
     60   1.6  rillig # This line generates 2 error messages.  The first comes from evaluating the
     61   1.6  rillig # malformed conditional "1 == == 2", which is reported as "Bad conditional
     62   1.6  rillig # expression" by ApplyModifier_IfElse.  The variable expression containing that
     63   1.6  rillig # conditional therefore returns a parse error from Var_Parse, and this parse
     64   1.6  rillig # error propagates to CondEvalExpression, where the "Malformed conditional"
     65   1.6  rillig # comes from.
     66   1.6  rillig .if ${1 == == 2:?yes:no} != ""
     67   1.6  rillig .  error
     68   1.6  rillig .else
     69   1.6  rillig .  error
     70   1.6  rillig .endif
     71   1.6  rillig 
     72   1.6  rillig # If the "Bad conditional expression" appears in a quoted string literal, the
     73   1.6  rillig # error message "Malformed conditional" is not printed, leaving only the "Bad
     74   1.6  rillig # conditional expression".
     75   1.6  rillig #
     76   1.6  rillig # XXX: The left-hand side is enclosed in quotes.  This results in Var_Parse
     77  1.11  rillig # being called without VARE_UNDEFERR.  When ApplyModifier_IfElse
     78   1.6  rillig # returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the
     79   1.6  rillig # value of the variable expression is still undefined.  CondParser_String is
     80   1.6  rillig # then supposed to do proper error handling, but since varUndefined is local
     81   1.6  rillig # to var.c, it cannot distinguish this return value from an ordinary empty
     82   1.6  rillig # string.  The left-hand side of the comparison is therefore just an empty
     83   1.6  rillig # string, which is obviously equal to the empty string on the right-hand side.
     84   1.6  rillig #
     85   1.6  rillig # XXX: The debug log for -dc shows a comparison between 1.0 and 0.0.  The
     86   1.6  rillig # condition should be detected as being malformed before any comparison is
     87   1.6  rillig # done since there is no well-formed comparison in the condition at all.
     88   1.6  rillig .MAKEFLAGS: -dc
     89   1.6  rillig .if "${1 == == 2:?yes:no}" != ""
     90   1.6  rillig .  error
     91   1.6  rillig .else
     92   1.6  rillig .  warning Oops, the parse error should have been propagated.
     93   1.6  rillig .endif
     94   1.6  rillig .MAKEFLAGS: -d0
     95   1.6  rillig 
     96   1.7  rillig # As of 2020-12-10, the variable "name" is first expanded, and the result of
     97   1.7  rillig # this expansion is then taken as the condition.  To force the variable
     98   1.7  rillig # expression in the condition to be evaluated at exactly the right point,
     99   1.7  rillig # the '$' of the intended '${VAR}' escapes from the parser in form of the
    100   1.7  rillig # expression ${:U\$}.  Because of this escaping, the variable "name" and thus
    101   1.7  rillig # the condition ends up as "${VAR} == value", just as intended.
    102   1.8  rillig #
    103   1.8  rillig # This hack does not work for variables from .for loops since these are
    104   1.8  rillig # expanded at parse time to their corresponding ${:Uvalue} expressions.
    105   1.8  rillig # Making the '$' of the '${VAR}' expression indirect hides this expression
    106   1.9  rillig # from the parser of the .for loop body.  See ForLoop_SubstVarLong.
    107   1.7  rillig .MAKEFLAGS: -dc
    108   1.7  rillig VAR=	value
    109   1.7  rillig .if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
    110   1.7  rillig .  error
    111   1.7  rillig .endif
    112   1.7  rillig .MAKEFLAGS: -d0
    113   1.7  rillig 
    114  1.12  rillig # Seen on 2021-04-19 when building external/bsd/tmux with HAVE_LLVM=yes
    115  1.12  rillig # and HAVE_GCC=no.
    116  1.12  rillig #
    117  1.12  rillig # TODO: make should at least describe the part of the condition that is
    118  1.12  rillig #  wrong. In this case it is probably the "no >= 10".  Ideally that should
    119  1.12  rillig #  not matter though since the left-hand side of the '&&' evaluates to false,
    120  1.12  rillig #  thus the right-hand side only needs to be parsed, not evaluated.  Since
    121  1.12  rillig #  this is the modifier ':?', which expands subexpressions before parsing
    122  1.12  rillig #  the condition, the "no >= 10" is probably a parse error since it "can be
    123  1.12  rillig #  seen at compile-time" that the operand types of '>=' don't match.  Only
    124  1.12  rillig #  that the concept of "compile-time" does not really apply here.
    125  1.12  rillig STRING=		string
    126  1.12  rillig NUMBER=		no
    127  1.12  rillig .info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
    128  1.12  rillig # XXX: In the diagnostic, the second placeholder is missing the quotes.
    129