1 /*        $NetBSD: msg_168.c,v 1.13 2024/03/30 17:12:26 rillig Exp $  */
2 # 3 "msg_168.c"
3 
4 // Test for message: array subscript %ju cannot be > %d [168]
5 
6 /* lint1-extra-flags: -X 351 */
7 
8 void print_string(const char *);
9 void print_char(char);
10 
11 void
example(void)12 example(void)
13 {
14           char buf[20] = {};  /* empty initializer is a GCC extension */
15 
16           print_string(buf + 19);       /* inside the array */
17 
18           /*
19            * It is valid to point at the end of the array, but reading a
20            * character from there invokes undefined behavior.
21            *
22            * The pointer to the end of the array is typically used in (begin,
23            * end) tuples.  These are more common in C++ than in C though.
24            */
25           print_string(buf + 20);
26 
27           print_string(buf + 21);       /* undefined behavior, not detected */
28 
29           print_char(buf[19]);
30           /* expect+1: warning: array subscript 20 cannot be > 19 [168] */
31           print_char(buf[20]);
32 }
33 
34 void
array_with_c99_initializer(void)35 array_with_c99_initializer(void)
36 {
37           static const char *const to_roman[] = {
38               ['0'] = "undefined",
39               ['5'] = "V",
40               ['9'] = "IX"
41           };
42 
43           print_string(to_roman['9']);
44           /* expect+1: warning: array subscript 58 cannot be > 57 [168] */
45           print_string(to_roman[':']);
46 }
47 
48 
49 /*
50  * In its expression tree, lint represents pointer addition as 'ptr + off',
51  * where 'off' is the offset in bytes, regardless of the pointer type.
52  *
53  * In the below code, the member 'offset_8' has type 'short', and the
54  * expression 's->offset_8' is represented as '&s + 8', or more verbose:
55  *
56  *        '+' type 'pointer to short'
57  *                  '&' type 'pointer to struct s'
58  *                            'name' 's' with auto 'array[1] of struct s', lvalue
59  *                  'constant' type 'long', value 8
60  *
61  * The constant 8 differs from the usual model of pointer arithmetics.  Since
62  * the type of the '&' expression is 'pointer to struct s', adding a constant
63  * would rather be interpreted as adding 'constant * sizeof(struct s)', and
64  * to access a member, the pointer to 'struct s' would need to be converted
65  * to 'pointer of byte' first, then adding the offset 8, then converting the
66  * pointer to the target type 'pointer to short'.
67  *
68  * Lint uses the simpler representation, saving a few conversions on the way.
69  * Without this pre-multiplied representation, the below code would generate
70  * warnings about out-of-bounds array access, starting with offset_1.
71  */
72 struct s {
73           char offset_0;
74           char offset_1;
75           int offset_4;
76           short offset_8;
77           char offset_10;
78 };
79 
80 struct s
s_init(void)81 s_init(void)
82 {
83           struct s s[1];
84           s->offset_0 = 1;
85           s->offset_1 = 2;
86           s->offset_4 = 3;
87           s->offset_8 = 4;
88           s->offset_10 = 5;
89           return s[0];
90 }
91