Skip to content

Commit fb056a7

Browse files
committed
Fixes #14921: Detect enums before simplifying platform types
1 parent 3deb36d commit fb056a7

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

lib/tokenlist.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,45 @@ bool TokenList::validateToken(const Token* tok) const
20772077
return false;
20782078
}
20792079

2080+
/** Return whether tok is the "{" that starts an enumerator list */
2081+
static bool isEnumStart(const Token* tok)
2082+
{
2083+
if (!Token::simpleMatch(tok, "{"))
2084+
return false;
2085+
tok = tok->previous();
2086+
while (tok && (!tok->isKeyword() || Token::isStandardType(tok->str())) && Token::Match(tok, "%name%|::|:"))
2087+
tok = tok->previous();
2088+
if (Token::simpleMatch(tok, "class"))
2089+
tok = tok->previous();
2090+
return Token::simpleMatch(tok, "enum");
2091+
}
2092+
2093+
// Collect the tokens that are enumerator names being declared in some enum's member list,
2094+
// e.g. the HANDLE in "enum E { HANDLE, ... };". Each enum body is scanned once, jumping over
2095+
// nested brackets in constant-expressions via link(), so the total cost is bounded by the
2096+
// combined size of all enum bodies -- not by unrelated code elsewhere in the file.
2097+
static std::unordered_set<const Token*> findEnumeratorNames(const Token* front)
2098+
{
2099+
std::unordered_set<const Token*> result;
2100+
for (const Token* tok = front; tok; tok = tok->next()) {
2101+
if (!isEnumStart(tok))
2102+
continue;
2103+
const Token* const bodyEnd = tok->link();
2104+
for (const Token* nameTok = tok->next(); nameTok && nameTok != bodyEnd;) {
2105+
if (nameTok->isName())
2106+
result.insert(nameTok);
2107+
while (nameTok != bodyEnd && nameTok->str() != ",") {
2108+
if (Token::Match(nameTok, "(|[|{"))
2109+
nameTok = nameTok->link();
2110+
nameTok = nameTok->next();
2111+
}
2112+
if (nameTok != bodyEnd) // skip the ","
2113+
nameTok = nameTok->next();
2114+
}
2115+
}
2116+
return result;
2117+
}
2118+
20802119
void TokenList::simplifyPlatformTypes()
20812120
{
20822121
const bool isCPP11 = isCPP() && (mSettings.standards.cpp >= Standards::CPP11);
@@ -2143,6 +2182,7 @@ void TokenList::simplifyPlatformTypes()
21432182
}
21442183

21452184
const std::string platform_type(mSettings.platform.toString());
2185+
const std::unordered_set<const Token*> enumeratorNames = findEnumeratorNames(front());
21462186

21472187
for (Token *tok = front(); tok; tok = tok->next()) {
21482188
if (tok->tokType() != Token::eType && tok->tokType() != Token::eName)
@@ -2151,6 +2191,9 @@ void TokenList::simplifyPlatformTypes()
21512191
const Library::PlatformType * const platformtype = mSettings.library.platform_type(tok->str(), platform_type);
21522192

21532193
if (platformtype) {
2194+
if (enumeratorNames.count(tok) != 0) // don't replace an enumerator's name, e.g. HANDLE in "enum E { HANDLE, ... };"
2195+
continue;
2196+
21542197
// check for namespace
21552198
if (tok->strAt(-1) == "::") {
21562199
const Token * tok1 = tok->tokAt(-2);

test/testtokenize.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ class TestTokenizer : public TestFixture {
400400
TEST_CASE(multipleAssignment);
401401

402402
TEST_CASE(platformWin);
403+
TEST_CASE(platformWinEnumerator);
403404
TEST_CASE(platformWin32A);
404405
TEST_CASE(platformWin32W);
405406
TEST_CASE(platformWin32AStringCat); // ticket #5015
@@ -6264,6 +6265,31 @@ class TestTokenizer : public TestFixture {
62646265
ASSERT_EQUALS(win32A, tokenizeAndStringify(code, settings_win32a));
62656266
}
62666267

6268+
void platformWinEnumerator() {
6269+
// #11538 - HANDLE is a platform type (-> void*) but here it's an enumerator and must not be replaced
6270+
{
6271+
const char code[] = "enum class E { HANDLE, OTHER };";
6272+
ASSERT_EQUALS("enum class E { HANDLE , OTHER } ;", tokenizeAndStringify(code, settings_win32a));
6273+
}
6274+
// a platform type used within an enumerator's constant-expression is not a name and must still be replaced
6275+
{
6276+
const char code[] = "enum E { A = sizeof(HANDLE) };";
6277+
ASSERT_EQUALS("enum E { A = sizeof ( void * ) } ;", tokenizeAndStringify(code, settings_win32a));
6278+
}
6279+
// combines both: DWORD is the enum's underlying type (-> replaced), HANDLE is the enumerator name (-> kept),
6280+
// and the HANDLE inside sizeof() is part of the constant-expression (-> replaced)
6281+
{
6282+
const char code[] = "enum class T : DWORD { HANDLE = sizeof(HANDLE) };";
6283+
ASSERT_EQUALS("enum class T : unsigned long { HANDLE = sizeof ( void * ) } ;", tokenizeAndStringify(code, settings_win32a));
6284+
}
6285+
// a first naive fix walked backwards from every platform-type token to find an enclosing "{".
6286+
// this implementation failed on the following example, therefore test it.
6287+
{
6288+
const char code[] = "enum T { }; void test(int, HANDLE, int);";
6289+
ASSERT_EQUALS("enum T { } ; void test ( int , void * , int ) ;", tokenizeAndStringify(code, settings_win32a));
6290+
}
6291+
}
6292+
62676293
void platformWin32A() {
62686294
const char code[] = "wchar_t wc;"
62696295
"TCHAR c;"

0 commit comments

Comments
 (0)