Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions lib/tokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Token*> findEnumeratorNames(const Token* front)
{
std::unordered_set<const Token*> 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);
Expand Down Expand Up @@ -2143,6 +2182,7 @@ void TokenList::simplifyPlatformTypes()
}

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

for (Token *tok = front(); tok; tok = tok->next()) {
if (tok->tokType() != Token::eType && tok->tokType() != Token::eName)
Expand All @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;"
Expand Down
Loading