Home | History | Annotate | Line # | Download | only in lint1
      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
     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
     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
     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