Home | History | Annotate | Line # | Download | only in unit-tests
varparse-undef-partial.mk revision 1.4
      1 # $NetBSD: varparse-undef-partial.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
      2 
      3 # When an undefined variable is expanded in a ':=' assignment, only the
      4 # initial '$' of the expression is skipped by the parser, while
      5 # the remaining expression is evaluated.  In edge cases this can lead to
      6 # a completely different interpretation of the partially expanded text.
      7 
      8 LIST=	${DEF} ${UNDEF} ${VAR.${PARAM}} end
      9 DEF=	defined
     10 PARAM=	:Q
     11 
     12 # The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
     13 # with the ":Q" being part of the name.  This variable is not defined,
     14 # therefore the initial '$' of that whole expression is skipped by the
     15 # parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
     16 # of the expression is expanded as usual.
     17 #
     18 # The resulting expression is ${VAR.:Q}, which means that the
     19 # interpretation of the ":Q" has changed from being part of the variable
     20 # name to being a variable modifier.  This is a classical code injection.
     21 EVAL:=	${LIST}
     22 .if ${EVAL} != "defined   end"
     23 .  error ${EVAL}
     24 .endif
     25 
     26 # Define the possible outcomes, to see which of them gets expanded.
     27 VAR.=		var-dot without parameter
     28 ${:UVAR.\:Q}=	var-dot with parameter :Q
     29 
     30 # At this point, the variable "VAR." is defined, therefore the expression
     31 # ${VAR.:Q} is expanded, consisting of the variable name "VAR." and the
     32 # modifier ":Q".
     33 .if ${EVAL} != "defined  var-dot\\ without\\ parameter end"
     34 .  error ${EVAL}
     35 .endif
     36 
     37 # In contrast to the previous line, evaluating the original LIST again now
     38 # produces a different result since the variable named "VAR.:Q" is now
     39 # defined.  It is expanded as usual, interpreting the ":Q" as part of the
     40 # variable name, as would be expected from reading the expression.
     41 EVAL:=	${LIST}
     42 .if ${EVAL} != "defined  var-dot with parameter :Q end"
     43 .  error ${EVAL}
     44 .endif
     45 
     46 # It's difficult to decide what the best behavior is in this situation.
     47 # Should the whole expression be skipped for now, or should the inner
     48 # subexpressions be expanded already?
     49 #
     50 # Example 1:
     51 # CFLAGS:=	${CFLAGS:N-W*} ${COPTS.${COMPILER}}
     52 #
     53 # The variable COMPILER typically contains an identifier and the variable is
     54 # not modified later.  In this practical case, it does not matter whether the
     55 # expression is expanded early, or whether the whole ${COPTS.${COMPILER}} is
     56 # expanded as soon as the variable COPTS.${COMPILER} becomes defined.  The
     57 # expression ${COMPILER} would be expanded several times, but in this simple
     58 # scenario there would not be any side effects.
     59 #
     60 # TODO: Add a practical example where early/lazy expansion actually makes a
     61 # difference.
     62 
     63 all:
     64 	@:
     65