Module Name:    src
Committed By:   rillig
Date:           Sun Mar  3 13:09:23 UTC 2024

Modified Files:
        src/tests/usr.bin/xlint/lint1: msg_358.c msg_362.c msg_363.c msg_372.c
            msg_373.c msg_374.c msg_377.c
        src/usr.bin/xlint/lint1: cksnprintb.c err.c

Log Message:
lint: warn about escaped snprintb directive

Repurpose message 362, as the previous version was redundant since null
bytes in old-style formats are already covered by message 371 (bit
position out of range) and 377 (redundant '\0' at the end).


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/tests/usr.bin/xlint/lint1/msg_358.c \
    src/tests/usr.bin/xlint/lint1/msg_362.c
cvs rdiff -u -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/msg_363.c \
    src/tests/usr.bin/xlint/lint1/msg_372.c \
    src/tests/usr.bin/xlint/lint1/msg_373.c \
    src/tests/usr.bin/xlint/lint1/msg_377.c
cvs rdiff -u -r1.3 -r1.4 src/tests/usr.bin/xlint/lint1/msg_374.c
cvs rdiff -u -r1.5 -r1.6 src/usr.bin/xlint/lint1/cksnprintb.c
cvs rdiff -u -r1.230 -r1.231 src/usr.bin/xlint/lint1/err.c

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

Modified files:

Index: src/tests/usr.bin/xlint/lint1/msg_358.c
diff -u src/tests/usr.bin/xlint/lint1/msg_358.c:1.1 src/tests/usr.bin/xlint/lint1/msg_358.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_358.c:1.1	Fri Mar  1 19:39:28 2024
+++ src/tests/usr.bin/xlint/lint1/msg_358.c	Sun Mar  3 13:09:23 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_358.c,v 1.1 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: msg_358.c,v 1.2 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_358.c"
 
 // Test for message: hex escape '%.*s' has more than 2 digits [358]
@@ -49,4 +49,13 @@ examples(unsigned u32, uint64_t u64)
 	snprintb(buf, sizeof(buf),
 	    "\177\020f\x00\x02bit\0",
 	    u64);
+
+	// In this example from the snprintb manual page, the descriptions
+	// that start with a hexadecimal digit must be separated from the
+	// hexadecimal escape sequence for the bit position.
+	snprintb(buf, sizeof(buf),
+	    "\20\x10NOTBOOT\x0f" "FPP\x0eSDVMA\x0cVIDEO"
+	    "\x0bLORES\x0a" "FPA\x09" "DIAG\x07" "CACHE"
+	    "\x06IOCACHE\x05LOOPBACK\x04" "DBGCACHE",
+	    u32);
 }
Index: src/tests/usr.bin/xlint/lint1/msg_362.c
diff -u src/tests/usr.bin/xlint/lint1/msg_362.c:1.1 src/tests/usr.bin/xlint/lint1/msg_362.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_362.c:1.1	Fri Mar  1 19:39:28 2024
+++ src/tests/usr.bin/xlint/lint1/msg_362.c	Sun Mar  3 13:09:23 2024
@@ -1,11 +1,11 @@
-/*	$NetBSD: msg_362.c,v 1.1 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: msg_362.c,v 1.2 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_362.c"
 
-// Test for message: old-style format contains '\0' [362]
+// Test for message: directive '%.*s' should not be escaped [362]
 
 /*
- * The old-style format uses 1-based bit positions, from 1 up to 32.
- * A null character would prematurely end the format argument.
+ * Since the characters used for the directive type are chosen to be easily
+ * readable, it doesn't make sense to obfuscate them.
  */
 
 /* lint1-extra-flags: -X 351 */
@@ -20,9 +20,14 @@ example(unsigned u32)
 {
 	char buf[64];
 
-	/* expect+1: warning: bit position '\000' (0) in '\000lsb' out of range 1..32 [371] */
-	snprintb(buf, sizeof(buf), "\020\000lsb\037msb", u32);
-	/* expect+2: warning: old-style format contains '\0' [362] */
-	/* expect+1: warning: bit position '\000' (0) in '\000lsb' out of range 1..32 [371] */
-	snprintb(buf, sizeof(buf), "\020\037msb\000lsb", u32);
+	/* expect+9: warning: directive '\142' should not be escaped [362] */
+	/* expect+8: warning: bit position 'o' in '\142old-style-lsb\0' should be escaped as octal or hex [369] */
+	/* expect+7: warning: bit position 'o' (111) in '\142old-style-lsb\0' out of range 0..63 [371] */
+	/* expect+6: warning: unknown directive '\001', must be one of 'bfF=:*' [374] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "\142old-style-lsb\0"
+	    "\001old-style-lsb\0"
+	    "\142\000old-style-lsb\0",
+	    u32);
 }

Index: src/tests/usr.bin/xlint/lint1/msg_363.c
diff -u src/tests/usr.bin/xlint/lint1/msg_363.c:1.2 src/tests/usr.bin/xlint/lint1/msg_363.c:1.3
--- src/tests/usr.bin/xlint/lint1/msg_363.c:1.2	Sun Mar  3 10:27:18 2024
+++ src/tests/usr.bin/xlint/lint1/msg_363.c	Sun Mar  3 13:09:23 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_363.c,v 1.2 2024/03/03 10:27:18 rillig Exp $	*/
+/*	$NetBSD: msg_363.c,v 1.3 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_363.c"
 
 // Test for message: non-printing character '%.*s' in description '%.*s' [363]
@@ -29,9 +29,7 @@ old_style_description(unsigned u32)
 	    "\001non\tprint\nable\377",
 	    u32);
 
-	/* expect+8: warning: old-style format contains '\0' [362] */
-	/* expect+7: warning: bit position '\000' (0) in '\000print' out of range 1..32 [371] */
-	/* expect+6: warning: old-style format contains '\0' [362] */
+	/* expect+6: warning: bit position '\000' (0) in '\000print' out of range 1..32 [371] */
 	/* expect+5: warning: bit position '\n' in '\nable' should be escaped as octal or hex [369] */
 	/* expect+4: warning: redundant '\0' at the end of the format [377] */
 	snprintb(buf, sizeof(buf),
Index: src/tests/usr.bin/xlint/lint1/msg_372.c
diff -u src/tests/usr.bin/xlint/lint1/msg_372.c:1.2 src/tests/usr.bin/xlint/lint1/msg_372.c:1.3
--- src/tests/usr.bin/xlint/lint1/msg_372.c:1.2	Sat Mar  2 11:56:37 2024
+++ src/tests/usr.bin/xlint/lint1/msg_372.c	Sun Mar  3 13:09:23 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: msg_372.c,v 1.2 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: msg_372.c,v 1.3 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_372.c"
 
-// Test for message: field width '%.*s' (%ju) in '%.*s' out of range 0..%u [372]
+// Test for message: field width '%.*s' (%ju) in '%.*s' out of range 0..64 [372]
 
 /*
  * In new-style formats, the width of a bit-field must be between 0 (an empty
Index: src/tests/usr.bin/xlint/lint1/msg_373.c
diff -u src/tests/usr.bin/xlint/lint1/msg_373.c:1.2 src/tests/usr.bin/xlint/lint1/msg_373.c:1.3
--- src/tests/usr.bin/xlint/lint1/msg_373.c:1.2	Sat Mar  2 11:56:37 2024
+++ src/tests/usr.bin/xlint/lint1/msg_373.c	Sun Mar  3 13:09:23 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_373.c,v 1.2 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: msg_373.c,v 1.3 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_373.c"
 
 // Test for message: bit field end %ju in '%.*s' out of range 0..64 [373]
@@ -34,4 +34,11 @@ example(uint64_t u64)
 	    "f\001\100oob64\0"
 	    "f\010\377oob64\0",
 	    u64);
+
+	/* expect+5: warning: bit position '\377' (255) in 'f\377\002wrap-around\0' out of range 0..63 [371] */
+	/* expect+4: warning: bit field end 257 in 'f\377\002wrap-around\0' out of range 0..64 [373] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "f\377\002wrap-around\0",
+	    u64);
 }
Index: src/tests/usr.bin/xlint/lint1/msg_377.c
diff -u src/tests/usr.bin/xlint/lint1/msg_377.c:1.2 src/tests/usr.bin/xlint/lint1/msg_377.c:1.3
--- src/tests/usr.bin/xlint/lint1/msg_377.c:1.2	Sun Mar  3 10:27:18 2024
+++ src/tests/usr.bin/xlint/lint1/msg_377.c	Sun Mar  3 13:09:23 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_377.c,v 1.2 2024/03/03 10:27:18 rillig Exp $	*/
+/*	$NetBSD: msg_377.c,v 1.3 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_377.c"
 
 // Test for message: redundant '\0' at the end of the format [377]
@@ -23,8 +23,6 @@ example(unsigned u32, uint64_t u64)
 {
 	char buf[64];
 
-	/* expect+9: warning: old-style format contains '\0' [362] */
-	/* expect+8: warning: old-style format contains '\0' [362] */
 	/* expect+7: warning: bit position '\000' (0) in '\000out-of-range' out of range 1..32 [371] */
 	/* expect+6: warning: redundant '\0' at the end of the format [377] */
 	snprintb(buf, sizeof(buf),

Index: src/tests/usr.bin/xlint/lint1/msg_374.c
diff -u src/tests/usr.bin/xlint/lint1/msg_374.c:1.3 src/tests/usr.bin/xlint/lint1/msg_374.c:1.4
--- src/tests/usr.bin/xlint/lint1/msg_374.c:1.3	Sun Mar  3 10:27:18 2024
+++ src/tests/usr.bin/xlint/lint1/msg_374.c	Sun Mar  3 13:09:23 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: msg_374.c,v 1.3 2024/03/03 10:27:18 rillig Exp $	*/
+/*	$NetBSD: msg_374.c,v 1.4 2024/03/03 13:09:23 rillig Exp $	*/
 # 3 "msg_374.c"
 
-// Test for message: unknown directive '%.*s' [374]
+// Test for message: unknown directive '%.*s', must be one of 'bfF=:*' [374]
 
 /*
  * In the new-style format, an unknown directive is assumed to have a single
@@ -20,13 +20,13 @@ example(uint64_t u64)
 {
 	char buf[64];
 
-	/* expect+4: warning: unknown directive 'x' [374] */
+	/* expect+4: warning: unknown directive 'x', must be one of 'bfF=:*' [374] */
 	snprintb(buf, sizeof(buf),
 	    "\177\020"
 	    "x12345\0",
 	    u64);
 
-	/* expect+4: warning: unknown directive '\000' [374] */
+	/* expect+4: warning: unknown directive '\000', must be one of 'bfF=:*' [374] */
 	snprintb(buf, sizeof(buf),
 	    "\177\020"
 	    "\00012345\0",
@@ -38,4 +38,15 @@ example(uint64_t u64)
 	    "b\00012345\0"
 	    "\0",
 	    u64);
+
+	// Real-life example: the '\b' is a typo.
+	//
+	// TODO: Warn about the description that is split between string
+	//  literals for no apparent reason.
+	//
+	/* expect+4: warning: unknown directive '\b', must be one of 'bfF=:*' [374] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "b\15ENCNT\0b\16" "TC\0\b\20DSBL_" "CSR_DRN\0",
+	    u64);
 }

Index: src/usr.bin/xlint/lint1/cksnprintb.c
diff -u src/usr.bin/xlint/lint1/cksnprintb.c:1.5 src/usr.bin/xlint/lint1/cksnprintb.c:1.6
--- src/usr.bin/xlint/lint1/cksnprintb.c:1.5	Sun Mar  3 10:27:18 2024
+++ src/usr.bin/xlint/lint1/cksnprintb.c	Sun Mar  3 13:09:22 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: cksnprintb.c,v 1.5 2024/03/03 10:27:18 rillig Exp $	*/
+/*	$NetBSD: cksnprintb.c,v 1.6 2024/03/03 13:09:22 rillig Exp $	*/
 
 /*-
  * Copyright (c) 2024 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: cksnprintb.c,v 1.5 2024/03/03 10:27:18 rillig Exp $");
+__RCSID("$NetBSD: cksnprintb.c,v 1.6 2024/03/03 13:09:22 rillig Exp $");
 #endif
 
 #include <stdbool.h>
@@ -47,6 +47,8 @@ typedef struct {
 	bool new_style;
 	const buffer *fmt;
 	const tnode_t *value;
+
+	quoted_iterator it;
 	uint64_t field_width;
 	uint64_t covered;
 	unsigned covered_start[64];
@@ -141,7 +143,7 @@ check_overlap(checker *ck, uint64_t dir_
 	uint64_t field_mask = value_bits((unsigned)width) << lsb;
 	uint64_t overlap = ck->covered & field_mask;
 	if (overlap == 0)
-		goto done;
+		goto update_covered;
 
 	for (unsigned i = lsb; i < 64; i++) {
 		if (!(overlap & bit(i)))
@@ -155,7 +157,7 @@ check_overlap(checker *ck, uint64_t dir_
 		break;
 	}
 
-done:
+update_covered:
 	ck->covered |= field_mask;
 	for (unsigned i = lsb; i < 64; i++) {
 		if (field_mask & bit(i)) {
@@ -174,39 +176,31 @@ check_reachable(checker *ck, uint64_t di
 		return;
 
 	uint64_t field_mask = value_bits((unsigned)width) << lsb;
-	if (!(possible_bits(ck->value) & field_mask)) {
+	if (!(possible_bits(ck->value) & field_mask))
 		/* directive '%.*s' is unreachable by input value */
 		warning(378, (int)(end - start), ck->fmt->data + start);
-	}
 }
 
 static void
-parse_description(const checker *ck, quoted_iterator *it,
-		  bool *seen_null, bool *descr_empty)
+parse_description(checker *ck, bool *seen_null, bool *descr_empty)
 {
-	bool new_style = ck->new_style;
-
-	quoted_iterator first = *it;
+	quoted_iterator first = ck->it;
 	(void)quoted_next(ck->fmt, &first);
 	size_t descr_start = first.start, descr_end = descr_start;
 
-	for (quoted_iterator peek = *it; quoted_next(ck->fmt, &peek);) {
-		if (new_style && peek.value == 0) {
+	for (quoted_iterator peek = ck->it; quoted_next(ck->fmt, &peek);) {
+		if (!ck->new_style && peek.value <= 32)
+			break;
+		ck->it = peek;
+		if (ck->new_style && peek.value == 0) {
 			*seen_null = true;
-			*it = peek;
 			break;
 		}
-		if (!new_style && peek.value == 0)
-			/* old-style format contains '\0' */
-			warning(362);
-		if (!new_style && peek.value <= 32)
-			break;
-		*it = peek;
 		descr_end = peek.i;
 		if (peek.escaped && !isprint((unsigned char)peek.value)) {
 			/* non-printing character '%.*s' in description ... */
 			warning(363,
-			    len(*it), start(*it, ck->fmt),
+			    len(ck->it), start(ck->it, ck->fmt),
 			    (int)(descr_end - descr_start),
 			    ck->fmt->data + descr_start);
 		}
@@ -215,9 +209,11 @@ parse_description(const checker *ck, quo
 }
 
 static bool
-check_directive(const buffer *fmt, quoted_iterator *it, bool new_style,
-		checker *ck)
+check_directive(checker *ck)
 {
+	bool new_style = ck->new_style;
+	const buffer *fmt = ck->fmt;
+	quoted_iterator *it = &ck->it;
 
 	if (!quoted_next(fmt, it))
 		return false;
@@ -263,14 +259,17 @@ check_directive(const buffer *fmt, quote
 	}
 
 	if (!has_bit && !has_cmp && !has_default) {
-		/* unknown directive '%.*s' */
+		/* unknown directive '%.*s', must be one of 'bfF=:*' */
 		warning(374, len(dir), start(dir, fmt));
 		return false;
 	}
+	if (new_style && dir.escaped)
+		/* directive '%.*s' should not be escaped */
+		warning(362, len(dir), start(dir, fmt));
 
 	bool needs_descr = !(new_style && dir.value == 'F');
 	bool seen_null = false, descr_empty = false;
-	parse_description(ck, it, &seen_null, &descr_empty);
+	parse_description(ck, &seen_null, &descr_empty);
 
 	if (has_bit)
 		check_hex_escape(fmt, bit);
@@ -293,12 +292,11 @@ check_directive(const buffer *fmt, quote
 		    range(dir, *it), start(dir, fmt),
 		    new_style ? 0 : 1, new_style ? 63 : 32);
 	}
-	if (has_width && width.value > (new_style ? 64 : 32)) {
-		/* field width '%.*s' (%ju) in '%.*s' out of range 0..%u */
+	if (has_width && width.value > 64) {
+		/* field width '%.*s' (%ju) in '%.*s' out of range 0..64 */
 		warning(372,
 		    len(width), start(width, fmt), val(width),
-		    range(dir, *it), start(dir, fmt),
-		    new_style ? 64 : 32);
+		    range(dir, *it), start(dir, fmt));
 	}
 	if (has_width && bit.value + width.value > 64) {
 		/* bit field end %ju in '%.*s' out of range 0..64 */
@@ -314,19 +312,14 @@ check_directive(const buffer *fmt, quote
 	if (has_bit) {
 		uint64_t w = has_width ? width.value : 1;
 		check_overlap(ck, bit.value, w, dir.start, it->i);
-	}
-	if (has_bit) {
-		uint64_t w = has_width ? width.value : 1;
 		check_reachable(ck, bit.value, w, dir.start, it->i);
 	}
-	if (needs_descr && descr_empty) {
+	if (needs_descr && descr_empty)
 		/* empty description in '%.*s' */
 		warning(367, range(dir, *it), start(dir, fmt));
-	}
-	if (new_style && !seen_null) {
+	if (new_style && !seen_null)
 		/* missing '\0' at the end of '%.*s' */
 		warning(366, range(dir, *it), start(dir, fmt));
-	}
 
 	if (has_width)
 		ck->field_width = width.value;
@@ -341,30 +334,29 @@ check_snprintb(const tnode_t *expr)
 	if (!match_snprintb_call(expr->tn_call, &fmt, &value))
 		return;
 
-	quoted_iterator it = { .i = 0 };
-	if (!quoted_next(fmt, &it)) {
+	checker ck = {
+		.fmt = fmt,
+		.value = value,
+		.field_width = 64,
+	};
+
+	if (!quoted_next(fmt, &ck.it)) {
 		/* missing new-style '\177' or old-style number base */
 		warning(359);
 		return;
 	}
-	bool new_style = it.value == '\177';
-	if (new_style && !quoted_next(fmt, &it)) {
+	ck.new_style = ck.it.value == '\177';
+	if (ck.new_style && !quoted_next(fmt, &ck.it)) {
 		/* missing new-style number base after '\177' */
 		warning(360);
 		return;
 	}
-	if (it.value != 8 && it.value != 10 && it.value != 16) {
+	if (ck.it.value != 8 && ck.it.value != 10 && ck.it.value != 16) {
 		/* number base '%.*s' is %ju, must be 8, 10 or 16 */
-		warning(361, len(it), start(it, fmt), val(it));
+		warning(361, len(ck.it), start(ck.it, fmt), val(ck.it));
 		return;
 	}
 
-	checker ck = {
-		.new_style = new_style,
-		.fmt = fmt,
-		.value = value,
-		.field_width = 64,
-	};
-	while (check_directive(fmt, &it, new_style, &ck))
+	while (check_directive(&ck))
 		continue;
 }

Index: src/usr.bin/xlint/lint1/err.c
diff -u src/usr.bin/xlint/lint1/err.c:1.230 src/usr.bin/xlint/lint1/err.c:1.231
--- src/usr.bin/xlint/lint1/err.c:1.230	Sun Mar  3 10:27:18 2024
+++ src/usr.bin/xlint/lint1/err.c	Sun Mar  3 13:09:22 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: err.c,v 1.230 2024/03/03 10:27:18 rillig Exp $	*/
+/*	$NetBSD: err.c,v 1.231 2024/03/03 13:09:22 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: err.c,v 1.230 2024/03/03 10:27:18 rillig Exp $");
+__RCSID("$NetBSD: err.c,v 1.231 2024/03/03 13:09:22 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -417,7 +417,7 @@ static const char *const msgs[] = {
 	"missing new-style '\\177' or old-style number base",		// 359
 	"missing new-style number base after '\\177'",			// 360
 	"number base '%.*s' is %ju, must be 8, 10 or 16",		// 361
-	"old-style format contains '\\0'",				// 362
+	"directive '%.*s' should not be escaped",			// 362
 	"non-printing character '%.*s' in description '%.*s'",		// 363
 	"missing bit position after '%.*s'",				// 364
 	"missing field width after '%.*s'",				// 365
@@ -427,9 +427,9 @@ static const char *const msgs[] = {
 	"bit position '%.*s' in '%.*s' should be escaped as octal or hex", // 369
 	"field width '%.*s' in '%.*s' should be escaped as octal or hex", // 370
 	"bit position '%.*s' (%ju) in '%.*s' out of range %u..%u",	// 371
-	"field width '%.*s' (%ju) in '%.*s' out of range 0..%u",	// 372
+	"field width '%.*s' (%ju) in '%.*s' out of range 0..64",	// 372
 	"bit field end %ju in '%.*s' out of range 0..64",		// 373
-	"unknown directive '%.*s'",					// 374
+	"unknown directive '%.*s', must be one of 'bfF=:*'",		// 374
 	"comparison value '%.*s' (%ju) exceeds maximum field value %ju", // 375
 	"'%.*s' overlaps earlier '%.*s' on bit %u",			// 376
 	"redundant '\\0' at the end of the format",			// 377

Reply via email to