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