[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:
Richard 2022-04-11 14:11:30 -06:00
parent 61baf2ffa7
commit 08881c2de6
2 changed files with 54 additions and 1 deletions

View file

@ -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;

View file

@ -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