Home | History | Annotate | Line # | Download | only in lint1
d_c99_bool_strict_syshdr.c revision 1.9
      1 /*	$NetBSD: d_c99_bool_strict_syshdr.c,v 1.9 2021/04/05 01:35:34 rillig Exp $	*/
      2 # 3 "d_c99_bool_strict_syshdr.c"
      3 
      4 /*
      5  * In strict bool mode, lint treats bool as incompatible with any other scalar
      6  * types.  This mode helps in migrating code from pre-C99 to C99.
      7  *
      8  * System headers, on the other hand, cannot be migrated if they need to stay
      9  * compatible with pre-C99 code.  Therefore, the checks for system headers are
     10  * loosened.  In contexts where a scalar expression is compared to 0, macros
     11  * and functions from system headers may use int expressions as well.
     12  *
     13  * These headers are not allowed to include <stdbool.h>[references needed].
     14  * Doing so would inject lint's own <stdbool.h>, which defines the macros
     15  * false and true to other identifiers instead of the plain 0 and 1, thereby
     16  * allowing to see whether the code really uses true and false as identifiers.
     17  *
     18  * Since the system headers cannot include <stdbool.h>, they need to use the
     19  * traditional bool constants 0 and 1.
     20  */
     21 
     22 /* lint1-extra-flags: -T */
     23 
     24 extern const unsigned short *ctype_table;
     25 
     26 extern void println(const char *);
     27 
     28 /*
     29  * On NetBSD 8, <sys/select.h> defines FD_ISSET by enclosing the statements
     30  * in the well-known 'do { ... } while (CONSTCOND 0)' loop.  The 0 in the
     31  * controlling expression has type INT but should be allowed nevertheless
     32  * since that header does not have a way to distinguish between bool and int.
     33  * It just follows the C99 standard, unlike the lint-provided stdbool.h, which
     34  * redefines 'false' to '__lint_false'.  Plus, <sys/select.h> must not include
     35  * <stdbool.h> itself.
     36  */
     37 void
     38 strict_bool_system_header_statement_macro(void)
     39 {
     40 
     41 	do {
     42 		println("nothing");
     43 	} while (/*CONSTCOND*/0);	/* expect: 333 */
     44 
     45 # 46 "d_c99_bool_strict_syshdr.c" 3 4
     46 	do {
     47 		println("nothing");
     48 	} while (/*CONSTCOND*/0);	/* ok */
     49 
     50 # 51 "d_c99_bool_strict_syshdr.c"
     51 	do {
     52 		println("nothing");
     53 	} while (/*CONSTCOND*/0);	/* expect: 333 */
     54 }
     55 
     56 
     57 /*
     58  * The macros from <ctype.h> can be implemented in different ways.  The C
     59  * standard defines them as returning 'int'.  In strict bool mode, the actual
     60  * return type can be INT or BOOL, depending on whether the macros do the
     61  * comparison against 0 themselves.
     62  *
     63  * Since that comparison is more code to write and in exceptional situations
     64  * more code to execute, they will probably leave out the extra comparison,
     65  * but both ways are possible.
     66  *
     67  * In strict bool mode, there must be a way to call these function-like macros
     68  * portably, without triggering type errors, no matter whether they return
     69  * BOOL or INT.
     70  *
     71  * The expressions from this example cross the boundary between system header
     72  * and application code.  They need to carry the information that they are
     73  * half-BOOL, half-INT across to the enclosing expressions.
     74  */
     75 void
     76 strict_bool_system_header_ctype(int c)
     77 {
     78 	/*
     79 	 * The macro returns INT, which may be outside the range of a
     80 	 * uint8_t variable, therefore it must not be assigned directly.
     81 	 * All other combinations of type are safe from truncation.
     82 	 */
     83 	_Bool system_int_assigned_to_bool =
     84 # 85 "d_c99_bool_strict_syshdr.c" 3 4
     85 	    (int)((ctype_table + 1)[c] & 0x0040)	/* INT */
     86 # 87 "d_c99_bool_strict_syshdr.c"
     87 	;			/* expect: 107 */
     88 
     89 	int system_bool_assigned_to_int =
     90 # 91 "d_c99_bool_strict_syshdr.c" 3 4
     91 	    (int)((ctype_table + 1)[c] & 0x0040) != 0	/* BOOL */
     92 # 93 "d_c99_bool_strict_syshdr.c"
     93 	;
     94 
     95 	if (
     96 # 97 "d_c99_bool_strict_syshdr.c" 3 4
     97 	    (int)((ctype_table + 1)[c] & 0x0040)	/* INT */
     98 # 99 "d_c99_bool_strict_syshdr.c"
     99 	)
    100 		println("system macro returning INT");
    101 
    102 	if (
    103 # 104 "d_c99_bool_strict_syshdr.c" 3 4
    104 	    ((ctype_table + 1)[c] & 0x0040) != 0	/* BOOL */
    105 # 106 "d_c99_bool_strict_syshdr.c"
    106 	)
    107 		println("system macro returning BOOL");
    108 }
    109 
    110 static inline _Bool
    111 ch_isspace_sys_int(char c)
    112 {
    113 	return
    114 # 115 "d_c99_bool_strict_syshdr.c" 3 4
    115 	    ((ctype_table + 1)[c] & 0x0040)
    116 # 117 "d_c99_bool_strict_syshdr.c"
    117 	    != 0;
    118 }
    119 
    120 /*
    121  * isspace is defined to return an int.  Comparing this int with 0 is the
    122  * safe way to convert it to _Bool.  This must be allowed even if isspace
    123  * does the comparison itself.
    124  */
    125 static inline _Bool
    126 ch_isspace_sys_bool(char c)
    127 {
    128 	return
    129 # 130 "d_c99_bool_strict_syshdr.c" 3 4
    130 	    ((ctype_table + 1)[(unsigned char)c] & 0x0040) != 0
    131 # 132 "d_c99_bool_strict_syshdr.c"
    132 	    != 0;
    133 }
    134 
    135 /*
    136  * There are several functions from system headers that have return type
    137  * int.  For this return type there are many API conventions:
    138  *
    139  *	* isspace: 0 means no, non-zero means yes
    140  *	* open: 0 means success, -1 means failure
    141  *	* main: 0 means success, non-zero means failure
    142  *	* strcmp: 0 means equal, < 0 means less than, > 0 means greater than
    143  *
    144  * Without a detailed list of individual functions, it's not possible to
    145  * guess what the return value means.  Therefore in strict bool mode, the
    146  * return value of these functions cannot be implicitly converted to bool,
    147  * not even in a context where the result is compared to 0.  Allowing that
    148  * would allow expressions like !strcmp(s1, s2), which is not correct since
    149  * strcmp returns an "ordered comparison result", not a bool.
    150  */
    151 
    152 # 1 "math.h" 3 4
    153 extern int finite(double);
    154 # 1 "string.h" 3 4
    155 extern int strcmp(const char *, const char *);
    156 # 157 "d_c99_bool_strict_syshdr.c"
    157 
    158 /*ARGSUSED*/
    159 _Bool
    160 call_finite_bad(double d)
    161 {
    162 	return finite(d);	/* expect: 211 */
    163 }
    164 
    165 _Bool
    166 call_finite_good(double d)
    167 {
    168 	return finite(d) != 0;
    169 }
    170 
    171 /*ARGSUSED*/
    172 _Bool
    173 str_equal_bad(const char *s1, const char *s2)
    174 {
    175 	return !strcmp(s1, s2);	/* expect: 330 *//* expect: 214 */
    176 }
    177 
    178 _Bool
    179 str_equal_good(const char *s1, const char *s2)
    180 {
    181 	return strcmp(s1, s2) == 0;
    182 }
    183