aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/unit-tests/varparse-undef-partial.mk
blob: 27f44d79b31a1764aba24eafea1a647269187854 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# $NetBSD: varparse-undef-partial.mk,v 1.3 2020/11/04 05:10:01 rillig Exp $

# When an undefined variable is expanded in a ':=' assignment, only the
# initial '$' of the variable expression is skipped by the parser, while
# the remaining expression is evaluated.  In edge cases this can lead to
# a completely different interpretation of the partially expanded text.

LIST=	${DEF} ${UNDEF} ${VAR.${PARAM}} end
DEF=	defined
PARAM=	:Q

# The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
# with the ":Q" being part of the name.  This variable is not defined,
# therefore the initial '$' of that whole expression is skipped by the
# parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
# of the expression is expanded as usual.
#
# The resulting variable expression is ${VAR.:Q}, which means that the
# interpretation of the ":Q" has changed from being part of the variable
# name to being a variable modifier.  This is a classical code injection.
EVAL:=	${LIST}
.if ${EVAL} != "defined   end"
.  error ${EVAL}
.endif

# Define the possible outcomes, to see which of them gets expanded.
VAR.=		var-dot without parameter
${:UVAR.\:Q}=	var-dot with parameter :Q

# At this point, the variable "VAR." is defined, therefore the expression
# ${VAR.:Q} is expanded, consisting of the variable name "VAR." and the
# modifier ":Q".
.if ${EVAL} != "defined  var-dot\\ without\\ parameter end"
.  error ${EVAL}
.endif

# In contrast to the previous line, evaluating the original LIST again now
# produces a different result since the variable named "VAR.:Q" is now
# defined.  It is expanded as usual, interpreting the ":Q" as part of the
# variable name, as would be expected from reading the variable expression.
EVAL:=	${LIST}
.if ${EVAL} != "defined  var-dot with parameter :Q end"
.  error ${EVAL}
.endif

# It's difficult to decide what the best behavior is in this situation.
# Should the whole expression be skipped for now, or should the inner
# subexpressions be expanded already?
#
# Example 1:
# CFLAGS:=	${CFLAGS:N-W*} ${COPTS.${COMPILER}}
#
# The variable COMPILER typically contains an identifier and the variable is
# not modified later.  In this practical case, it does not matter whether the
# expression is expanded early, or whether the whole ${COPTS.${COMPILER}} is
# expanded as soon as the variable COPTS.${COMPILER} becomes defined.  The
# expression ${COMPILER} would be expanded several times, but in this simple
# scenario there would not be any side effects.
#
# TODO: Add a practical example where early/lazy expansion actually makes a
# difference.

all:
	@: