Module Name:    src
Committed By:   rillig
Date:           Fri Jun 23 04:56:54 UTC 2023

Modified Files:
        src/usr.bin/make: cond.c dir.c str.c str.h var.c
        src/usr.bin/make/unit-tests: cond-func-make.exp cond-func-make.mk
            varmod-match-escape.exp varmod-match-escape.mk varmod-match.exp
            varmod-match.mk

Log Message:
make: warn about malformed patterns in ':M', ':N' and '.if make(...)'

These patterns shouldn't occur in practice, as their results are tricky
to predict.  Generate a warning for now, and maybe an error later.

Reviewed by sjg@.


To generate a diff of this commit:
cvs rdiff -u -r1.351 -r1.352 src/usr.bin/make/cond.c
cvs rdiff -u -r1.281 -r1.282 src/usr.bin/make/dir.c
cvs rdiff -u -r1.97 -r1.98 src/usr.bin/make/str.c
cvs rdiff -u -r1.16 -r1.17 src/usr.bin/make/str.h
cvs rdiff -u -r1.1057 -r1.1058 src/usr.bin/make/var.c
cvs rdiff -u -r1.2 -r1.3 src/usr.bin/make/unit-tests/cond-func-make.exp
cvs rdiff -u -r1.4 -r1.5 src/usr.bin/make/unit-tests/cond-func-make.mk
cvs rdiff -u -r1.17 -r1.18 \
    src/usr.bin/make/unit-tests/varmod-match-escape.exp
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/make/unit-tests/varmod-match-escape.mk \
    src/usr.bin/make/unit-tests/varmod-match.exp
cvs rdiff -u -r1.14 -r1.15 src/usr.bin/make/unit-tests/varmod-match.mk

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/cond.c
diff -u src/usr.bin/make/cond.c:1.351 src/usr.bin/make/cond.c:1.352
--- src/usr.bin/make/cond.c:1.351	Wed Jun 21 04:20:20 2023
+++ src/usr.bin/make/cond.c	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: cond.c,v 1.351 2023/06/21 04:20:20 sjg Exp $	*/
+/*	$NetBSD: cond.c,v 1.352 2023/06/23 04:56:54 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -92,7 +92,7 @@
 #include "dir.h"
 
 /*	"@(#)cond.c	8.2 (Berkeley) 1/2/94"	*/
-MAKE_RCSID("$NetBSD: cond.c,v 1.351 2023/06/21 04:20:20 sjg Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.352 2023/06/23 04:56:54 rillig Exp $");
 
 /*
  * Conditional expressions conform to this grammar:
@@ -295,10 +295,19 @@ static bool
 FuncMake(const char *targetPattern)
 {
 	StringListNode *ln;
+	bool warned = false;
 
-	for (ln = opts.create.first; ln != NULL; ln = ln->next)
-		if (Str_Match(ln->datum, targetPattern))
+	for (ln = opts.create.first; ln != NULL; ln = ln->next) {
+		StrMatchResult res = Str_Match(ln->datum, targetPattern);
+		if (res.error != NULL && !warned) {
+			warned = true;
+			Parse_Error(PARSE_WARNING,
+			    "%s in pattern argument '%s' to function 'make'",
+			    res.error, targetPattern);
+		}
+		if (res.matched)
 			return true;
+	}
 	return false;
 }
 

Index: src/usr.bin/make/dir.c
diff -u src/usr.bin/make/dir.c:1.281 src/usr.bin/make/dir.c:1.282
--- src/usr.bin/make/dir.c:1.281	Thu Jun 22 09:09:08 2023
+++ src/usr.bin/make/dir.c	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: dir.c,v 1.281 2023/06/22 09:09:08 rillig Exp $	*/
+/*	$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -138,7 +138,7 @@
 #include "job.h"
 
 /*	"@(#)dir.c	8.2 (Berkeley) 1/2/94"	*/
-MAKE_RCSID("$NetBSD: dir.c,v 1.281 2023/06/22 09:09:08 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
 
 /*
  * A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -668,8 +668,10 @@ DirMatchFiles(const char *pattern, Cache
 	HashIter_InitSet(&hi, &dir->files);
 	while (HashIter_Next(&hi) != NULL) {
 		const char *base = hi.entry->key;
+		StrMatchResult res = Str_Match(base, pattern);
+		/* TODO: handle errors from res.error */
 
-		if (!Str_Match(base, pattern))
+		if (!res.matched)
 			continue;
 
 		/*

Index: src/usr.bin/make/str.c
diff -u src/usr.bin/make/str.c:1.97 src/usr.bin/make/str.c:1.98
--- src/usr.bin/make/str.c:1.97	Thu Jun 22 16:59:17 2023
+++ src/usr.bin/make/str.c	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: str.c,v 1.97 2023/06/22 16:59:17 rillig Exp $	*/
+/*	$NetBSD: str.c,v 1.98 2023/06/23 04:56:54 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -71,7 +71,7 @@
 #include "make.h"
 
 /*	"@(#)str.c	5.8 (Berkeley) 6/1/90"	*/
-MAKE_RCSID("$NetBSD: str.c,v 1.97 2023/06/22 16:59:17 rillig Exp $");
+MAKE_RCSID("$NetBSD: str.c,v 1.98 2023/06/23 04:56:54 rillig Exp $");
 
 
 static HashTable interned_strings;
@@ -316,13 +316,12 @@ in_range(char e1, char c, char e2)
  * Test if a string matches a pattern like "*.[ch]". The pattern matching
  * characters are '*', '?' and '[]', as in fnmatch(3).
  *
- * XXX: this function does not detect or report malformed patterns.
- *
  * See varmod-match.mk for examples and edge cases.
  */
-bool
+StrMatchResult
 Str_Match(const char *str, const char *pat)
 {
+	StrMatchResult res = { NULL, false };
 	const char *fixed_str, *fixed_pat;
 	bool asterisk, matched;
 
@@ -336,7 +335,7 @@ match_fixed_length:
 	matched = false;
 	for (; *pat != '\0' && *pat != '*'; str++, pat++) {
 		if (*str == '\0')
-			return false;
+			return res;
 
 		if (*pat == '?')	/* match any single character */
 			continue;
@@ -346,6 +345,9 @@ match_fixed_length:
 			pat += neg ? 2 : 1;
 
 			for (;;) {
+				if (*pat == '\0')
+					res.error =
+					    "Unfinished character list";
 				if (*pat == ']' || *pat == '\0') {
 					if (neg)
 						break;
@@ -354,8 +356,13 @@ match_fixed_length:
 				if (*pat == *str)
 					break;
 				if (pat[1] == '-') {
-					if (pat[2] == '\0')
-						return neg;
+					if (pat[2] == '\0') {
+						res.error =
+						    "Unfinished character "
+						    "range";
+						res.matched = neg;
+						return res;
+					}
 					if (in_range(pat[0], *str, pat[2]))
 						break;
 					pat += 2;
@@ -381,9 +388,11 @@ match_fixed_length:
 match_done:
 	if (!asterisk) {
 		if (!matched)
-			return false;
-		if (*pat == '\0')
-			return *str == '\0';
+			return res;
+		if (*pat == '\0') {
+			res.matched = *str == '\0';
+			return res;
+		}
 		asterisk = true;
 	} else {
 		if (!matched) {
@@ -391,8 +400,10 @@ match_done:
 			goto match_fixed_length;
 		}
 		if (*pat == '\0') {
-			if (*str == '\0')
-				return true;
+			if (*str == '\0') {
+				res.matched = true;
+				return res;
+			}
 			fixed_str += strlen(str);
 			goto match_fixed_length;
 		}
@@ -400,8 +411,10 @@ match_done:
 
 	while (*pat == '*')
 		pat++;
-	if (*pat == '\0')
-		return true;
+	if (*pat == '\0') {
+		res.matched = true;
+		return res;
+	}
 	fixed_str = str;
 	fixed_pat = pat;
 	goto match_fixed_length;

Index: src/usr.bin/make/str.h
diff -u src/usr.bin/make/str.h:1.16 src/usr.bin/make/str.h:1.17
--- src/usr.bin/make/str.h:1.16	Mon Dec  5 23:41:24 2022
+++ src/usr.bin/make/str.h	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: str.h,v 1.16 2022/12/05 23:41:24 rillig Exp $	*/
+/*	$NetBSD: str.h,v 1.17 2023/06/23 04:56:54 rillig Exp $	*/
 
 /*
  Copyright (c) 2021 Roland Illig <ril...@netbsd.org>
@@ -70,6 +70,11 @@ typedef struct SubstringWords {
 	void *freeIt;
 } SubstringWords;
 
+typedef struct StrMatchResult {
+	const char *error;
+	bool matched;
+} StrMatchResult;
+
 
 MAKE_INLINE FStr
 FStr_Init(const char *str, void *freeIt)
@@ -336,7 +341,7 @@ SubstringWords_Free(SubstringWords w)
 char *str_concat2(const char *, const char *);
 char *str_concat3(const char *, const char *, const char *);
 
-bool Str_Match(const char *, const char *);
+StrMatchResult Str_Match(const char *, const char *);
 
 void Str_Intern_Init(void);
 void Str_Intern_End(void);

Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.1057 src/usr.bin/make/var.c:1.1058
--- src/usr.bin/make/var.c:1.1057	Thu Jun 22 08:55:33 2023
+++ src/usr.bin/make/var.c	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.1057 2023/06/22 08:55:33 rillig Exp $	*/
+/*	$NetBSD: var.c,v 1.1058 2023/06/23 04:56:54 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.1057 2023/06/22 08:55:33 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.1058 2023/06/23 04:56:54 rillig Exp $");
 
 /*
  * Variables are defined using one of the VAR=value assignments.  Their
@@ -2793,14 +2793,23 @@ ParseModifier_Match(const char **pp, con
 struct ModifyWord_MatchArgs {
 	const char *pattern;
 	bool neg;
+	bool error_reported;
 };
 
 static void
 ModifyWord_Match(Substring word, SepBuf *buf, void *data)
 {
 	struct ModifyWord_MatchArgs *args = data;
+	StrMatchResult res;
 	assert(word.end[0] == '\0');	/* assume null-terminated word */
-	if (Str_Match(word.start, args->pattern) != args->neg)
+	res = Str_Match(word.start, args->pattern);
+	if (res.error != NULL && !args->error_reported) {
+		args->error_reported = true;
+		Parse_Error(PARSE_WARNING,
+		    "%s in pattern '%s' of modifier '%s'",
+		    res.error, args->pattern, args->neg ? ":N" : ":M");
+	}
+	if (res.matched != args->neg)
 		SepBuf_AddSubstring(buf, word);
 }
 
@@ -2817,6 +2826,7 @@ ApplyModifier_Match(const char **pp, Mod
 		struct ModifyWord_MatchArgs args;
 		args.pattern = pattern;
 		args.neg = mod == 'N';
+		args.error_reported = false;
 		ModifyWords(ch, ModifyWord_Match, &args, ch->oneBigWord);
 	}
 

Index: src/usr.bin/make/unit-tests/cond-func-make.exp
diff -u src/usr.bin/make/unit-tests/cond-func-make.exp:1.2 src/usr.bin/make/unit-tests/cond-func-make.exp:1.3
--- src/usr.bin/make/unit-tests/cond-func-make.exp:1.2	Fri Sep 25 20:11:06 2020
+++ src/usr.bin/make/unit-tests/cond-func-make.exp	Fri Jun 23 04:56:54 2023
@@ -1,3 +1,4 @@
+make: "cond-func-make.mk" line 24: warning: Unfinished character list in pattern argument '[' to function 'make'
 : via-cmdline
 : via-dot-makeflags
 exit status 0

Index: src/usr.bin/make/unit-tests/cond-func-make.mk
diff -u src/usr.bin/make/unit-tests/cond-func-make.mk:1.4 src/usr.bin/make/unit-tests/cond-func-make.mk:1.5
--- src/usr.bin/make/unit-tests/cond-func-make.mk:1.4	Thu Jun 22 09:09:08 2023
+++ src/usr.bin/make/unit-tests/cond-func-make.mk	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-make.mk,v 1.4 2023/06/22 09:09:08 rillig Exp $
+# $NetBSD: cond-func-make.mk,v 1.5 2023/06/23 04:56:54 rillig Exp $
 #
 # Tests for the make() function in .if conditions, which tests whether
 # the argument has been passed as a target via the command line or later
@@ -20,7 +20,7 @@
 .  error
 .endif
 
-# TODO: warn about the malformed pattern
+# expect+1: warning: Unfinished character list in pattern argument '[' to function 'make'
 .if make([)
 .  error
 .endif

Index: src/usr.bin/make/unit-tests/varmod-match-escape.exp
diff -u src/usr.bin/make/unit-tests/varmod-match-escape.exp:1.17 src/usr.bin/make/unit-tests/varmod-match-escape.exp:1.18
--- src/usr.bin/make/unit-tests/varmod-match-escape.exp:1.17	Thu Jun  1 20:56:35 2023
+++ src/usr.bin/make/unit-tests/varmod-match-escape.exp	Fri Jun 23 04:56:54 2023
@@ -34,6 +34,8 @@ make: "varmod-match-escape.mk" line 43: 
 Global: .MAKEFLAGS =  -r -k -d cv -d
 Global: .MAKEFLAGS =  -r -k -d cv -d 0
 make: "varmod-match-escape.mk" line 69: Dollar followed by nothing
+make: "varmod-match-escape.mk" line 110: warning: Unfinished character list in pattern '[A-]' of modifier ':M'
+make: "varmod-match-escape.mk" line 110: warning: Unfinished character list in pattern '[^A-]' of modifier ':M'
 make: Fatal errors encountered -- cannot continue
 make: stopped in unit-tests
 exit status 1

Index: src/usr.bin/make/unit-tests/varmod-match-escape.mk
diff -u src/usr.bin/make/unit-tests/varmod-match-escape.mk:1.9 src/usr.bin/make/unit-tests/varmod-match-escape.mk:1.10
--- src/usr.bin/make/unit-tests/varmod-match-escape.mk:1.9	Thu Jun 22 20:36:24 2023
+++ src/usr.bin/make/unit-tests/varmod-match-escape.mk	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-match-escape.mk,v 1.9 2023/06/22 20:36:24 rillig Exp $
+# $NetBSD: varmod-match-escape.mk,v 1.10 2023/06/23 04:56:54 rillig Exp $
 #
 # As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
 # depending on whether there was a variable expression somewhere before the
@@ -105,6 +105,8 @@ EXP.[^A-]]=	a
 EXP.[^A-]]]=	a]
 
 .for pattern in [A-] [A-]] [A-]]] [^A-] [^A-]] [^A-]]]
+# expect+2: warning: Unfinished character list in pattern '[A-]' of modifier ':M'
+# expect+1: warning: Unfinished character list in pattern '[^A-]' of modifier ':M'
 .  if ${WORDS:M${pattern}} != ${EXP.${pattern}}
 .    warning ${pattern}: ${WORDS:M${pattern}} != ${EXP.${pattern}}
 .  endif
Index: src/usr.bin/make/unit-tests/varmod-match.exp
diff -u src/usr.bin/make/unit-tests/varmod-match.exp:1.9 src/usr.bin/make/unit-tests/varmod-match.exp:1.10
--- src/usr.bin/make/unit-tests/varmod-match.exp:1.9	Thu Jun 22 13:02:42 2023
+++ src/usr.bin/make/unit-tests/varmod-match.exp	Fri Jun 23 04:56:54 2023
@@ -10,8 +10,16 @@ CondParser_Eval: ${:Ua \$ sign:M*$$*} !=
 Comparing "$" != "$"
 CondParser_Eval: ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
 Comparing "any-asterisk" != "any-asterisk"
-make: "varmod-match.mk" line 161: Unknown modifier "]"
-make: "varmod-match.mk" line 161: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
+make: "varmod-match.mk" line 162: warning: Unfinished character list in pattern '[' of modifier ':M'
+make: "varmod-match.mk" line 162: Unknown modifier "]"
+make: "varmod-match.mk" line 162: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
+make: "varmod-match.mk" line 205: warning: Unfinished character list in pattern 'a[' of modifier ':M'
+make: "varmod-match.mk" line 213: warning: Unfinished character list in pattern 'a[^' of modifier ':M'
+make: "varmod-match.mk" line 221: warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 229: warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 238: warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 258: warning: Unfinished character range in pattern '[x-' of modifier ':M'
+make: "varmod-match.mk" line 270: warning: Unfinished character range in pattern '[^x-' of modifier ':M'
 make: Fatal errors encountered -- cannot continue
 make: stopped in unit-tests
 exit status 1

Index: src/usr.bin/make/unit-tests/varmod-match.mk
diff -u src/usr.bin/make/unit-tests/varmod-match.mk:1.14 src/usr.bin/make/unit-tests/varmod-match.mk:1.15
--- src/usr.bin/make/unit-tests/varmod-match.mk:1.14	Thu Jun 22 12:59:54 2023
+++ src/usr.bin/make/unit-tests/varmod-match.mk	Fri Jun 23 04:56:54 2023
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-match.mk,v 1.14 2023/06/22 12:59:54 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.15 2023/06/23 04:56:54 rillig Exp $
 #
 # Tests for the :M variable modifier, which filters words that match the
 # given pattern.
@@ -156,6 +156,7 @@ WORDS=		a\b a[\]b ab
 .endif
 
 #	[:]	matches never since the ':' starts the next modifier
+# expect+3: warning: Unfinished character list in pattern '[' of modifier ':M'
 # expect+2: Unknown modifier "]"
 # expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
 .if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":"
@@ -200,6 +201,7 @@ WORDS=		- -]
 
 #	[	Incomplete empty character list, never matches.
 WORDS=		a a[
+# expect+1: warning: Unfinished character list in pattern 'a[' of modifier ':M'
 .if ${WORDS:Ma[} != ""
 .  error
 .endif
@@ -207,6 +209,7 @@ WORDS=		a a[
 #	[^	Incomplete negated empty character list, matches any single
 #		character.
 WORDS=		a a[ aX
+# expect+1: warning: Unfinished character list in pattern 'a[^' of modifier ':M'
 .if ${WORDS:Ma[^} != "a[ aX"
 .  error
 .endif
@@ -214,6 +217,7 @@ WORDS=		a a[ aX
 #	[-x1-3	Incomplete character list, matches those elements that can be
 #		parsed without lookahead.
 WORDS=		- + x xx 0 1 2 3 4 [x1-3
+# expect+1: warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
 .if ${WORDS:M[-x1-3} != "- x 1 2 3"
 .  error
 .endif
@@ -221,6 +225,7 @@ WORDS=		- + x xx 0 1 2 3 4 [x1-3
 #	*[-x1-3	Incomplete character list after a wildcard, matches those
 #		words that end with one of the characters from the list.
 WORDS=		- + x xx 0 1 2 3 4 00 01 10 11 000 001 010 011 100 101 110 111 [x1-3
+# expect+1: warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
 .if ${WORDS:M*[-x1-3} != "- x xx 1 2 3 01 11 001 011 101 111 [x1-3"
 .  warning ${WORDS:M*[-x1-3}
 .endif
@@ -229,6 +234,7 @@ WORDS=		- + x xx 0 1 2 3 4 00 01 10 11 0
 #		Incomplete negated character list, matches any character
 #		except those elements that can be parsed without lookahead.
 WORDS=		- + x xx 0 1 2 3 4 [x1-3
+# expect+1: warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
 .if ${WORDS:M[^-x1-3} != "+ 0 4"
 .  error
 .endif
@@ -248,6 +254,7 @@ WORDS=		\\ \a ${:Ux\\}
 #	[x-	Incomplete character list containing an incomplete character
 #		range, matches only the 'x'.
 WORDS=		[x- x x- y
+# expect+1: warning: Unfinished character range in pattern '[x-' of modifier ':M'
 .if ${WORDS:M[x-} != "x"
 .  error
 .endif
@@ -259,6 +266,7 @@ WORDS=		[x- x x- y
 #		XXX: Even matches strings that are longer than a single
 #		character.
 WORDS=		[x- x x- y yyyyy
+# expect+1: warning: Unfinished character range in pattern '[^x-' of modifier ':M'
 .if ${WORDS:M[^x-} != "[x- y yyyyy"
 .  error
 .endif

Reply via email to