Sun Mar 14 10:57:13 2021 UTC ()
make: fix wrong expression evaluation in -dL mode

The modifier ':C' now only compiles the regular expression if the result
of the expression is actually needed.

Several other modifiers have the same bug of evaluating the expression
in cases where this is not needed.  It just doesn't show up because they
don't have any noticeable side effects, other than wasting CPU time.
This affects irrelevant conditions as well.


(rillig)
diff -r1.855 -r1.856 src/usr.bin/make/var.c
diff -r1.15 -r1.16 src/usr.bin/make/unit-tests/opt-debug-lint.exp
diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/opt-debug-lint.mk

cvs diff -r1.855 -r1.856 src/usr.bin/make/var.c (expand / switch to unified diff)

--- src/usr.bin/make/var.c 2021/02/23 16:29:52 1.855
+++ src/usr.bin/make/var.c 2021/03/14 10:57:12 1.856
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: var.c,v 1.855 2021/02/23 16:29:52 rillig Exp $ */ 1/* $NetBSD: var.c,v 1.856 2021/03/14 10:57:12 rillig Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988, 1989, 1990, 1993 4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor. 8 * Adam de Boor.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -130,27 +130,27 @@ @@ -130,27 +130,27 @@
130#include <regex.h> 130#include <regex.h>
131#endif 131#endif
132#include <errno.h> 132#include <errno.h>
133#include <inttypes.h> 133#include <inttypes.h>
134#include <limits.h> 134#include <limits.h>
135#include <time.h> 135#include <time.h>
136 136
137#include "make.h" 137#include "make.h"
138#include "dir.h" 138#include "dir.h"
139#include "job.h" 139#include "job.h"
140#include "metachar.h" 140#include "metachar.h"
141 141
142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ 142/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
143MAKE_RCSID("$NetBSD: var.c,v 1.855 2021/02/23 16:29:52 rillig Exp $"); 143MAKE_RCSID("$NetBSD: var.c,v 1.856 2021/03/14 10:57:12 rillig Exp $");
144 144
145typedef enum VarFlags { 145typedef enum VarFlags {
146 VFL_NONE = 0, 146 VFL_NONE = 0,
147 147
148 /* 148 /*
149 * The variable's value is currently being used by Var_Parse or 149 * The variable's value is currently being used by Var_Parse or
150 * Var_Subst. This marker is used to avoid endless recursion. 150 * Var_Subst. This marker is used to avoid endless recursion.
151 */ 151 */
152 VFL_IN_USE = 1 << 0, 152 VFL_IN_USE = 1 << 0,
153 153
154 /* 154 /*
155 * The variable comes from the environment. 155 * The variable comes from the environment.
156 * These variables are not registered in any GNode, therefore they 156 * These variables are not registered in any GNode, therefore they
@@ -2882,26 +2882,32 @@ ApplyModifier_Regex(const char **pp, App @@ -2882,26 +2882,32 @@ ApplyModifier_Regex(const char **pp, App
2882 args.matched = FALSE; 2882 args.matched = FALSE;
2883 oneBigWord = st->oneBigWord; 2883 oneBigWord = st->oneBigWord;
2884 for (;; (*pp)++) { 2884 for (;; (*pp)++) {
2885 if (**pp == 'g') 2885 if (**pp == 'g')
2886 args.pflags.subGlobal = TRUE; 2886 args.pflags.subGlobal = TRUE;
2887 else if (**pp == '1') 2887 else if (**pp == '1')
2888 args.pflags.subOnce = TRUE; 2888 args.pflags.subOnce = TRUE;
2889 else if (**pp == 'W') 2889 else if (**pp == 'W')
2890 oneBigWord = TRUE; 2890 oneBigWord = TRUE;
2891 else 2891 else
2892 break; 2892 break;
2893 } 2893 }
2894 2894
 2895 if (!(st->expr->eflags & VARE_WANTRES)) {
 2896 free(args.replace);
 2897 free(re);
 2898 return AMR_OK;
 2899 }
 2900
2895 error = regcomp(&args.re, re, REG_EXTENDED); 2901 error = regcomp(&args.re, re, REG_EXTENDED);
2896 free(re); 2902 free(re);
2897 if (error != 0) { 2903 if (error != 0) {
2898 VarREError(error, &args.re, "Regex compilation error"); 2904 VarREError(error, &args.re, "Regex compilation error");
2899 free(args.replace); 2905 free(args.replace);
2900 return AMR_CLEANUP; 2906 return AMR_CLEANUP;
2901 } 2907 }
2902 2908
2903 args.nsub = args.re.re_nsub + 1; 2909 args.nsub = args.re.re_nsub + 1;
2904 if (args.nsub > 10) 2910 if (args.nsub > 10)
2905 args.nsub = 10; 2911 args.nsub = 10;
2906 2912
2907 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord); 2913 ModifyWords(st, ModifyWord_SubstRegex, &args, oneBigWord);

cvs diff -r1.15 -r1.16 src/usr.bin/make/unit-tests/opt-debug-lint.exp (expand / switch to unified diff)

--- src/usr.bin/make/unit-tests/opt-debug-lint.exp 2021/03/14 10:45:51 1.15
+++ src/usr.bin/make/unit-tests/opt-debug-lint.exp 2021/03/14 10:57:12 1.16
@@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
1make: "opt-debug-lint.mk" line 19: Variable "X" is undefined 1make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
2make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined 2make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
3make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L" 3make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
4make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P" 4make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
5make: "opt-debug-lint.mk" line 69: Unknown modifier "${" 5make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
6make: Regex compilation error: (details omitted) 
7make: Fatal errors encountered -- cannot continue 6make: Fatal errors encountered -- cannot continue
8make: stopped in unit-tests 7make: stopped in unit-tests
9exit status 1 8exit status 1

cvs diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/opt-debug-lint.mk (expand / switch to unified diff)

--- src/usr.bin/make/unit-tests/opt-debug-lint.mk 2021/03/14 10:45:51 1.13
+++ src/usr.bin/make/unit-tests/opt-debug-lint.mk 2021/03/14 10:57:12 1.14
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1# $NetBSD: opt-debug-lint.mk,v 1.13 2021/03/14 10:45:51 rillig Exp $ 1# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
2# 2#
3# Tests for the -dL command line option, which runs additional checks 3# Tests for the -dL command line option, which runs additional checks
4# to catch common mistakes, such as unclosed variable expressions. 4# to catch common mistakes, such as unclosed variable expressions.
5 5
6.MAKEFLAGS: -dL 6.MAKEFLAGS: -dL
7 7
8# Since 2020-09-13, undefined variables that are used on the left-hand side 8# Since 2020-09-13, undefined variables that are used on the left-hand side
9# of a condition at parse time get a proper error message. Before, the 9# of a condition at parse time get a proper error message. Before, the
10# error message was "Malformed conditional" only, which was wrong and 10# error message was "Malformed conditional" only, which was wrong and
11# misleading. The form of the condition is totally fine, it's the evaluation 11# misleading. The form of the condition is totally fine, it's the evaluation
12# that fails. 12# that fails.
13# 13#
14# Since 2020-09-13, the "Malformed conditional" error message is not printed 14# Since 2020-09-13, the "Malformed conditional" error message is not printed
@@ -67,26 +67,29 @@ ${UNDEF}: ${UNDEF} @@ -67,26 +67,29 @@ ${UNDEF}: ${UNDEF}
67# since make always fell back trying to parse the indirect modifier as a 67# since make always fell back trying to parse the indirect modifier as a
68# SysV modifier. 68# SysV modifier.
69.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here. 69.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here.
70. error ${value:${:UL}PL} 70. error ${value:${:UL}PL}
71.endif 71.endif
72 72
73# Typically, an indirect modifier is followed by a colon or the closing 73# Typically, an indirect modifier is followed by a colon or the closing
74# brace. This one isn't, therefore make falls back to parsing it as the SysV 74# brace. This one isn't, therefore make falls back to parsing it as the SysV
75# modifier ":lue=lid". 75# modifier ":lue=lid".
76.if ${value:L:${:Ulue}=${:Ulid}} != "valid" 76.if ${value:L:${:Ulue}=${:Ulid}} != "valid"
77. error 77. error
78.endif 78.endif
79 79
80# Before var.c 1.XXX from 2021-03-14, the whole variable text was evaluated 80# In lint mode, the whole variable text is evaluated to check for unclosed
81# to check for unclosed expressions and unknown operators. During this check, 81# expressions and unknown operators. During this check, the subexpression
82# the subexpression '${:U2}' was not expanded, instead it was copied verbatim 82# '${:U2}' is not expanded, instead it is copied verbatim into the regular
83# into the regular expression, which was '.*=.{1,${:U2}}$'. This regular 83# expression, leading to '.*=.{1,${:U2}}$'.
84# expression was then compiled (despite VARE_WANTRES being unset), which 84#
85# resulted in a wrong error message. 85# Before var.c 1.856 from 2021-03-14, this regular expression was then
 86# compiled even though that was not necessary for checking the syntax at the
 87# level of variable expressions. The unexpanded '$' then resulted in a wrong
 88# error message.
86# 89#
87# This only happened in lint mode since in default mode the early check for 90# This only happened in lint mode since in default mode the early check for
88# unclosed expressions and unknown modifiers is skipped. 91# unclosed expressions and unknown modifiers is skipped.
89# 92#
90# See VarCheckSyntax, ApplyModifier_Regex. 93# See VarCheckSyntax, ApplyModifier_Regex.
91# 94#
92VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g} 95VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g}