Module Name:    src
Committed By:   rillig
Date:           Tue Jan 12 20:42:01 UTC 2021

Modified Files:
        src/tests/usr.bin/xlint/lint1: d_c99_bool_strict.c
            d_c99_bool_strict.exp
        src/usr.bin/xlint/common: externs.h
        src/usr.bin/xlint/lint1: cgram.y err.c externs1.h func.c main1.c op.h
            oper.c ops.def tree.c
        src/usr.bin/xlint/lint2: main2.c
        src/usr.bin/xlint/xlint: lint.1 xlint.c
Added Files:
        src/tests/usr.bin/xlint: check-expect.lua

Log Message:
lint: add new check for strict bool mode

In strict bool mode, bool is considered incompatible with all other
scalar types, just as in Java, C#, Pascal.

The controlling expressions in if statements, while loops, for loops and
the '?:' operator must be of type bool.  The logical operators work on
bool instead of int, the bitwise operators accept both integer and bool.
The arithmetic operators don't accept bool.

Since <stdbool.h> implements bool using C preprocessor macros instead of
predefining the identifiers "true" and "false", the integer constants 0
and 1 may be used in all contexts that require a bool expression.
Except from these, no implicit conversion between bool and scalar types
is allowed.

See usr.bin/tests/xlint/lint1/d_c99_bool_strict.c for more details.

The command line option -T has been chosen because all obvious choices
(-b or -B for bool, -s or -S for strict) are already in use.  The -T may
stand for "types are checked strictly".

The default behavior of lint doesn't change.  The strict bool check is
purely optional.

An example program for strict bool mode is usr.bin/make, which has been
using explicit comparisons such as p != NULL, ch != '\0' or n > 0 in
most places for a long time now, even before the refactoring in 2020.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/xlint/check-expect.lua
cvs rdiff -u -r1.3 -r1.4 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
cvs rdiff -u -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp
cvs rdiff -u -r1.12 -r1.13 src/usr.bin/xlint/common/externs.h
cvs rdiff -u -r1.138 -r1.139 src/usr.bin/xlint/lint1/cgram.y
cvs rdiff -u -r1.65 -r1.66 src/usr.bin/xlint/lint1/err.c
cvs rdiff -u -r1.54 -r1.55 src/usr.bin/xlint/lint1/externs1.h
cvs rdiff -u -r1.56 -r1.57 src/usr.bin/xlint/lint1/func.c
cvs rdiff -u -r1.33 -r1.34 src/usr.bin/xlint/lint1/main1.c
cvs rdiff -u -r1.11 -r1.12 src/usr.bin/xlint/lint1/op.h
cvs rdiff -u -r1.2 -r1.3 src/usr.bin/xlint/lint1/oper.c
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/xlint/lint1/ops.def
cvs rdiff -u -r1.150 -r1.151 src/usr.bin/xlint/lint1/tree.c
cvs rdiff -u -r1.10 -r1.11 src/usr.bin/xlint/lint2/main2.c
cvs rdiff -u -r1.41 -r1.42 src/usr.bin/xlint/xlint/lint.1
cvs rdiff -u -r1.52 -r1.53 src/usr.bin/xlint/xlint/xlint.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/d_c99_bool_strict.c
diff -u src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.3 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.4
--- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.3	Mon Jan 11 00:28:28 2021
+++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c	Tue Jan 12 20:42:01 2021
@@ -1,9 +1,9 @@
-/*	$NetBSD: d_c99_bool_strict.c,v 1.3 2021/01/11 00:28:28 rillig Exp $	*/
+/*	$NetBSD: d_c99_bool_strict.c,v 1.4 2021/01/12 20:42:01 rillig Exp $	*/
 # 3 "d_c99_bool_strict.c"
 
 /*
- * Experimental feature:  allow to treat _Bool as incompatible with all
- * scalar types.  This means:
+ * The option -T treats _Bool as incompatible with all other scalar types.
+ * This means:
  *
  * SB001: Controlling expressions in 'if', 'while', 'for', '?:' must be of
  * type _Bool instead of scalar.
@@ -18,9 +18,9 @@
  *
  * SB005: There is no implicit conversion from _Bool to any other type.
  *
- * SB006: A constant integer expression is compatible with type _Bool if
- * it is an integer constant with value 0 or 1, or if the result type of
- * its main operator is _Bool.
+ * SB006: An expression is compatible with type _Bool if its main operator
+ * returns type _Bool, or if the expression is an integer constant expression
+ * with value 0 or 1.
  *
  * SB007: Expressions like "flags & FLAG" are compatible with _Bool if
  * they appear in a context where they are immediately compared to zero.
@@ -32,7 +32,7 @@
  * typically have type _Bool:1 and can be converted to _Bool and back.
  */
 
-// Not yet implemented: /* lint1-extra-flags: -T */
+/* lint1-extra-flags: -T */
 
 /*
  * The header <stdbool.h> defines the macros bool = _Bool, false = 0 and
@@ -61,23 +61,23 @@ SB001_controlling_expression(bool b, int
 		return;
 
 	/* Not allowed: 2 is not a boolean expression. */
-	if (/*CONSTCOND*/2)
+	if (/*CONSTCOND*/2)	/* expect: 333 */
 		return;
 
 	/* Not allowed: There is no implicit conversion from scalar to bool. */
-	if (i)
+	if (i)			/* expect: 333 */
 		return;
 	if (i != 0)
 		return;
 
 	/* Not allowed: There is no implicit conversion from scalar to bool. */
-	if (d)
+	if (d)			/* expect: 333 */
 		return;
 	if (d != 0.0)
 		return;
 
 	/* Not allowed: There is no implicit conversion from scalar to bool. */
-	if (p)
+	if (p)			/* expect: 333 */
 		return;
 	if (p != (void *)0)
 		return;
@@ -88,15 +88,15 @@ SB001_controlling_expression(bool b, int
 }
 
 void
-SB002_operator_result(bool b)
+SB002_operator_result_type(bool b)
 {
 	b = b;
-	char c = b;
-	int i = b;
-	double d = b;
-	void *p = b;
+	char c = b;		/* expect: 107 */
+	int i = b;		/* expect: 107 */
+	double d = b;		/* expect: 107 */
+	void *p = b;		/* expect: 107 */
 
-	/* These assignments are all ok. */
+	/* The right-hand sides of these assignments are all ok. */
 	b = !b;
 	b = i == i;
 	b = i != i;
@@ -108,59 +108,70 @@ SB002_operator_result(bool b)
 	b = b || b;
 
 	/*
-	 * These assignments are not ok, they implicitly convert from bool
-	 * to int.
+	 * The right-hand sides of these assignments are not ok, they
+	 * implicitly convert from bool to int.
 	 */
-	i = !b;
-	i = i == i;
-	i = i != i;
-	i = i < i;
-	i = i <= i;
-	i = i >= i;
-	i = i > i;
-	i = b && b;
-	i = b || b;
+	i = !b;			/* expect: 107 */
+	i = i == i;		/* expect: 107 */
+	i = i != i;		/* expect: 107 */
+	i = i < i;		/* expect: 107 */
+	i = i <= i;		/* expect: 107 */
+	i = i >= i;		/* expect: 107 */
+	i = i > i;		/* expect: 107 */
+	i = b && b;		/* expect: 107 */
+	i = b || b;		/* expect: 107 */
 }
 
-void
+int
 SB003_operands(bool b, int i)
 {
 
-	/* These assignments are ok. */
+	/* The right-hand sides of these assignments are ok. */
 	b = !b;
 	b = b && b;
 	b = b || b;
 
-	/* These assignments implicitly convert from scalar to bool. */
-	b = !i;
-	b = i && i;
-	b = i || i;
+	/*
+	 * The right-hand sides of these assignments implicitly convert from
+	 * scalar to bool.
+	 */
+	b = !i;			/* expect: 330 */
+	b = i && i;		/* expect: 331, 332 */
+	b = i || i;		/* expect: 331, 332 */
+
+	b = b && 0;
+	b = 0 && b;
+	b = b || 0;
+	b = 0 || b;
+
+	return i;
 }
 
+/*ARGSUSED*/
 void
-SB004_non_bool_operands(bool b, unsigned u)
+SB004_operators_and_bool_operands(bool b, unsigned u)
 {
 	b = !b;			/* ok */
-	b = ~b;			/* not ok */
-	++b;			/* not ok */
-	--b;			/* not ok */
-	b++;			/* not ok */
-	b--;			/* not ok */
-	b = +b;			/* not ok */
-	b = -b;			/* not ok */
-
-	b = b * b;		/* not ok */
-	b = b / b;		/* not ok */
-	b = b % b;		/* not ok */
-	b = b + b;		/* not ok */
-	b = b - b;		/* not ok */
-	b = b << b;		/* not ok */
-	b = b >> b;		/* not ok */
-
-	b = b < b;		/* not ok */
-	b = b <= b;		/* not ok */
-	b = b > b;		/* not ok */
-	b = b >= b;		/* not ok */
+	b = ~b;			/* expect: 335 */
+	++b;			/* expect: 335 */
+	--b;			/* expect: 335 */
+	b++;			/* expect: 335 */
+	b--;			/* expect: 335 */
+	b = +b;			/* expect: 335 */
+	b = -b;			/* expect: 335 */
+
+	b = b * b;		/* expect: 336, 337 */
+	b = b / b;		/* expect: 336, 337 */
+	b = b % b;		/* expect: 336, 337 */
+	b = b + b;		/* expect: 336, 337 */
+	b = b - b;		/* expect: 336, 337 */
+	b = b << b;		/* expect: 336, 337 */
+	b = b >> b;		/* expect: 336, 337 */
+
+	b = b < b;		/* expect: 336, 337 */
+	b = b <= b;		/* expect: 336, 337 */
+	b = b > b;		/* expect: 336, 337 */
+	b = b >= b;		/* expect: 336, 337 */
 	b = b == b;		/* ok */
 	b = b != b;		/* ok */
 
@@ -172,37 +183,38 @@ SB004_non_bool_operands(bool b, unsigned
 	b = b ? b : b;		/* ok */
 
 	b = b;			/* ok */
-	b *= b;			/* not ok */
-	b /= b;			/* not ok */
-	b %= b;			/* not ok */
-	b += b;			/* not ok */
-	b -= b;			/* not ok */
-	b <<= b;		/* not ok */
-	b >>= b;		/* not ok */
+	b *= b;			/* expect: 336, 337 */
+	b /= b;			/* expect: 336, 337 */
+	b %= b;			/* expect: 336, 337 */
+	b += b;			/* expect: 336, 337 */
+	b -= b;			/* expect: 336, 337 */
+	b <<= b;		/* expect: 336, 337 */
+	b >>= b;		/* expect: 336, 337 */
 	b &= b;			/* ok */
 	b ^= b;			/* ok */
 	b |= b;			/* ok */
 
 	/* Operations with mixed types. */
-	u = b * u;		/* not ok */
-	u = u * b;		/* not ok */
-	u = b / u;		/* not ok */
-	u = u / b;		/* not ok */
-	u = b % u;		/* not ok */
-	u = u % b;		/* not ok */
-	u = b + u;		/* not ok */
-	u = u + b;		/* not ok */
-	u = b - u;		/* not ok */
-	u = u - b;		/* not ok */
-	u = b << u;		/* not ok */
-	u = u << b;		/* not ok */
-	u = b >> u;		/* not ok */
-	u = u >> b;		/* not ok */
+	u = b * u;		/* expect: 336 */
+	u = u * b;		/* expect: 337 */
+	u = b / u;		/* expect: 336 */
+	u = u / b;		/* expect: 337 */
+	u = b % u;		/* expect: 336 */
+	u = u % b;		/* expect: 337 */
+	u = b + u;		/* expect: 336 */
+	u = u + b;		/* expect: 337 */
+	u = b - u;		/* expect: 336 */
+	u = u - b;		/* expect: 337 */
+	u = b << u;		/* expect: 336 */
+	u = u << b;		/* expect: 337 */
+	u = b >> u;		/* expect: 336 */
+	u = u >> b;		/* expect: 337 */
 	u = b ? u : u;		/* ok */
-	u = b ? b : u;		/* not ok */
-	u = b ? u : b;		/* not ok */
+	u = b ? b : u;		/* expect: 107 */
+	u = b ? u : b;		/* expect: 107 */
 }
 
+/*ARGSUSED*/
 void
 SB005_convert_from_bool_to_scalar(bool b)
 {
@@ -211,10 +223,10 @@ SB005_convert_from_bool_to_scalar(bool b
 	double d;
 	void *p;
 
-	i = b;			/* not ok */
-	u = b;			/* not ok */
-	d = b;			/* not ok */
-	p = b;			/* not ok */
+	i = b;			/* expect: 107 */
+	u = b;			/* expect: 107 */
+	d = b;			/* expect: 107 */
+	p = b;			/* expect: 107 */
 }
 
 enum SB006_bool_constant_expression {
@@ -225,27 +237,32 @@ enum SB006_bool_constant_expression {
 	INT1 = 1 ? 100 : 101,
 
 	/* Not ok: 2 is not a boolean constant (neither 0 nor 1). */
-	INT2 = 2 ? 100 : 101,
+	INT2 = 2 ? 100 : 101,	/* expect: 331 */
 
 	/*
-	 * Not ok: the intermediate expression "2 - 2" has return type
-	 * scalar, not bool.  It is irrelevant that the final result
-	 * is 0, which would be a boolean constant.
+	 * The intermediate expression "2" has type int, which is not
+	 * compatible with _Bool.  The expression "2 - 2" is an integer
+	 * constant expression with value 0 and is thus a bool constant
+	 * expression.  This particular case probably does not occur in
+	 * practice.
 	 */
 	ARITH = (2 - 2) ? 100 : 101,
 
 	/*
-	 * Ok: The 13 and 12 are not boolean expressions, but they
-	 * are not in the calculation path that leads to the final
-	 * result.  The important point is that the operator '>' has
-	 * return type bool.
+	 * These two variants of an expression can occur when a preprocessor
+	 * macro is either defined to 1 or left empty, as in lint1/ops.def.
 	 */
+	BINARY_PLUS = (1 + 0) ? 100 : 101,
+	UNARY_PLUS = (+0) ? 100 : 101,
+
+	/* The main operator '>' has return type bool. */
 	Q1 = (13 > 12) ? 100 : 101,
 
 	/*
-	 * Not ok: The 7 is irrelevant for the final result of the
-	 * expression, yet it turns the result type of the operator
-	 * '?:' to be int, not bool.
+	 * The 7 is part of the integer constant expression, but it is
+	 * irrelevant for the final result.  The expression in parentheses
+	 * is an integer constant expression with value 1 and thus is a
+	 * bool constant expression.
 	 */
 	Q2 = (13 > 12 ? 1 : 7) ? 100 : 101,
 
@@ -277,7 +294,8 @@ enum Flags {
  * The usual way to query one of the flags is demonstrated below.
  */
 
-extern void println(const char *);
+extern void
+println(const char *);
 
 void
 query_flag_from_enum_bit_set(enum Flags flags)
@@ -291,6 +309,7 @@ query_flag_from_enum_bit_set(enum Flags 
 
 	if ((flags & (FLAG0 | FLAG1)) == (FLAG0 | FLAG1))
 		println("FLAG0 and FLAG1 are both set");
+
 	if (flags & FLAG0 && flags & FLAG1)
 		println("FLAG0 and FLAG1 are both set");
 
@@ -306,52 +325,53 @@ query_flag_from_enum_bit_set(enum Flags 
  * calls them), the result of the operator '&' is compared against 0.  This
  * makes this pattern work, no matter whether the bits are in the low-value
  * range or in the high-value range (such as FLAG28, which has the value
- * 1073741824, which is more than what fits into an unsigned char).  Even
- * if an enum could be extended to larger types than int, this pattern
+ * 1073741824, which is more than what would fit into an unsigned char).
+ * Even if an enum could be extended to larger types than int, this pattern
  * would work.
  */
 
 /*
  * There is a crucial difference between a _Bool variable and an ordinary
- * integer variable though.  C99 6.3.1.2 defines a conversion from an
- * arbitrary scalar type to _Bool as (value != 0 ? 1 : 0).  This means that
- * even if _Bool is implemented as an 8-bit unsigned integer, assigning 256
- * to it would still result in the value 1 being stored.  Storing 256 in an
- * ordinary 8-bit unsigned integer would result in the value 0 being stored.
- * See the test d_c99_bool.c for more details.
+ * integer variable.  C99 6.3.1.2 defines a conversion from an arbitrary
+ * scalar value to _Bool as equivalent to (value != 0 ? 1 : 0).  This means
+ * that even if _Bool is implemented as an 8-bit unsigned integer, assigning
+ * 256 to it would still result in the value 1 being stored.  Storing 256 in
+ * an ordinary 8-bit unsigned integer would result in the value 0 being
+ * stored.  See the test d_c99_bool.c for more details.
  *
  * Because of this, expressions like (flags & FLAG28) are only allowed in
  * bool context if they are guaranteed not to be truncated, even if the
  * result were to be stored in a plain unsigned integer.
  */
 
+/*ARGSUSED*/
 void
 SB007_allow_flag_test_on_bit_set_enums(enum Flags flags)
 {
 	bool b;
 
 	/*
-	 * FLAG0 has the value 1 and can therefore be stored in a bool
-	 * variable without truncation.  Nevertheless this special case
-	 * is not allowed because it would be too confusing if FLAG0 would
-	 * work and all the other flags wouldn't.
+	 * FLAG0 has the value 1 and thus can be stored in a bool variable
+	 * without truncation.  Nevertheless this special case is not allowed
+	 * because it would be too confusing if FLAG0 would work and all the
+	 * other flags wouldn't.
 	 */
-	b = flags & FLAG0;
+	b = flags & FLAG0;	/* expect: 107 */
 
 	/*
 	 * Assuming that FLAG1 is set in flags, a _Bool variable stores this
-	 * as 1, as defined by C99 6.3.1.2.  An unsigned char variable would
-	 * store it as 2, as that is the integer value of FLAG1.  Since FLAG1
-	 * fits in an unsigned char, no truncation takes place.
+	 * as 1, as defined by C99 6.3.1.2.  A uint8_t variable would store
+	 * it as 2, as that is the integer value of FLAG1.  Since FLAG1 fits
+	 * in a uint8_t, no truncation takes place.
 	 */
-	b = flags & FLAG1;
+	b = flags & FLAG1;	/* expect: 107 */
 
 	/*
-	 * In a _Bool variable, FLAG28 is stored as 1, as above.  In an
-	 * unsigned char, the stored value would be 0 since bit 28 is out of
-	 * range for an unsigned char, which usually has 8 significant bits.
+	 * In a _Bool variable, FLAG28 is stored as 1, since it is unequal to
+	 * zero.  In a uint8_t, the stored value would be 0 since bit 28 is
+	 * out of range for a uint8_t and thus gets truncated.
 	 */
-	b = flags & FLAG28;
+	b = flags & FLAG28;	/* expect: 107 */
 }
 
 /* A bool bit field is compatible with bool. Other bit fields are not. */
@@ -361,40 +381,64 @@ struct flags {
 	unsigned uint_flag: 1;
 };
 
+/*ARGSUSED*/
 void
-SB008_flags_from_bit_fields(const struct flags *flags)
+SB008_bit_fields(const struct flags *flags)
 {
 	bool b;
 
 	b = flags->bool_flag;	/* ok */
-	b = flags->uint_flag;	/* not ok */
+	b = flags->uint_flag;	/* expect: 107 */
+	flags->bool_flag = b;
+	flags->uint_flag = b;	/* expect: 107 */
 }
 
 /* Test implicit conversion when returning a value from a function. */
 
+/*ARGSUSED*/
 bool
 returning_bool(bool b, int i, const char *p)
 {
 	if (i > 0)
 		return b;	/* ok */
 	if (i < 0)
-		return i;	/* not ok */
-	return p;		/* not ok */
+		return i;	/* expect: 211 */
+	return p;		/* expect: 211 */
 }
 
+/*ARGSUSED*/
 char
 returning_char(bool b, int i, const char *p)
 {
 	if (i > 0)
-		return b;	/* not ok */
+		return b;	/* expect: 211 */
 	if (i < 0)
-		return i;	/* not ok, but not related to bool */
-	return p;		/* not ok */
+		return i;	/* XXX: narrowing conversion */
+	return p;		/* expect: 183 */
+}
+
+bool
+return_constant_false(void)
+{
+	return 0;
+}
+
+bool
+return_constant_true(void)
+{
+	return 1;
+}
+
+bool
+return_invalid_integer(void)
+{
+	return 2;		/* expect: 211 */
 }
 
 /* Test passing arguments to a function. */
 
-extern void taking_arguments(bool, int, const char *, ...);
+extern void
+taking_arguments(bool, int, const char *, ...);
 
 void
 passing_arguments(bool b, int i, const char *p)
@@ -403,14 +447,77 @@ passing_arguments(bool b, int i, const c
 	taking_arguments(b, i, p);
 
 	/* Implicitly converting bool to other scalar types. */
-	taking_arguments(b, b, b);
+	taking_arguments(b, b, b);	/* expect: 334, 334 */
 
 	/* Implicitly converting int to bool (arg #1). */
-	taking_arguments(i, i, i);
+	taking_arguments(i, i, i);	/* expect: 334, 154 */
 
 	/* Implicitly converting pointer to bool (arg #1). */
-	taking_arguments(p, p, p);
+	taking_arguments(p, p, p);	/* expect: 334, 154 */
 
 	/* Passing bool as vararg. */
-	taking_arguments(b, i, p, b, i, p);
+	taking_arguments(b, i, p, b, i, p); /* expect: arg#4 */ // TODO
+
+	/* Passing a bool constant. */
+	taking_arguments(0, i, p);
+
+	/* Passing a bool constant. */
+	taking_arguments(1, i, p);
+
+	/* Trying to pass an invalid integer. */
+	taking_arguments(2, i, p);	/* expect: 334 */
+}
+
+/*
+ * This is just normal access to a bool member of a struct, to ensure that
+ * these produce no errors.
+ */
+void
+struct_access_operators(void)
+{
+	struct bool_struct {
+		bool b;
+	};
+
+	/* Initialize and assign using boolean constants. */
+	bool b = 0;
+	b = 1;
+
+	/* Access a struct member using the '.' operator. */
+	struct bool_struct bs = { 1 };
+	b = bs.b;
+	bs.b = b;
+	bs.b = 0;
+
+	/* Access a struct member using the '->' operator. */
+	struct bool_struct *bsp = &bs;
+	b = bsp->b;
+	bsp->b = b;
+	bsp->b = 0;
+
+	/* Taking the address of a bool lvalue. */
+	bool *bp;
+	bp = &b;
+	*bp = b;
+	b = *bp;
+}
+
+/*
+ * Comparing a _Bool expression to 0 or 1 is redundant.  It may come from
+ * an earlier version of the code, before it got migrated to using _Bool.
+ * Usually, bool expressions are used directly as control expressions or
+ * as argument to the boolean operators such as '!', '&&', '||'.
+ *
+ * Since lint steps in after the C preprocessor, it has no chance of seeing
+ * the original source code, which may well have been "b == false" instead
+ * of "b == 0".
+ */
+bool
+compare_var_with_constant(bool b)
+{
+	bool t1 = b == 0;
+	bool t2 = t1 != 0;
+	bool t3 = t2 == 1;
+	bool t4 = t3 != 1;
+	return t4 ^ t3;
 }

Index: src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp
diff -u src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp:1.2 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp:1.3
--- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp:1.2	Mon Jan 11 00:28:28 2021
+++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp	Tue Jan 12 20:42:01 2021
@@ -1,6 +1,103 @@
-d_c99_bool_strict.c(97): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
-d_c99_bool_strict.c(217): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
-d_c99_bool_strict.c(392): warning: illegal combination of integer (char) and pointer (pointer to const char) [183]
-d_c99_bool_strict.c(406): warning: illegal combination of pointer (pointer to const char) and integer (_Bool), arg #3 [154]
-d_c99_bool_strict.c(409): warning: illegal combination of pointer (pointer to const char) and integer (int), arg #3 [154]
-d_c99_bool_strict.c(412): warning: illegal combination of integer (int) and pointer (pointer to const char), arg #2 [154]
+d_c99_bool_strict.c(64): controlling expression must be bool, not 'int' [333]
+d_c99_bool_strict.c(68): controlling expression must be bool, not 'int' [333]
+d_c99_bool_strict.c(74): controlling expression must be bool, not 'double' [333]
+d_c99_bool_strict.c(80): controlling expression must be bool, not 'pointer' [333]
+d_c99_bool_strict.c(94): operands of '=' have incompatible types (char != _Bool) [107]
+d_c99_bool_strict.c(95): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(96): operands of '=' have incompatible types (double != _Bool) [107]
+d_c99_bool_strict.c(97): operands of '=' have incompatible types (pointer != _Bool) [107]
+d_c99_bool_strict.c(114): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(115): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(116): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(117): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(118): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(119): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(120): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(121): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(122): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(138): operand of '!' must be bool, not 'int' [330]
+d_c99_bool_strict.c(139): left operand of '&&' must be bool, not 'int' [331]
+d_c99_bool_strict.c(139): right operand of '&&' must be bool, not 'int' [332]
+d_c99_bool_strict.c(140): left operand of '||' must be bool, not 'int' [331]
+d_c99_bool_strict.c(140): right operand of '||' must be bool, not 'int' [332]
+d_c99_bool_strict.c(155): operand of '~' must not be bool [335]
+d_c99_bool_strict.c(156): operand of '++x' must not be bool [335]
+d_c99_bool_strict.c(157): operand of '--x' must not be bool [335]
+d_c99_bool_strict.c(158): operand of 'x++' must not be bool [335]
+d_c99_bool_strict.c(159): operand of 'x--' must not be bool [335]
+d_c99_bool_strict.c(160): operand of '+' must not be bool [335]
+d_c99_bool_strict.c(161): operand of '-' must not be bool [335]
+d_c99_bool_strict.c(163): left operand of '*' must not be bool [336]
+d_c99_bool_strict.c(163): right operand of '*' must not be bool [337]
+d_c99_bool_strict.c(164): left operand of '/' must not be bool [336]
+d_c99_bool_strict.c(164): right operand of '/' must not be bool [337]
+d_c99_bool_strict.c(165): left operand of '%' must not be bool [336]
+d_c99_bool_strict.c(165): right operand of '%' must not be bool [337]
+d_c99_bool_strict.c(166): left operand of '+' must not be bool [336]
+d_c99_bool_strict.c(166): right operand of '+' must not be bool [337]
+d_c99_bool_strict.c(167): left operand of '-' must not be bool [336]
+d_c99_bool_strict.c(167): right operand of '-' must not be bool [337]
+d_c99_bool_strict.c(168): left operand of '<<' must not be bool [336]
+d_c99_bool_strict.c(168): right operand of '<<' must not be bool [337]
+d_c99_bool_strict.c(169): left operand of '>>' must not be bool [336]
+d_c99_bool_strict.c(169): right operand of '>>' must not be bool [337]
+d_c99_bool_strict.c(171): left operand of '<' must not be bool [336]
+d_c99_bool_strict.c(171): right operand of '<' must not be bool [337]
+d_c99_bool_strict.c(172): left operand of '<=' must not be bool [336]
+d_c99_bool_strict.c(172): right operand of '<=' must not be bool [337]
+d_c99_bool_strict.c(173): left operand of '>' must not be bool [336]
+d_c99_bool_strict.c(173): right operand of '>' must not be bool [337]
+d_c99_bool_strict.c(174): left operand of '>=' must not be bool [336]
+d_c99_bool_strict.c(174): right operand of '>=' must not be bool [337]
+d_c99_bool_strict.c(186): left operand of '*=' must not be bool [336]
+d_c99_bool_strict.c(186): right operand of '*=' must not be bool [337]
+d_c99_bool_strict.c(187): left operand of '/=' must not be bool [336]
+d_c99_bool_strict.c(187): right operand of '/=' must not be bool [337]
+d_c99_bool_strict.c(188): left operand of '%=' must not be bool [336]
+d_c99_bool_strict.c(188): right operand of '%=' must not be bool [337]
+d_c99_bool_strict.c(189): left operand of '+=' must not be bool [336]
+d_c99_bool_strict.c(189): right operand of '+=' must not be bool [337]
+d_c99_bool_strict.c(190): left operand of '-=' must not be bool [336]
+d_c99_bool_strict.c(190): right operand of '-=' must not be bool [337]
+d_c99_bool_strict.c(191): left operand of '<<=' must not be bool [336]
+d_c99_bool_strict.c(191): right operand of '<<=' must not be bool [337]
+d_c99_bool_strict.c(192): left operand of '>>=' must not be bool [336]
+d_c99_bool_strict.c(192): right operand of '>>=' must not be bool [337]
+d_c99_bool_strict.c(198): left operand of '*' must not be bool [336]
+d_c99_bool_strict.c(199): right operand of '*' must not be bool [337]
+d_c99_bool_strict.c(200): left operand of '/' must not be bool [336]
+d_c99_bool_strict.c(201): right operand of '/' must not be bool [337]
+d_c99_bool_strict.c(202): left operand of '%' must not be bool [336]
+d_c99_bool_strict.c(203): right operand of '%' must not be bool [337]
+d_c99_bool_strict.c(204): left operand of '+' must not be bool [336]
+d_c99_bool_strict.c(205): right operand of '+' must not be bool [337]
+d_c99_bool_strict.c(206): left operand of '-' must not be bool [336]
+d_c99_bool_strict.c(207): right operand of '-' must not be bool [337]
+d_c99_bool_strict.c(208): left operand of '<<' must not be bool [336]
+d_c99_bool_strict.c(209): right operand of '<<' must not be bool [337]
+d_c99_bool_strict.c(210): left operand of '>>' must not be bool [336]
+d_c99_bool_strict.c(211): right operand of '>>' must not be bool [337]
+d_c99_bool_strict.c(213): operands of ':' have incompatible types (_Bool != unsigned int) [107]
+d_c99_bool_strict.c(214): operands of ':' have incompatible types (unsigned int != _Bool) [107]
+d_c99_bool_strict.c(226): operands of '=' have incompatible types (int != _Bool) [107]
+d_c99_bool_strict.c(227): operands of '=' have incompatible types (unsigned int != _Bool) [107]
+d_c99_bool_strict.c(228): operands of '=' have incompatible types (double != _Bool) [107]
+d_c99_bool_strict.c(229): operands of '=' have incompatible types (pointer != _Bool) [107]
+d_c99_bool_strict.c(240): left operand of '?' must be bool, not 'int' [331]
+d_c99_bool_strict.c(359): operands of '=' have incompatible types (_Bool != int) [107]
+d_c99_bool_strict.c(367): operands of '=' have incompatible types (_Bool != int) [107]
+d_c99_bool_strict.c(374): operands of '=' have incompatible types (_Bool != int) [107]
+d_c99_bool_strict.c(391): operands of '=' have incompatible types (_Bool != unsigned int) [107]
+d_c99_bool_strict.c(393): operands of '=' have incompatible types (unsigned int != _Bool) [107]
+d_c99_bool_strict.c(405): return value type mismatch (_Bool) and (int) [211]
+d_c99_bool_strict.c(406): return value type mismatch (_Bool) and (pointer to const char) [211]
+d_c99_bool_strict.c(414): return value type mismatch (char) and (_Bool) [211]
+d_c99_bool_strict.c(417): warning: illegal combination of integer (char) and pointer (pointer to const char) [183]
+d_c99_bool_strict.c(435): return value type mismatch (_Bool) and (int) [211]
+d_c99_bool_strict.c(450): argument #2 expects 'int', gets passed '_Bool' [334]
+d_c99_bool_strict.c(450): argument #3 expects 'pointer', gets passed '_Bool' [334]
+d_c99_bool_strict.c(453): argument #1 expects '_Bool', gets passed 'int' [334]
+d_c99_bool_strict.c(453): warning: illegal combination of pointer (pointer to const char) and integer (int), arg #3 [154]
+d_c99_bool_strict.c(456): argument #1 expects '_Bool', gets passed 'pointer' [334]
+d_c99_bool_strict.c(456): warning: illegal combination of integer (int) and pointer (pointer to const char), arg #2 [154]
+d_c99_bool_strict.c(468): argument #1 expects '_Bool', gets passed 'int' [334]

Index: src/usr.bin/xlint/common/externs.h
diff -u src/usr.bin/xlint/common/externs.h:1.12 src/usr.bin/xlint/common/externs.h:1.13
--- src/usr.bin/xlint/common/externs.h:1.12	Mon Jan  4 01:12:20 2021
+++ src/usr.bin/xlint/common/externs.h	Tue Jan 12 20:42:00 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: externs.h,v 1.12 2021/01/04 01:12:20 rillig Exp $	*/
+/*	$NetBSD: externs.h,v 1.13 2021/01/12 20:42:00 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -35,6 +35,8 @@
  * main[12].c
  */
 extern	int	pflag;
+/* Treat _Bool as incompatible to all other scalar types. */
+extern	bool	Tflag;
 
 /*
  * inittyp.c

Index: src/usr.bin/xlint/lint1/cgram.y
diff -u src/usr.bin/xlint/lint1/cgram.y:1.138 src/usr.bin/xlint/lint1/cgram.y:1.139
--- src/usr.bin/xlint/lint1/cgram.y:1.138	Sun Jan 10 14:07:34 2021
+++ src/usr.bin/xlint/lint1/cgram.y	Tue Jan 12 20:42:01 2021
@@ -1,5 +1,5 @@
 %{
-/* $NetBSD: cgram.y,v 1.138 2021/01/10 14:07:34 rillig Exp $ */
+/* $NetBSD: cgram.y,v 1.139 2021/01/12 20:42:01 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: cgram.y,v 1.138 2021/01/10 14:07:34 rillig Exp $");
+__RCSID("$NetBSD: cgram.y,v 1.139 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -2097,6 +2097,11 @@ toicon(tnode_t *tn, int required)
 
 	v = constant(tn, required);
 
+	if (tn == NULL) {
+		i = 1;
+		goto done;
+	}
+
 	/*
 	 * Abstract declarations are used inside expression. To free
 	 * the memory would be a fatal error.
@@ -2126,6 +2131,8 @@ toicon(tnode_t *tn, int required)
 			}
 		}
 	}
+
+done:
 	free(v);
 	return i;
 }

Index: src/usr.bin/xlint/lint1/err.c
diff -u src/usr.bin/xlint/lint1/err.c:1.65 src/usr.bin/xlint/lint1/err.c:1.66
--- src/usr.bin/xlint/lint1/err.c:1.65	Sat Jan  9 17:21:33 2021
+++ src/usr.bin/xlint/lint1/err.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: err.c,v 1.65 2021/01/09 17:21:33 rillig Exp $	*/
+/*	$NetBSD: err.c,v 1.66 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: err.c,v 1.65 2021/01/09 17:21:33 rillig Exp $");
+__RCSID("$NetBSD: err.c,v 1.66 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <sys/types.h>
@@ -389,6 +389,14 @@ const	char *msgs[] = {
 	"declarations after statements is a C9X feature",	      /* 327 */
 	"union cast is a C9X feature",				      /* 328 */
 	"type '%s' is not a member of '%s'",			      /* 329 */
+	"operand of '%s' must be bool, not '%s'",		      /* 330 */
+	"left operand of '%s' must be bool, not '%s'",		      /* 331 */
+	"right operand of '%s' must be bool, not '%s'",		      /* 332 */
+	"controlling expression must be bool, not '%s'",	      /* 333 */
+	"argument #%d expects '%s', gets passed '%s'",		      /* 334 */
+	"operand of '%s' must not be bool",			      /* 335 */
+	"left operand of '%s' must not be bool",		      /* 336 */
+	"right operand of '%s' must not be bool",		      /* 337 */
 };
 
 /*

Index: src/usr.bin/xlint/lint1/externs1.h
diff -u src/usr.bin/xlint/lint1/externs1.h:1.54 src/usr.bin/xlint/lint1/externs1.h:1.55
--- src/usr.bin/xlint/lint1/externs1.h:1.54	Mon Jan 11 19:29:49 2021
+++ src/usr.bin/xlint/lint1/externs1.h	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: externs1.h,v 1.54 2021/01/11 19:29:49 rillig Exp $	*/
+/*	$NetBSD: externs1.h,v 1.55 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -204,6 +204,7 @@ extern	tnode_t	*new_string_node(strg_t *
 extern	sym_t	*struct_or_union_member(tnode_t *, op_t, sym_t *);
 extern	tnode_t	*build(op_t, tnode_t *, tnode_t *);
 extern	tnode_t	*cconv(tnode_t *);
+extern	bool	is_strict_bool(const tnode_t *);
 extern	bool	typeok(op_t, int, const tnode_t *, const tnode_t *);
 extern	tnode_t	*promote(op_t, int, tnode_t *);
 extern	tnode_t	*convert(op_t, int, type_t *, tnode_t *);

Index: src/usr.bin/xlint/lint1/func.c
diff -u src/usr.bin/xlint/lint1/func.c:1.56 src/usr.bin/xlint/lint1/func.c:1.57
--- src/usr.bin/xlint/lint1/func.c:1.56	Sun Jan 10 11:17:53 2021
+++ src/usr.bin/xlint/lint1/func.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: func.c,v 1.56 2021/01/10 11:17:53 rillig Exp $	*/
+/*	$NetBSD: func.c,v 1.57 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: func.c,v 1.56 2021/01/10 11:17:53 rillig Exp $");
+__RCSID("$NetBSD: func.c,v 1.57 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <stdlib.h>
@@ -552,6 +552,12 @@ check_controlling_expression(tnode_t *tn
 		return NULL;
 	}
 
+	if (tn != NULL && Tflag && !is_strict_bool(tn)) {
+		/* controlling expression must be bool, not '%s' */
+		error(333, tspec_name(tn->tn_type->t_tspec));
+		return NULL;
+	}
+
 	return tn;
 }
 

Index: src/usr.bin/xlint/lint1/main1.c
diff -u src/usr.bin/xlint/lint1/main1.c:1.33 src/usr.bin/xlint/lint1/main1.c:1.34
--- src/usr.bin/xlint/lint1/main1.c:1.33	Sun Jan 10 14:12:48 2021
+++ src/usr.bin/xlint/lint1/main1.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: main1.c,v 1.33 2021/01/10 14:12:48 rillig Exp $	*/
+/*	$NetBSD: main1.c,v 1.34 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: main1.c,v 1.33 2021/01/10 14:12:48 rillig Exp $");
+__RCSID("$NetBSD: main1.c,v 1.34 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <sys/types.h>
@@ -101,6 +101,8 @@ int	rflag;
 /* Strict ANSI C mode. */
 int	sflag;
 
+bool	Tflag;
+
 /* Traditional C mode. */
 int	tflag;
 
@@ -176,7 +178,7 @@ main(int argc, char *argv[])
 	setprogname(argv[0]);
 
 	ERR_ZERO(&msgset);
-	while ((c = getopt(argc, argv, "abcdeghmprstuvwyzFPR:SX:")) != -1) {
+	while ((c = getopt(argc, argv, "abcdeghmprstuvwyzFPR:STX:")) != -1) {
 		switch (c) {
 		case 'a':	aflag++;	break;
 		case 'b':	bflag = 1;	break;
@@ -191,6 +193,7 @@ main(int argc, char *argv[])
 		case 'r':	rflag = 1;	break;
 		case 's':	sflag = 1;	break;
 		case 'S':	Sflag = 1;	break;
+		case 'T':	Tflag = true;	break;
 		case 't':	tflag = 1;	break;
 		case 'u':	uflag = 0;	break;
 		case 'w':	wflag = 1;	break;

Index: src/usr.bin/xlint/lint1/op.h
diff -u src/usr.bin/xlint/lint1/op.h:1.11 src/usr.bin/xlint/lint1/op.h:1.12
--- src/usr.bin/xlint/lint1/op.h:1.11	Sat Jan  9 17:36:10 2021
+++ src/usr.bin/xlint/lint1/op.h	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: op.h,v 1.11 2021/01/09 17:36:10 rillig Exp $	*/
+/*	$NetBSD: op.h,v 1.12 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -38,7 +38,9 @@
  */
 typedef	struct {
 	bool	m_binary : 1;	/* binary operator */
-	bool	m_logical : 1;	/* logical operator, result is int */
+	bool	m_returns_bool : 1; /* logical operator, result is 0 or 1 */
+	bool	m_takes_bool : 1; /* only in strict bool mode */
+	bool	m_takes_only_bool : 1; /* only in strict bool mode */
 	bool	m_requires_integer : 1;
 	bool	m_requires_integer_or_complex : 1;
 	bool	m_requires_arith : 1;
@@ -62,7 +64,7 @@ extern mod_t   modtab[];
 
 #define begin_ops() typedef enum {
 #define op(name, repr, \
-		bi, lo, in, sc, ar, fo, va, ts, ba, se, \
+		bi, lo, tb, to, in, sc, ar, fo, va, ts, ba, se, \
 		lu, ru, pc, cm, ve, de, ew, ic, active) \
 	name,
 #define end_ops() } op_t;

Index: src/usr.bin/xlint/lint1/oper.c
diff -u src/usr.bin/xlint/lint1/oper.c:1.2 src/usr.bin/xlint/lint1/oper.c:1.3
--- src/usr.bin/xlint/lint1/oper.c:1.2	Sat Jan  9 22:19:11 2021
+++ src/usr.bin/xlint/lint1/oper.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: oper.c,v 1.2 2021/01/09 22:19:11 rillig Exp $	*/
+/*	$NetBSD: oper.c,v 1.3 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*-
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -41,9 +41,9 @@ static const struct {
 } imods[] =
 #define begin_ops() {
 #define op(name, repr, \
-		bi, lo, in, sc, ar, fo, va, ts, ba, se, \
+		bi, lo, tb, to, in, sc, ar, fo, va, ts, ba, se, \
 		lu, ru, pc, cm, ve, de, ew, ic, active) \
-	{ { bi + 0, lo + 0, in + 0, sc + 0, ar + 0, \
+	{ { bi + 0, lo + 0, tb + 0, to + 0, in + 0, sc + 0, ar + 0, \
 	    fo + 0, va + 0, ts + 0, ba + 0, se + 0, \
 	    lu + 0, ru + 0, pc + 0, cm + 0, ve + 0, \
 	    de + 0, ew + 0, ic + 0, repr }, active },

Index: src/usr.bin/xlint/lint1/ops.def
diff -u src/usr.bin/xlint/lint1/ops.def:1.9 src/usr.bin/xlint/lint1/ops.def:1.10
--- src/usr.bin/xlint/lint1/ops.def:1.9	Sat Jan  9 22:19:11 2021
+++ src/usr.bin/xlint/lint1/ops.def	Tue Jan 12 20:42:01 2021
@@ -1,85 +1,85 @@
-/*	$NetBSD: ops.def,v 1.9 2021/01/09 22:19:11 rillig Exp $ */
+/*	$NetBSD: ops.def,v 1.10 2021/01/12 20:42:01 rillig Exp $ */
 
 begin_ops()
 
 /* See mod_t in op.h for the definition of the table columns. */
 
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	NOOP,	"no-op",	 , , , , , , , , , , , , , , , , , ,	0)
-op(	ARROW,	"->",		1, , , , , , ,1, , , , , , , , , , ,	1)
-op(	POINT,	".",		1, , , , , , , , , , , , , , , , , ,	1)
-op(	NOT,	"!",		 ,1, , , ,1,1, ,1, , , , , , , ,1, ,	1)
-op(	COMPL,	"~",		 , , ,1, , ,1,1, , , , , , , , ,1,1,	1)
-op(	INC,	"++",		 , , , , , , , , , , , , , , , , , ,	0)
-op(	DEC,	"--",		 , , , , , , , , , , , , , , , , , ,	0)
-op(	INCBEF,	"++x",		 , , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	DECBEF,	"--x",		 , , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	INCAFT,	"x++",		 , , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	DECAFT,	"x--",		 , , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	UPLUS,	"+",		 , , , ,1, ,1,1, , , , , , , , ,1,1,	1)
-op(	UMINUS,	"-",		 , , , ,1, ,1,1, , , ,1, , , , ,1,1,	1)
-op(	STAR,	"*",		 , , , , , , ,1, , , , , , , , , , ,	1)
-op(	AMPER,	"&",		 , , , , , , , , , , , , , , , , , ,	1)
-
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	MULT,	"*",		1, , , ,1, ,1,1, ,1, , ,1, , , ,1,1,	1)
-op(	DIV,	"/",		1, , , ,1, ,1,1, ,1, ,1,1, , , ,1,1,	1)
-op(	MOD,	"%",		1, ,1, , , ,1,1, ,1, ,1,1, , , ,1,1,	1)
-op(	PLUS,	"+",		1, , , , ,1,1,1, ,1, , , , , , ,1, ,	1)
-op(	MINUS,	"-",		1, , , , ,1,1,1, ,1, , , , , , ,1, ,	1)
-op(	SHL,	"<<",		1, ,1, , , ,1,1, , , , , ,1, , ,1,1,	1)
-op(	SHR,	">>",		1, ,1, , , ,1,1, , , ,1, ,1, , ,1,1,	1)
-
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	LT,	"<",		1,1, , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,	1)
-op(	LE,	"<=",		1,1, , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,	1)
-op(	GT,	">",		1,1, , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,	1)
-op(	GE,	">=",		1,1, , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,	1)
-op(	EQ,	"==",		1,1, , , ,1,1,1, ,1, , , , ,1,1, ,1,	1)
-op(	NE,	"!=",		1,1, , , ,1,1,1, ,1, , , , ,1,1, ,1,	1)
-
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	AND,	"&",		1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,	1)
-op(	XOR,	"^",		1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,	1)
-op(	OR,	"|",		1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,	1)
-op(	LOGAND,	"&&",		1,1, , , ,1,1, ,1, , , , , , , ,1, ,	1)
-op(	LOGOR,	"||",		1,1, , , ,1,1, ,1, , , , ,1, , ,1, ,	1)
-op(	QUEST,	"?",		1, , , , , ,1, ,1, , , , , , , , , ,	1)
-op(	COLON,	":",		1, , , , , , ,1, ,1, , , , , ,1, , ,	1)
-
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	ASSIGN,	"=",		1, , , , , , , , , ,1, , , , ,1, , ,	1)
-op(	MULASS,	"*=",		1, , , ,1, , , , , ,1, , , , , ,1, ,	1)
-op(	DIVASS,	"/=",		1, , , ,1, , , , , ,1, ,1, , , ,1, ,	1)
-op(	MODASS,	"%=",		1, ,1, , , , , , , ,1, ,1, , , ,1, ,	1)
-op(	ADDASS,	"+=",		1, , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	SUBASS,	"-=",		1, , , , ,1, , , , ,1, , , , , ,1, ,	1)
-op(	SHLASS,	"<<=",		1, ,1, , , , , , , ,1, , , , , ,1, ,	1)
-op(	SHRASS,	">>=",		1, ,1, , , , , , , ,1, , , , , ,1, ,	1)
-op(	ANDASS,	"&=",		1, ,1, , , , , , , ,1, , , , , ,1, ,	1)
-op(	XORASS,	"^=",		1, ,1, , , , , , , ,1, , , , , ,1, ,	1)
-op(	ORASS,	"|=",		1, ,1, , , , , , , ,1, , , , , ,1, ,	1)
-
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	NAME,	"name",		 , , , , , , , , , , , , , , , , , ,	1)
-op(	CON,	"const",	 , , , , , , , , , , , , , , , , , ,	1)
-op(	STRING,	"string",	 , , , , , , , , , , , , , , , , , ,	1)
-op(	FSEL,	"fsel",		 , , , , , , , , , , , , , , , , , ,	1)
-op(	CALL,	"call",		1, , , , , , , , , ,1, , , , , , , ,	1)
-op(	COMMA,	",",		1, , , , , , , , , , , , , , , , ,1,	1)
-op(	CVT,	"convert",	 , , , , , , ,1, , , , , , , , , , ,	1)
-op(	ICALL,	"icall",	1, , , , , , , , , ,1, , , , , , , ,	1)
-op(	LOAD,	"load",		 , , , , , , , , , , , , , , , , , ,	1)
-op(	PUSH,	"push",		 , , , , , , ,1, , , , , , , , , , ,	1)
-op(	RETURN,	"return",	1, , , , , , , , , ,1, , , , ,1, , ,	1)
-op(	REAL,	"real",		 , , , , , , , , , , , , , , , , , ,	0)
-op(	IMAG,	"imag",		 , , , , , , , , , , , , , , , , , ,	0)
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	NOOP,	"no-op",	 , , , , , , , , , , , , , , , , , , , ,0)
+op(	ARROW,	"->",		1, ,1, , , , , , ,1, , , , , , , , , , ,1)
+op(	POINT,	".",		1, ,1, , , , , , , , , , , , , , , , , ,1)
+op(	NOT,	"!",		 ,1,1,1, , , ,1,1, ,1, , , , , , , ,1, ,1)
+op(	COMPL,	"~",		 , , , , ,1, , ,1,1, , , , , , , , ,1,1,1)
+op(	INC,	"++",		 , , , , , , , , , , , , , , , , , , , ,0)
+op(	DEC,	"--",		 , , , , , , , , , , , , , , , , , , , ,0)
+op(	INCBEF,	"++x",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	DECBEF,	"--x",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	INCAFT,	"x++",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	DECAFT,	"x--",		 , , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	UPLUS,	"+",		 , , , , , ,1, ,1,1, , , , , , , , ,1,1,1)
+op(	UMINUS,	"-",		 , , , , , ,1, ,1,1, , , ,1, , , , ,1,1,1)
+op(	STAR,	"*",		 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	AMPER,	"&",		 , ,1, , , , , , , , , , , , , , , , , ,1)
+
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	MULT,	"*",		1, , , , , ,1, ,1,1, ,1, , ,1, , , ,1,1,1)
+op(	DIV,	"/",		1, , , , , ,1, ,1,1, ,1, ,1,1, , , ,1,1,1)
+op(	MOD,	"%",		1, , , ,1, , , ,1,1, ,1, ,1,1, , , ,1,1,1)
+op(	PLUS,	"+",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, ,1)
+op(	MINUS,	"-",		1, , , , , , ,1,1,1, ,1, , , , , , ,1, ,1)
+op(	SHL,	"<<",		1, , , ,1, , , ,1,1, , , , , ,1, , ,1,1,1)
+op(	SHR,	">>",		1, , , ,1, , , ,1,1, , , ,1, ,1, , ,1,1,1)
+
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	LT,	"<",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	LE,	"<=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	GT,	">",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	GE,	">=",		1,1, , , , , ,1,1,1, ,1, ,1,1, ,1,1, ,1,1)
+op(	EQ,	"==",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1,1)
+op(	NE,	"!=",		1,1,1, , , , ,1,1,1, ,1, , , , ,1,1, ,1,1)
+
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	AND,	"&",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	XOR,	"^",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	OR,	"|",		1, ,1, ,1, , , ,1,1, ,1, , , ,1, , ,1, ,1)
+op(	LOGAND,	"&&",		1,1,1,1, , , ,1,1, ,1, , , , , , , ,1, ,1)
+op(	LOGOR,	"||",		1,1,1,1, , , ,1,1, ,1, , , , ,1, , ,1, ,1)
+op(	QUEST,	"?",		1, , , , , , , ,1, ,1, , , , , , , , , ,1)
+op(	COLON,	":",		1, ,1, , , , , , ,1, ,1, , , , , ,1, , ,1)
+
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	ASSIGN,	"=",		1, ,1, , , , , , , , , ,1, , , , ,1, , ,1)
+op(	MULASS,	"*=",		1, , , , , ,1, , , , , ,1, , , , , ,1, ,1)
+op(	DIVASS,	"/=",		1, , , , , ,1, , , , , ,1, ,1, , , ,1, ,1)
+op(	MODASS,	"%=",		1, , , ,1, , , , , , , ,1, ,1, , , ,1, ,1)
+op(	ADDASS,	"+=",		1, , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	SUBASS,	"-=",		1, , , , , , ,1, , , , ,1, , , , , ,1, ,1)
+op(	SHLASS,	"<<=",		1, , , ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	SHRASS,	">>=",		1, , , ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	ANDASS,	"&=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	XORASS,	"^=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+op(	ORASS,	"|=",		1, ,1, ,1, , , , , , , ,1, , , , , ,1, ,1)
+
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	NAME,	"name",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	CON,	"const",	 , , , , , , , , , , , , , , , , , , , ,1)
+op(	STRING,	"string",	 , , , , , , , , , , , , , , , , , , , ,1)
+op(	FSEL,	"fsel",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	CALL,	"call",		1, , , , , , , , , , , ,1, , , , , , , ,1)
+op(	COMMA,	",",		1, , , , , , , , , , , , , , , , , , ,1,1)
+op(	CVT,	"convert",	 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	ICALL,	"icall",	1, , , , , , , , , , , ,1, , , , , , , ,1)
+op(	LOAD,	"load",		 , , , , , , , , , , , , , , , , , , , ,1)
+op(	PUSH,	"push",		 , , , , , , , , ,1, , , , , , , , , , ,1)
+op(	RETURN,	"return",	1, , , , , , , , , , , ,1, , , , ,1, , ,1)
+op(	REAL,	"real",		 , , , , , , , , , , , , , , , , , , , ,0)
+op(	IMAG,	"imag",		 , , , , , , , , , , , , , , , , , , , ,0)
 
 /* INIT, CASE and FARG are pseudo operators that don't appear in the tree. */
-/*	name	repr		b l i c a s f v t b s l r p c e e =	act */
-op(	INIT,	"init",		1, , , , , , , , , , , , , , ,1, , ,	1)
-op(	CASE,	"case",		 , , , , , , , , , , , , , , , , , ,	0)
-op(	FARG,	"farg",		1, , , , , , , , , , , , , , ,1, , ,	1)
+/*	name	repr		b l b o i c a s f v t b s l r p c e e =	act */
+op(	INIT,	"init",		1, ,1, , , , , , , , , , , , , , ,1, , ,1)
+op(	CASE,	"case",		 , , , , , , , , , , , , , , , , , , , ,0)
+op(	FARG,	"farg",		1, , , , , , , , , , , , , , , , ,1, , ,1)
 
 end_ops()
 

Index: src/usr.bin/xlint/lint1/tree.c
diff -u src/usr.bin/xlint/lint1/tree.c:1.150 src/usr.bin/xlint/lint1/tree.c:1.151
--- src/usr.bin/xlint/lint1/tree.c:1.150	Mon Jan 11 20:04:01 2021
+++ src/usr.bin/xlint/lint1/tree.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: tree.c,v 1.150 2021/01/11 20:04:01 rillig Exp $	*/
+/*	$NetBSD: tree.c,v 1.151 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: tree.c,v 1.150 2021/01/11 20:04:01 rillig Exp $");
+__RCSID("$NetBSD: tree.c,v 1.151 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <float.h>
@@ -603,7 +603,8 @@ build(op_t op, tnode_t *ln, tnode_t *rn)
 		ntn = build_real_imag(op, ln);
 		break;
 	default:
-		rtp = mp->m_logical ? gettyp(INT) : ln->tn_type;
+		rtp = mp->m_returns_bool
+		    ? gettyp(Tflag ? BOOL : INT) : ln->tn_type;
 		lint_assert(mp->m_binary || rn == NULL);
 		ntn = new_tnode(op, rtp, ln, rn);
 		break;
@@ -692,6 +693,61 @@ cconv(tnode_t *tn)
 	return tn;
 }
 
+static const tnode_t *
+before_conversion(const tnode_t *tn)
+{
+	while (tn->tn_op == CVT && !tn->tn_cast)
+		tn = tn->tn_left;
+	return tn;
+}
+
+static bool
+is_strict_bool_constant(const tnode_t *tn)
+{
+	tspec_t t;
+
+	tn = before_conversion(tn);
+	t = tn->tn_type->t_tspec;
+
+	if (t == BOOL)
+		return true;
+
+	return t == INT && tn->tn_op == CON &&
+	       (tn->tn_val->v_quad == 0 || tn->tn_val->v_quad == 1);
+}
+
+/* In strict bool mode, see if the node's type is compatible with bool. */
+bool
+is_strict_bool(const tnode_t *tn)
+{
+	tspec_t t;
+
+	tn = before_conversion(tn);
+	t = tn->tn_type->t_tspec;
+
+	if (t == BOOL)
+		return true;
+
+	if (t == INT && tn->tn_op == CON &&
+	    (tn->tn_val->v_quad == 0 || tn->tn_val->v_quad == 1))
+		return true;
+
+	/* For enums that are used as bit sets, allow "flags & FLAG". */
+	if (tn->tn_op == AND &&
+	    tn->tn_left->tn_op == CVT &&
+	    tn->tn_left->tn_type->t_tspec == INT && !tn->tn_left->tn_cast &&
+	    tn->tn_left->tn_left->tn_type->t_tspec == ENUM &&
+	    /*
+	     * XXX: Somehow the type information got lost here.  The type
+	     * of the enum constant on the right-hand side should still be
+	     * ENUM, but is INT.
+	     */
+	    tn->tn_right->tn_type->t_tspec == INT)
+		return true;
+
+	return false;
+}
+
 static bool
 typeok_incdec(const mod_t *mp, const tnode_t *tn, const type_t *tp)
 {
@@ -796,14 +852,6 @@ typeok_minus(op_t op,
 	return true;
 }
 
-static const tnode_t *
-before_promotion_and_balancing(const tnode_t *tn)
-{
-	while (tn->tn_op == CVT && !tn->tn_cast)
-		tn = tn->tn_left;
-	return tn;
-}
-
 static void
 typeok_shr(const mod_t *mp,
 	   const tnode_t *ln, tspec_t lt,
@@ -811,8 +859,8 @@ typeok_shr(const mod_t *mp,
 {
 	tspec_t olt, ort;
 
-	olt = before_promotion_and_balancing(ln)->tn_type->t_tspec;
-	ort = before_promotion_and_balancing(rn)->tn_type->t_tspec;
+	olt = before_conversion(ln)->tn_type->t_tspec;
+	ort = before_conversion(rn)->tn_type->t_tspec;
 
 	/* operands have integer types (checked above) */
 	if (pflag && !is_uinteger(lt)) {
@@ -1037,9 +1085,125 @@ typeok_assign(const mod_t *mp, const tno
 	return true;
 }
 
+/*
+ * For assignment operators in strict bool mode, the type check is stricter
+ * than for operators that compare to 0.  Code that passes this strict check
+ * can be compiled in a pre-C99 environment that doesn't implement the
+ * special rule C99 6.3.1.2, without silent change in behavior.
+ */
+static bool
+typeok_strict_bool_assign(op_t op, int arg,
+			  const tnode_t *ln, tspec_t lt,
+			  const tnode_t *rn, tspec_t rt)
+{
+	if ((lt == BOOL) == (rt == BOOL))
+		return true;
+
+	if (lt == BOOL && is_strict_bool_constant(rn))
+		return true;
+
+	if (op == FARG) {
+		/* argument #%d expects '%s', gets passed '%s' */
+		error(334,
+		    arg,
+		    tspec_name(ln->tn_type->t_tspec),
+		    tspec_name(rn->tn_type->t_tspec));
+	} else if (op == RETURN) {
+		/* return value type mismatch (%s) and (%s) */
+		error(211,
+		    type_name(ln->tn_type),
+		    type_name(rn->tn_type));
+		return false;
+	} else {
+		/* operands of '%s' have incompatible types (%s != %s) */
+		error(107,
+		    getopname(op),
+		    tspec_name(ln->tn_type->t_tspec),
+		    tspec_name(rn->tn_type->t_tspec));
+	}
+
+	return false;
+}
+
+/*
+ * In strict bool mode, check whether the types of the operands match the
+ * operator.
+ */
+static bool
+typeok_scalar_strict_bool(op_t op, const mod_t *mp, int arg,
+			  const tnode_t *ln,
+			  const tnode_t *rn)
+
+{
+	tspec_t lt, rt;
+
+	ln = before_conversion(ln);
+	lt = ln->tn_type->t_tspec;
+
+	if (rn != NULL) {
+		rn = before_conversion(rn);
+		rt = rn->tn_type->t_tspec;
+	} else {
+		rt = NOTSPEC;
+	}
+
+	if (op == ASSIGN || op == FARG || op == RETURN || op == COLON)
+		return typeok_strict_bool_assign(op, arg, ln, lt, rn, rt);
+
+	if (mp->m_takes_only_bool || op == QUEST) {
+		bool binary = mp->m_binary;
+		bool lbool = is_strict_bool(ln);
+		bool ok = true;
+
+		if (!binary && !lbool) {
+			/* operand of '%s' must be bool, not '%s' */
+			error(330, getopname(op), tspec_name(lt));
+			ok = false;
+		}
+		if (binary && !lbool) {
+			/* left operand of '%s' must be bool, not '%s' */
+			error(331, getopname(op), tspec_name(lt));
+			ok = false;
+		}
+		if (binary && op != QUEST && !is_strict_bool(rn)) {
+			/* right operand of '%s' must be bool, not '%s' */
+			error(332, getopname(op), tspec_name(rt));
+			ok = false;
+		}
+		return ok;
+	}
+
+	if (!mp->m_takes_bool) {
+		bool binary = mp->m_binary;
+		bool lbool = before_conversion(ln)->tn_type->t_tspec == BOOL;
+		bool ok = true;
+
+		if (!binary && lbool) {
+			/* operand of '%s' must not be bool */
+			error(335, getopname(op));
+			ok = false;
+		}
+		if (binary && lbool) {
+			/* left operand of '%s' must not be bool */
+			error(336, getopname(op));
+			ok = false;
+		}
+		if (binary && before_conversion(rn)->tn_type->t_tspec == BOOL) {
+			/* right operand of '%s' must not be bool */
+			error(337, getopname(op));
+			ok = false;
+		}
+		return ok;
+	}
+
+	return true;
+}
+
 /* Check the types using the information from modtab[]. */
 static bool
-typeok_scalar(op_t op, const mod_t *mp, tspec_t lt, tspec_t rt)
+typeok_scalar(op_t op, const mod_t *mp,
+	      const tnode_t *ln, tspec_t lt,
+	      const tnode_t *rn, tspec_t rt)
 {
 	if (mp->m_requires_integer) {
 		if (!is_integer(lt) || (mp->m_binary && !is_integer(rt))) {
@@ -1265,7 +1429,9 @@ typeok(op_t op, int arg, const tnode_t *
 		rt = NOTSPEC;
 	}
 
-	if (!typeok_scalar(op, mp, lt, rt))
+	if (Tflag && !typeok_scalar_strict_bool(op, mp, arg, ln, rn))
+		return false;
+	if (!typeok_scalar(op, mp, ln, lt, rn, rt))
 		return false;
 
 	if (!typeok_op(op, mp, arg, ln, ltp, lt, rn, rtp, rt))
@@ -2980,7 +3146,7 @@ fold_test(tnode_t *tn)
 
 	v = xcalloc(1, sizeof (val_t));
 	v->v_tspec = tn->tn_type->t_tspec;
-	lint_assert(tn->tn_type->t_tspec == INT);
+	lint_assert(v->v_tspec == INT || (Tflag && v->v_tspec == BOOL));
 
 	if (is_floating(tn->tn_left->tn_type->t_tspec)) {
 		l = tn->tn_left->tn_val->v_ldbl != 0.0;
@@ -3522,6 +3688,8 @@ constant(tnode_t *tn, int required)
 
 	if (tn == NULL) {
 		lint_assert(nerr != 0);
+		if (dflag)
+			printf("constant node is null; returning 1 instead\n");
 		v->v_tspec = INT;
 		v->v_quad = 1;
 		return v;

Index: src/usr.bin/xlint/lint2/main2.c
diff -u src/usr.bin/xlint/lint2/main2.c:1.10 src/usr.bin/xlint/lint2/main2.c:1.11
--- src/usr.bin/xlint/lint2/main2.c:1.10	Mon Jan  4 22:26:51 2021
+++ src/usr.bin/xlint/lint2/main2.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: main2.c,v 1.10 2021/01/04 22:26:51 rillig Exp $	*/
+/*	$NetBSD: main2.c,v 1.11 2021/01/12 20:42:01 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: main2.c,v 1.10 2021/01/04 22:26:51 rillig Exp $");
+__RCSID("$NetBSD: main2.c,v 1.11 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <stdio.h>
@@ -68,6 +68,7 @@ int	pflag;
  */
 int	sflag;
 
+bool	Tflag;
 int	tflag;
 
 /*
@@ -100,11 +101,14 @@ main(int argc, char *argv[])
 	libs = xcalloc(1, sizeof (char *));
 
 	opterr = 0;
-	while ((c = getopt(argc, argv, "hpstxuC:HFl:")) != -1) {
+	while ((c = getopt(argc, argv, "hpstxuC:HTFl:")) != -1) {
 		switch (c) {
 		case 's':
 			sflag = 1;
 			break;
+		case 'T':
+			Tflag = true;
+			break;
 		case 't':
 			tflag = 1;
 			break;

Index: src/usr.bin/xlint/xlint/lint.1
diff -u src/usr.bin/xlint/xlint/lint.1:1.41 src/usr.bin/xlint/xlint/lint.1:1.42
--- src/usr.bin/xlint/xlint/lint.1:1.41	Sun May 24 09:55:43 2020
+++ src/usr.bin/xlint/xlint/lint.1	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-.\" $NetBSD: lint.1,v 1.41 2020/05/24 09:55:43 wiz Exp $
+.\" $NetBSD: lint.1,v 1.42 2021/01/12 20:42:01 rillig Exp $
 .\"
 .\" Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
 .\" Copyright (c) 1994, 1995 Jochen Pohl
@@ -30,7 +30,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 23, 2020
+.Dd Jan 12, 2021
 .Dt LINT 1
 .Os
 .Sh NAME
@@ -38,7 +38,7 @@
 .Nd a C program verifier
 .Sh SYNOPSIS
 .Nm
-.Op Fl abceFgHhPprVvwxz
+.Op Fl abceFgHhPprTVvwxz
 .Op Fl i | Fl nu
 .Op Fl S | Fl s | Fl t
 .Op Fl B Ar directory
@@ -54,7 +54,7 @@
 .Op Fl Z Ar cpparg
 .Ar
 .Nm lint
-.Op Fl abceFgHhprVvwz
+.Op Fl abceFgHhprTVvwz
 .Op Fl S | Fl s | Fl t
 .Fl C Ar library
 .Op Fl B Ar directory
@@ -369,6 +369,10 @@ With the
 flag,
 .Li __STRICT_ANSI__
 is a predefined preprocessor macro.
+.It Fl T
+Treat
+.Sy _Bool
+as a data type that is incompatible with all other scalar types.
 .It Fl t
 Traditional C mode.
 .Li __STDC__

Index: src/usr.bin/xlint/xlint/xlint.c
diff -u src/usr.bin/xlint/xlint/xlint.c:1.52 src/usr.bin/xlint/xlint/xlint.c:1.53
--- src/usr.bin/xlint/xlint/xlint.c:1.52	Mon Jan  4 22:26:51 2021
+++ src/usr.bin/xlint/xlint/xlint.c	Tue Jan 12 20:42:01 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: xlint.c,v 1.52 2021/01/04 22:26:51 rillig Exp $ */
+/* $NetBSD: xlint.c,v 1.53 2021/01/12 20:42:01 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -38,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: xlint.c,v 1.52 2021/01/04 22:26:51 rillig Exp $");
+__RCSID("$NetBSD: xlint.c,v 1.53 2021/01/12 20:42:01 rillig Exp $");
 #endif
 
 #include <sys/param.h>
@@ -290,13 +290,13 @@ usage(void)
 {
 
 	(void)fprintf(stderr,
-	    "Usage: %s [-abceghprvwxzHFS] [-s|-t] [-i|-nu] [-Dname[=def]]"
+	    "Usage: %s [-abceghprvwxzHFST] [-s|-t] [-i|-nu] [-Dname[=def]]"
 	    " [-Uname] [-X <id>[,<id>]... [-Z <cpparg>]\n", getprogname());
 	(void)fprintf(stderr,
 	    "\t[-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile]"
 	    " file...\n");
 	(void)fprintf(stderr,
-	    "       %s [-abceghprvwzHFS] [|-s|-t] -Clibrary [-Dname[=def]]\n"
+	    "       %s [-abceghprvwzHFST] [|-s|-t] -Clibrary [-Dname[=def]]\n"
 	    " [-X <id>[,<id>]... [-Z <cpparg>]\n", getprogname());
 	(void)fprintf(stderr, "\t[-Idirectory] [-Uname] [-Bpath] [-R old=new]"
 	    " file ...\n");
@@ -370,7 +370,8 @@ main(int argc, char *argv[])
 	(void)signal(SIGINT, terminate);
 	(void)signal(SIGQUIT, terminate);
 	(void)signal(SIGTERM, terminate);
-	while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:M:PR:SU:VX:Z:")) != -1) {
+	while ((c = getopt(argc, argv,
+	    "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:M:PR:STU:VX:Z:")) != -1) {
 		switch (c) {
 
 		case 'a':
@@ -391,6 +392,7 @@ main(int argc, char *argv[])
 			/* FALLTHROUGH */
 		case 'u':
 		case 'h':
+		case 'T':
 			(void)sprintf(flgbuf, "-%c", c);
 			appcstrg(&l1flags, flgbuf);
 			appcstrg(&l2flags, flgbuf);

Added files:

Index: src/tests/usr.bin/xlint/check-expect.lua
diff -u /dev/null src/tests/usr.bin/xlint/check-expect.lua:1.1
--- /dev/null	Tue Jan 12 20:42:01 2021
+++ src/tests/usr.bin/xlint/check-expect.lua	Tue Jan 12 20:42:01 2021
@@ -0,0 +1,135 @@
+#!  /usr/bin/lua
+-- $NetBSD: check-expect.lua,v 1.1 2021/01/12 20:42:01 rillig Exp $
+
+--[[
+
+usage: lua ./check-expect.lua *.c
+
+Check that the /* expect: ... */ comments in the .c source files match the
+actual messages found in the corresponding .exp files.
+
+]]
+
+
+local function load_lines(fname)
+  local lines = {}
+
+  local f = io.open(fname, "r")
+  if f == nil then return nil end
+
+  for line in f:lines() do
+    table.insert(lines, line)
+  end
+  f:close()
+
+  return lines
+end
+
+local function load_expect_comments_from_c(fname)
+
+  local lines = load_lines(fname)
+  if lines == nil then return nil end
+
+  local comments_by_line = {}
+  local seen_comment = false
+  for lineno, line in ipairs(lines) do
+    local comments_in_line = {}
+    for comments in line:gmatch("/%* expect: (.-) %*/$") do
+      for comment in comments:gmatch("[^,]+") do
+	table.insert(comments_in_line, comment:match("^%s*(.-)%s*$"))
+        seen_comment = true
+      end
+    end
+    comments_by_line[lineno] = comments_in_line
+  end
+
+  if seen_comment then return comments_by_line else return nil end
+end
+
+
+local function load_actual_messages_from_exp(fname)
+
+  local lines = load_lines(fname)
+  if lines == nil then return nil end
+
+  local messages = {}
+  for lineno, line in ipairs(lines) do
+    for c_lineno, message in line:gmatch("%S+%((%d+)%): (.+)$") do
+      table.insert(messages, {
+        exp_lineno = lineno,
+        c_lineno = tonumber(c_lineno),
+        msg = message
+      })
+    end
+  end
+
+  return messages
+end
+
+
+local function check_test(c_fname, errors)
+  local exp_fname = c_fname:gsub("%.c$", ".exp")
+  local comments = load_expect_comments_from_c(c_fname)
+  if comments == nil or #comments == 0 then return end
+  local messages = load_actual_messages_from_exp(exp_fname)
+  if messages == nil then return end
+
+  local remaining = 0
+  for lineno, exps in ipairs(comments) do
+    for _, msg in ipairs(exps) do
+      -- print("comment", lineno, msg)
+      remaining = remaining + 1
+    end
+  end
+
+  for _, act in ipairs(messages) do
+    -- print("messages", act.exp_lineno, act.c_lineno, act.msg)
+
+    local exp = comments[act.c_lineno] or {}
+
+    local found = false
+    for i, msg in ipairs(exp) do
+      if msg ~= "" and act.msg:find(msg, 1, true) then
+        exp[i] = ""
+        found = true
+        remaining = remaining - 1
+        -- print("found", act.c_lineno, act.msg, msg, remaining)
+        break
+      end
+    end
+
+    if not found then
+      errors:add("error: %s:%d: message \"%s\" is not declared in %s:%d",
+        exp_fname, act.exp_lineno, act.msg, c_fname, act.c_lineno)
+    end
+  end
+
+  for lineno, exps in ipairs(comments) do
+    for _, msg in ipairs(exps) do
+      if msg ~= "" then
+        errors:add("error: %s:%d: declared message \"%s\" is not in the actual output",
+          c_fname, lineno, msg)
+      end
+    end
+  end
+end
+
+
+local function main(args)
+  local errors = {}
+  errors.add = function(self, fmt, ...)
+    table.insert(self, string.format(fmt, ...))
+  end
+
+  for _, name in ipairs(args) do
+    check_test(name, errors)
+  end
+
+  for _, error in ipairs(errors) do
+    print(error)
+  end
+
+  return #errors == 0
+end
+
+os.exit(main(arg))

Reply via email to