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
239extern void foo();
240#else
241inline void foo() {}
242#endif
243
244#if USE_BAR
245extern void bar();
246#else
247inline void bar() {}
248#endif
249
250#if USE_IF
251inline void used_if() {}
252#endif
253
254#if 0
255#elif USE_ELIF
256inline void used_elif() {}
257#endif
258
259#ifdef USE_IFDEF
260inline void used_ifdef() {}
261#endif
262
263#ifndef USE_IFNDEF
264#else
265inline 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
335extern void draw(unsigned int Color);
336
337void 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.
346void 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.
362extern void g2(
363#define INSIDE3 3
364#define INSIDE4 4
365);
366
367// Ignore macros defined inside a record (structure) declaration.
368struct S {
369#define INSIDE5 5
370#define INSIDE6 6
371 char storage[INSIDE5];
372};
373class C {
374#define INSIDE7 7
375#define INSIDE8 8
376};
377
378// Ignore macros defined inside a template function definition.
379template <int N>
380#define INSIDE9 9
381bool fn()
382{
383#define INSIDE10 10
384 return INSIDE9 > 1 || INSIDE10 < N;
385}
386
387// Ignore macros defined inside a variable declaration.
388extern int
389#define INSIDE11 11
390v;
391
392// Ignore macros defined inside a template class definition.
393template <int N>
394class C2 {
395public:
396#define INSIDE12 12
397 char storage[N];
398 bool f() {
399 return N > INSIDE12;
400 }
401 bool g();
402};
403
404// Ignore macros defined inside a template member function definition.
405template <int N>
406#define INSIDE13 13
407bool 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.
413template <typename T>
414class C3 {
415 T data;
416};
417template <typename T>
418#define INSIDE15 15
419using Data = C3<T[INSIDE15]>;
420
421// Ignore macros defined inside a type alias.
422using Data2 =
423#define INSIDE16 16
424 char[INSIDE16];
425
426// Ignore macros defined inside a (constexpr) variable definition.
427constexpr int
428#define INSIDE17 17
429value = 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
438extern void FN_NAME(g, INSIDE18)();
439
440void gg()
441{
442 g18();
443}
444

source code of clang-tools-extra/test/clang-tidy/checkers/modernize/macro-to-enum.cpp