xref: /netbsd-src/tests/usr.bin/xlint/lint1/msg_130.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: msg_130.c,v 1.14 2021/05/25 19:04:07 rillig Exp $	*/
2 # 3 "msg_130.c"
3 
4 // Test for message: enum type mismatch: '%s' '%s' '%s' [130]
5 
6 /* See also msg_241.c, which covers unusual operators on enums. */
7 
8 enum color {
9 	RED	= 1 << 0,
10 	GREEN	= 1 << 1,
11 	BLUE	= 1 << 2
12 };
13 
14 enum size {
15 	SMALL,
16 	MEDIUM,
17 	LARGE
18 };
19 
20 enum daytime {
21 	NIGHT, MORNING, NOON, EVENING
22 };
23 
24 void sink(_Bool);
25 
26 void
27 example(_Bool cond, enum color c, enum size s)
28 {
29 	sink(cond ? GREEN : MORNING);	/* expect: 130 */
30 
31 	sink(c != s);			/* expect: 130 */
32 	sink(c == s);			/* expect: 130 */
33 	sink((c & MEDIUM) != 0);	/* might be useful to warn about */
34 	sink((c | MEDIUM) != 0);	/* might be useful to warn about */
35 
36 	c |= MEDIUM;			/* might be useful to warn about */
37 	c &= MEDIUM;			/* might be useful to warn about */
38 
39 	/* The cast to unsigned is required by GCC at WARNS=6. */
40 	c &= ~(unsigned)MEDIUM;		/* might be useful to warn about */
41 }
42 
43 void
44 switch_example(enum color c)
45 {
46 	switch (c) {
47 	case EVENING:			/* maybe someday expect: 130 */
48 	case LARGE:			/* maybe someday expect: 130 */
49 	case 0:				/* maybe someday expect: 130 */
50 		sink(1 == 1);
51 		break;
52 	default:
53 		break;
54 	}
55 }
56 
57 /*
58  * Unnamed enum types can be used as a container for constants, especially
59  * since in C90 and C99, even after the declaration 'static const int x = 3',
60  * 'x' is not a constant expression.
61  */
62 enum {
63 	sizeof_int = sizeof(int),
64 	sizeof_short = sizeof(short)
65 };
66 
67 enum {
68 	sizeof_uint = sizeof(unsigned int)
69 };
70 
71 int
72 enum_constant_from_unnamed_type(int x)
73 {
74 	/* using an enum constant as constant-expression */
75 	switch (x) {
76 	case sizeof_int:
77 		return 1;
78 	case sizeof_short:
79 		return 2;
80 	default:
81 		break;
82 	}
83 
84 	if (x == sizeof_int)
85 		return 4;
86 	if (x > sizeof_int)
87 		return 5;
88 
89 	if (sizeof_int == sizeof_uint)	/* expect: 130 *//* FIXME */
90 		return 6;
91 
92 	return 0;		/* expect: statement not reached */
93 }
94 
95 /*
96  * A typical legitimate use case for an anonymous enum type that should not
97  * be mixed with other types is a state machine.
98  *
99  * This example demonstrates that the type of the 'switch' expression can be
100  * an anonymous enum.
101  */
102 void
103 state_machine(const char *str)
104 {
105 	enum {
106 		begin,
107 		seen_letter,
108 		seen_letter_digit,
109 		error
110 	} state = begin;
111 
112 	for (const char *p = str; *p != '\0'; p++) {
113 		switch (state) {
114 		case begin:
115 			state = *p == 'A' ? seen_letter : error;
116 			break;
117 		case seen_letter:
118 			state = *p == '1' ? seen_letter_digit : error;
119 			break;
120 		default:
121 			state = error;
122 		}
123 	}
124 
125 	if (state == 2)			/* might be worth a warning */
126 		return;
127 	if (state == sizeof_int)	/* expect: 130 */
128 		return;
129 }
130 
131 /*
132  * For check_case_label_enum, a warning only makes sense if the type of the
133  * enum can actually be specified somehow, either explicitly by using a tag
134  * name or a typedef name, or implicitly by using a variable in a switch
135  * expression.
136  */
137 
138 typedef enum {
139 	has_typedef = 1001
140 } typedef_name;
141 
142 enum tag_name {
143 	has_tag = 1002
144 };
145 
146 enum {
147 	has_variable = 1003
148 } variable;
149 
150 enum {
151 	inaccessible = 1004
152 };
153 
154 /*
155  * This check is already done by Clang, so it may not be necessary to add it
156  * to lint as well.  Except if there are some cases that Clang didn't
157  * implement.
158  */
159 void
160 test_check_case_label_enum(enum color color)
161 {
162 	switch (color)
163 	{
164 	case has_typedef:
165 	case has_tag:
166 	case has_variable:
167 	case inaccessible:
168 		return;
169 	}
170 }
171