444 lines · cpp
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-parsing2// C++14 or later required for binary literals.3 4#if 15#include "modernize-macro-to-enum.h"6 7// These macros are skipped due to being inside a conditional compilation block.8#define GOO_RED 19#define GOO_GREEN 210#define GOO_BLUE 311 12#endif13 14// Macros expanding to expressions involving only literals are converted.15#define EXPR1 1 - 116#define EXPR2 1 + 117#define EXPR3 1 * 118#define EXPR4 1 / 119#define EXPR5 1 | 120#define EXPR6 1 & 121#define EXPR7 1 << 122#define EXPR8 1 >> 123#define EXPR9 1 % 224#define EXPR10 1 ^ 125#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 instead29// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR2' defines an integral constant; prefer an enum instead30// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR3' defines an integral constant; prefer an enum instead31// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR4' defines an integral constant; prefer an enum instead32// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR5' defines an integral constant; prefer an enum instead33// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR6' defines an integral constant; prefer an enum instead34// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR7' defines an integral constant; prefer an enum instead35// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR8' defines an integral constant; prefer an enum instead36// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR9' defines an integral constant; prefer an enum instead37// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR10' defines an integral constant; prefer an enum instead38// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR11' defines an integral constant; prefer an enum instead39// CHECK-MESSAGES: :[[@LINE-13]]:9: warning: macro 'EXPR12' defines an integral constant; prefer an enum instead40// 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 0xFF000056#define GREEN 0x00FF0057#define BLUE 0x0000FF58// 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 instead60// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GREEN' defines an integral constant; prefer an enum instead61// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BLUE' defines an integral constant; prefer an enum instead62// CHECK-FIXES: enum {63// CHECK-FIXES-NEXT: RED = 0xFF0000,64// CHECK-FIXES-NEXT: GREEN = 0x00FF00,65// CHECK-FIXES-NEXT: BLUE = 0x0000FF66// 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 enum72// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModeOrigin' defines an integral constant; prefer an enum instead73// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModePrevious' defines an integral constant; prefer an enum instead74// 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 grabbed83 - 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 control88 // list from other than the local host.89 //90#define BadAlloc 11 /* insufficient resources */91// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: replace macro with enum92// CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadDrawable' defines an integral constant; prefer an enum instead93// CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadAccess' defines an integral constant; prefer an enum instead94// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BadAlloc' defines an integral constant; prefer an enum instead95// 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 grabbed99// 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 control104// 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 macros110// from being considered as an enum.111#define REMOVED1 1112#define REMOVED2 2113#define REMOVED3 3114#undef REMOVED2115#define VALID1 1116// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: replace macro with enum117// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: macro 'VALID1' defines an integral constant; prefer an enum instead118// CHECK-FIXES: enum {119// CHECK-FIXES-NEXT: VALID1 = 1120// CHECK-FIXES-NEXT: };121 122#define UNDEF1 1123#define UNDEF2 2124#define UNDEF3 3125 126// Undefining a macro later invalidates the set of possible adjacent macros127// from being considered as an enum.128#undef UNDEF2129 130// Integral constants can have an optional sign131#define SIGNED1 +1132#define SIGNED2 -1133// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum134// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED1' defines an integral constant; prefer an enum instead135// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED2' defines an integral constant; prefer an enum instead136// CHECK-FIXES: enum {137// CHECK-FIXES-NEXT: SIGNED1 = +1,138// CHECK-FIXES-NEXT: SIGNED2 = -1139// CHECK-FIXES-NEXT: };140 141// Integral constants with bitwise negated values142#define UNOP1 ~0U143#define UNOP2 ~1U144// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum145// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP1' defines an integral constant; prefer an enum instead146// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP2' defines an integral constant; prefer an enum instead147// CHECK-FIXES: enum {148// CHECK-FIXES-NEXT: UNOP1 = ~0U,149// CHECK-FIXES-NEXT: UNOP2 = ~1U150// CHECK-FIXES-NEXT: };151 152// Integral constants in other bases and with suffixes are OK153#define BASE1 0777 // octal154#define BASE2 0xDEAD // hexadecimal155#define BASE3 0b0011 // binary156#define SUFFIX1 +1U157#define SUFFIX2 -1L158#define SUFFIX3 +1UL159#define SUFFIX4 -1LL160#define SUFFIX5 +1ULL161// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: replace macro with enum162// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE1' defines an integral constant; prefer an enum instead163// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE2' defines an integral constant; prefer an enum instead164// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE3' defines an integral constant; prefer an enum instead165// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX1' defines an integral constant; prefer an enum instead166// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX2' defines an integral constant; prefer an enum instead167// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX3' defines an integral constant; prefer an enum instead168// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX4' defines an integral constant; prefer an enum instead169// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX5' defines an integral constant; prefer an enum instead170// CHECK-FIXES: enum {171// CHECK-FIXES-NEXT: BASE1 = 0777, // octal172// CHECK-FIXES-NEXT: BASE2 = 0xDEAD, // hexadecimal173// CHECK-FIXES-NEXT: BASE3 = 0b0011, // binary174// 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 = +1ULL179// CHECK-FIXES-NEXT: };180 181// A limited form of constant expression is recognized: a parenthesized182// 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 enum188// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN1' defines an integral constant; prefer an enum instead189// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN2' defines an integral constant; prefer an enum instead190// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN3' defines an integral constant; prefer an enum instead191// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: macro 'PAREN4' defines an integral constant; prefer an enum instead192// 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+1204#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 enum209// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'COMPLEX_PAREN5' defines an integral constant; prefer an enum instead210// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'COMPLEX_PAREN6' defines an integral constant; prefer an enum instead211// 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 enum218// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: macro 'GOOD_COMMA' defines an integral constant; prefer an enum instead219// 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 replaced224// by enums.225#define USE_FOO 1226#define USE_BAR 0227#define USE_IF 1228#define USE_ELIF 1229#define USE_IFDEF 1230#define USE_IFNDEF 1231 232// Undef'ing first and then defining later should still exclude this macro233#undef USE_UINT64234#define USE_UINT64 0235#undef USE_INT64236#define USE_INT64 0237 238#if defined(USE_FOO) && USE_FOO239extern void foo();240#else241inline void foo() {}242#endif243 244#if USE_BAR245extern void bar();246#else247inline void bar() {}248#endif249 250#if USE_IF251inline void used_if() {}252#endif253 254#if 0255#elif USE_ELIF256inline void used_elif() {}257#endif258 259#ifdef USE_IFDEF260inline void used_ifdef() {}261#endif262 263#ifndef USE_IFNDEF264#else265inline void used_ifndef() {}266#endif267 268// Regular conditional compilation blocks should leave previous269// macro enums alone.270#if 0271#include <non-existent.h>272#endif273 274// Conditional compilation blocks invalidate adjacent macros275// from being considered as an enum. Conditionally compiled276// blocks could contain macros that should rightly be included277// in the enum, but we can't explore multiple branches of a278// conditionally compiled section in clang-tidy, only the active279// branch based on compilation options.280#define CONDITION1 1281#define CONDITION2 2282#if 0283#define CONDITION3 3284#else285#define CONDITION3 -3286#endif287 288#define IFDEF1 1289#define IFDEF2 2290#ifdef FROB291#define IFDEF3 3292#endif293 294#define IFNDEF1 1295#define IFNDEF2 2296#ifndef GOINK297#define IFNDEF3 3298#endif299 300// Macros used in conditions are invalidated, even if they look301// like enums after they are used in conditions.302#if DEFINED_LATER1303#endif304#ifdef DEFINED_LATER2305#endif306#ifndef DEFINED_LATER3307#endif308#undef DEFINED_LATER4309#if ((defined(DEFINED_LATER5) || DEFINED_LATER6) && DEFINED_LATER7) || (DEFINED_LATER8 > 10)310#endif311 312#define DEFINED_LATER1 1313#define DEFINED_LATER2 2314#define DEFINED_LATER3 3315#define DEFINED_LATER4 4316#define DEFINED_LATER5 5317#define DEFINED_LATER6 6318#define DEFINED_LATER7 7319#define DEFINED_LATER8 8320 321// Sometimes an argument to ifdef can be classified as a keyword token.322#ifdef __restrict323#endif324 325// These macros do not expand to integral constants.326#define HELLO "Hello, "327#define WORLD "World"328#define EPS1 1.0F329#define EPS2 1e5330#define EPS3 1.331 332// Ignore macros invoking comma operator unless they are inside parens.333#define BAD_COMMA 1, 2334 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 1350#define INSIDE2 2351 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 3364#define INSIDE4 4365);366 367// Ignore macros defined inside a record (structure) declaration.368struct S {369#define INSIDE5 5370#define INSIDE6 6371 char storage[INSIDE5];372};373class C {374#define INSIDE7 7375#define INSIDE8 8376};377 378// Ignore macros defined inside a template function definition.379template <int N>380#define INSIDE9 9381bool fn()382{383#define INSIDE10 10384 return INSIDE9 > 1 || INSIDE10 < N;385}386 387// Ignore macros defined inside a variable declaration.388extern int389#define INSIDE11 11390v;391 392// Ignore macros defined inside a template class definition.393template <int N>394class C2 {395public:396#define INSIDE12 12397 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 13407bool C2<N>::g() {408#define INSIDE14 14409 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 15419using Data = C3<T[INSIDE15]>;420 421// Ignore macros defined inside a type alias.422using Data2 =423#define INSIDE16 16424 char[INSIDE16];425 426// Ignore macros defined inside a (constexpr) variable definition.427constexpr int428#define INSIDE17 17429value = INSIDE17;430 431// Ignore macros used in the expansion of other macros432#define INSIDE18 18433#define INSIDE19 19434 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