1*def8e179Srillig /* $NetBSD: msg_168.c,v 1.13 2024/03/30 17:12:26 rillig Exp $ */
2a0a15c14Srillig # 3 "msg_168.c"
3a0a15c14Srillig
4*def8e179Srillig // Test for message: array subscript %ju cannot be > %d [168]
5a0a15c14Srillig
6b2baa501Srillig /* lint1-extra-flags: -X 351 */
7b2baa501Srillig
8fe3677c5Srillig void print_string(const char *);
9fe3677c5Srillig void print_char(char);
10fe3677c5Srillig
11fe3677c5Srillig void
example(void)12fe3677c5Srillig example(void)
13fe3677c5Srillig {
1423e30dfbSrillig char buf[20] = {}; /* empty initializer is a GCC extension */
15fe3677c5Srillig
16fe3677c5Srillig print_string(buf + 19); /* inside the array */
17fe3677c5Srillig
18fe3677c5Srillig /*
19fe3677c5Srillig * It is valid to point at the end of the array, but reading a
20fe3677c5Srillig * character from there invokes undefined behavior.
21fe3677c5Srillig *
22fe3677c5Srillig * The pointer to the end of the array is typically used in (begin,
23fe3677c5Srillig * end) tuples. These are more common in C++ than in C though.
24fe3677c5Srillig */
25fe3677c5Srillig print_string(buf + 20);
26fe3677c5Srillig
27fe3677c5Srillig print_string(buf + 21); /* undefined behavior, not detected */
28fe3677c5Srillig
29fe3677c5Srillig print_char(buf[19]);
30c6466ed0Srillig /* expect+1: warning: array subscript 20 cannot be > 19 [168] */
3165e5c21bSrillig print_char(buf[20]);
32fe3677c5Srillig }
3305f8f43dSrillig
3405f8f43dSrillig void
array_with_c99_initializer(void)3505f8f43dSrillig array_with_c99_initializer(void)
3605f8f43dSrillig {
3705f8f43dSrillig static const char *const to_roman[] = {
3805f8f43dSrillig ['0'] = "undefined",
3905f8f43dSrillig ['5'] = "V",
4005f8f43dSrillig ['9'] = "IX"
4105f8f43dSrillig };
4205f8f43dSrillig
43f829424dSrillig print_string(to_roman['9']);
44c6466ed0Srillig /* expect+1: warning: array subscript 58 cannot be > 57 [168] */
4565e5c21bSrillig print_string(to_roman[':']);
4605f8f43dSrillig }
47354bb84eSrillig
48354bb84eSrillig
49f7a984baSrillig /*
50f7a984baSrillig * In its expression tree, lint represents pointer addition as 'ptr + off',
51f7a984baSrillig * where 'off' is the offset in bytes, regardless of the pointer type.
52f7a984baSrillig *
53f7a984baSrillig * In the below code, the member 'offset_8' has type 'short', and the
54f7a984baSrillig * expression 's->offset_8' is represented as '&s + 8', or more verbose:
55f7a984baSrillig *
56f7a984baSrillig * '+' type 'pointer to short'
57f7a984baSrillig * '&' type 'pointer to struct s'
58f7a984baSrillig * 'name' 's' with auto 'array[1] of struct s', lvalue
59f7a984baSrillig * 'constant' type 'long', value 8
60f7a984baSrillig *
61f7a984baSrillig * The constant 8 differs from the usual model of pointer arithmetics. Since
62f7a984baSrillig * the type of the '&' expression is 'pointer to struct s', adding a constant
63f7a984baSrillig * would rather be interpreted as adding 'constant * sizeof(struct s)', and
64f7a984baSrillig * to access a member, the pointer to 'struct s' would need to be converted
65f7a984baSrillig * to 'pointer of byte' first, then adding the offset 8, then converting the
66f7a984baSrillig * pointer to the target type 'pointer to short'.
67f7a984baSrillig *
68f7a984baSrillig * Lint uses the simpler representation, saving a few conversions on the way.
69f7a984baSrillig * Without this pre-multiplied representation, the below code would generate
70f7a984baSrillig * warnings about out-of-bounds array access, starting with offset_1.
71f7a984baSrillig */
72354bb84eSrillig struct s {
73354bb84eSrillig char offset_0;
74354bb84eSrillig char offset_1;
75354bb84eSrillig int offset_4;
76354bb84eSrillig short offset_8;
77354bb84eSrillig char offset_10;
78354bb84eSrillig };
79354bb84eSrillig
80354bb84eSrillig struct s
s_init(void)81354bb84eSrillig s_init(void)
82354bb84eSrillig {
83354bb84eSrillig struct s s[1];
84354bb84eSrillig s->offset_0 = 1;
85354bb84eSrillig s->offset_1 = 2;
86354bb84eSrillig s->offset_4 = 3;
87354bb84eSrillig s->offset_8 = 4;
88354bb84eSrillig s->offset_10 = 5;
89354bb84eSrillig return s[0];
90354bb84eSrillig }
91