Skip to content

Commit dd866f2

Browse files
authored
fix using type alias with derived class (cppcheck-opensource#3050)
1 parent b2ed372 commit dd866f2

3 files changed

Lines changed: 121 additions & 66 deletions

File tree

lib/tokenize.cpp

Lines changed: 71 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -421,19 +421,41 @@ namespace {
421421

422422
static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount)
423423
{
424-
Token *tok1;
425424
std::string name;
426425
bool isConst = false;
426+
Token *tok1 = tok->next();
427427

428-
if (tok->next()->str() == "const") {
429-
tok->deleteNext();
428+
// skip const if present
429+
if (tok1->str() == "const") {
430+
tok1->deleteThis();
430431
isConst = true;
431432
}
432433

433-
if (tok->strAt(2) == "{") { // unnamed
434-
tok1 = tok->linkAt(2);
434+
// skip "class|struct|union|enum"
435+
tok1 = tok1->next();
436+
437+
const bool hasName = Token::Match(tok1, "%name%");
438+
439+
// skip name
440+
if (hasName) {
441+
name = tok1->str();
442+
tok1 = tok1->next();
443+
}
444+
445+
// skip base classes if present
446+
if (tok1->str() == ":") {
447+
tok1 = tok1->next();
448+
while (tok1 && tok1->str() != "{")
449+
tok1 = tok1->next();
450+
if (!tok1)
451+
return nullptr;
452+
}
453+
454+
// skip to end
455+
tok1 = tok1->link();
435456

436-
if (tok1 && tok1->next()) {
457+
if (!hasName) { // unnamed
458+
if (tok1->next()) {
437459
// use typedef name if available
438460
if (Token::Match(tok1->next(), "%type%"))
439461
name = tok1->next()->str();
@@ -442,23 +464,6 @@ static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount)
442464
tok->next()->insertToken(name);
443465
} else
444466
return nullptr;
445-
} else if (tok->strAt(3) == ":") {
446-
tok1 = tok->tokAt(4);
447-
while (tok1 && tok1->str() != "{")
448-
tok1 = tok1->next();
449-
if (!tok1)
450-
return nullptr;
451-
452-
tok1 = tok1->link();
453-
454-
name = tok->strAt(2);
455-
} else { // has a name
456-
tok1 = tok->linkAt(3);
457-
458-
if (!tok1)
459-
return nullptr;
460-
461-
name = tok->strAt(2);
462467
}
463468

464469
tok1->insertToken(";");
@@ -623,21 +628,11 @@ void Tokenizer::simplifyTypedef()
623628

624629
// pull struct, union, enum or class definition out of typedef
625630
// use typedef name for unnamed struct, union, enum or class
626-
if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {")) {
631+
if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {|:")) {
627632
Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount);
628633
if (!tok1)
629634
continue;
630635
tok = tok1;
631-
} else if (Token::Match(tok->next(), "const| struct|class %type% :")) {
632-
Token *tok1 = tok;
633-
while (tok1 && tok1->str() != ";" && tok1->str() != "{")
634-
tok1 = tok1->next();
635-
if (tok1 && tok1->str() == "{") {
636-
tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount);
637-
if (!tok1)
638-
continue;
639-
tok = tok1;
640-
}
641636
}
642637

643638
/** @todo add support for union */
@@ -2003,25 +1998,31 @@ bool Tokenizer::simplifyUsing()
20031998
// Move struct defined in using out of using.
20041999
// using T = struct t { }; => struct t { }; using T = struct t;
20052000
// fixme: this doesn't handle attributes
2006-
if (Token::Match(start, "struct|union|enum %name%| {")) {
2007-
if (start->strAt(1) != "{") {
2008-
Token *structEnd = start->linkAt(2);
2009-
structEnd->insertToken(";", "");
2010-
TokenList::copyTokens(structEnd->next(), tok, start->next());
2011-
usingStart = structEnd->tokAt(2);
2012-
nameToken = usingStart->next();
2013-
if (usingStart->strAt(2) == "=")
2014-
start = usingStart->tokAt(3);
2015-
else
2016-
start = usingStart->linkAt(2)->tokAt(3);
2017-
usingEnd = findSemicolon(start);
2018-
tok->deleteThis();
2019-
tok->deleteThis();
2020-
tok->deleteThis();
2021-
tok = usingStart;
2022-
} else {
2023-
Token *structEnd = start->linkAt(1);
2024-
structEnd->insertToken(";", "");
2001+
if (Token::Match(start, "class|struct|union|enum %name%| {|:")) {
2002+
Token *structEnd = start->tokAt(1);
2003+
const bool hasName = Token::Match(structEnd, "%name%");
2004+
2005+
// skip over name if present
2006+
if (hasName)
2007+
structEnd = structEnd->next();
2008+
2009+
// skip over base class information
2010+
if (structEnd->str() == ":") {
2011+
structEnd = structEnd->next(); // skip over ":"
2012+
while (structEnd && structEnd->str() != "{")
2013+
structEnd = structEnd->next();
2014+
if (!structEnd)
2015+
continue;
2016+
}
2017+
2018+
// use link to go to end
2019+
structEnd = structEnd->link();
2020+
2021+
// add ';' after end of struct
2022+
structEnd->insertToken(";", "");
2023+
2024+
// add name for anonymous struct
2025+
if (!hasName) {
20252026
std::string newName;
20262027
if (structEnd->strAt(2) == ";")
20272028
newName = name;
@@ -2030,19 +2031,23 @@ bool Tokenizer::simplifyUsing()
20302031
TokenList::copyTokens(structEnd->next(), tok, start);
20312032
structEnd->tokAt(5)->insertToken(newName, "");
20322033
start->insertToken(newName, "");
2034+
} else
2035+
TokenList::copyTokens(structEnd->next(), tok, start->next());
20332036

2034-
usingStart = structEnd->tokAt(2);
2035-
nameToken = usingStart->next();
2036-
if (usingStart->strAt(2) == "=")
2037-
start = usingStart->tokAt(3);
2038-
else
2039-
start = usingStart->linkAt(2)->tokAt(3);
2040-
usingEnd = findSemicolon(start);
2041-
tok->deleteThis();
2042-
tok->deleteThis();
2043-
tok->deleteThis();
2044-
tok = usingStart;
2045-
}
2037+
// add using after end of struct
2038+
usingStart = structEnd->tokAt(2);
2039+
nameToken = usingStart->next();
2040+
if (usingStart->strAt(2) == "=")
2041+
start = usingStart->tokAt(3);
2042+
else
2043+
start = usingStart->linkAt(2)->tokAt(3);
2044+
usingEnd = findSemicolon(start);
2045+
2046+
// delete original using before struct
2047+
tok->deleteThis();
2048+
tok->deleteThis();
2049+
tok->deleteThis();
2050+
tok = usingStart;
20462051
}
20472052

20482053
// remove 'typename' and 'template'
@@ -2131,7 +2136,7 @@ bool Tokenizer::simplifyUsing()
21312136
if (Token::Match(type, "%type%"))
21322137
type = type->next();
21332138
} else if (Token::Match(type, "%type%")) {
2134-
while (Token::Match(type, "const|struct|union|enum %type%") ||
2139+
while (Token::Match(type, "const|class|struct|union|enum %type%") ||
21352140
(type->next() && type->next()->isStandardType()))
21362141
type = type->next();
21372142

test/testsimplifytypedef.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ class TestSimplifyTypedef : public TestFixture {
176176
TEST_CASE(simplifyTypedef133); // ticket #9812 - using
177177
TEST_CASE(simplifyTypedef134);
178178
TEST_CASE(simplifyTypedef135); // ticket #10068
179+
TEST_CASE(simplifyTypedef136);
179180

180181
TEST_CASE(simplifyTypedefFunction1);
181182
TEST_CASE(simplifyTypedefFunction2); // ticket #1685
@@ -2712,6 +2713,30 @@ class TestSimplifyTypedef : public TestFixture {
27122713
ASSERT_EQUALS(expected, tok(code));
27132714
}
27142715

2716+
void simplifyTypedef136() {
2717+
const char code[] = "class C1 {};\n"
2718+
"typedef class S1 {} S1;\n"
2719+
"typedef class S2 : public C1 {} S2;\n"
2720+
"typedef class {} S3;\n"
2721+
"typedef class : public C1 {} S4;\n"
2722+
"S1 s1;\n"
2723+
"S2 s2;\n"
2724+
"S3 s3;\n"
2725+
"S4 s4;";
2726+
2727+
const char expected[] = "class C1 { } ; "
2728+
"class S1 { } ; "
2729+
"class S2 : public C1 { } ; "
2730+
"class S3 { } ; "
2731+
"class S4 : public C1 { } ; "
2732+
"class S1 s1 ; "
2733+
"class S2 s2 ; "
2734+
"class S3 s3 ; "
2735+
"class S4 s4 ;";
2736+
2737+
ASSERT_EQUALS(expected, tok(code, false));
2738+
}
2739+
27152740
void simplifyTypedefFunction1() {
27162741
{
27172742
const char code[] = "typedef void (*my_func)();\n"

test/testsimplifyusing.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class TestSimplifyUsing : public TestFixture {
6363
TEST_CASE(simplifyUsing14);
6464
TEST_CASE(simplifyUsing15);
6565
TEST_CASE(simplifyUsing16);
66+
TEST_CASE(simplifyUsing17);
6667

6768
TEST_CASE(simplifyUsing8970);
6869
TEST_CASE(simplifyUsing8971);
@@ -428,6 +429,30 @@ class TestSimplifyUsing : public TestFixture {
428429
ASSERT_EQUALS("", errout.str());
429430
}
430431

432+
void simplifyUsing17() {
433+
const char code[] = "class C1 {};\n"
434+
"using S1 = class S1 {};\n"
435+
"using S2 = class S2 : public C1 {};\n"
436+
"using S3 = class {};\n"
437+
"using S4 = class : public C1 {};\n"
438+
"S1 s1;\n"
439+
"S2 s2;\n"
440+
"S3 s3;\n"
441+
"S4 s4;";
442+
443+
const char expected[] = "class C1 { } ; "
444+
"class S1 { } ; "
445+
"class S2 : public C1 { } ; "
446+
"class S3 { } ; "
447+
"class S4 : public C1 { } ; "
448+
"class S1 s1 ; "
449+
"class S2 s2 ; "
450+
"class S3 s3 ; "
451+
"class S4 s4 ;";
452+
453+
ASSERT_EQUALS(expected, tok(code, false));
454+
}
455+
431456
void simplifyUsing8970() {
432457
const char code[] = "using V = std::vector<int>;\n"
433458
"struct A {\n"

0 commit comments

Comments
 (0)