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)