xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize/macro-to-enum.cpp (revision 89a1d03e2b379e325daa5249411e414bbd995b5e)
1 // RUN: %check_clang_tidy -std=c++14-or-later %s modernize-macro-to-enum %t -- -- -I%S/Inputs/macro-to-enum -fno-delayed-template-parsing
2 // C++14 or later required for binary literals.
3 
4 #if 1
5 #include "modernize-macro-to-enum.h"
6 
7 // These macros are skipped due to being inside a conditional compilation block.
8 #define GOO_RED 1
9 #define GOO_GREEN 2
10 #define GOO_BLUE 3
11 
12 #endif
13 
14 // Macros expanding to expressions involving only literals are converted.
15 #define EXPR1 1 - 1
16 #define EXPR2 1 + 1
17 #define EXPR3 1 * 1
18 #define EXPR4 1 / 1
19 #define EXPR5 1 | 1
20 #define EXPR6 1 & 1
21 #define EXPR7 1 << 1
22 #define EXPR8 1 >> 1
23 #define EXPR9 1 % 2
24 #define EXPR10 1 ^ 1
25 #define EXPR11 (1 + (2))
26 #define EXPR12 ((1) + (2 + 0) + (1 * 1) + (1 / 1) + (1 | 1 ) + (1 & 1) + (1 << 1) + (1 >> 1) + (1 % 2) + (1 ^ 1))
27 // CHECK-MESSAGES: :[[@LINE-12]]:1: warning: replace macro with enum [modernize-macro-to-enum]
28 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR1' defines an integral constant; prefer an enum instead
29 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR2' defines an integral constant; prefer an enum instead
30 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR3' defines an integral constant; prefer an enum instead
31 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR4' defines an integral constant; prefer an enum instead
32 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR5' defines an integral constant; prefer an enum instead
33 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR6' defines an integral constant; prefer an enum instead
34 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR7' defines an integral constant; prefer an enum instead
35 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR8' defines an integral constant; prefer an enum instead
36 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR9' defines an integral constant; prefer an enum instead
37 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR10' defines an integral constant; prefer an enum instead
38 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR11' defines an integral constant; prefer an enum instead
39 // CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR12' defines an integral constant; prefer an enum instead
40 // CHECK-FIXES: enum {
41 // CHECK-FIXES-NEXT: EXPR1 = 1 - 1,
42 // CHECK-FIXES-NEXT: EXPR2 = 1 + 1,
43 // CHECK-FIXES-NEXT: EXPR3 = 1 * 1,
44 // CHECK-FIXES-NEXT: EXPR4 = 1 / 1,
45 // CHECK-FIXES-NEXT: EXPR5 = 1 | 1,
46 // CHECK-FIXES-NEXT: EXPR6 = 1 & 1,
47 // CHECK-FIXES-NEXT: EXPR7 = 1 << 1,
48 // CHECK-FIXES-NEXT: EXPR8 = 1 >> 1,
49 // CHECK-FIXES-NEXT: EXPR9 = 1 % 2,
50 // CHECK-FIXES-NEXT: EXPR10 = 1 ^ 1,
51 // CHECK-FIXES-NEXT: EXPR11 = (1 + (2)),
52 // CHECK-FIXES-NEXT: EXPR12 = ((1) + (2 + 0) + (1 * 1) + (1 / 1) + (1 | 1 ) + (1 & 1) + (1 << 1) + (1 >> 1) + (1 % 2) + (1 ^ 1))
53 // CHECK-FIXES-NEXT: };
54 
55 #define RED 0xFF0000
56 #define GREEN 0x00FF00
57 #define BLUE 0x0000FF
58 // CHECK-MESSAGES: :[[@LINE-3]]:1: warning: replace macro with enum [modernize-macro-to-enum]
59 // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'RED' defines an integral constant; prefer an enum instead
60 // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GREEN' defines an integral constant; prefer an enum instead
61 // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BLUE' defines an integral constant; prefer an enum instead
62 // CHECK-FIXES: enum {
63 // CHECK-FIXES-NEXT: RED = 0xFF0000,
64 // CHECK-FIXES-NEXT: GREEN = 0x00FF00,
65 // CHECK-FIXES-NEXT: BLUE = 0x0000FF
66 // CHECK-FIXES-NEXT: };
67 
68 // Verify that comments are preserved.
69 #define CoordModeOrigin         0   /* relative to the origin */
70 #define CoordModePrevious       1   /* relative to previous point */
71 // CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
72 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModeOrigin' defines an integral constant; prefer an enum instead
73 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModePrevious' defines an integral constant; prefer an enum instead
74 // CHECK-FIXES: enum {
75 // CHECK-FIXES-NEXT: CoordModeOrigin =         0,   /* relative to the origin */
76 // CHECK-FIXES-NEXT: CoordModePrevious =       1   /* relative to previous point */
77 // CHECK-FIXES-NEXT: };
78 
79 // Verify that multiline comments are preserved.
80 #define BadDrawable         9   /* parameter not a Pixmap or Window */
81 #define BadAccess           10  /* depending on context:
82                                 - key/button already grabbed
83                                 - attempt to free an illegal
84                                   cmap entry
85                                 - attempt to store into a read-only
86                                   color map entry. */
87                                 // - attempt to modify the access control
88                                 //   list from other than the local host.
89                                 //
90 #define BadAlloc            11  /* insufficient resources */
91 // CHECK-MESSAGES: :[[@LINE-11]]:1: warning: replace macro with enum
92 // CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadDrawable' defines an integral constant; prefer an enum instead
93 // CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadAccess' defines an integral constant; prefer an enum instead
94 // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BadAlloc' defines an integral constant; prefer an enum instead
95 // CHECK-FIXES: enum {
96 // CHECK-FIXES-NEXT: BadDrawable =         9,   /* parameter not a Pixmap or Window */
97 // CHECK-FIXES-NEXT: BadAccess =           10,  /* depending on context:
98 // CHECK-FIXES-NEXT:                                 - key/button already grabbed
99 // CHECK-FIXES-NEXT:                                 - attempt to free an illegal
100 // CHECK-FIXES-NEXT:                                   cmap entry
101 // CHECK-FIXES-NEXT:                                 - attempt to store into a read-only
102 // CHECK-FIXES-NEXT:                                   color map entry. */
103 // CHECK-FIXES-NEXT:                                 // - attempt to modify the access control
104 // CHECK-FIXES-NEXT:                                 //   list from other than the local host.
105 // CHECK-FIXES-NEXT:                                 //
106 // CHECK-FIXES-NEXT: BadAlloc =            11  /* insufficient resources */
107 // CHECK-FIXES-NEXT: };
108 
109 // Undefining a macro invalidates adjacent macros
110 // from being considered as an enum.
111 #define REMOVED1 1
112 #define REMOVED2 2
113 #define REMOVED3 3
114 #undef REMOVED2
115 #define VALID1 1
116 // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: replace macro with enum
117 // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: macro 'VALID1' defines an integral constant; prefer an enum instead
118 // CHECK-FIXES: enum {
119 // CHECK-FIXES-NEXT: VALID1 = 1
120 // CHECK-FIXES-NEXT: };
121 
122 #define UNDEF1 1
123 #define UNDEF2 2
124 #define UNDEF3 3
125 
126 // Undefining a macro later invalidates the set of possible adjacent macros
127 // from being considered as an enum.
128 #undef UNDEF2
129 
130 // Integral constants can have an optional sign
131 #define SIGNED1 +1
132 #define SIGNED2 -1
133 // CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
134 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED1' defines an integral constant; prefer an enum instead
135 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED2' defines an integral constant; prefer an enum instead
136 // CHECK-FIXES: enum {
137 // CHECK-FIXES-NEXT: SIGNED1 = +1,
138 // CHECK-FIXES-NEXT: SIGNED2 = -1
139 // CHECK-FIXES-NEXT: };
140 
141 // Integral constants with bitwise negated values
142 #define UNOP1 ~0U
143 #define UNOP2 ~1U
144 // CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
145 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP1' defines an integral constant; prefer an enum instead
146 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP2' defines an integral constant; prefer an enum instead
147 // CHECK-FIXES: enum {
148 // CHECK-FIXES-NEXT: UNOP1 = ~0U,
149 // CHECK-FIXES-NEXT: UNOP2 = ~1U
150 // CHECK-FIXES-NEXT: };
151 
152 // Integral constants in other bases and with suffixes are OK
153 #define BASE1 0777    // octal
154 #define BASE2 0xDEAD  // hexadecimal
155 #define BASE3 0b0011  // binary
156 #define SUFFIX1 +1U
157 #define SUFFIX2 -1L
158 #define SUFFIX3 +1UL
159 #define SUFFIX4 -1LL
160 #define SUFFIX5 +1ULL
161 // CHECK-MESSAGES: :[[@LINE-8]]:1: warning: replace macro with enum
162 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE1' defines an integral constant; prefer an enum instead
163 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE2' defines an integral constant; prefer an enum instead
164 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE3' defines an integral constant; prefer an enum instead
165 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX1' defines an integral constant; prefer an enum instead
166 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX2' defines an integral constant; prefer an enum instead
167 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX3' defines an integral constant; prefer an enum instead
168 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX4' defines an integral constant; prefer an enum instead
169 // CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX5' defines an integral constant; prefer an enum instead
170 // CHECK-FIXES: enum {
171 // CHECK-FIXES-NEXT: BASE1 = 0777,    // octal
172 // CHECK-FIXES-NEXT: BASE2 = 0xDEAD,  // hexadecimal
173 // CHECK-FIXES-NEXT: BASE3 = 0b0011,  // binary
174 // CHECK-FIXES-NEXT: SUFFIX1 = +1U,
175 // CHECK-FIXES-NEXT: SUFFIX2 = -1L,
176 // CHECK-FIXES-NEXT: SUFFIX3 = +1UL,
177 // CHECK-FIXES-NEXT: SUFFIX4 = -1LL,
178 // CHECK-FIXES-NEXT: SUFFIX5 = +1ULL
179 // CHECK-FIXES-NEXT: };
180 
181 // A limited form of constant expression is recognized: a parenthesized
182 // literal or a parenthesized literal with the unary operators +, - or ~.
183 #define PAREN1 (-1)
184 #define PAREN2 (1)
185 #define PAREN3 (+1)
186 #define PAREN4 (~1)
187 // CHECK-MESSAGES: :[[@LINE-4]]:1: warning: replace macro with enum
188 // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN1' defines an integral constant; prefer an enum instead
189 // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN2' defines an integral constant; prefer an enum instead
190 // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN3' defines an integral constant; prefer an enum instead
191 // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN4' defines an integral constant; prefer an enum instead
192 // CHECK-FIXES: enum {
193 // CHECK-FIXES-NEXT: PAREN1 = (-1),
194 // CHECK-FIXES-NEXT: PAREN2 = (1),
195 // CHECK-FIXES-NEXT: PAREN3 = (+1),
196 // CHECK-FIXES-NEXT: PAREN4 = (~1)
197 // CHECK-FIXES-NEXT: };
198 
199 // More complicated parenthesized expressions are excluded.
200 // Expansions that are not surrounded by parentheses are excluded.
201 // Nested matching parentheses are stripped.
202 #define COMPLEX_PAREN1 (x+1)
203 #define COMPLEX_PAREN2 (x+1
204 #define COMPLEX_PAREN3 (())
205 #define COMPLEX_PAREN4 ()
206 #define COMPLEX_PAREN5 (+1)
207 #define COMPLEX_PAREN6 ((+1))
208 // CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
209 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'COMPLEX_PAREN5' defines an integral constant; prefer an enum instead
210 // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'COMPLEX_PAREN6' defines an integral constant; prefer an enum instead
211 // CHECK-FIXES: enum {
212 // CHECK-FIXES-NEXT: COMPLEX_PAREN5 = (+1),
213 // CHECK-FIXES-NEXT: COMPLEX_PAREN6 = ((+1))
214 // CHECK-FIXES-NEXT: };
215 
216 #define GOOD_COMMA (1, 2)
217 // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: replace macro with enum
218 // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: macro 'GOOD_COMMA' defines an integral constant; prefer an enum instead
219 // CHECK-FIXES: enum {
220 // CHECK-FIXES-NEXT: GOOD_COMMA = (1, 2)
221 // CHECK-FIXES-NEXT: };
222 
223 // Macros appearing in conditional expressions can't be replaced
224 // by enums.
225 #define USE_FOO 1
226 #define USE_BAR 0
227 #define USE_IF 1
228 #define USE_ELIF 1
229 #define USE_IFDEF 1
230 #define USE_IFNDEF 1
231 
232 // Undef'ing first and then defining later should still exclude this macro
233 #undef USE_UINT64
234 #define USE_UINT64 0
235 #undef USE_INT64
236 #define USE_INT64 0
237 
238 #if defined(USE_FOO) && USE_FOO
239 extern void foo();
240 #else
foo()241 inline void foo() {}
242 #endif
243 
244 #if USE_BAR
245 extern void bar();
246 #else
bar()247 inline void bar() {}
248 #endif
249 
250 #if USE_IF
used_if()251 inline void used_if() {}
252 #endif
253 
254 #if 0
255 #elif USE_ELIF
used_elif()256 inline void used_elif() {}
257 #endif
258 
259 #ifdef USE_IFDEF
used_ifdef()260 inline void used_ifdef() {}
261 #endif
262 
263 #ifndef USE_IFNDEF
264 #else
used_ifndef()265 inline void used_ifndef() {}
266 #endif
267 
268 // Regular conditional compilation blocks should leave previous
269 // macro enums alone.
270 #if 0
271 #include <non-existent.h>
272 #endif
273 
274 // Conditional compilation blocks invalidate adjacent macros
275 // from being considered as an enum.  Conditionally compiled
276 // blocks could contain macros that should rightly be included
277 // in the enum, but we can't explore multiple branches of a
278 // conditionally compiled section in clang-tidy, only the active
279 // branch based on compilation options.
280 #define CONDITION1 1
281 #define CONDITION2 2
282 #if 0
283 #define CONDITION3 3
284 #else
285 #define CONDITION3 -3
286 #endif
287 
288 #define IFDEF1 1
289 #define IFDEF2 2
290 #ifdef FROB
291 #define IFDEF3 3
292 #endif
293 
294 #define IFNDEF1 1
295 #define IFNDEF2 2
296 #ifndef GOINK
297 #define IFNDEF3 3
298 #endif
299 
300 // Macros used in conditions are invalidated, even if they look
301 // like enums after they are used in conditions.
302 #if DEFINED_LATER1
303 #endif
304 #ifdef DEFINED_LATER2
305 #endif
306 #ifndef DEFINED_LATER3
307 #endif
308 #undef DEFINED_LATER4
309 #if ((defined(DEFINED_LATER5) || DEFINED_LATER6) && DEFINED_LATER7) || (DEFINED_LATER8 > 10)
310 #endif
311 
312 #define DEFINED_LATER1 1
313 #define DEFINED_LATER2 2
314 #define DEFINED_LATER3 3
315 #define DEFINED_LATER4 4
316 #define DEFINED_LATER5 5
317 #define DEFINED_LATER6 6
318 #define DEFINED_LATER7 7
319 #define DEFINED_LATER8 8
320 
321 // Sometimes an argument to ifdef can be classified as a keyword token.
322 #ifdef __restrict
323 #endif
324 
325 // These macros do not expand to integral constants.
326 #define HELLO "Hello, "
327 #define WORLD "World"
328 #define EPS1 1.0F
329 #define EPS2 1e5
330 #define EPS3 1.
331 
332 // Ignore macros invoking comma operator unless they are inside parens.
333 #define BAD_COMMA 1, 2
334 
335 extern void draw(unsigned int Color);
336 
f()337 void f()
338 {
339   // Usage of macros converted to enums should still compile.
340   draw(RED);
341   draw(GREEN | RED);
342   draw(BLUE + RED);
343 }
344 
345 // Ignore macros defined inside a top-level function definition.
g(int x)346 void g(int x)
347 {
348   if (x != 0) {
349 #define INSIDE1 1
350 #define INSIDE2 2
351     if (INSIDE1 > 1) {
352       f();
353     }
354   } else {
355     if (INSIDE2 == 1) {
356       f();
357     }
358   }
359 }
360 
361 // Ignore macros defined inside a top-level function declaration.
362 extern void g2(
363 #define INSIDE3 3
364 #define INSIDE4 4
365 );
366 
367 // Ignore macros defined inside a record (structure) declaration.
368 struct S {
369 #define INSIDE5 5
370 #define INSIDE6 6
371   char storage[INSIDE5];
372 };
373 class C {
374 #define INSIDE7 7
375 #define INSIDE8 8
376 };
377 
378 // Ignore macros defined inside a template function definition.
379 template <int N>
380 #define INSIDE9 9
fn()381 bool fn()
382 {
383 #define INSIDE10 10
384   return INSIDE9 > 1 || INSIDE10 < N;
385 }
386 
387 // Ignore macros defined inside a variable declaration.
388 extern int
389 #define INSIDE11 11
390 v;
391 
392 // Ignore macros defined inside a template class definition.
393 template <int N>
394 class C2 {
395 public:
396 #define INSIDE12 12
397     char storage[N];
f()398   bool f() {
399     return N > INSIDE12;
400   }
401   bool g();
402 };
403 
404 // Ignore macros defined inside a template member function definition.
405 template <int N>
406 #define INSIDE13 13
g()407 bool C2<N>::g() {
408 #define INSIDE14 14
409   return N < INSIDE12 || N > INSIDE13 || INSIDE14 > N;
410 };
411 
412 // Ignore macros defined inside a template type alias.
413 template <typename T>
414 class C3 {
415   T data;
416 };
417 template <typename T>
418 #define INSIDE15 15
419 using Data = C3<T[INSIDE15]>;
420 
421 // Ignore macros defined inside a type alias.
422 using Data2 =
423 #define INSIDE16 16
424     char[INSIDE16];
425 
426 // Ignore macros defined inside a (constexpr) variable definition.
427 constexpr int
428 #define INSIDE17 17
429 value = INSIDE17;
430 
431 // Ignore macros used in the expansion of other macros
432 #define INSIDE18 18
433 #define INSIDE19 19
434 
435 #define CONCAT(n_, s_) n_##s_
436 #define FN_NAME(n_, s_) CONCAT(n_, s_)
437 
438 extern void FN_NAME(g, INSIDE18)();
439 
gg()440 void gg()
441 {
442     g18();
443 }
444