Home | History | Annotate | Line # | Download | only in lint1
msg_247.c revision 1.20
      1 /*	$NetBSD: msg_247.c,v 1.20 2022/06/24 19:27:43 rillig Exp $	*/
      2 # 3 "msg_247.c"
      3 
      4 // Test for message: pointer cast from '%s' to '%s' may be troublesome [247]
      5 
      6 /* lint1-extra-flags: -c */
      7 
      8 /* example taken from Xlib.h */
      9 typedef struct {
     10 	int id;
     11 } *PDisplay;
     12 
     13 struct Other {
     14 	int id;
     15 };
     16 
     17 void
     18 example(struct Other *arg)
     19 {
     20 	PDisplay display;
     21 
     22 	/*
     23 	 * XXX: The target type is reported as 'struct <unnamed>'.  In cases
     24 	 *  like these, it would be helpful to print at least the type name
     25 	 *  of the pointer.  This type name though is discarded immediately
     26 	 *  in the grammar rule 'typespec: T_TYPENAME'.
     27 	 *  After that, the target type of the cast is just an unnamed struct,
     28 	 *  with no hint at all that there is a typedef for a pointer to the
     29 	 *  struct.
     30 	 */
     31 	/* expect+1: warning: pointer cast from 'pointer to struct Other' to 'pointer to struct <unnamed>' may be troublesome [247] */
     32 	display = (PDisplay)arg;
     33 }
     34 
     35 /*
     36  * C code with a long history that has existed in pre-C90 times already often
     37  * uses 'pointer to char' where modern code would use 'pointer to void'.
     38  * Since 'char' is the most general underlying type, there is nothing wrong
     39  * with casting to it.  An example for this type of code is X11.
     40  *
     41  * Casting to 'pointer to char' may also be used by programmers who don't know
     42  * about endianness, but that's not something lint can do anything about.  The
     43  * code for these two use cases looks exactly the same, so lint errs on the
     44  * side of fewer false positive warnings here.
     45  */
     46 char *
     47 cast_to_char_pointer(struct Other *arg)
     48 {
     49 	return (char *)arg;
     50 }
     51 
     52 /*
     53  * In traditional C there was 'unsigned char' as well, so the same reasoning
     54  * as for plain 'char' applies here.
     55  */
     56 unsigned char *
     57 cast_to_unsigned_char_pointer(struct Other *arg)
     58 {
     59 	return (unsigned char *)arg;
     60 }
     61 
     62 /*
     63  * Traditional C does not have the type specifier 'signed', which means that
     64  * this type cannot be used by old code.  Therefore warn about this.  All code
     65  * that triggers this warning should do the intermediate cast via 'void
     66  * pointer'.
     67  */
     68 signed char *
     69 cast_to_signed_char_pointer(struct Other *arg)
     70 {
     71 	/* expect+1: warning: pointer cast from 'pointer to struct Other' to 'pointer to signed char' may be troublesome [247] */
     72 	return (signed char *)arg;
     73 }
     74 
     75 char *
     76 cast_to_void_pointer_then_to_char_pointer(struct Other *arg)
     77 {
     78 	return (char *)(void *)arg;
     79 }
     80 
     81 
     82 /*
     83  * When implementing types that have a public part that is exposed to the user
     84  * (in this case 'struct counter') and a private part that is only visible to
     85  * the implementation (in this case 'struct counter_impl'), a common
     86  * implementation technique is to use a struct in which the public part is the
     87  * first member.  C guarantees that the pointer to the first member is at the
     88  * same address as the pointer to the whole struct.
     89  *
     90  * Seen in external/mpl/bind/dist/lib/isc/mem.c for 'struct isc_mem' and
     91  * 'struct isc__mem'.
     92  */
     93 
     94 struct counter {
     95 	int count;
     96 };
     97 
     98 struct counter_impl {
     99 	struct counter public_part;
    100 	int saved_count;
    101 };
    102 
    103 void *allocate(void);
    104 
    105 struct counter *
    106 counter_new(void)
    107 {
    108 	struct counter_impl *impl = allocate();
    109 	impl->public_part.count = 12345;
    110 	impl->saved_count = 12346;
    111 	return &impl->public_part;
    112 }
    113 
    114 void
    115 counter_increment(struct counter *counter)
    116 {
    117 	/*
    118 	 * Before tree.c 1.272 from 2021-04-08, lint warned about the cast
    119 	 * from 'struct counter' to 'struct counter_impl'.
    120 	 */
    121 	struct counter_impl *impl = (struct counter_impl *)counter;
    122 	impl->saved_count = impl->public_part.count;
    123 	impl->public_part.count++;
    124 }
    125 
    126 
    127 /*
    128  * In OpenSSL, the hashing API uses the incomplete 'struct lhash_st' for their
    129  * type-generic hashing API while defining a separate struct for each type to
    130  * be hashed.
    131  *
    132  * Before 2021-04-09, in a typical NetBSD build this led to about 38,000 lint
    133  * warnings about possibly troublesome pointer casts.
    134  */
    135 
    136 /* expect+1: warning: struct 'lhash_st' never defined [233] */
    137 struct lhash_st;
    138 
    139 struct lhash_st *OPENSSL_LH_new(void);
    140 
    141 struct lhash_st_OPENSSL_STRING {
    142 	union lh_OPENSSL_STRING_dummy {
    143 		void *d1;
    144 		unsigned long d2;
    145 		int d3;
    146 	} dummy;
    147 };
    148 
    149 # 196 "lhash.h" 1 3 4
    150 struct lhash_st_OPENSSL_STRING *
    151 lh_OPENSSL_STRING_new(void)
    152 {
    153 	/*
    154 	 * Since tree.c 1.274 from 2021-04-09, lint does not warn about casts
    155 	 * to or from incomplete structs anymore.
    156 	 */
    157 	return (struct lhash_st_OPENSSL_STRING *)OPENSSL_LH_new();
    158 }
    159 # 160 "msg_247.c" 2
    160 
    161 void sink(const void *);
    162 
    163 /*
    164  * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from
    165  * unsigned char or plain char to another type.  These casts often occur in
    166  * traditional code that does not use void pointers, even 30 years after C90
    167  * introduced 'void'.
    168  */
    169 void
    170 unsigned_char_to_unsigned_type(unsigned char *ucp)
    171 {
    172 	unsigned short *usp;
    173 
    174 	usp = (unsigned short *)ucp;
    175 	sink(usp);
    176 }
    177 
    178 /*
    179  * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from
    180  * unsigned char or plain char to another type.  These casts often occur in
    181  * traditional code that does not use void pointers, even 30 years after C90
    182  * introduced 'void'.
    183  */
    184 void
    185 plain_char_to_unsigned_type(char *cp)
    186 {
    187 	unsigned short *usp;
    188 
    189 	usp = (unsigned short *)cp;
    190 	sink(usp);
    191 }
    192 
    193 /*
    194  * Before tree.c 1.460 from 2022-06-24, lint warned about pointer casts from
    195  * unsigned char or plain char to a struct or union type.  These casts often
    196  * occur in traditional code that does not use void pointers, even 30 years
    197  * after C90 introduced 'void'.
    198  */
    199 void
    200 char_to_struct(void *ptr)
    201 {
    202 
    203 	sink((struct counter *)(char *)ptr);
    204 
    205 	sink((struct counter *)(unsigned char *)ptr);
    206 
    207 	/* expect+1: warning: pointer cast from 'pointer to signed char' to 'pointer to struct counter' may be troublesome [247] */
    208 	sink((struct counter *)(signed char *)ptr);
    209 }
    210