[clang-tidy] Improve macro handling in modernize-macro-to-enum
When a macro is undef'ed or used in a preprocessor conditional expression, we need to remember that macro should it later be defined in the file to an integral value. We need to exclude such macro names from being turned into an enum. Maintain a blacklist of identifiers that we've seen in an undef or conditional preprocessor directive. When the file is done processing, remove all the blacklisted identifiers from conversion to an enum. Differential Revision: https://reviews.llvm.org/D123889 Fixes #54842
This commit is contained in:
parent
61baf2ffa7
commit
08881c2de6
|
@ -222,6 +222,8 @@ private:
|
|||
void conditionStart(const SourceLocation &Loc);
|
||||
void checkCondition(SourceRange ConditionRange);
|
||||
void checkName(const Token &MacroNameTok);
|
||||
void rememberExpressionName(const Token &MacroNameTok);
|
||||
void invalidateExpressionNames();
|
||||
void warnMacroEnum(const EnumMacro &Macro) const;
|
||||
void fixEnumMacro(const MacroList &MacroList) const;
|
||||
|
||||
|
@ -230,6 +232,7 @@ private:
|
|||
const SourceManager &SM;
|
||||
SmallVector<MacroList> Enums;
|
||||
SmallVector<FileState> Files;
|
||||
std::vector<std::string> ExpressionNames;
|
||||
FileState *CurrentFile = nullptr;
|
||||
};
|
||||
|
||||
|
@ -284,8 +287,9 @@ void MacroToEnumCallbacks::checkCondition(SourceRange Range) {
|
|||
}
|
||||
|
||||
void MacroToEnumCallbacks::checkName(const Token &MacroNameTok) {
|
||||
StringRef Id = getTokenName(MacroNameTok);
|
||||
rememberExpressionName(MacroNameTok);
|
||||
|
||||
StringRef Id = getTokenName(MacroNameTok);
|
||||
llvm::erase_if(Enums, [&Id](const MacroList &MacroList) {
|
||||
return llvm::any_of(MacroList, [&Id](const EnumMacro &Macro) {
|
||||
return getTokenName(Macro.Name) == Id;
|
||||
|
@ -293,6 +297,14 @@ void MacroToEnumCallbacks::checkName(const Token &MacroNameTok) {
|
|||
});
|
||||
}
|
||||
|
||||
void MacroToEnumCallbacks::rememberExpressionName(const Token &MacroNameTok) {
|
||||
std::string Id = getTokenName(MacroNameTok).str();
|
||||
auto Pos = llvm::lower_bound(ExpressionNames, Id);
|
||||
if (Pos == ExpressionNames.end() || *Pos != Id) {
|
||||
ExpressionNames.insert(Pos, Id);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroToEnumCallbacks::FileChanged(SourceLocation Loc,
|
||||
FileChangeReason Reason,
|
||||
SrcMgr::CharacteristicKind FileType,
|
||||
|
@ -384,6 +396,8 @@ void MacroToEnumCallbacks::MacroDefined(const Token &MacroNameTok,
|
|||
void MacroToEnumCallbacks::MacroUndefined(const Token &MacroNameTok,
|
||||
const MacroDefinition &MD,
|
||||
const MacroDirective *Undef) {
|
||||
rememberExpressionName(MacroNameTok);
|
||||
|
||||
auto MatchesToken = [&MacroNameTok](const EnumMacro &Macro) {
|
||||
return getTokenName(Macro.Name) == getTokenName(MacroNameTok);
|
||||
};
|
||||
|
@ -447,7 +461,19 @@ void MacroToEnumCallbacks::PragmaDirective(SourceLocation Loc,
|
|||
CurrentFile->GuardScanner = IncludeGuard::IfGuard;
|
||||
}
|
||||
|
||||
void MacroToEnumCallbacks::invalidateExpressionNames() {
|
||||
for (const std::string &Id : ExpressionNames) {
|
||||
llvm::erase_if(Enums, [Id](const MacroList &MacroList) {
|
||||
return llvm::any_of(MacroList, [&Id](const EnumMacro &Macro) {
|
||||
return getTokenName(Macro.Name) == Id;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MacroToEnumCallbacks::EndOfMainFile() {
|
||||
invalidateExpressionNames();
|
||||
|
||||
for (const MacroList &MacroList : Enums) {
|
||||
if (MacroList.empty())
|
||||
continue;
|
||||
|
|
|
@ -181,6 +181,12 @@
|
|||
#define USE_IFDEF 1
|
||||
#define USE_IFNDEF 1
|
||||
|
||||
// Undef'ing first and then defining later should still exclude this macro
|
||||
#undef USE_UINT64
|
||||
#define USE_UINT64 0
|
||||
#undef USE_INT64
|
||||
#define USE_INT64 0
|
||||
|
||||
#if defined(USE_FOO) && USE_FOO
|
||||
extern void foo();
|
||||
#else
|
||||
|
@ -243,6 +249,27 @@ inline void used_ifndef() {}
|
|||
#define IFNDEF3 3
|
||||
#endif
|
||||
|
||||
// Macros used in conditions are invalidated, even if they look
|
||||
// like enums after they are used in conditions.
|
||||
#if DEFINED_LATER1
|
||||
#endif
|
||||
#ifdef DEFINED_LATER2
|
||||
#endif
|
||||
#ifndef DEFINED_LATER3
|
||||
#endif
|
||||
#undef DEFINED_LATER4
|
||||
#if ((defined(DEFINED_LATER5) || DEFINED_LATER6) && DEFINED_LATER7) || (DEFINED_LATER8 > 10)
|
||||
#endif
|
||||
|
||||
#define DEFINED_LATER1 1
|
||||
#define DEFINED_LATER2 2
|
||||
#define DEFINED_LATER3 3
|
||||
#define DEFINED_LATER4 4
|
||||
#define DEFINED_LATER5 5
|
||||
#define DEFINED_LATER6 6
|
||||
#define DEFINED_LATER7 7
|
||||
#define DEFINED_LATER8 8
|
||||
|
||||
// Sometimes an argument to ifdef can be classified as a keyword token.
|
||||
#ifdef __restrict
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue