fix using type alias with derived class (#3050)

This commit is contained in:
IOBYTE 2021-01-17 10:10:53 -05:00 committed by GitHub
parent b2ed372f75
commit dd866f2898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 66 deletions

View File

@ -421,19 +421,41 @@ namespace {
static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount)
{ {
Token *tok1;
std::string name; std::string name;
bool isConst = false; bool isConst = false;
Token *tok1 = tok->next();
if (tok->next()->str() == "const") { // skip const if present
tok->deleteNext(); if (tok1->str() == "const") {
tok1->deleteThis();
isConst = true; isConst = true;
} }
if (tok->strAt(2) == "{") { // unnamed // skip "class|struct|union|enum"
tok1 = tok->linkAt(2); tok1 = tok1->next();
if (tok1 && tok1->next()) { const bool hasName = Token::Match(tok1, "%name%");
// skip name
if (hasName) {
name = tok1->str();
tok1 = tok1->next();
}
// skip base classes if present
if (tok1->str() == ":") {
tok1 = tok1->next();
while (tok1 && tok1->str() != "{")
tok1 = tok1->next();
if (!tok1)
return nullptr;
}
// skip to end
tok1 = tok1->link();
if (!hasName) { // unnamed
if (tok1->next()) {
// use typedef name if available // use typedef name if available
if (Token::Match(tok1->next(), "%type%")) if (Token::Match(tok1->next(), "%type%"))
name = tok1->next()->str(); name = tok1->next()->str();
@ -442,23 +464,6 @@ static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount)
tok->next()->insertToken(name); tok->next()->insertToken(name);
} else } else
return nullptr; return nullptr;
} else if (tok->strAt(3) == ":") {
tok1 = tok->tokAt(4);
while (tok1 && tok1->str() != "{")
tok1 = tok1->next();
if (!tok1)
return nullptr;
tok1 = tok1->link();
name = tok->strAt(2);
} else { // has a name
tok1 = tok->linkAt(3);
if (!tok1)
return nullptr;
name = tok->strAt(2);
} }
tok1->insertToken(";"); tok1->insertToken(";");
@ -623,21 +628,11 @@ void Tokenizer::simplifyTypedef()
// pull struct, union, enum or class definition out of typedef // pull struct, union, enum or class definition out of typedef
// use typedef name for unnamed struct, union, enum or class // use typedef name for unnamed struct, union, enum or class
if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {")) { if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {|:")) {
Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount);
if (!tok1) if (!tok1)
continue; continue;
tok = tok1; tok = tok1;
} else if (Token::Match(tok->next(), "const| struct|class %type% :")) {
Token *tok1 = tok;
while (tok1 && tok1->str() != ";" && tok1->str() != "{")
tok1 = tok1->next();
if (tok1 && tok1->str() == "{") {
tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount);
if (!tok1)
continue;
tok = tok1;
}
} }
/** @todo add support for union */ /** @todo add support for union */
@ -2003,25 +1998,31 @@ bool Tokenizer::simplifyUsing()
// Move struct defined in using out of using. // Move struct defined in using out of using.
// using T = struct t { }; => struct t { }; using T = struct t; // using T = struct t { }; => struct t { }; using T = struct t;
// fixme: this doesn't handle attributes // fixme: this doesn't handle attributes
if (Token::Match(start, "struct|union|enum %name%| {")) { if (Token::Match(start, "class|struct|union|enum %name%| {|:")) {
if (start->strAt(1) != "{") { Token *structEnd = start->tokAt(1);
Token *structEnd = start->linkAt(2); const bool hasName = Token::Match(structEnd, "%name%");
structEnd->insertToken(";", "");
TokenList::copyTokens(structEnd->next(), tok, start->next()); // skip over name if present
usingStart = structEnd->tokAt(2); if (hasName)
nameToken = usingStart->next(); structEnd = structEnd->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3); // skip over base class information
else if (structEnd->str() == ":") {
start = usingStart->linkAt(2)->tokAt(3); structEnd = structEnd->next(); // skip over ":"
usingEnd = findSemicolon(start); while (structEnd && structEnd->str() != "{")
tok->deleteThis(); structEnd = structEnd->next();
tok->deleteThis(); if (!structEnd)
tok->deleteThis(); continue;
tok = usingStart; }
} else {
Token *structEnd = start->linkAt(1); // use link to go to end
structEnd->insertToken(";", ""); structEnd = structEnd->link();
// add ';' after end of struct
structEnd->insertToken(";", "");
// add name for anonymous struct
if (!hasName) {
std::string newName; std::string newName;
if (structEnd->strAt(2) == ";") if (structEnd->strAt(2) == ";")
newName = name; newName = name;
@ -2030,19 +2031,23 @@ bool Tokenizer::simplifyUsing()
TokenList::copyTokens(structEnd->next(), tok, start); TokenList::copyTokens(structEnd->next(), tok, start);
structEnd->tokAt(5)->insertToken(newName, ""); structEnd->tokAt(5)->insertToken(newName, "");
start->insertToken(newName, ""); start->insertToken(newName, "");
} else
TokenList::copyTokens(structEnd->next(), tok, start->next());
usingStart = structEnd->tokAt(2); // add using after end of struct
nameToken = usingStart->next(); usingStart = structEnd->tokAt(2);
if (usingStart->strAt(2) == "=") nameToken = usingStart->next();
start = usingStart->tokAt(3); if (usingStart->strAt(2) == "=")
else start = usingStart->tokAt(3);
start = usingStart->linkAt(2)->tokAt(3); else
usingEnd = findSemicolon(start); start = usingStart->linkAt(2)->tokAt(3);
tok->deleteThis(); usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis(); // delete original using before struct
tok = usingStart; tok->deleteThis();
} tok->deleteThis();
tok->deleteThis();
tok = usingStart;
} }
// remove 'typename' and 'template' // remove 'typename' and 'template'
@ -2131,7 +2136,7 @@ bool Tokenizer::simplifyUsing()
if (Token::Match(type, "%type%")) if (Token::Match(type, "%type%"))
type = type->next(); type = type->next();
} else if (Token::Match(type, "%type%")) { } else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") || while (Token::Match(type, "const|class|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType())) (type->next() && type->next()->isStandardType()))
type = type->next(); type = type->next();

View File

@ -176,6 +176,7 @@ private:
TEST_CASE(simplifyTypedef133); // ticket #9812 - using TEST_CASE(simplifyTypedef133); // ticket #9812 - using
TEST_CASE(simplifyTypedef134); TEST_CASE(simplifyTypedef134);
TEST_CASE(simplifyTypedef135); // ticket #10068 TEST_CASE(simplifyTypedef135); // ticket #10068
TEST_CASE(simplifyTypedef136);
TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction1);
TEST_CASE(simplifyTypedefFunction2); // ticket #1685 TEST_CASE(simplifyTypedefFunction2); // ticket #1685
@ -2712,6 +2713,30 @@ private:
ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS(expected, tok(code));
} }
void simplifyTypedef136() {
const char code[] = "class C1 {};\n"
"typedef class S1 {} S1;\n"
"typedef class S2 : public C1 {} S2;\n"
"typedef class {} S3;\n"
"typedef class : public C1 {} S4;\n"
"S1 s1;\n"
"S2 s2;\n"
"S3 s3;\n"
"S4 s4;";
const char expected[] = "class C1 { } ; "
"class S1 { } ; "
"class S2 : public C1 { } ; "
"class S3 { } ; "
"class S4 : public C1 { } ; "
"class S1 s1 ; "
"class S2 s2 ; "
"class S3 s3 ; "
"class S4 s4 ;";
ASSERT_EQUALS(expected, tok(code, false));
}
void simplifyTypedefFunction1() { void simplifyTypedefFunction1() {
{ {
const char code[] = "typedef void (*my_func)();\n" const char code[] = "typedef void (*my_func)();\n"

View File

@ -63,6 +63,7 @@ private:
TEST_CASE(simplifyUsing14); TEST_CASE(simplifyUsing14);
TEST_CASE(simplifyUsing15); TEST_CASE(simplifyUsing15);
TEST_CASE(simplifyUsing16); TEST_CASE(simplifyUsing16);
TEST_CASE(simplifyUsing17);
TEST_CASE(simplifyUsing8970); TEST_CASE(simplifyUsing8970);
TEST_CASE(simplifyUsing8971); TEST_CASE(simplifyUsing8971);
@ -428,6 +429,30 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void simplifyUsing17() {
const char code[] = "class C1 {};\n"
"using S1 = class S1 {};\n"
"using S2 = class S2 : public C1 {};\n"
"using S3 = class {};\n"
"using S4 = class : public C1 {};\n"
"S1 s1;\n"
"S2 s2;\n"
"S3 s3;\n"
"S4 s4;";
const char expected[] = "class C1 { } ; "
"class S1 { } ; "
"class S2 : public C1 { } ; "
"class S3 { } ; "
"class S4 : public C1 { } ; "
"class S1 s1 ; "
"class S2 s2 ; "
"class S3 s3 ; "
"class S4 s4 ;";
ASSERT_EQUALS(expected, tok(code, false));
}
void simplifyUsing8970() { void simplifyUsing8970() {
const char code[] = "using V = std::vector<int>;\n" const char code[] = "using V = std::vector<int>;\n"
"struct A {\n" "struct A {\n"