Revert "[clang-tidy] Add modernize-macro-to-enum check"

This reverts commit 39b80c8380.

This change was causing build failures on several build bots:
- https://lab.llvm.org/buildbot/#/builders/139/builds/19210
- https://lab.llvm.org/buildbot/#/builders/93/builds/7956
This commit is contained in:
Douglas Yung 2022-03-25 11:53:42 -07:00
parent 26e201b796
commit cef52105bd
12 changed files with 0 additions and 914 deletions

View File

@ -11,7 +11,6 @@ add_clang_library(clangTidyModernizeModule
DeprecatedIosBaseAliasesCheck.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
MacroToEnumCheck.cpp
MakeSharedCheck.cpp
MakeSmartPtrCheck.cpp
MakeUniqueCheck.cpp

View File

@ -1,482 +0,0 @@
//===--- MacroToEnumCheck.cpp - clang-tidy --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "MacroToEnumCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/STLExtras.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <string>
namespace clang {
namespace tidy {
namespace modernize {
static bool hasOnlyComments(SourceLocation Loc, const LangOptions &Options,
StringRef Text) {
// Use a lexer to look for tokens; if we find something other than a single
// hash, then there were intervening tokens between macro definitions.
std::string Buffer{Text};
Lexer Lex(Loc, Options, Buffer.c_str(), Buffer.c_str(),
Buffer.c_str() + Buffer.size());
Token Tok;
bool SeenHash = false;
while (!Lex.LexFromRawLexer(Tok)) {
if (Tok.getKind() == tok::hash && !SeenHash) {
SeenHash = true;
continue;
}
return false;
}
// Everything in between was whitespace, so now just look for two blank lines,
// consisting of two consecutive EOL sequences, either '\n', '\r' or '\r\n'.
enum class WhiteSpace {
Nothing,
CR,
LF,
CRLF,
CRLFCR,
};
WhiteSpace State = WhiteSpace::Nothing;
for (char C : Text) {
switch (C) {
case '\r':
if (State == WhiteSpace::CR)
return false;
State = State == WhiteSpace::CRLF ? WhiteSpace::CRLFCR : WhiteSpace::CR;
break;
case '\n':
if (State == WhiteSpace::LF || State == WhiteSpace::CRLFCR)
return false;
State = State == WhiteSpace::CR ? WhiteSpace::CRLF : WhiteSpace::LF;
break;
default:
State = WhiteSpace::Nothing;
break;
}
}
return true;
}
// Validate that this literal token is a valid integer literal. A literal token
// could be a floating-point token, which isn't acceptable as a value for an
// enumeration. A floating-point token must either have a decimal point or an
// exponent ('E' or 'P').
static bool isIntegralConstant(const Token &Token) {
const char *Begin = Token.getLiteralData();
const char *End = Begin + Token.getLength();
// not a hexadecimal floating-point literal
if (Token.getLength() > 2 && Begin[0] == '0' && std::toupper(Begin[1]) == 'X')
return std::none_of(Begin + 2, End, [](char C) {
return C == '.' || std::toupper(C) == 'P';
});
// not a decimal floating-point literal
return std::none_of(
Begin, End, [](char C) { return C == '.' || std::toupper(C) == 'E'; });
}
namespace {
struct EnumMacro {
EnumMacro(Token Name, const MacroDirective *Directive)
: Name(Name), Directive(Directive) {}
Token Name;
const MacroDirective *Directive;
};
using MacroList = SmallVector<EnumMacro>;
enum class IncludeGuard { None, FileChanged, IfGuard, DefineGuard };
struct FileState {
FileState()
: ConditionScopes(0), LastLine(0), GuardScanner(IncludeGuard::None) {}
int ConditionScopes;
unsigned int LastLine;
IncludeGuard GuardScanner;
SourceLocation LastMacroLocation;
};
class MacroToEnumCallbacks : public PPCallbacks {
public:
MacroToEnumCallbacks(MacroToEnumCheck *Check, const LangOptions &LangOptions,
const SourceManager &SM)
: Check(Check), LangOptions(LangOptions), SM(SM) {}
void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID) override;
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
const Module *Imported,
SrcMgr::CharacteristicKind FileType) override {
clearCurrentEnum(HashLoc);
}
// Keep track of macro definitions that look like enums.
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override;
// Undefining an enum-like macro results in the enum set being dropped.
void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
const MacroDirective *Undef) override;
// Conditional compilation clears any adjacent enum-like macros.
// Macros used in conditional expressions clear any adjacent enum-like
// macros.
// Include guards are either
// #if !defined(GUARD)
// or
// #ifndef GUARD
void If(SourceLocation Loc, SourceRange ConditionRange,
ConditionValueKind ConditionValue) override {
conditionStart(Loc);
checkCondition(ConditionRange);
}
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
conditionStart(Loc);
checkName(MacroNameTok);
}
void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
conditionStart(Loc);
checkName(MacroNameTok);
}
void Elif(SourceLocation Loc, SourceRange ConditionRange,
ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
checkCondition(ConditionRange);
}
void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
checkName(MacroNameTok);
}
void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override {
checkName(MacroNameTok);
}
void Endif(SourceLocation Loc, SourceLocation IfLoc) override;
void PragmaDirective(SourceLocation Loc,
PragmaIntroducerKind Introducer) override;
// After we've seen everything, issue warnings and fix-its.
void EndOfMainFile() override;
private:
void newEnum() {
if (Enums.empty() || !Enums.back().empty())
Enums.emplace_back();
}
bool insideConditional() const {
return (CurrentFile->GuardScanner == IncludeGuard::DefineGuard &&
CurrentFile->ConditionScopes > 1) ||
(CurrentFile->GuardScanner != IncludeGuard::DefineGuard &&
CurrentFile->ConditionScopes > 0);
}
bool isConsecutiveMacro(const MacroDirective *MD) const;
void rememberLastMacroLocation(const MacroDirective *MD) {
CurrentFile->LastLine = SM.getSpellingLineNumber(MD->getLocation());
CurrentFile->LastMacroLocation = Lexer::getLocForEndOfToken(
MD->getMacroInfo()->getDefinitionEndLoc(), 0, SM, LangOptions);
}
void clearLastMacroLocation() {
CurrentFile->LastLine = 0;
CurrentFile->LastMacroLocation = SourceLocation{};
}
void clearCurrentEnum(SourceLocation Loc);
void conditionStart(const SourceLocation &Loc);
void checkCondition(SourceRange ConditionRange);
void checkName(const Token &MacroNameTok);
void warnMacroEnum(const EnumMacro &Macro) const;
void fixEnumMacro(const MacroList &MacroList) const;
MacroToEnumCheck *Check;
const LangOptions &LangOptions;
const SourceManager &SM;
SmallVector<MacroList> Enums;
SmallVector<FileState> Files;
FileState *CurrentFile = nullptr;
};
bool MacroToEnumCallbacks::isConsecutiveMacro(const MacroDirective *MD) const {
if (CurrentFile->LastMacroLocation.isInvalid())
return false;
SourceLocation Loc = MD->getLocation();
if (CurrentFile->LastLine + 1 == SM.getSpellingLineNumber(Loc))
return true;
SourceLocation Define =
SM.translateLineCol(SM.getFileID(Loc), SM.getSpellingLineNumber(Loc), 1);
CharSourceRange BetweenMacros{
SourceRange{CurrentFile->LastMacroLocation, Define}, true};
CharSourceRange CharRange =
Lexer::makeFileCharRange(BetweenMacros, SM, LangOptions);
StringRef BetweenText = Lexer::getSourceText(CharRange, SM, LangOptions);
return hasOnlyComments(Define, LangOptions, BetweenText);
}
void MacroToEnumCallbacks::clearCurrentEnum(SourceLocation Loc) {
// Only drop the most recent Enum set if the directive immediately follows.
if (!Enums.empty() && !Enums.back().empty() &&
SM.getSpellingLineNumber(Loc) == CurrentFile->LastLine + 1)
Enums.pop_back();
clearLastMacroLocation();
}
void MacroToEnumCallbacks::conditionStart(const SourceLocation &Loc) {
++CurrentFile->ConditionScopes;
clearCurrentEnum(Loc);
if (CurrentFile->GuardScanner == IncludeGuard::FileChanged)
CurrentFile->GuardScanner = IncludeGuard::IfGuard;
}
void MacroToEnumCallbacks::checkCondition(SourceRange Range) {
CharSourceRange CharRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Range), SM, LangOptions);
std::string Text = Lexer::getSourceText(CharRange, SM, LangOptions).str();
Lexer Lex(CharRange.getBegin(), LangOptions, Text.data(), Text.data(),
Text.data() + Text.size());
Token Tok;
bool End = false;
while (!End) {
End = Lex.LexFromRawLexer(Tok);
if (Tok.is(tok::raw_identifier) &&
Tok.getRawIdentifier().str() != "defined")
checkName(Tok);
}
}
void MacroToEnumCallbacks::checkName(const Token &MacroNameTok) {
std::string Id;
if (MacroNameTok.is(tok::raw_identifier))
Id = MacroNameTok.getRawIdentifier().str();
else if (MacroNameTok.is(tok::identifier))
Id = MacroNameTok.getIdentifierInfo()->getName().str();
else {
assert(false && "Expected either an identifier or raw identifier token");
return;
}
llvm::erase_if(Enums, [&Id](const MacroList &MacroList) {
return llvm::any_of(MacroList, [&Id](const EnumMacro &Macro) {
return Macro.Name.getIdentifierInfo()->getName().str() == Id;
});
});
}
void MacroToEnumCallbacks::FileChanged(SourceLocation Loc,
FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType,
FileID PrevFID) {
newEnum();
if (Reason == EnterFile) {
Files.emplace_back();
if (!SM.isInMainFile(Loc))
Files.back().GuardScanner = IncludeGuard::FileChanged;
} else if (Reason == ExitFile) {
assert(CurrentFile->ConditionScopes == 0);
Files.pop_back();
}
CurrentFile = &Files.back();
}
void MacroToEnumCallbacks::MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) {
// Include guards are never candidates for becoming an enum.
if (CurrentFile->GuardScanner == IncludeGuard::IfGuard) {
CurrentFile->GuardScanner = IncludeGuard::DefineGuard;
return;
}
if (insideConditional())
return;
if (SM.getFilename(MD->getLocation()).empty())
return;
const MacroInfo *Info = MD->getMacroInfo();
if (Info->isFunctionLike() || Info->isBuiltinMacro() ||
Info->tokens().empty() || Info->tokens().size() > 2)
return;
// It can be +Lit, -Lit or just Lit.
Token Tok = Info->tokens().front();
if (Info->tokens().size() == 2) {
if (!Tok.isOneOf(tok::TokenKind::minus, tok::TokenKind::plus,
tok::TokenKind::tilde))
return;
Tok = Info->tokens().back();
}
if (!Tok.isLiteral() || isStringLiteral(Tok.getKind()) ||
!isIntegralConstant(Tok))
return;
if (!isConsecutiveMacro(MD))
newEnum();
Enums.back().emplace_back(MacroNameTok, MD);
rememberLastMacroLocation(MD);
}
// Any macro that is undefined removes all adjacent macros from consideration as
// an enum and starts a new enum scan.
void MacroToEnumCallbacks::MacroUndefined(const Token &MacroNameTok,
const MacroDefinition &MD,
const MacroDirective *Undef) {
auto MatchesToken = [&MacroNameTok](const EnumMacro &Macro) {
return Macro.Name.getIdentifierInfo()->getName() ==
MacroNameTok.getIdentifierInfo()->getName();
};
auto It = llvm::find_if(Enums, [MatchesToken](const MacroList &MacroList) {
return llvm::any_of(MacroList, MatchesToken);
});
if (It != Enums.end())
Enums.erase(It);
clearLastMacroLocation();
CurrentFile->GuardScanner = IncludeGuard::None;
}
void MacroToEnumCallbacks::Endif(SourceLocation Loc, SourceLocation IfLoc) {
// The if directive for the include guard isn't counted in the
// ConditionScopes.
if (CurrentFile->ConditionScopes == 0 &&
CurrentFile->GuardScanner == IncludeGuard::DefineGuard)
return;
// We don't need to clear the current enum because the start of the
// conditional block already took care of that.
assert(CurrentFile->ConditionScopes > 0);
--CurrentFile->ConditionScopes;
}
namespace {
template <size_t N>
bool textEquals(const char (&Needle)[N], const char *HayStack) {
return StringRef{HayStack, N - 1} == Needle;
}
template <size_t N> size_t len(const char (&)[N]) { return N - 1; }
} // namespace
void MacroToEnumCallbacks::PragmaDirective(SourceLocation Loc,
PragmaIntroducerKind Introducer) {
if (CurrentFile->GuardScanner != IncludeGuard::FileChanged)
return;
bool Invalid = false;
const char *Text = SM.getCharacterData(
Lexer::getLocForEndOfToken(Loc, 0, SM, LangOptions), &Invalid);
if (Invalid)
return;
while (*Text && std::isspace(*Text))
++Text;
if (textEquals("pragma", Text))
return;
Text += len("pragma");
while (*Text && std::isspace(*Text))
++Text;
if (textEquals("once", Text))
CurrentFile->GuardScanner = IncludeGuard::IfGuard;
}
void MacroToEnumCallbacks::EndOfMainFile() {
for (const MacroList &MacroList : Enums) {
if (MacroList.empty())
continue;
for (const EnumMacro &Macro : MacroList)
warnMacroEnum(Macro);
fixEnumMacro(MacroList);
}
}
void MacroToEnumCallbacks::warnMacroEnum(const EnumMacro &Macro) const {
Check->diag(Macro.Directive->getLocation(),
"macro %0 defines an integral constant; prefer an enum instead")
<< Macro.Name.getIdentifierInfo();
}
void MacroToEnumCallbacks::fixEnumMacro(const MacroList &MacroList) const {
SourceLocation Begin =
MacroList.front().Directive->getMacroInfo()->getDefinitionLoc();
Begin = SM.translateLineCol(SM.getFileID(Begin),
SM.getSpellingLineNumber(Begin), 1);
DiagnosticBuilder Diagnostic =
Check->diag(Begin, "replace macro with enum")
<< FixItHint::CreateInsertion(Begin, "enum {\n");
for (size_t I = 0u; I < MacroList.size(); ++I) {
const EnumMacro &Macro = MacroList[I];
SourceLocation DefineEnd =
Macro.Directive->getMacroInfo()->getDefinitionLoc();
SourceLocation DefineBegin = SM.translateLineCol(
SM.getFileID(DefineEnd), SM.getSpellingLineNumber(DefineEnd), 1);
CharSourceRange DefineRange;
DefineRange.setBegin(DefineBegin);
DefineRange.setEnd(DefineEnd);
Diagnostic << FixItHint::CreateRemoval(DefineRange);
SourceLocation NameEnd = Lexer::getLocForEndOfToken(
Macro.Directive->getMacroInfo()->getDefinitionLoc(), 0, SM,
LangOptions);
Diagnostic << FixItHint::CreateInsertion(NameEnd, " =");
SourceLocation ValueEnd = Lexer::getLocForEndOfToken(
Macro.Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
LangOptions);
if (I < MacroList.size() - 1)
Diagnostic << FixItHint::CreateInsertion(ValueEnd, ",");
}
SourceLocation End = Lexer::getLocForEndOfToken(
MacroList.back().Directive->getMacroInfo()->getDefinitionEndLoc(), 0, SM,
LangOptions);
End = SM.translateLineCol(SM.getFileID(End),
SM.getSpellingLineNumber(End) + 1, 1);
Diagnostic << FixItHint::CreateInsertion(End, "};\n");
}
} // namespace
void MacroToEnumCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
PP->addPPCallbacks(
std::make_unique<MacroToEnumCallbacks>(this, getLangOpts(), SM));
}
} // namespace modernize
} // namespace tidy
} // namespace clang

View File

@ -1,34 +0,0 @@
//===--- MacroToEnumCheck.h - clang-tidy ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MACROTOENUMCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MACROTOENUMCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace modernize {
/// Replaces groups of related macros with an unscoped anonymous enum.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-macro-to-enum.html
class MacroToEnumCheck : public ClangTidyCheck {
public:
MacroToEnumCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
};
} // namespace modernize
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MACROTOENUMCHECK_H

View File

@ -15,7 +15,6 @@
#include "DeprecatedHeadersCheck.h"
#include "DeprecatedIosBaseAliasesCheck.h"
#include "LoopConvertCheck.h"
#include "MacroToEnumCheck.h"
#include "MakeSharedCheck.h"
#include "MakeUniqueCheck.h"
#include "PassByValueCheck.h"
@ -60,7 +59,6 @@ public:
CheckFactories.registerCheck<DeprecatedIosBaseAliasesCheck>(
"modernize-deprecated-ios-base-aliases");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
CheckFactories.registerCheck<MacroToEnumCheck>("modernize-macro-to-enum");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");

View File

@ -106,18 +106,9 @@ New checks
Finds initializations of C++ shared pointers to non-array type that are initialized with an array.
- New :doc:`modernize-macro-to-enum
<clang-tidy/checks/modernize-macro-to-enum>` check.
Replaces groups of adjacent macros with an unscoped anonymous enum.
New check aliases
^^^^^^^^^^^^^^^^^
- New alias :doc:`cppcoreguidelines-macro-to-enum
<clang-tidy/checks/cppcoreguidelines-macro-to-enum>` to :doc:`modernize-macro-to-enum
<clang-tidy/checks/modernize-macro-to-enum>` was added.
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,9 +0,0 @@
.. title:: clang-tidy - cppcoreguidelines-macro-to-enum
.. meta::
:http-equiv=refresh: 5;URL=modernize-macro-to-enum.html
cppcoreguidelines-macro-to-enum
===============================
The cppcoreguidelines-macro-to-enum check is an alias, please see
:doc:`modernize-macro-to-enum <modernize-macro-to-enum>` for more information.

View File

@ -234,7 +234,6 @@ Clang-Tidy Checks
`modernize-deprecated-headers <modernize-deprecated-headers.html>`_, "Yes"
`modernize-deprecated-ios-base-aliases <modernize-deprecated-ios-base-aliases.html>`_, "Yes"
`modernize-loop-convert <modernize-loop-convert.html>`_, "Yes"
`modernize-macro-to-enum <modernize-macro-to-enum.html>`_, "Yes"
`modernize-make-shared <modernize-make-shared.html>`_, "Yes"
`modernize-make-unique <modernize-make-unique.html>`_, "Yes"
`modernize-pass-by-value <modernize-pass-by-value.html>`_, "Yes"
@ -427,7 +426,6 @@ Clang-Tidy Checks
`cppcoreguidelines-avoid-magic-numbers <cppcoreguidelines-avoid-magic-numbers.html>`_, `readability-magic-numbers <readability-magic-numbers.html>`_,
`cppcoreguidelines-c-copy-assignment-signature <cppcoreguidelines-c-copy-assignment-signature.html>`_, `misc-unconventional-assign-operator <misc-unconventional-assign-operator.html>`_,
`cppcoreguidelines-explicit-virtual-functions <cppcoreguidelines-explicit-virtual-functions.html>`_, `modernize-use-override <modernize-use-override.html>`_, "Yes"
`cppcoreguidelines-macro-to-enum <cppcoreguidelines-macro-to-enum.html>`_, `modernize-macro-to-enum <modernize-macro-to-enum.html>`_, "Yes"
`cppcoreguidelines-non-private-member-variables-in-classes <cppcoreguidelines-non-private-member-variables-in-classes.html>`_, `misc-non-private-member-variables-in-classes <misc-non-private-member-variables-in-classes.html>`_,
`fuchsia-header-anon-namespaces <fuchsia-header-anon-namespaces.html>`_, `google-build-namespaces <google-build-namespaces.html>`_,
`google-readability-braces-around-statements <google-readability-braces-around-statements.html>`_, `readability-braces-around-statements <readability-braces-around-statements.html>`_, "Yes"

View File

@ -1,66 +0,0 @@
.. title:: clang-tidy - modernize-macro-to-enum
modernize-macro-to-enum
=======================
Replaces groups of adjacent macros with an unscoped anonymous enum.
Using an unscoped anonymous enum ensures that everywhere the macro
token was used previously, the enumerator name may be safely used.
This check can be used to enforce the C++ core guideline `Enum.1:
Prefer enumerations over macros
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#enum1-prefer-enumerations-over-macros>`_,
within the constraints outlined below.
Potential macros for replacement must meet the following constraints:
- Macros must expand only to integral literal tokens. The unary operators
plus, minus and tilde are recognized to allow for positive, negative
and bitwise negated integers. More complicated integral constant
expressions are not currently recognized by this check.
- Macros must be defined on sequential source file lines, or with
only comment lines in between macro definitions.
- Macros must all be defined in the same source file.
- Macros must not be defined within a conditional compilation block.
(Conditional include guards are exempt from this constraint.)
- Macros must not be defined adjacent to other preprocessor directives.
- Macros must not be used in any conditional preprocessing directive.
- Macros must not be undefined.
Each cluster of macros meeting the above constraints is presumed to
be a set of values suitable for replacement by an anonymous enum.
From there, a developer can give the anonymous enum a name and
continue refactoring to a scoped enum if desired. Comments on the
same line as a macro definition or between subsequent macro definitions
are preserved in the output. No formatting is assumed in the provided
replacements, although clang-tidy can optionally format all fixes.
Examples:
.. code-block:: c++
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define TM_ONE 1 // Use tailored method one.
#define TM_TWO 2 // Use tailored method two. Method two
// is preferable to method one.
#define TM_THREE 3 // Use tailored method three.
becomes
.. code-block:: c++
enum {
RED = 0xFF0000,
GREEN = 0x00FF00,
BLUE = 0x0000FF
};
enum {
TM_ONE = 1, // Use tailored method one.
TM_TWO = 2, // Use tailored method two. Method two
// is preferable to method one.
TM_THREE = 3 // Use tailored method three.
};

View File

@ -1,25 +0,0 @@
#if !defined(MODERNIZE_MACRO_TO_ENUM_H)
#define MODERNIZE_MACRO_TO_ENUM_H
#include "modernize-macro-to-enum2.h"
#define GG_RED 0xFF0000
#define GG_GREEN 0x00FF00
#define GG_BLUE 0x0000FF
// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG_RED' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG_GREEN' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG_BLUE' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: GG_RED = 0xFF0000,
// CHECK-FIXES-NEXT: GG_GREEN = 0x00FF00,
// CHECK-FIXES-NEXT: GG_BLUE = 0x0000FF
// CHECK-FIXES-NEXT: };
#if 1
#define RR_RED 1
#define RR_GREEN 2
#define RR_BLUE 3
#endif
#endif

View File

@ -1,25 +0,0 @@
#ifndef MODERNIZE_MACRO_TO_ENUM2_H
#define MODERNIZE_MACRO_TO_ENUM2_H
#include "modernize-macro-to-enum3.h"
#define GG2_RED 0xFF0000
#define GG2_GREEN 0x00FF00
#define GG2_BLUE 0x0000FF
// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG2_RED' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG2_GREEN' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG2_BLUE' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: GG2_RED = 0xFF0000,
// CHECK-FIXES-NEXT: GG2_GREEN = 0x00FF00,
// CHECK-FIXES-NEXT: GG2_BLUE = 0x0000FF
// CHECK-FIXES-NEXT: };
#if 1
#define RR2_RED 1
#define RR2_GREEN 2
#define RR2_BLUE 3
#endif
#endif

View File

@ -1,20 +0,0 @@
#pragma once
#define GG3_RED 0xFF0000
#define GG3_GREEN 0x00FF00
#define GG3_BLUE 0x0000FF
// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG3_RED' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG3_GREEN' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GG3_BLUE' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: GG3_RED = 0xFF0000,
// CHECK-FIXES-NEXT: GG3_GREEN = 0x00FF00,
// CHECK-FIXES-NEXT: GG3_BLUE = 0x0000FF
// CHECK-FIXES-NEXT: };
#if 1
#define RR3_RED 1
#define RR3_GREEN 2
#define RR3_BLUE 3
#endif

View File

@ -1,239 +0,0 @@
// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-macro-to-enum %t -- -- -I%S/Inputs/modernize-macro-to-enum
// C++14 or later required for binary literals.
#if 1
#include "modernize-macro-to-enum.h"
// These macros are skipped due to being inside a conditional compilation block.
#define GOO_RED 1
#define GOO_GREEN 2
#define GOO_BLUE 3
#endif
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: replace macro with enum [modernize-macro-to-enum]
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'RED' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'GREEN' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BLUE' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: RED = 0xFF0000,
// CHECK-FIXES-NEXT: GREEN = 0x00FF00,
// CHECK-FIXES-NEXT: BLUE = 0x0000FF
// CHECK-FIXES-NEXT: };
// Verify that comments are preserved.
#define CoordModeOrigin 0 /* relative to the origin */
#define CoordModePrevious 1 /* relative to previous point */
// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModeOrigin' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'CoordModePrevious' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: CoordModeOrigin = 0, /* relative to the origin */
// CHECK-FIXES-NEXT: CoordModePrevious = 1 /* relative to previous point */
// CHECK-FIXES-NEXT: };
// Verify that multiline comments are preserved.
#define BadDrawable 9 /* parameter not a Pixmap or Window */
#define BadAccess 10 /* depending on context:
- key/button already grabbed
- attempt to free an illegal
cmap entry
- attempt to store into a read-only
color map entry. */
// - attempt to modify the access control
// list from other than the local host.
//
#define BadAlloc 11 /* insufficient resources */
// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadDrawable' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-12]]:9: warning: macro 'BadAccess' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: macro 'BadAlloc' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: BadDrawable = 9, /* parameter not a Pixmap or Window */
// CHECK-FIXES-NEXT: BadAccess = 10, /* depending on context:
// CHECK-FIXES-NEXT: - key/button already grabbed
// CHECK-FIXES-NEXT: - attempt to free an illegal
// CHECK-FIXES-NEXT: cmap entry
// CHECK-FIXES-NEXT: - attempt to store into a read-only
// CHECK-FIXES-NEXT: color map entry. */
// CHECK-FIXES-NEXT: // - attempt to modify the access control
// CHECK-FIXES-NEXT: // list from other than the local host.
// CHECK-FIXES-NEXT: //
// CHECK-FIXES-NEXT: BadAlloc = 11 /* insufficient resources */
// CHECK-FIXES-NEXT: };
// Undefining a macro invalidates adjacent macros
// from being considered as an enum.
#define REMOVED1 1
#define REMOVED2 2
#define REMOVED3 3
#undef REMOVED2
#define VALID1 1
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: macro 'VALID1' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: VALID1 = 1
// CHECK-FIXES-NEXT: };
#define UNDEF1 1
#define UNDEF2 2
#define UNDEF3 3
// Undefining a macro later invalidates the set of possible adjacent macros
// from being considered as an enum.
#undef UNDEF2
// Integral constants can have an optional sign
#define SIGNED1 +1
#define SIGNED2 -1
// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED1' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'SIGNED2' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: SIGNED1 = +1,
// CHECK-FIXES-NEXT: SIGNED2 = -1
// CHECK-FIXES-NEXT: };
// Integral constants with bitwise negated values
#define UNOP1 ~0U
#define UNOP2 ~1U
// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP1' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: macro 'UNOP2' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: UNOP1 = ~0U,
// CHECK-FIXES-NEXT: UNOP2 = ~1U
// CHECK-FIXES-NEXT: };
// Integral constants in other bases and with suffixes are OK
#define BASE1 0777 // octal
#define BASE2 0xDEAD // hexadecimal
#define BASE3 0b0011 // binary
#define SUFFIX1 +1U
#define SUFFIX2 -1L
#define SUFFIX3 +1UL
#define SUFFIX4 -1LL
#define SUFFIX5 +1ULL
// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: replace macro with enum
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE1' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE2' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'BASE3' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX1' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX2' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX3' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX4' defines an integral constant; prefer an enum instead
// CHECK-MESSAGES: :[[@LINE-9]]:9: warning: macro 'SUFFIX5' defines an integral constant; prefer an enum instead
// CHECK-FIXES: enum {
// CHECK-FIXES-NEXT: BASE1 = 0777, // octal
// CHECK-FIXES-NEXT: BASE2 = 0xDEAD, // hexadecimal
// CHECK-FIXES-NEXT: BASE3 = 0b0011, // binary
// CHECK-FIXES-NEXT: SUFFIX1 = +1U,
// CHECK-FIXES-NEXT: SUFFIX2 = -1L,
// CHECK-FIXES-NEXT: SUFFIX3 = +1UL,
// CHECK-FIXES-NEXT: SUFFIX4 = -1LL,
// CHECK-FIXES-NEXT: SUFFIX5 = +1ULL
// CHECK-FIXES-NEXT: };
// Macros appearing in conditional expressions can't be replaced
// by enums.
#define USE_FOO 1
#define USE_BAR 0
#define USE_IF 1
#define USE_ELIF 1
#define USE_IFDEF 1
#define USE_IFNDEF 1
#if defined(USE_FOO) && USE_FOO
extern void foo();
#else
inline void foo() {}
#endif
#if USE_BAR
extern void bar();
#else
inline void bar() {}
#endif
#if USE_IF
inline void used_if() {}
#endif
#if 0
#elif USE_ELIF
inline void used_elif() {}
#endif
#ifdef USE_IFDEF
inline void used_ifdef() {}
#endif
#ifndef USE_IFNDEF
#else
inline void used_ifndef() {}
#endif
// Regular conditional compilation blocks should leave previous
// macro enums alone.
#if 0
#include <non-existent.h>
#endif
// Conditional compilation blocks invalidate adjacent macros
// from being considered as an enum. Conditionally compiled
// blocks could contain macros that should rightly be included
// in the enum, but we can't explore multiple branches of a
// conditionally compiled section in clang-tidy, only the active
// branch based on compilation options.
#define CONDITION1 1
#define CONDITION2 2
#if 0
#define CONDITION3 3
#else
#define CONDITION3 -3
#endif
#define IFDEF1 1
#define IFDEF2 2
#ifdef FROB
#define IFDEF3 3
#endif
#define IFNDEF1 1
#define IFNDEF2 2
#ifndef GOINK
#define IFNDEF3 3
#endif
// These macros do not expand to integral constants.
#define HELLO "Hello, "
#define WORLD "World"
#define EPS1 1.0F
#define EPS2 1e5
#define EPS3 1.
#define DO_RED draw(RED)
#define DO_GREEN draw(GREEN)
#define DO_BLUE draw(BLUE)
#define FN_RED(x) draw(RED | x)
#define FN_GREEN(x) draw(GREEN | x)
#define FN_BLUE(x) draw(BLUE | x)
extern void draw(unsigned int Color);
void f()
{
draw(RED);
draw(GREEN);
draw(BLUE);
DO_RED;
DO_GREEN;
DO_BLUE;
FN_RED(0);
FN_GREEN(0);
FN_BLUE(0);
}