diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 594de5c0295..36d0465c9ed 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -2077,6 +2077,45 @@ bool TokenList::validateToken(const Token* tok) const return false; } +/** Return whether tok is the "{" that starts an enumerator list */ +static bool isEnumStart(const Token* tok) +{ + if (!Token::simpleMatch(tok, "{")) + return false; + tok = tok->previous(); + while (tok && (!tok->isKeyword() || Token::isStandardType(tok->str())) && Token::Match(tok, "%name%|::|:")) + tok = tok->previous(); + if (Token::simpleMatch(tok, "class")) + tok = tok->previous(); + return Token::simpleMatch(tok, "enum"); +} + +// Collect the tokens that are enumerator names being declared in some enum's member list, +// e.g. the HANDLE in "enum E { HANDLE, ... };". Each enum body is scanned once, jumping over +// nested brackets in constant-expressions via link(), so the total cost is bounded by the +// combined size of all enum bodies -- not by unrelated code elsewhere in the file. +static std::unordered_set findEnumeratorNames(const Token* front) +{ + std::unordered_set result; + for (const Token* tok = front; tok; tok = tok->next()) { + if (!isEnumStart(tok)) + continue; + const Token* const bodyEnd = tok->link(); + for (const Token* nameTok = tok->next(); nameTok && nameTok != bodyEnd;) { + if (nameTok->isName()) + result.insert(nameTok); + while (nameTok != bodyEnd && nameTok->str() != ",") { + if (Token::Match(nameTok, "(|[|{")) + nameTok = nameTok->link(); + nameTok = nameTok->next(); + } + if (nameTok != bodyEnd) // skip the "," + nameTok = nameTok->next(); + } + } + return result; +} + void TokenList::simplifyPlatformTypes() { const bool isCPP11 = isCPP() && (mSettings.standards.cpp >= Standards::CPP11); @@ -2143,6 +2182,7 @@ void TokenList::simplifyPlatformTypes() } const std::string platform_type(mSettings.platform.toString()); + const std::unordered_set enumeratorNames = findEnumeratorNames(front()); for (Token *tok = front(); tok; tok = tok->next()) { if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) @@ -2151,6 +2191,9 @@ void TokenList::simplifyPlatformTypes() const Library::PlatformType * const platformtype = mSettings.library.platform_type(tok->str(), platform_type); if (platformtype) { + if (enumeratorNames.count(tok) != 0) // don't replace an enumerator's name, e.g. HANDLE in "enum E { HANDLE, ... };" + continue; + // check for namespace if (tok->strAt(-1) == "::") { const Token * tok1 = tok->tokAt(-2); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index f1a2a85e212..140bbe45868 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -400,6 +400,7 @@ class TestTokenizer : public TestFixture { TEST_CASE(multipleAssignment); TEST_CASE(platformWin); + TEST_CASE(platformWinEnumerator); TEST_CASE(platformWin32A); TEST_CASE(platformWin32W); TEST_CASE(platformWin32AStringCat); // ticket #5015 @@ -6264,6 +6265,31 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS(win32A, tokenizeAndStringify(code, settings_win32a)); } + void platformWinEnumerator() { + // #11538 - HANDLE is a platform type (-> void*) but here it's an enumerator and must not be replaced + { + const char code[] = "enum class E { HANDLE, OTHER };"; + ASSERT_EQUALS("enum class E { HANDLE , OTHER } ;", tokenizeAndStringify(code, settings_win32a)); + } + // a platform type used within an enumerator's constant-expression is not a name and must still be replaced + { + const char code[] = "enum E { A = sizeof(HANDLE) };"; + ASSERT_EQUALS("enum E { A = sizeof ( void * ) } ;", tokenizeAndStringify(code, settings_win32a)); + } + // combines both: DWORD is the enum's underlying type (-> replaced), HANDLE is the enumerator name (-> kept), + // and the HANDLE inside sizeof() is part of the constant-expression (-> replaced) + { + const char code[] = "enum class T : DWORD { HANDLE = sizeof(HANDLE) };"; + ASSERT_EQUALS("enum class T : unsigned long { HANDLE = sizeof ( void * ) } ;", tokenizeAndStringify(code, settings_win32a)); + } + // a first naive fix walked backwards from every platform-type token to find an enclosing "{". + // this implementation failed on the following example, therefore test it. + { + const char code[] = "enum T { }; void test(int, HANDLE, int);"; + ASSERT_EQUALS("enum T { } ; void test ( int , void * , int ) ;", tokenizeAndStringify(code, settings_win32a)); + } + } + void platformWin32A() { const char code[] = "wchar_t wc;" "TCHAR c;"