Module Name:    src
Committed By:   rillig
Date:           Mon Aug  8 18:23:31 UTC 2022

Modified Files:
        src/usr.bin/make: var.c
        src/usr.bin/make/unit-tests: parse-var.exp parse-var.mk
            var-eval-short.exp varmod-defined.exp varname-dot-suffixes.exp

Log Message:
make: fix parsing of modifiers containing unbalanced subexpressions


To generate a diff of this commit:
cvs rdiff -u -r1.1027 -r1.1028 src/usr.bin/make/var.c
cvs rdiff -u -r1.2 -r1.3 src/usr.bin/make/unit-tests/parse-var.exp \
    src/usr.bin/make/unit-tests/parse-var.mk
cvs rdiff -u -r1.18 -r1.19 src/usr.bin/make/unit-tests/var-eval-short.exp
cvs rdiff -u -r1.11 -r1.12 src/usr.bin/make/unit-tests/varmod-defined.exp
cvs rdiff -u -r1.3 -r1.4 src/usr.bin/make/unit-tests/varname-dot-suffixes.exp

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.1027 src/usr.bin/make/var.c:1.1028
--- src/usr.bin/make/var.c:1.1027	Fri Aug  5 20:59:54 2022
+++ src/usr.bin/make/var.c	Mon Aug  8 18:23:30 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.1027 2022/08/05 20:59:54 rillig Exp $	*/
+/*	$NetBSD: var.c,v 1.1028 2022/08/08 18:23:30 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -139,7 +139,7 @@
 #include "metachar.h"
 
 /*	"@(#)var.c	8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.1027 2022/08/05 20:59:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.1028 2022/08/08 18:23:30 rillig Exp $");
 
 /*
  * Variables are defined using one of the VAR=value assignments.  Their
@@ -2131,43 +2131,33 @@ ParseModifierPartExpr(const char **pp, L
 	*pp = p;
 }
 
-/*
- * In a part of a modifier, parse a subexpression but don't evaluate it.
- *
- * XXX: This whole block is very similar to Var_Parse with VARE_PARSE_ONLY.
- * There may be subtle edge cases though that are not yet covered in the unit
- * tests and that are parsed differently, depending on whether they are
- * evaluated or not.
- *
- * This subtle difference is not documented in the manual page, neither is
- * the difference between parsing ':D' and ':M' documented.  No code should
- * ever depend on these details, but who knows.
- */
+/* In a part of a modifier, parse a subexpression but don't evaluate it. */
 static void
 ParseModifierPartDollar(const char **pp, LazyBuf *part)
 {
 	const char *p = *pp;
-	const char *start = *pp;
 
 	if (p[1] == '(' || p[1] == '{') {
-		char startc = p[1];
-		int endc = startc == '(' ? ')' : '}';
-		int depth = 1;
-
-		for (p += 2; *p != '\0' && depth > 0; p++) {
-			if (p[-1] != '\\') {
-				if (*p == startc)
-					depth++;
-				if (*p == endc)
-					depth--;
-			}
-		}
-		LazyBuf_AddBytesBetween(part, start, p);
-		*pp = p;
+		FStr unused;
+		Var_Parse(&p, SCOPE_GLOBAL, VARE_PARSE_ONLY, &unused);
+		/* TODO: handle errors */
+		FStr_Done(&unused);
 	} else {
-		LazyBuf_Add(part, *start);
-		*pp = p + 1;
+		/*
+		 * Only skip the '$' but not the next character; see
+		 * ParseModifierPartSubst, the case for "Unescaped '$' at
+		 * end", which also doesn't skip '$' + delimiter.  That is a
+		 * hack as well, but for now it's consistent in both cases.
+		 */
+		p++;
 	}
+
+	/*
+	 * XXX: There should be no need to add anything to the buffer, as it
+	 * will be discarded anyway.
+	 */
+	LazyBuf_AddBytesBetween(part, *pp, p);
+	*pp = p;
 }
 
 /* See ParseModifierPart for the documentation. */
@@ -4498,6 +4488,8 @@ Var_Parse(const char **pp, GNode *scope,
 	if (Var_Parse_FastLane(pp, emode, out_val))
 		return VPR_OK;
 
+	/* TODO: Reduce computations in parse-only mode. */
+
 	DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
 
 	*out_val = FStr_InitRefer(NULL);
@@ -4524,7 +4516,7 @@ Var_Parse(const char **pp, GNode *scope,
 	}
 
 	expr.name = v->name.str;
-	if (v->inUse) {
+	if (v->inUse && VarEvalMode_ShouldEval(emode)) {
 		if (scope->fname != NULL) {
 			fprintf(stderr, "In a command near ");
 			PrintLocation(stderr, false, scope);

Index: src/usr.bin/make/unit-tests/parse-var.exp
diff -u src/usr.bin/make/unit-tests/parse-var.exp:1.2 src/usr.bin/make/unit-tests/parse-var.exp:1.3
--- src/usr.bin/make/unit-tests/parse-var.exp:1.2	Sat Aug  6 21:26:05 2022
+++ src/usr.bin/make/unit-tests/parse-var.exp	Mon Aug  8 18:23:30 2022
@@ -1,5 +1 @@
-make: Unfinished modifier for "BRACE_GROUP" (',' missing)
-make: "parse-var.mk" line 47: Malformed conditional (0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,})
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-exit status 1
+exit status 0
Index: src/usr.bin/make/unit-tests/parse-var.mk
diff -u src/usr.bin/make/unit-tests/parse-var.mk:1.2 src/usr.bin/make/unit-tests/parse-var.mk:1.3
--- src/usr.bin/make/unit-tests/parse-var.mk:1.2	Sat Aug  6 21:26:05 2022
+++ src/usr.bin/make/unit-tests/parse-var.mk	Mon Aug  8 18:23:30 2022
@@ -1,4 +1,4 @@
-# $NetBSD: parse-var.mk,v 1.2 2022/08/06 21:26:05 rillig Exp $
+# $NetBSD: parse-var.mk,v 1.3 2022/08/08 18:23:30 rillig Exp $
 #
 # Tests for parsing variable expressions.
 
@@ -14,18 +14,23 @@ VAR.${:U param }=	value
 .endif
 
 
-# As of var.c 1.1027 from 2022-08-05, the exact way of parsing an expression
-# depends on whether the expression is actually evaluated or merely parsed.
+# Before var.c 1.1028 from 2022-08-08, the exact way of parsing an expression
+# depended on whether the expression was actually evaluated or merely parsed.
 #
-# If it is evaluated, nested expressions are parsed correctly, parsing each
-# modifier according to its exact definition.  If the expression is merely
-# parsed but not evaluated (because its value would not influence the outcome
-# of the condition), and the expression contains a modifier, and that modifier
-# contains a nested expression, the nested expression is not parsed
-# correctly.  Instead, make only counts the opening and closing delimiters,
-# which fails for nested modifiers with unbalanced braces.
+# If it was evaluated, nested expressions were parsed correctly, parsing each
+# modifier according to its exact definition (see varmod.mk).
 #
-# See ParseModifierPartDollar.
+# If the expression was merely parsed but not evaluated (for example, because
+# its value would not influence the outcome of the condition, or during the
+# first pass of the ':@var@body@' modifier), and the expression contained a
+# modifier, and that modifier contained a nested expression, the nested
+# expression was not parsed correctly.  Instead, make only counted the opening
+# and closing delimiters, which failed for nested modifiers with unbalanced
+# braces.
+#
+# This naive brace counting was implemented in ParseModifierPartDollar.  As of
+# var.c 1., there are still several other places that merely count braces
+# instead of properly parsing subexpressions.
 
 #.MAKEFLAGS: -dcpv
 # Keep these braces outside the conditions below, to keep them simple to
@@ -41,9 +46,11 @@ BRACE_GROUP=	{{{{}}}}
 # In the first case, the outer expression is relevant and is parsed correctly.
 .if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
 .endif
-# In the second case, the outer expression is irrelevant.  In this case, in
-# the parts of the outer ':S' modifier, make only counts the braces, and since
-# the inner expression '${:U...}' contains more '{' than '}', parsing fails.
+# In the second case, the outer expression was irrelevant.  In this case, in
+# the parts of the outer ':S' modifier, make only counted the braces, and since
+# the inner expression '${BRACE_PAIR:...}' contains more '{' than '}', parsing
+# failed with the error message 'Unfinished modifier for "BRACE_GROUP"'.  Fixed
+# in var.c 1.1028 from 2022-08-08.
 .if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
 .endif
 #.MAKEFLAGS: -d0

Index: src/usr.bin/make/unit-tests/var-eval-short.exp
diff -u src/usr.bin/make/unit-tests/var-eval-short.exp:1.18 src/usr.bin/make/unit-tests/var-eval-short.exp:1.19
--- src/usr.bin/make/unit-tests/var-eval-short.exp:1.18	Tue Dec 28 15:49:00 2021
+++ src/usr.bin/make/unit-tests/var-eval-short.exp	Mon Aug  8 18:23:30 2022
@@ -7,7 +7,9 @@ make: "var-eval-short.mk" line 98: Malfo
 CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
 Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
 Parsing modifier ${0:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
 Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
 Modifier part: "${FAIL}else"
 Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
 Parsing line 163: DEFINED=	defined
@@ -17,7 +19,9 @@ Var_Parse: ${DEFINED:L:?${FAIL}then:${FA
 Parsing modifier ${DEFINED:L}
 Result of ${DEFINED:L} is "defined" (parse-only, regular)
 Parsing modifier ${DEFINED:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
 Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
 Modifier part: "${FAIL}else"
 Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
 Parsing line 166: .MAKEFLAGS: -d0

Index: src/usr.bin/make/unit-tests/varmod-defined.exp
diff -u src/usr.bin/make/unit-tests/varmod-defined.exp:1.11 src/usr.bin/make/unit-tests/varmod-defined.exp:1.12
--- src/usr.bin/make/unit-tests/varmod-defined.exp:1.11	Sat Mar 26 14:34:07 2022
+++ src/usr.bin/make/unit-tests/varmod-defined.exp	Mon Aug  8 18:23:30 2022
@@ -10,6 +10,7 @@ Global: VAR = $$$$$$$$
 Var_Parse: ${VAR:@var@${8_DOLLARS}@} (eval-keep-dollar-and-undefined)
 Evaluating modifier ${VAR:@...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
 Modifier part: "var"
+Var_Parse: ${8_DOLLARS}@} (parse-only)
 Modifier part: "${8_DOLLARS}"
 ModifyWords: split "$$$$$$$$" into 1 word
 Global: var = $$$$$$$$

Index: src/usr.bin/make/unit-tests/varname-dot-suffixes.exp
diff -u src/usr.bin/make/unit-tests/varname-dot-suffixes.exp:1.3 src/usr.bin/make/unit-tests/varname-dot-suffixes.exp:1.4
--- src/usr.bin/make/unit-tests/varname-dot-suffixes.exp:1.3	Sat Mar 26 14:34:07 2022
+++ src/usr.bin/make/unit-tests/varname-dot-suffixes.exp	Mon Aug  8 18:23:30 2022
@@ -24,6 +24,7 @@ Evaluating modifier ${1 2:L} on value ""
 Result of ${1 2:L} is "1 2" (eval-defined, defined)
 Evaluating modifier ${1 2:@...} on value "1 2" (eval-defined, defined)
 Modifier part: ".SUFFIXES"
+Var_Parse: ${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (parse-only)
 Modifier part: "${.SUFFIXES}"
 ModifyWords: split "1 2" into 2 words
 Command: .SUFFIXES = 1 ignored (read-only)

Reply via email to