From dd866f28982aab7078a09e7bbb16e6be3050eb45 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Sun, 17 Jan 2021 10:10:53 -0500 Subject: [PATCH] fix using type alias with derived class (#3050) --- lib/tokenize.cpp | 137 ++++++++++++++++++----------------- test/testsimplifytypedef.cpp | 25 +++++++ test/testsimplifyusing.cpp | 25 +++++++ 3 files changed, 121 insertions(+), 66 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 2702a16e1..3e476ca38 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -421,19 +421,41 @@ namespace { static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) { - Token *tok1; std::string name; bool isConst = false; + Token *tok1 = tok->next(); - if (tok->next()->str() == "const") { - tok->deleteNext(); + // skip const if present + if (tok1->str() == "const") { + tok1->deleteThis(); isConst = true; } - if (tok->strAt(2) == "{") { // unnamed - tok1 = tok->linkAt(2); + // skip "class|struct|union|enum" + 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 if (Token::Match(tok1->next(), "%type%")) name = tok1->next()->str(); @@ -442,23 +464,6 @@ static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) tok->next()->insertToken(name); } else 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(";"); @@ -623,21 +628,11 @@ void Tokenizer::simplifyTypedef() // pull struct, union, enum or class definition out of typedef // 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); if (!tok1) continue; 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 */ @@ -2003,25 +1998,31 @@ bool Tokenizer::simplifyUsing() // Move struct defined in using out of using. // using T = struct t { }; => struct t { }; using T = struct t; // fixme: this doesn't handle attributes - if (Token::Match(start, "struct|union|enum %name%| {")) { - if (start->strAt(1) != "{") { - Token *structEnd = start->linkAt(2); - structEnd->insertToken(";", ""); - TokenList::copyTokens(structEnd->next(), tok, start->next()); - usingStart = structEnd->tokAt(2); - nameToken = usingStart->next(); - if (usingStart->strAt(2) == "=") - start = usingStart->tokAt(3); - else - start = usingStart->linkAt(2)->tokAt(3); - usingEnd = findSemicolon(start); - tok->deleteThis(); - tok->deleteThis(); - tok->deleteThis(); - tok = usingStart; - } else { - Token *structEnd = start->linkAt(1); - structEnd->insertToken(";", ""); + if (Token::Match(start, "class|struct|union|enum %name%| {|:")) { + Token *structEnd = start->tokAt(1); + const bool hasName = Token::Match(structEnd, "%name%"); + + // skip over name if present + if (hasName) + structEnd = structEnd->next(); + + // skip over base class information + if (structEnd->str() == ":") { + structEnd = structEnd->next(); // skip over ":" + while (structEnd && structEnd->str() != "{") + structEnd = structEnd->next(); + if (!structEnd) + continue; + } + + // use link to go to end + structEnd = structEnd->link(); + + // add ';' after end of struct + structEnd->insertToken(";", ""); + + // add name for anonymous struct + if (!hasName) { std::string newName; if (structEnd->strAt(2) == ";") newName = name; @@ -2030,19 +2031,23 @@ bool Tokenizer::simplifyUsing() TokenList::copyTokens(structEnd->next(), tok, start); structEnd->tokAt(5)->insertToken(newName, ""); start->insertToken(newName, ""); + } else + TokenList::copyTokens(structEnd->next(), tok, start->next()); - usingStart = structEnd->tokAt(2); - nameToken = usingStart->next(); - if (usingStart->strAt(2) == "=") - start = usingStart->tokAt(3); - else - start = usingStart->linkAt(2)->tokAt(3); - usingEnd = findSemicolon(start); - tok->deleteThis(); - tok->deleteThis(); - tok->deleteThis(); - tok = usingStart; - } + // add using after end of struct + usingStart = structEnd->tokAt(2); + nameToken = usingStart->next(); + if (usingStart->strAt(2) == "=") + start = usingStart->tokAt(3); + else + start = usingStart->linkAt(2)->tokAt(3); + usingEnd = findSemicolon(start); + + // delete original using before struct + tok->deleteThis(); + tok->deleteThis(); + tok->deleteThis(); + tok = usingStart; } // remove 'typename' and 'template' @@ -2131,7 +2136,7 @@ bool Tokenizer::simplifyUsing() if (Token::Match(type, "%type%")) type = type->next(); } 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 = type->next(); diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index e9b8a223c..67805fcd4 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -176,6 +176,7 @@ private: TEST_CASE(simplifyTypedef133); // ticket #9812 - using TEST_CASE(simplifyTypedef134); TEST_CASE(simplifyTypedef135); // ticket #10068 + TEST_CASE(simplifyTypedef136); TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction2); // ticket #1685 @@ -2712,6 +2713,30 @@ private: 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() { { const char code[] = "typedef void (*my_func)();\n" diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index c1fbc8252..2fac16c0b 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -63,6 +63,7 @@ private: TEST_CASE(simplifyUsing14); TEST_CASE(simplifyUsing15); TEST_CASE(simplifyUsing16); + TEST_CASE(simplifyUsing17); TEST_CASE(simplifyUsing8970); TEST_CASE(simplifyUsing8971); @@ -428,6 +429,30 @@ private: 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() { const char code[] = "using V = std::vector;\n" "struct A {\n"