From 155e4ce9129435610429a36ba044c3b0702e7e33 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Sat, 9 Feb 2019 02:34:59 -0500 Subject: [PATCH] Fixed #8971 ("(debug) Unknown type 'x'." using alias in class members) (#1653) * Fixed #8971 ("(debug) Unknown type 'x'." using alias in class members) * template simplifier: partial fix for #8972 Add support for multi-token default template parameters. * template simplifier: fix for #8971 Remove typename outside of templates. --- lib/templatesimplifier.cpp | 463 +++++++++++++++++++--------------- test/testsimplifytemplate.cpp | 38 +++ test/testsimplifytypedef.cpp | 8 +- test/testsimplifyusing.cpp | 40 +++ test/testvarid.cpp | 2 +- 5 files changed, 340 insertions(+), 211 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 5375ecf2c..d05ab4d08 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -2415,6 +2415,10 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() getTemplateParametersInDeclaration(forwardDecl.token->tokAt(2), params1); for (auto & decl : mTemplateDeclarations) { + // skip partializations + if (decl.isPartialSpecialization()) + continue; + std::vector params2; getTemplateParametersInDeclaration(decl.token->tokAt(2), params2); @@ -2432,8 +2436,18 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() for (size_t k = 0; k < params1.size(); k++) { // copy default value to declaration if not present if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { - const_cast(params2[k])->insertToken(params1[k]->strAt(2)); - const_cast(params2[k])->insertToken(params1[k]->strAt(1)); + int level = 0; + const Token *end = params1[k]->next(); + while (end && !(level == 0 && Token::Match(end, ",|>"))) { + if (Token::Match(end, "{|(|<")) + level++; + else if (Token::Match(end, "}|)|>")) + level--; + end = end->next(); + } + if (end) + TokenList::copyTokens(const_cast(params2[k]), params1[k]->next(), end->previous()); + break; } } @@ -2629,6 +2643,97 @@ static Token *findSemicolon(Token *tok) return tok; } +static bool usingMatch( + const Token *nameToken, + const std::string &scope, + Token **tok, + const std::string &scope1, + const std::list &scopeList1) +{ + Token *tok1 = *tok; + + if (tok1 && tok1->str() != nameToken->str()) + return false; + + // skip this using + if (tok1 == nameToken) { + *tok = findSemicolon(tok1); + return false; + } + + // skip other using with this name + if (tok1->strAt(-1) == "using") { + // fixme: this is wrong + // skip to end of scope + if (scopeList1.back().bodyEnd) + *tok = scopeList1.back().bodyEnd->previous(); + return false; + } + + if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) { + // fixme + return false; + } + + // get qualification + std::string qualification; + const Token* tok2 = tok1; + std::string::size_type index = scope.size(); + std::string::size_type new_index = std::string::npos; + bool match = true; + while (tok2->strAt(-1) == "::") { + std::string last; + if (match && !scope1.empty()) { + new_index = scope1.rfind(' ', index - 1); + if (new_index != std::string::npos) + last = scope1.substr(new_index, index - new_index); + else if (!qualification.empty()) + last.clear(); + else + last = scope1; + } else + match = false; + if (match && tok2->strAt(-2) == last) + index = new_index; + else { + if (!qualification.empty()) + qualification = " :: " + qualification; + qualification = tok2->strAt(-2) + qualification; + } + tok2 = tok2->tokAt(-2); + } + + // todo: check using namespace + std::string fullScope1 = scope1; + if (!scope1.empty() && !qualification.empty()) + fullScope1 += " :: "; + fullScope1 += qualification; + + if (scope == fullScope1) + return true; + + std::string newScope1 = scope1; + + // scopes didn't match so try higher scopes + while (!newScope1.empty()) { + std::string::size_type separator = newScope1.rfind(" :: ", index - 1); + if (separator != std::string::npos) + newScope1 = newScope1.substr(0, separator); + else + newScope1.clear(); + + std::string newFullScope1 = newScope1; + if (!newScope1.empty() && !qualification.empty()) + newFullScope1 += " :: "; + newFullScope1 += qualification; + + if (scope == newFullScope1) + return true; + } + + return false; +} + bool TemplateSimplifier::simplifyUsing() { bool substitute = false; @@ -2755,203 +2860,152 @@ bool TemplateSimplifier::simplifyUsing() scope1 = getScopeName(scopeList1); continue; } - if (tok1 && tok1->str() == name) { - // skip this using - if (tok1 == nameToken) { - tok1 = findSemicolon(tok1); - continue; - } - // skip other using with this name - if (tok1->strAt(-1) == "using") { - // fixme: this is wrong - // skip to end of scope - if (scopeList1.back().bodyEnd) - tok1 = scopeList1.back().bodyEnd->previous(); - continue; - } - if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) { - // fixme - continue; + + if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1)) + continue; + + // remove the qualification + while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) { + tok1->deletePrevious(); + tok1->deletePrevious(); + } + + Token * arrayStart = nullptr; + + // parse the type + Token *type = start; + if (type->str() == "::") { + type = type->next(); + while (Token::Match(type, "%type% ::")) + type = type->tokAt(2); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type% ::")) { + do { + type = type->tokAt(2); + } while (Token::Match(type, "%type% ::")); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type%")) { + while (Token::Match(type, "const|struct|union|enum %type%") || + (type->next() && type->next()->isStandardType())) + type = type->next(); + + type = type->next(); + + while (Token::Match(type, "%type%") && + (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { + type = type->next(); } - // get qualification - std::string qualification; - const Token* tok2 = tok1; - std::string::size_type index = scope.size(); - std::string::size_type new_index = std::string::npos; - bool match = true; - while (tok2->strAt(-1) == "::") { - std::string last; - if (match && !scope1.empty()) { - new_index = scope1.rfind(' ', index - 1); - if (new_index != std::string::npos) - last = scope1.substr(new_index, index - new_index); - else if (!qualification.empty()) - last.clear(); + bool atEnd = false; + while (!atEnd) { + if (type && type->str() == "::") { + type = type->next(); + } + + if (Token::Match(type, "%type%") && + type->next() && !Token::Match(type->next(), "[|;|,|(")) { + type = type->next(); + } else if (Token::simpleMatch(type, "const (")) { + type = type->next(); + atEnd = true; + } else + atEnd = true; + } + } else + syntaxError(type); + + // check for invalid input + if (!type) + syntaxError(tok1); + + // check for template + if (type->str() == "<") { + type = type->findClosingBracket(); + + while (type && Token::Match(type->next(), ":: %type%")) + type = type->tokAt(2); + + if (!type) { + syntaxError(tok1); + } + + while (Token::Match(type->next(), "const|volatile")) + type = type->next(); + + type = type->next(); + } + + // check for pointers and references + std::list pointers; + while (Token::Match(type, "*|&|&&|const")) { + pointers.push_back(type->str()); + type = type->next(); + } + + // check for array + if (type && type->str() == "[") { + do { + if (!arrayStart) + arrayStart = type; + + bool atEnd = false; + while (!atEnd) { + while (type->next() && !Token::Match(type->next(), ";|,")) { + type = type->next(); + } + + if (!type->next()) + syntaxError(type); // invalid input + else if (type->next()->str() == ";") + atEnd = true; + else if (type->str() == "]") + atEnd = true; else - last = scope1; - } else - match = false; - if (match && tok2->strAt(-2) == last) - index = new_index; - else { - if (!qualification.empty()) - qualification = " :: " + qualification; - qualification = tok2->strAt(-2) + qualification; + type = type->next(); } - tok2 = tok2->tokAt(-2); + + type = type->next(); + } while (type && type->str() == "["); + } + + Token* after = tok1->next(); + // check if type was parsed + if (type && type == usingEnd) { + // check for array syntax and add type around variable + if (arrayStart) { + if (Token::Match(tok1->next(), "%name%")) { + mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous()); + mTokenList.copyTokens(tok1, start, arrayStart->previous()); + tok1->deleteThis(); + substitute = true; + } + } else { + // just replace simple type aliases + mTokenList.copyTokens(tok1, start, usingEnd->previous()); + tok1->deleteThis(); + substitute = true; } - - // todo: check using namespace - std::string fullScope1 = scope1; - if (!scope1.empty() && !qualification.empty()) - fullScope1 += " :: "; - fullScope1 += qualification; - - if (scope == fullScope1) { - // remove the qualification - while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) { - tok1->deletePrevious(); - tok1->deletePrevious(); + } else { + skip = true; + if (mSettings->debugwarnings && mErrorLogger) { + std::string str; + for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { + if (!str.empty()) + str += ' '; + str += tok3->str(); } + str += " ;"; + std::list callstack(1, usingStart); + mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", - Token * arrayStart = nullptr; - - // parse the type - Token *type = start; - if (type->str() == "::") { - type = type->next(); - while (Token::Match(type, "%type% ::")) - type = type->tokAt(2); - if (Token::Match(type, "%type%")) - type = type->next(); - } else if (Token::Match(type, "%type% ::")) { - do { - type = type->tokAt(2); - } while (Token::Match(type, "%type% ::")); - if (Token::Match(type, "%type%")) - type = type->next(); - } else if (Token::Match(type, "%type%")) { - while (Token::Match(type, "const|struct|union|enum %type%") || - (type->next() && type->next()->isStandardType())) - type = type->next(); - - type = type->next(); - - while (Token::Match(type, "%type%") && - (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { - type = type->next(); - } - - bool atEnd = false; - while (!atEnd) { - if (type && type->str() == "::") { - type = type->next(); - } - - if (Token::Match(type, "%type%") && - type->next() && !Token::Match(type->next(), "[|;|,|(")) { - type = type->next(); - } else if (Token::simpleMatch(type, "const (")) { - type = type->next(); - atEnd = true; - } else - atEnd = true; - } - } else - syntaxError(type); - - // check for invalid input - if (!type) - syntaxError(tok1); - - // check for template - if (type->str() == "<") { - type = type->findClosingBracket(); - - while (type && Token::Match(type->next(), ":: %type%")) - type = type->tokAt(2); - - if (!type) { - syntaxError(tok1); - } - - while (Token::Match(type->next(), "const|volatile")) - type = type->next(); - - type = type->next(); - } - - // check for pointers and references - std::list pointers; - while (Token::Match(type, "*|&|&&|const")) { - pointers.push_back(type->str()); - type = type->next(); - } - - // check for array - if (type && type->str() == "[") { - do { - if (!arrayStart) - arrayStart = type; - - bool atEnd = false; - while (!atEnd) { - while (type->next() && !Token::Match(type->next(), ";|,")) { - type = type->next(); - } - - if (!type->next()) - syntaxError(type); // invalid input - else if (type->next()->str() == ";") - atEnd = true; - else if (type->str() == "]") - atEnd = true; - else - type = type->next(); - } - - type = type->next(); - } while (type && type->str() == "["); - } - - Token* after = tok1->next(); - // check if type was parsed - if (type && type == usingEnd) { - // check for array syntax and add type around variable - if (arrayStart) { - if (Token::Match(tok1->next(), "%name%")) { - mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous()); - mTokenList.copyTokens(tok1, start, arrayStart->previous()); - tok1->deleteThis(); - substitute = true; - } - } else { - // just replace simple type aliases - mTokenList.copyTokens(tok1, start, usingEnd->previous()); - tok1->deleteThis(); - substitute = true; - } - } else { - skip = true; - if (mSettings->debugwarnings && mErrorLogger) { - std::string str; - for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { - if (!str.empty()) - str += ' '; - str += tok3->str(); - } - str += " ;"; - std::list callstack(1, usingStart); - mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", - - "Failed to parse \'" + str + "\'. The checking continues anyway.", false)); - } - } - tok1 = after; + "Failed to parse \'" + str + "\'. The checking continues anyway.", false)); } } + tok1 = after; } + if (!skip) usingList.emplace_back(usingStart, usingEnd); } @@ -2988,6 +3042,19 @@ void TemplateSimplifier::simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates) { + // Remove "typename" unless used in template arguments.. + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "typename %name%")) + tok->deleteThis(); + + if (Token::simpleMatch(tok, "template <")) { + while (tok && tok->str() != ">") + tok = tok->next(); + if (!tok) + break; + } + } + // TODO: 2 is not the ideal number of loops. // We should loop until the number of declarations is 0 but we can't // do that until we instantiate unintstantiated templates with their symbolic types. @@ -3014,24 +3081,8 @@ void TemplateSimplifier::simplifyTemplates( bool hasTemplates = getTemplateDeclarations(); - if (i == 0) { + if (i == 0) codeWithTemplates = hasTemplates; - if (hasTemplates) { - // There are templates.. - // Remove "typename" unless used in template arguments.. - for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { - if (tok->str() == "typename") - tok->deleteThis(); - - if (Token::simpleMatch(tok, "template <")) { - while (tok && tok->str() != ">") - tok = tok->next(); - if (!tok) - break; - } - } - } - } // Make sure there is something to simplify. if (mTemplateDeclarations.empty()) diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 61c197d9d..8c15f6172 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -138,6 +138,7 @@ private: TEST_CASE(template98); // #8959 TEST_CASE(template99); // #8960 TEST_CASE(template100); // #8967 + TEST_CASE(template101); // #8968 TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) @@ -2117,6 +2118,24 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template101() { // #8968 + const char code[] = "class A {\n" + "public:\n" + " using ArrayType = std::vector;\n" + " void func(typename ArrayType::size_type i) {\n" + " }\n" + "};"; + + const char exp[] = "class A { " + "public: " + "void func ( std :: vector < int > :: size_type i ) { " + "} " + "} ;"; + + ASSERT_EQUALS(exp, tok(code)); + ASSERT_EQUALS("", errout.str()); + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" @@ -2334,6 +2353,25 @@ private: "{ int ar [ 3 ] ; } ;"; ASSERT_EQUALS(exp, tok(code)); } + { + const char code[] = "template\n" + "struct ei_solve_triangular_selector;\n" + "template\n" + "struct ei_solve_triangular_selector {\n" + "};\n" + "template\n" + "struct ei_solve_triangular_selector { };"; + + const char exp[] = "template < typename Lhs , int TriangularPart = ( int ( Lhs :: Flags ) & LowerTriangularBit ) > " + "struct ei_solve_triangular_selector ; " + "template < typename Lhs , int UpLo > " + "struct ei_solve_triangular_selector < Lhs , UpLo > { " + "} ; " + "template < typename Lhs , int TriangularPart = ( int ( Lhs :: Flags ) & LowerTriangularBit ) > " + "struct ei_solve_triangular_selector { } ;"; + + ASSERT_EQUALS(exp, tok(code)); + } } void template_default_type() { diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 6e331eb08..c27896739 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -2307,25 +2307,25 @@ private: const char code1[] = "typedef typename A B;\n" "typedef typename B C;\n" "typename C c;\n"; - const char expected1[] = "typename A c ;"; + const char expected1[] = "A c ;"; ASSERT_EQUALS(expected1, tok(code1)); const char code2[] = "typedef typename A B;\n" "typedef typename B C;\n" "C c;\n"; - const char expected2[] = "typename A c ;"; + const char expected2[] = "A c ;"; ASSERT_EQUALS(expected2, tok(code2)); const char code3[] = "typedef typename A B;\n" "typedef B C;\n" "C c;\n"; - const char expected3[] = "typename A c ;"; + const char expected3[] = "A c ;"; ASSERT_EQUALS(expected3, tok(code3)); const char code4[] = "typedef A B;\n" "typedef typename B C;\n" "C c;\n"; - const char expected4[] = "typename A c ;"; + const char expected4[] = "A c ;"; ASSERT_EQUALS(expected4, tok(code4)); const char code5[] = "typedef A B;\n" diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 646916d4e..5c40d6705 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -58,6 +58,9 @@ private: TEST_CASE(simplifyUsing14); TEST_CASE(simplifyUsing15); TEST_CASE(simplifyUsing16); + + TEST_CASE(simplifyUsing8970); + TEST_CASE(simplifyUsing8971); } std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { @@ -412,6 +415,43 @@ private: ASSERT_EQUALS("", errout.str()); } + void simplifyUsing8970() { + const char code[] = "using V = std::vector;\n" + "struct A {\n" + " V p;\n" + "};"; + + const char expected[] = "struct A { " + "std :: vector < int > p ; " + "} ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + + void simplifyUsing8971() { + const char code[] = "class A {\n" + "public:\n" + " using V = std::vector;\n" + "};\n" + "using V = std::vector;\n" + "class I {\n" + "private:\n" + " A::V v_;\n" + " V v2_;\n" + "};"; + + const char expected[] = "class A { " + "public: " + "} ; " + "class I { " + "private: " + "std :: vector < double > v_ ; " + "std :: vector < int > v2_ ; " + "} ;"; + + ASSERT_EQUALS(expected, tok(code, false)); + } + }; REGISTER_TEST(TestSimplifyUsing) diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 8054287c5..36ab7be6d 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -2187,7 +2187,7 @@ private: ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template struct S {};")); - ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;")); + ASSERT_EQUALS("1: A a@1 ;\n", tokenize("typename A a;")); } void varid_rvalueref() {