Module Name: src Committed By: rillig Date: Sun May 12 11:46:14 UTC 2024
Modified Files: src/tests/usr.bin/xlint/lint1: d_c99_bool_strict.c d_c99_bool_strict_syshdr.c Log Message: tests/lint: clean up tests for strict bool mode To generate a diff of this commit: cvs rdiff -u -r1.48 -r1.49 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c cvs rdiff -u -r1.22 -r1.23 \ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.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.48 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.49 --- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.48 Sat Dec 30 17:09:42 2023 +++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Sun May 12 11:46:14 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: d_c99_bool_strict.c,v 1.48 2023/12/30 17:09:42 rillig Exp $ */ +/* $NetBSD: d_c99_bool_strict.c,v 1.49 2024/05/12 11:46:14 rillig Exp $ */ # 3 "d_c99_bool_strict.c" /* @@ -27,7 +27,7 @@ * * strict-bool-controlling-expression: * Controlling expressions in 'if', 'while', 'for', '?:' must be of - * type bool. + * type bool, except for a literal 0 in a do-while loop. * * strict-bool-operand-unary: * Operator bool? scalar? @@ -62,29 +62,17 @@ * the resulting value is used in a context where it is implicitly and * immediately compared to zero. * - * Note: An efficient implementation technique for a collection of bool - * flags is an enum. The enum declaration groups the available - * constants, and as of 2020, compilers such as GCC and Clang have basic - * support for detecting type mismatches on enums. Another implementation - * technique for bit sets is a plain integer. - * * Note: Examples for such contexts are controlling expressions or the * operands of the operators '!', '&&', '||'. * - * Note: Counterexamples for contexts are assignments to a bool variable. - * - * Note: These rules ensure that conforming code can be compiled without - * change in behavior using old compilers that implement bool as an - * ordinary integer type, without the special rule C99 6.3.1.2. - * - * Note: There is a crucial difference between a _Bool variable and an - * ordinary 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. + * Note: Counterexamples for contexts are assignments to a bool variable, + * as without the conversion from C99 6.3.1.2, converting an integer to a + * "bool-like" integer type truncated the value instead of comparing it + * to 0. + * + * Note: These rules ensure that conforming code behaves the same in both + * C99 and in environments that emulate a boolean type using a small + * integer type. */ /* @@ -413,9 +401,7 @@ strict_bool_conversion_from_bool_to_scal } /* - * strict-bool-controlling-expression: - * Controlling expressions in 'if', 'while', 'for', '?:' must be of - * type bool. + * strict-bool-controlling-expression */ void @@ -466,14 +452,36 @@ strict_bool_controlling_expression(bool do_nothing(); if (p != (void *)0) do_nothing(); + + // An endless loop. The preferred form is 'for (;;)' instead. + do { + /* expect+1: warning: constant in conditional context [161] */ + } while (__lint_true); + + // A do-once "loop", often used in statement macros. + /* expect+1: warning: loop not entered at top [207] */ + do { + } while (__lint_false); + + // This form is too unusual to be allowed in strict bool mode. + do { + /* expect+2: error: controlling expression must be bool, not 'int' [333] */ + /* expect+1: warning: constant in conditional context [161] */ + } while (1); + + // Even though 0 is an integer instead of a bool, this idiom is so + // common that it is frequently used in system headers. Since the + // Clang preprocessor does not mark each token as coming from a system + // header or from user code, this idiom can only be allowed everywhere + // or nowhere. + /* expect+1: warning: loop not entered at top [207] */ + do { + /* expect+1: error: controlling expression must be bool, not 'int' [333] */ + } while (0); } /* - * strict-bool-operand-unary: - * Operator bool? scalar? - * ! yes - - * & yes yes - * The other unary operators do not accept bool operands. + * strict-bool-operand-unary */ void @@ -517,23 +525,7 @@ strict_bool_operand_unary_address(void) /* see strict_bool_operand_unary_all below for the other unary operators. */ /* - * strict-bool-operand-binary: - * Operator left: bool? other? right: bool? other? - * . - yes yes yes - * -> - yes yes yes - * <=, <, >=, > - yes - yes - * ==, != yes yes yes yes - * & yes yes yes yes - * ^ yes yes yes yes - * | yes yes yes yes - * && yes - yes - - * || yes - yes - - * ? yes - yes yes - * : yes yes yes yes - * = yes yes yes yes - * &=, ^=, |= yes yes yes yes - * , yes yes yes yes - * The other binary operators do not accept bool operands. + * strict-bool-operand-binary */ /* @@ -739,9 +731,7 @@ strict_bool_operand_binary_comma(bool b, } /* - * strict-bool-operator-result: - * The result type of the operators '!', '<', '<=', '>', '>=', - * '==', '!=', '&&', '||' is _Bool instead of int. + * strict-bool-operator-result */ void @@ -793,20 +783,7 @@ strict_bool_operator_result(bool b) /* - * strict-bool-bitwise-and: - * Expressions of the form "flags & FLAG" are compatible with _Bool if - * the resulting value is used in a context where it is implicitly and - * immediately compared to zero. - * - * Note: Examples for such contexts are controlling expressions or the - * operands of the operators '!', '&&', '||'. - * - * Note: Counterexamples for contexts are assignments to a bool variable, - * as before C99, the conversion was defined differently. - * - * Note: These rules ensure that conforming code can be compiled without - * change in behavior using old compilers that implement bool as an - * ordinary integer type, without the special rule C99 6.3.1.2. + * strict-bool-bitwise-and */ enum Flags { @@ -955,23 +932,6 @@ bool_as_array_index(bool cond) } void -do_while_false(void) -{ - do { - - } while (__lint_false); -} - -void -do_while_true(void) -{ - do { - - } while (__lint_true); - /* expect-1: warning: constant in conditional context [161] */ -} - -void initialization(void) { struct { @@ -985,127 +945,3 @@ initialization(void) { 1 }, }; } - -/* - * For expressions that originate from a system header, the strict type rules - * are relaxed a bit, to allow for expressions like 'flags & FLAG', even - * though they are not strictly boolean. - * - * This shouldn't apply to function call expressions though since one of the - * goals of strict bool mode is to normalize all expressions calling 'strcmp' - * to be of the form 'strcmp(a, b) == 0' instead of '!strcmp(a, b)'. - */ -# 1 "stdio.h" 1 3 4 -typedef struct stdio_file { - int fd; -} FILE; -int ferror(FILE *); -FILE stdio_files[3]; -FILE *stdio_stdout; -# 1006 "d_c99_bool_strict.c" 2 -# 1 "string.h" 1 3 4 -int strcmp(const char *, const char *); -# 1009 "d_c99_bool_strict.c" 2 - -void -controlling_expression(FILE *f, const char *a, const char *b) -{ - /* expect+1: error: controlling expression must be bool, not 'int' [333] */ - if (ferror(f)) - return; - /* expect+1: error: controlling expression must be bool, not 'int' [333] */ - if (strcmp(a, b)) - return; - /* expect+1: error: operand of '!' must be bool, not 'int' [330] */ - if (!ferror(f)) - return; - /* expect+1: error: operand of '!' must be bool, not 'int' [330] */ - if (!strcmp(a, b)) - return; - - /* - * Before tree.c 1.395 from 2021-11-16, the expression below didn't - * produce a warning since the expression 'stdio_files' came from a - * system header (via a macro), and this property was passed up to - * the expression 'ferror(stdio_files[1])'. - * - * That was wrong though since the type of a function call expression - * only depends on the function itself but not its arguments types. - * The old rule had allowed a raw condition 'strcmp(a, b)' without - * the comparison '!= 0', as long as one of its arguments came from a - * system header. - * - * Seen in bin/echo/echo.c, function main, call to ferror. - */ - /* expect+5: error: controlling expression must be bool, not 'int' [333] */ - if (ferror( -# 1043 "d_c99_bool_strict.c" 3 4 - &stdio_files[1] -# 1045 "d_c99_bool_strict.c" - )) - return; - - /* - * Before cgram.y 1.369 from 2021-11-16, at the end of parsing the - * name 'stdio_stdout', the parser already looked ahead to the next - * token, to see whether it was the '(' of a function call. - * - * At that point, the parser was no longer in a system header, - * therefore 'stdio_stdout' had tn_sys == false, and this information - * was pushed down to the whole function call expression (which was - * another bug that got fixed in tree.c 1.395 from 2021-11-16). - */ - /* expect+5: error: controlling expression must be bool, not 'int' [333] */ - if (ferror( -# 1061 "d_c99_bool_strict.c" 3 4 - stdio_stdout -# 1063 "d_c99_bool_strict.c" - )) - return; - - /* - * In this variant of the pattern, there is a token ')' after the - * name 'stdio_stdout', which even before tree.c 1.395 from - * 2021-11-16 had the effect that at the end of parsing the name, the - * parser was still in the system header, thus setting tn_sys (or - * rather tn_relaxed at that time) to true. - */ - /* expect+5: error: controlling expression must be bool, not 'int' [333] */ - if (ferror( -# 1076 "d_c99_bool_strict.c" 3 4 - (stdio_stdout) -# 1078 "d_c99_bool_strict.c" - )) - return; - - /* - * Before cgram.y 1.369 from 2021-11-16, the comment following - * 'stdio_stdout' did not prevent the search for '('. At the point - * where build_name called expr_alloc_tnode, the parser was already - * in the main file again, thus treating 'stdio_stdout' as not coming - * from a system header. - * - * This has been fixed in tree.c 1.395 from 2021-11-16. Before that, - * an expression had come from a system header if its operands came - * from a system header, but that was only close to the truth. In a - * case where both operands come from a system header but the - * operator comes from the main translation unit, the main - * translation unit still has control over the whole expression. So - * the correct approach is to focus on the operator, not the - * operands. There are a few corner cases where the operator is - * invisible (for implicit conversions) or synthetic (for translating - * 'arr[index]' to '*(arr + index)', but these are handled as well. - */ - /* expect+5: error: controlling expression must be bool, not 'int' [333] */ - if (ferror( -# 1102 "d_c99_bool_strict.c" 3 4 - stdio_stdout /* comment */ -# 1104 "d_c99_bool_strict.c" - )) - return; -} - -// In strict bool mode, the identifiers '__lint_false' and '__lint_true' are -// predefined, but not any others. -/* expect+1: error: '__lint_unknown' undefined [99] */ -int unknown = sizeof __lint_unknown; Index: src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c diff -u src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.22 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.23 --- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.22 Sun Aug 6 19:44:50 2023 +++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c Sun May 12 11:46:14 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: d_c99_bool_strict_syshdr.c,v 1.22 2023/08/06 19:44:50 rillig Exp $ */ +/* $NetBSD: d_c99_bool_strict_syshdr.c,v 1.23 2024/05/12 11:46:14 rillig Exp $ */ # 3 "d_c99_bool_strict_syshdr.c" /* @@ -286,3 +286,123 @@ if_pointer_or_int(void) ) return; } + + +/* + * For expressions that originate from a system header, the strict type rules + * are relaxed a bit, to allow for expressions like 'flags & FLAG', even + * though they are not strictly boolean. + * + * This shouldn't apply to function call expressions though since one of the + * goals of strict bool mode is to normalize all expressions calling 'strcmp' + * to be of the form 'strcmp(a, b) == 0' instead of '!strcmp(a, b)'. + */ +# 1 "stdio.h" 1 3 4 +typedef struct stdio_file { + int fd; +} FILE; +int ferror(FILE *); +FILE stdio_files[3]; +FILE *stdio_stdout; +# 308 "d_c99_bool_strict_syshdr.c" 2 +# 1 "string.h" 1 3 4 +int strcmp(const char *, const char *); +# 311 "d_c99_bool_strict_syshdr.c" 2 + +void +controlling_expression(FILE *f, const char *a, const char *b) +{ + /* expect+1: error: controlling expression must be bool, not 'int' [333] */ + if (ferror(f)) + return; + /* expect+1: error: controlling expression must be bool, not 'int' [333] */ + if (strcmp(a, b)) + return; + /* expect+1: error: operand of '!' must be bool, not 'int' [330] */ + if (!ferror(f)) + return; + /* expect+1: error: operand of '!' must be bool, not 'int' [330] */ + if (!strcmp(a, b)) + return; + + /* + * Before tree.c 1.395 from 2021-11-16, the expression below didn't + * produce a warning since the expression 'stdio_files' came from a + * system header (via a macro), and this property was passed up to + * the expression 'ferror(stdio_files[1])'. + * + * That was wrong though since the type of a function call expression + * only depends on the function itself but not its arguments types. + * The old rule had allowed a raw condition 'strcmp(a, b)' without + * the comparison '!= 0', as long as one of its arguments came from a + * system header. + * + * Seen in bin/echo/echo.c, function main, call to ferror. + */ + /* expect+5: error: controlling expression must be bool, not 'int' [333] */ + if (ferror( +# 345 "d_c99_bool_strict_syshdr.c" 3 4 + &stdio_files[1] +# 347 "d_c99_bool_strict_syshdr.c" + )) + return; + + /* + * Before cgram.y 1.369 from 2021-11-16, at the end of parsing the + * name 'stdio_stdout', the parser already looked ahead to the next + * token, to see whether it was the '(' of a function call. + * + * At that point, the parser was no longer in a system header, + * therefore 'stdio_stdout' had tn_sys == false, and this information + * was pushed down to the whole function call expression (which was + * another bug that got fixed in tree.c 1.395 from 2021-11-16). + */ + /* expect+5: error: controlling expression must be bool, not 'int' [333] */ + if (ferror( +# 363 "d_c99_bool_strict_syshdr.c" 3 4 + stdio_stdout +# 365 "d_c99_bool_strict_syshdr.c" + )) + return; + + /* + * In this variant of the pattern, there is a token ')' after the + * name 'stdio_stdout', which even before tree.c 1.395 from + * 2021-11-16 had the effect that at the end of parsing the name, the + * parser was still in the system header, thus setting tn_sys (or + * rather tn_relaxed at that time) to true. + */ + /* expect+5: error: controlling expression must be bool, not 'int' [333] */ + if (ferror( +# 378 "d_c99_bool_strict_syshdr.c" 3 4 + (stdio_stdout) +# 380 "d_c99_bool_strict_syshdr.c" + )) + return; + + /* + * Before cgram.y 1.369 from 2021-11-16, the comment following + * 'stdio_stdout' did not prevent the search for '('. At the point + * where build_name called expr_alloc_tnode, the parser was already + * in the main file again, thus treating 'stdio_stdout' as not coming + * from a system header. + * + * This has been fixed in tree.c 1.395 from 2021-11-16. Before that, + * an expression had come from a system header if its operands came + * from a system header, but that was only close to the truth. In a + * case where both operands come from a system header but the + * operator comes from the main translation unit, the main + * translation unit still has control over the whole expression. So + * the correct approach is to focus on the operator, not the + * operands. There are a few corner cases where the operator is + * invisible (for implicit conversions) or synthetic (for translating + * 'arr[index]' to '*(arr + index)', but these are handled as well. + */ + /* expect+5: error: controlling expression must be bool, not 'int' [333] */ + if (ferror( +# 404 "d_c99_bool_strict_syshdr.c" 3 4 + stdio_stdout /* comment */ +# 406 "d_c99_bool_strict_syshdr.c" + )) + return; +}