Module Name:    src
Committed By:   rillig
Date:           Sun Mar  3 00:50:42 UTC 2024

Modified Files:
        src/distrib/sets/lists/tests: mi
        src/tests/usr.bin/xlint/lint1: msg_360.c msg_361.c msg_366.c msg_374.c
            msg_375.c msg_376.c t_usage.sh
        src/usr.bin/xlint/lint1: cksnprintb.c err.c externs1.h tree.c
Added Files:
        src/tests/usr.bin/xlint/lint1: msg_377.c msg_378.c

Log Message:
lint: check for unreachable bits and fields in snprintb formats

While here, clean up a few existing checks.


To generate a diff of this commit:
cvs rdiff -u -r1.1308 -r1.1309 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.1 -r1.2 src/tests/usr.bin/xlint/lint1/msg_360.c \
    src/tests/usr.bin/xlint/lint1/msg_361.c \
    src/tests/usr.bin/xlint/lint1/msg_366.c \
    src/tests/usr.bin/xlint/lint1/msg_374.c \
    src/tests/usr.bin/xlint/lint1/msg_376.c
cvs rdiff -u -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/msg_375.c
cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/xlint/lint1/msg_377.c \
    src/tests/usr.bin/xlint/lint1/msg_378.c
cvs rdiff -u -r1.17 -r1.18 src/tests/usr.bin/xlint/lint1/t_usage.sh
cvs rdiff -u -r1.3 -r1.4 src/usr.bin/xlint/lint1/cksnprintb.c
cvs rdiff -u -r1.228 -r1.229 src/usr.bin/xlint/lint1/err.c
cvs rdiff -u -r1.217 -r1.218 src/usr.bin/xlint/lint1/externs1.h
cvs rdiff -u -r1.607 -r1.608 src/usr.bin/xlint/lint1/tree.c

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

Modified files:

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1308 src/distrib/sets/lists/tests/mi:1.1309
--- src/distrib/sets/lists/tests/mi:1.1308	Sat Mar  2 11:56:37 2024
+++ src/distrib/sets/lists/tests/mi	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1308 2024/03/02 11:56:37 rillig Exp $
+# $NetBSD: mi,v 1.1309 2024/03/03 00:50:41 rillig Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -7491,6 +7491,8 @@
 ./usr/tests/usr.bin/xlint/lint1/msg_374.c			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/xlint/lint1/msg_375.c			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/xlint/lint1/msg_376.c			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/xlint/lint1/msg_377.c			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/xlint/lint1/msg_378.c			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/xlint/lint1/op_colon.c			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/xlint/lint1/op_colon.exp			tests-obsolete		obsolete,atf
 ./usr/tests/usr.bin/xlint/lint1/op_shl_lp64.c			tests-usr.bin-tests	compattestfile,atf

Index: src/tests/usr.bin/xlint/lint1/msg_360.c
diff -u src/tests/usr.bin/xlint/lint1/msg_360.c:1.1 src/tests/usr.bin/xlint/lint1/msg_360.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_360.c:1.1	Fri Mar  1 19:39:28 2024
+++ src/tests/usr.bin/xlint/lint1/msg_360.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_360.c,v 1.1 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: msg_360.c,v 1.2 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_360.c"
 
 // Test for message: missing new-style number base after '\177' [360]
@@ -23,6 +23,6 @@ new_style_number_base(void)
 
 	/* expect+1: warning: missing new-style number base after '\177' [360] */
 	snprintb(buf, sizeof(buf), "\177", 0);
-	/* expect+1: warning: number base '\002' is 2, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "\177\002", 0);
 }
Index: src/tests/usr.bin/xlint/lint1/msg_361.c
diff -u src/tests/usr.bin/xlint/lint1/msg_361.c:1.1 src/tests/usr.bin/xlint/lint1/msg_361.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_361.c:1.1	Fri Mar  1 19:39:28 2024
+++ src/tests/usr.bin/xlint/lint1/msg_361.c	Sun Mar  3 00:50:41 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: msg_361.c,v 1.1 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: msg_361.c,v 1.2 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_361.c"
 
-// Test for message: number base '%.*s' is %ju, should be 8, 10 or 16 [361]
+// Test for message: number base '%.*s' is %ju, must be 8, 10 or 16 [361]
 
 /*
  * The first or second character of the snprintb format specifies the number
@@ -22,12 +22,12 @@ old_style_number_base(void)
 
 	/* expect+1: warning: missing new-style '\177' or old-style number base [359] */
 	snprintb(buf, sizeof(buf), "", 0);
-	/* expect+1: warning: number base '\002' is 2, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "\002", 0);
 	snprintb(buf, sizeof(buf), "\010", 0);
 	snprintb(buf, sizeof(buf), "\n", 0);
 	snprintb(buf, sizeof(buf), "\020", 0);
-	/* expect+1: warning: number base '\014' is 12, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\014' is 12, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "" "\014" "", 0);
 	snprintb(buf, sizeof(buf), "" "\020" "", 0);
 }
@@ -39,14 +39,14 @@ new_style_number_base(void)
 
 	/* expect+1: warning: missing new-style number base after '\177' [360] */
 	snprintb(buf, sizeof(buf), "\177", 0);
-	/* expect+1: warning: number base '\0' is 0, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\0' is 0, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "\177\0", 0);
-	/* expect+1: warning: number base '\002' is 2, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "\177\002", 0);
 	snprintb(buf, sizeof(buf), "\177\010", 0);
 	snprintb(buf, sizeof(buf), "\177\n", 0);
 	snprintb(buf, sizeof(buf), "\177\020", 0);
-	/* expect+1: warning: number base '\014' is 12, should be 8, 10 or 16 [361] */
+	/* expect+1: warning: number base '\014' is 12, must be 8, 10 or 16 [361] */
 	snprintb(buf, sizeof(buf), "" "\177\014" "", 0);
 	snprintb(buf, sizeof(buf), "" "\177\020" "", 0);
 }
Index: src/tests/usr.bin/xlint/lint1/msg_366.c
diff -u src/tests/usr.bin/xlint/lint1/msg_366.c:1.1 src/tests/usr.bin/xlint/lint1/msg_366.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_366.c:1.1	Fri Mar  1 19:39:28 2024
+++ src/tests/usr.bin/xlint/lint1/msg_366.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_366.c,v 1.1 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: msg_366.c,v 1.2 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_366.c"
 
 // Test for message: missing '\0' at the end of '%.*s' [366]
@@ -24,7 +24,7 @@ example(unsigned u32)
 {
 	char buf[64];
 
-	/* expect+4: warning: unknown directive '\0' [374] */
+	/* expect+4: warning: redundant '\0' at the end of new-style format [377] */
 	snprintb(buf, sizeof(buf),
 	    "\177\020"
 	    "\0",
Index: src/tests/usr.bin/xlint/lint1/msg_374.c
diff -u src/tests/usr.bin/xlint/lint1/msg_374.c:1.1 src/tests/usr.bin/xlint/lint1/msg_374.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_374.c:1.1	Fri Mar  1 19:39:29 2024
+++ src/tests/usr.bin/xlint/lint1/msg_374.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_374.c,v 1.1 2024/03/01 19:39:29 rillig Exp $	*/
+/*	$NetBSD: msg_374.c,v 1.2 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_374.c"
 
 // Test for message: unknown directive '%.*s' [374]
@@ -31,4 +31,11 @@ example(uint64_t u64)
 	    "\177\020"
 	    "\00012345\0",
 	    u64);
+
+	/* expect+5: warning: redundant '\0' at the end of new-style format [377] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "b\00012345\0"
+	    "\0",
+	    u64);
 }
Index: src/tests/usr.bin/xlint/lint1/msg_376.c
diff -u src/tests/usr.bin/xlint/lint1/msg_376.c:1.1 src/tests/usr.bin/xlint/lint1/msg_376.c:1.2
--- src/tests/usr.bin/xlint/lint1/msg_376.c:1.1	Sat Mar  2 11:56:37 2024
+++ src/tests/usr.bin/xlint/lint1/msg_376.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg_376.c,v 1.1 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: msg_376.c,v 1.2 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_376.c"
 
 // Test for message: '%.*s' overlaps earlier '%.*s' on bit %u [376]
@@ -32,7 +32,7 @@ example(unsigned u32, uint64_t u64)
 	    "\x21oob",
 	    u32);
 
-	// In the new-style format, bit positions are 1-based.
+	// In the new-style format, bit positions are 0-based.
 	/* expect+10: warning: 'b\x00lsb\0' overlaps earlier 'b\000lsb\0' on bit 0 [376] */
 	/* expect+9: warning: 'b\x3fmsb\0' overlaps earlier 'b\077msb\0' on bit 63 [376] */
 	/* expect+8: warning: bit position '\x40' (64) in 'b\x40oob\0' out of range 0..63 [371] */

Index: src/tests/usr.bin/xlint/lint1/msg_375.c
diff -u src/tests/usr.bin/xlint/lint1/msg_375.c:1.2 src/tests/usr.bin/xlint/lint1/msg_375.c:1.3
--- src/tests/usr.bin/xlint/lint1/msg_375.c:1.2	Sat Mar  2 11:56:37 2024
+++ src/tests/usr.bin/xlint/lint1/msg_375.c	Sun Mar  3 00:50:41 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: msg_375.c,v 1.2 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: msg_375.c,v 1.3 2024/03/03 00:50:41 rillig Exp $	*/
 # 3 "msg_375.c"
 
-// Test for message: comparison value '%.*s' (%ju) exceeds field width %ju [375]
+// Test for message: comparison value '%.*s' (%ju) exceeds maximum field value %ju [375]
 
 /*
  * When a bit field can take the values 0 to 15, there is no point comparing
@@ -20,10 +20,10 @@ example(uint64_t u64)
 {
 	char buf[64];
 
-	/* expect+14: warning: comparison value '\020' (16) exceeds field width 4 [375] */
-	/* expect+13: warning: comparison value '\377' (255) exceeds field width 4 [375] */
-	/* expect+12: warning: comparison value '\020' (16) exceeds field width 4 [375] */
-	/* expect+11: warning: comparison value '\377' (255) exceeds field width 4 [375] */
+	/* expect+14: warning: comparison value '\020' (16) exceeds maximum field value 15 [375] */
+	/* expect+13: warning: comparison value '\377' (255) exceeds maximum field value 15 [375] */
+	/* expect+12: warning: comparison value '\020' (16) exceeds maximum field value 15 [375] */
+	/* expect+11: warning: comparison value '\377' (255) exceeds maximum field value 15 [375] */
 	snprintb(buf, sizeof(buf),
 	    "\177\020"
 	    "f\000\004low\0"

Index: src/tests/usr.bin/xlint/lint1/t_usage.sh
diff -u src/tests/usr.bin/xlint/lint1/t_usage.sh:1.17 src/tests/usr.bin/xlint/lint1/t_usage.sh:1.18
--- src/tests/usr.bin/xlint/lint1/t_usage.sh:1.17	Sat Mar  2 11:56:37 2024
+++ src/tests/usr.bin/xlint/lint1/t_usage.sh	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-# $NetBSD: t_usage.sh,v 1.17 2024/03/02 11:56:37 rillig Exp $
+# $NetBSD: t_usage.sh,v 1.18 2024/03/03 00:50:41 rillig Exp $
 #
 # Copyright (c) 2023 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -39,13 +39,13 @@ suppress_messages_body()
 
 	# The largest known message.
 	atf_check \
-	    "$lint1" -X 376 code.c /dev/null
+	    "$lint1" -X 378 code.c /dev/null
 
 	# Larger than the largest known message.
 	atf_check \
 	    -s 'exit:1' \
-	    -e "inline:lint1: invalid message ID '377'\n" \
-	    "$lint1" -X 377 code.c /dev/null
+	    -e "inline:lint1: invalid message ID '379'\n" \
+	    "$lint1" -X 379 code.c /dev/null
 
 	# Whitespace is not allowed before a message ID.
 	atf_check \

Index: src/usr.bin/xlint/lint1/cksnprintb.c
diff -u src/usr.bin/xlint/lint1/cksnprintb.c:1.3 src/usr.bin/xlint/lint1/cksnprintb.c:1.4
--- src/usr.bin/xlint/lint1/cksnprintb.c:1.3	Sat Mar  2 11:56:37 2024
+++ src/usr.bin/xlint/lint1/cksnprintb.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: cksnprintb.c,v 1.3 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: cksnprintb.c,v 1.4 2024/03/03 00:50:41 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.3 2024/03/02 11:56:37 rillig Exp $");
+__RCSID("$NetBSD: cksnprintb.c,v 1.4 2024/03/03 00:50:41 rillig Exp $");
 #endif
 
 #include <stdbool.h>
@@ -46,10 +46,11 @@ __RCSID("$NetBSD: cksnprintb.c,v 1.3 202
 typedef struct {
 	bool new_style;
 	const buffer *fmt;
+	const tnode_t *value;
 	uint64_t field_width;
 	uint64_t covered;
-	size_t covered_start[64];
-	size_t covered_end[64];
+	unsigned covered_start[64];
+	unsigned covered_end[64];
 } checker;
 
 static bool
@@ -133,9 +134,9 @@ static void
 check_overlap(checker *ck, uint64_t dir_lsb, uint64_t width,
 	      size_t start, size_t end)
 {
-	if (dir_lsb >= 64 || width == 0 || width > 64)
-		return;
 	unsigned lsb = (unsigned)(ck->new_style ? dir_lsb : dir_lsb - 1);
+	if (lsb >= 64 || width == 0 || width > 64)
+		return;
 
 	uint64_t field_mask = value_bits((unsigned)width) << lsb;
 	uint64_t overlap = ck->covered & field_mask;
@@ -158,12 +159,27 @@ done:
 	ck->covered |= field_mask;
 	for (unsigned i = lsb; i < 64; i++) {
 		if (field_mask & bit(i)) {
-			ck->covered_start[i] = start;
-			ck->covered_end[i] = end;
+			ck->covered_start[i] = (unsigned)start;
+			ck->covered_end[i] = (unsigned)end;
 		}
 	}
 }
 
+static void
+check_reachable(checker *ck, uint64_t dir_lsb, uint64_t width,
+		size_t start, size_t end)
+{
+	unsigned lsb = (unsigned)(ck->new_style ? dir_lsb : dir_lsb - 1);
+	if (lsb >= 64 || width == 0 || width > 64)
+		return;
+
+	uint64_t field_mask = value_bits((unsigned)width) << lsb;
+	if (!(possible_bits(ck->value) & field_mask)) {
+		/* directive '%.*s' is unreachable by input value */
+		warning(378, (int)(end - start), ck->fmt->data + start);
+	}
+}
+
 static bool
 check_directive(const buffer *fmt, quoted_iterator *it, bool new_style,
 		checker *ck)
@@ -177,7 +193,7 @@ check_directive(const buffer *fmt, quote
 	    || dir.value == 'b' || dir.value == 'f' || dir.value == 'F';
 	if (has_bit && new_style && !quoted_next(fmt, it)) {
 		/* missing bit position after '%.*s' */
-		warning(364, len(dir), start(dir, fmt));
+		warning(364, range(dir, *it), start(dir, fmt));
 		return false;
 	}
 	/* LINTED 86 "automatic 'bit' hides external declaration" */
@@ -187,7 +203,7 @@ check_directive(const buffer *fmt, quote
 	    && (dir.value == 'f' || dir.value == 'F');
 	if (has_width && !quoted_next(fmt, it)) {
 		/* missing field width after '%.*s' */
-		warning(365, range(dir, bit), start(dir, fmt));
+		warning(365, range(dir, *it), start(dir, fmt));
 		return false;
 	}
 	quoted_iterator width = *it;
@@ -196,7 +212,7 @@ check_directive(const buffer *fmt, quote
 	    && (dir.value == '=' || dir.value == ':');
 	if (has_cmp && !quoted_next(fmt, it)) {
 		/* missing comparison value after directive '%.*s' */
-		warning(368, range(dir, bit), start(dir, fmt));
+		warning(368, range(dir, *it), start(dir, fmt));
 		return false;
 	}
 	quoted_iterator cmp = *it;
@@ -208,6 +224,15 @@ check_directive(const buffer *fmt, quote
 		return false;
 	}
 
+	if (new_style && dir.value == '\0') {
+		quoted_iterator end = *it;
+		if (!quoted_next(fmt, &end)) {
+			/* redundant '\0' at the end of new-style format */
+			warning(377);
+			return false;
+		}
+	}
+
 	if (!has_bit && !has_cmp && !has_default) {
 		/* unknown directive '%.*s' */
 		warning(374, len(dir), start(dir, fmt));
@@ -288,14 +313,18 @@ check_directive(const buffer *fmt, quote
 	}
 	if (has_cmp && ck->field_width < 64
 	    && cmp.value & ~(uint64_t)0 << ck->field_width) {
-		/* comparison value '%.*s' (%ju) exceeds field width %ju */
+		/* comparison value '%.*s' (%ju) exceeds maximum field ... */
 		warning(375, len(cmp), start(cmp, fmt), val(cmp),
-		    (uintmax_t)ck->field_width);
+		    (uintmax_t)value_bits((unsigned)ck->field_width));
 	}
 	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 (descr.i == prev.i && dir.value != 'F') {
 		/* empty description in '%.*s' */
 		warning(367, range(dir, *it), start(dir, fmt));
@@ -327,7 +356,7 @@ check_snprintb(const tnode_t *expr)
 		return;
 	}
 	if (it.value != 8 && it.value != 10 && it.value != 16) {
-		/* number base '%.*s' is %ju, should be 8, 10 or 16 */
+		/* number base '%.*s' is %ju, must be 8, 10 or 16 */
 		warning(361, len(it), start(it, fmt), val(it));
 		return;
 	}
@@ -335,6 +364,7 @@ check_snprintb(const tnode_t *expr)
 	checker ck = {
 		.new_style = new_style,
 		.fmt = fmt,
+		.value = value,
 		.field_width = 64,
 	};
 	while (check_directive(fmt, &it, new_style, &ck))

Index: src/usr.bin/xlint/lint1/err.c
diff -u src/usr.bin/xlint/lint1/err.c:1.228 src/usr.bin/xlint/lint1/err.c:1.229
--- src/usr.bin/xlint/lint1/err.c:1.228	Sat Mar  2 11:56:37 2024
+++ src/usr.bin/xlint/lint1/err.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: err.c,v 1.228 2024/03/02 11:56:37 rillig Exp $	*/
+/*	$NetBSD: err.c,v 1.229 2024/03/03 00:50:41 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.228 2024/03/02 11:56:37 rillig Exp $");
+__RCSID("$NetBSD: err.c,v 1.229 2024/03/03 00:50:41 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -416,7 +416,7 @@ static const char *const msgs[] = {
 	"hex escape '%.*s' has more than 2 digits",			// 358
 	"missing new-style '\\177' or old-style number base",		// 359
 	"missing new-style number base after '\\177'",			// 360
-	"number base '%.*s' is %ju, should be 8, 10 or 16",		// 361
+	"number base '%.*s' is %ju, must be 8, 10 or 16",		// 361
 	"old-style format contains '\\0'",				// 362
 	"non-printing character '%.*s' in description '%.*s'",		// 363
 	"missing bit position after '%.*s'",				// 364
@@ -430,8 +430,10 @@ static const char *const msgs[] = {
 	"field width '%.*s' (%ju) in '%.*s' out of range 0..%u",	// 372
 	"bit field end %ju in '%.*s' out of range 0..64",		// 373
 	"unknown directive '%.*s'",					// 374
-	"comparison value '%.*s' (%ju) exceeds field width %ju",	// 375
+	"comparison value '%.*s' (%ju) exceeds maximum field value %ju", // 375
 	"'%.*s' overlaps earlier '%.*s' on bit %u",			// 376
+	"redundant '\\0' at the end of new-style format",		// 377
+	"directive '%.*s' is unreachable by input value",		// 378
 };
 
 static bool is_suppressed[sizeof(msgs) / sizeof(msgs[0])];

Index: src/usr.bin/xlint/lint1/externs1.h
diff -u src/usr.bin/xlint/lint1/externs1.h:1.217 src/usr.bin/xlint/lint1/externs1.h:1.218
--- src/usr.bin/xlint/lint1/externs1.h:1.217	Fri Mar  1 19:39:28 2024
+++ src/usr.bin/xlint/lint1/externs1.h	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: externs1.h,v 1.217 2024/03/01 19:39:28 rillig Exp $	*/
+/*	$NetBSD: externs1.h,v 1.218 2024/03/03 00:50:41 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -301,6 +301,7 @@ bool constant_addr(const tnode_t *, cons
 buffer *cat_strings(buffer *, buffer *);
 unsigned int type_size_in_bits(const type_t *);
 sym_t *find_member(const struct_or_union *, const char *);
+uint64_t possible_bits(const tnode_t *);
 
 void begin_statement_expr(void);
 void do_statement_expr(tnode_t *);

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.607 src/usr.bin/xlint/lint1/tree.c:1.608
--- src/usr.bin/xlint/lint1/tree.c:1.607	Fri Mar  1 21:52:48 2024
+++ src/usr.bin/xlint/lint1/tree.c	Sun Mar  3 00:50:41 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.607 2024/03/01 21:52:48 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.608 2024/03/03 00:50:41 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID)
-__RCSID("$NetBSD: tree.c,v 1.607 2024/03/01 21:52:48 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.608 2024/03/03 00:50:41 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -317,6 +317,12 @@ ic_expr(const tnode_t *tn)
 	}
 }
 
+uint64_t
+possible_bits(const tnode_t *tn)
+{
+	return ~ic_expr(tn).bclr;
+}
+
 /* Build 'pointer to tp', 'array of tp' or 'function returning tp'. */
 type_t *
 block_derive_type(type_t *tp, tspec_t t)

Added files:

Index: src/tests/usr.bin/xlint/lint1/msg_377.c
diff -u /dev/null src/tests/usr.bin/xlint/lint1/msg_377.c:1.1
--- /dev/null	Sun Mar  3 00:50:42 2024
+++ src/tests/usr.bin/xlint/lint1/msg_377.c	Sun Mar  3 00:50:41 2024
@@ -0,0 +1,38 @@
+/*	$NetBSD: msg_377.c,v 1.1 2024/03/03 00:50:41 rillig Exp $	*/
+# 3 "msg_377.c"
+
+// Test for message: redundant '\0' at the end of new-style format [377]
+
+/*
+ * Each directive in the new-style format ends with a '\0'. The final '\0'
+ * that ends the whole format is provided implicitly by the compiler as part
+ * of the string literal.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char*, size_t, const char*, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+	char buf[64];
+
+	/* expect+6: warning: old-style format contains '\0' [362] */
+	/* expect+5: warning: empty description in '\0' [367] */
+	snprintb(buf, sizeof(buf),
+	    "\020"
+	    "\005bit"
+	    "\0",
+	    u32);
+
+	/* expect+5: warning: redundant '\0' at the end of new-style format [377] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "b\005bit\0"
+	    "\0",
+	    u64);
+}
Index: src/tests/usr.bin/xlint/lint1/msg_378.c
diff -u /dev/null src/tests/usr.bin/xlint/lint1/msg_378.c:1.1
--- /dev/null	Sun Mar  3 00:50:42 2024
+++ src/tests/usr.bin/xlint/lint1/msg_378.c	Sun Mar  3 00:50:41 2024
@@ -0,0 +1,52 @@
+/*	$NetBSD: msg_378.c,v 1.1 2024/03/03 00:50:41 rillig Exp $	*/
+# 3 "msg_378.c"
+
+// Test for message: directive '%.*s' is unreachable by input value [378]
+
+/*
+ * The typical use case of snprintb is to have a format that is specifically
+ * tailored to a particular input value.  Often, a format is only used in a
+ * single place.  Therefore, bits that are unreachable are redundant and may
+ * hint at typos.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char*, size_t, const char*, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+	char buf[64];
+
+	/* expect+5: warning: directive '\040bit32' is unreachable by input value [378] */
+	snprintb(buf, sizeof(buf),
+	    "\020"
+	    "\037bit31"
+	    "\040bit32",
+	    u32 >> 1);
+
+	/* expect+5: warning: directive 'b\075bit61\0' is unreachable by input value [378] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "b\074bit60\0"
+	    "b\075bit61\0",
+	    u64 >> 3);
+
+	/* expect+12: warning: directive 'b\000bit0\0' is unreachable by input value [378] */
+	/* expect+11: warning: directive 'b\011bit9\0' is unreachable by input value [378] */
+	/* expect+10: warning: directive 'f\017\002bits15-16\0' is unreachable by input value [378] */
+	/* expect+9: warning: directive 'f\050\030bits40-63\0' is unreachable by input value [378] */
+	snprintb(buf, sizeof(buf),
+	    "\177\020"
+	    "b\000bit0\0"
+	    "b\010bit8\0"
+	    "b\011bit9\0"
+	    "f\012\002bits10-11\0"
+	    "f\017\002bits15-16\0"
+	    "f\050\030bits40-63\0",
+	    (u32 & 0xaa55aa55) << 8);
+}

Reply via email to