xref: /netbsd-src/tests/usr.bin/xlint/lint1/msg_247.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: msg_247.c,v 1.13 2021/04/09 20:00:07 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 	 *  when the parser reduces 'T_TYPENAME clrtyp' to 'clrtyp_typespec'.
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 	display = (PDisplay)arg;	/* expect: 247 */
32 }
33 
34 /*
35  * C code with a long history that has existed in pre-C90 times already often
36  * uses 'pointer to char' where modern code would use 'pointer to void'.
37  * Since 'char' is the most general underlying type, there is nothing wrong
38  * with casting to it.  An example for this type of code is X11.
39  *
40  * Casting to 'pointer to char' may also be used by programmers who don't know
41  * about endianness, but that's not something lint can do anything about.  The
42  * code for these two use cases looks exactly the same, so lint errs on the
43  * side of fewer false positive warnings here.
44  */
45 char *
46 cast_to_char_pointer(struct Other *arg)
47 {
48 	return (char *)arg;
49 }
50 
51 /*
52  * In traditional C there was 'unsigned char' as well, so the same reasoning
53  * as for plain 'char' applies here.
54  */
55 unsigned char *
56 cast_to_unsigned_char_pointer(struct Other *arg)
57 {
58 	return (unsigned char *)arg;
59 }
60 
61 /*
62  * Traditional C does not have the type specifier 'signed', which means that
63  * this type cannot be used by old code.  Therefore warn about this.  All code
64  * that triggers this warning should do the intermediate cast via 'void
65  * pointer'.
66  */
67 signed char *
68 cast_to_signed_char_pointer(struct Other *arg)
69 {
70 	return (signed char *)arg;	/* expect: 247 */
71 }
72 
73 char *
74 cast_to_void_pointer_then_to_char_pointer(struct Other *arg)
75 {
76 	return (char *)(void *)arg;
77 }
78 
79 
80 /*
81  * When implementing types that have a public part that is exposed to the user
82  * (in this case 'struct counter') and a private part that is only visible to
83  * the implementation (in this case 'struct counter_impl'), a common
84  * implementation technique is to use a struct in which the public part is the
85  * first member.  C guarantees that the pointer to the first member is at the
86  * same address as the pointer to the whole struct.
87  *
88  * Seen in external/mpl/bind/dist/lib/isc/mem.c for 'struct isc_mem' and
89  * 'struct isc__mem'.
90  */
91 
92 struct counter {
93 	int count;
94 };
95 
96 struct counter_impl {
97 	struct counter public_part;
98 	int saved_count;
99 };
100 
101 void *allocate(void);
102 
103 struct counter *
104 counter_new(void)
105 {
106 	struct counter_impl *impl = allocate();
107 	impl->public_part.count = 12345;
108 	impl->saved_count = 12346;
109 	return &impl->public_part;
110 }
111 
112 void
113 counter_increment(struct counter *counter)
114 {
115 	/*
116 	 * Before tree.c 1.272 from 2021-04-08, lint warned about the cast
117 	 * from 'struct counter' to 'struct counter_impl'.
118 	 */
119 	struct counter_impl *impl = (struct counter_impl *)counter;
120 	impl->saved_count = impl->public_part.count;
121 	impl->public_part.count++;
122 }
123 
124 
125 /*
126  * In OpenSSL, the hashing API uses the incomplete 'struct lhash_st' for their
127  * type-generic hashing API while defining a separate struct for each type to
128  * be hashed.
129  *
130  * Before 2021-04-09, in a typical NetBSD build this led to about 38,000 lint
131  * warnings about possibly troublesome pointer casts.
132  */
133 
134 struct lhash_st;		/* expect: struct lhash_st never defined */
135 
136 struct lhash_st *OPENSSL_LH_new(void);
137 
138 struct lhash_st_OPENSSL_STRING {
139 	union lh_OPENSSL_STRING_dummy {
140 		void *d1;
141 		unsigned long d2;
142 		int d3;
143 	} dummy;
144 };
145 
146 # 196 "lhash.h" 1 3 4
147 struct lhash_st_OPENSSL_STRING *
148 lh_OPENSSL_STRING_new(void)
149 {
150 	/*
151 	 * Since tree.c 1.274 from 2021-04-09, lint does not warn about casts
152 	 * to or from incomplete structs anymore.
153 	 */
154 	return (struct lhash_st_OPENSSL_STRING *)OPENSSL_LH_new();
155 }
156 # 157 "msg_247.c" 2
157