From 2a4b28c267059b5520f47f528a67f903d910f2df Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Wed, 12 Jun 2019 01:44:48 -0400 Subject: [PATCH] Fixed #9155 (Syntax error on valid C++ code) (#1880) Refactored simplifyTemplateAliases to iterate over template type aliases rather than instantiations. This fixed template type aliases that were not templates. Don't instantiate templates in template type aliases. They will get instantiated once the type alias is instantiated. This required increasing the template simplifier pass count to 3 so one of the existing tests continued to work. --- lib/templatesimplifier.cpp | 154 ++++++++++++---------------------- test/testsimplifytemplate.cpp | 28 ++++++- test/testtokenize.cpp | 1 - 3 files changed, 80 insertions(+), 103 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 5602a0fca..6742aa041 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -771,7 +771,7 @@ void TemplateSimplifier::getTemplateInstantiations() const Token *tok2 = Token::findmatch(tok, "{|;"); if (tok2 && tok2->str() == "{") tok = tok2->link(); - else if (!isUsing && tok2 && tok2->str() == ";") + else if (tok2 && tok2->str() == ";") tok = const_cast(tok2); } } else if (Token::Match(tok, "template using %name% <")) { @@ -1150,39 +1150,13 @@ void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) void TemplateSimplifier::simplifyTemplateAliases() { - std::list::iterator it1, it2, it3; - for (it1 = mTemplateInstantiations.begin(); it1 != mTemplateInstantiations.end();) { - it3 = it1; - TokenAndName &templateAlias = *it1; - ++it1; - Token *startToken = templateAlias.token; - if (!startToken) - continue; - while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) - startToken = startToken->tokAt(-2); - if (!(Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<") || - Token::Match(startToken->tokAt(-5), "> using %name% = typename %name% ::|<"))) - continue; - const bool hasTypename(startToken->strAt(-1) == "typename"); + for (std::list::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) { + TokenAndName &aliasDeclaration = *it1; - // Get start token for alias - startToken = startToken->tokAt(hasTypename ? -6 : -5); - while (Token::Match(startToken, "%name%|<|>|>>|,")) - startToken = startToken->previous(); - // handle case where 'template' is first token - if (!startToken) { - if (!Token::simpleMatch(mTokenList.front(), "template <")) - continue; - } else if (!Token::Match(startToken, "[;{}] template <")) + if (!aliasDeclaration.isAlias()) { + ++it1; continue; - - std::list::iterator it5 = std::find_if(mTemplateDeclarations.begin(), - mTemplateDeclarations.end(), - FindToken(!startToken ? mTokenList.front() : startToken->next())); - if (it5 == mTemplateDeclarations.end()) - continue; - - TokenAndName &aliasDeclaration = *it5; + } // alias parameters.. std::vector aliasParameters; @@ -1192,15 +1166,19 @@ void TemplateSimplifier::simplifyTemplateAliases() aliasParameterNames[aliasParameters[argnr]->str()] = argnr; // Look for alias usages.. - const Token *endToken = nullptr; - for (it2 = it1; it2 != mTemplateInstantiations.end(); ++it2) { + bool found = false; + for (std::list::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) { TokenAndName &aliasUsage = *it2; - if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName) + if (!aliasUsage.token || aliasUsage.fullName != aliasDeclaration.fullName) { + ++it2; continue; + } // don't recurse - if (aliasDeclaration.isAliasToken(aliasUsage.token)) + if (aliasDeclaration.isAliasToken(aliasUsage.token)) { + ++it2; continue; + } std::vector> args; Token *tok2 = aliasUsage.token->tokAt(2); @@ -1221,37 +1199,32 @@ void TemplateSimplifier::simplifyTemplateAliases() break; } } - if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) + if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) { + ++it2; continue; - - // Replace template alias code.. - aliasUsage.name = templateAlias.name; - aliasUsage.fullName = templateAlias.fullName; - aliasUsage.scope = templateAlias.scope; - const Token *temp = aliasDeclaration.aliasStartToken(); - if (temp->str() == "typename") - temp = temp->next(); - std::stack links; - while (temp && temp != templateAlias.token) { - aliasUsage.token->insertToken(temp->str(), "", true); - - Token * previous = aliasUsage.token->previous(); - if (Token::Match(previous, "(|[|{")) - links.push(previous); - else if (!links.empty() && Token::Match(previous, ")|]|}")) { - Token::createMutualLinks(links.top(), previous); - links.pop(); - } - - temp = temp->next(); } - aliasUsage.token->str(templateAlias.token->str()); - tok2 = aliasUsage.token->next(); // the '<' - const Token * const endToken1 = templateAlias.token->next()->findClosingBracket(); - const Token * const endToken2 = TokenList::copyTokens(tok2, templateAlias.token->tokAt(2), endToken1->previous(), false)->next(); - for (const Token *tok1 = templateAlias.token->next(); tok2 != endToken2; tok1 = tok1->next(), tok2 = tok2->next()) { - if (!tok2->isName()) + // copy template-id from declaration to after instantiation + Token * dst = aliasUsage.token->next()->findClosingBracket(); + Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next(); + + // replace parameters + for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { + if (!tok1->isName()) + continue; + if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) { + const unsigned int argnr = aliasParameterNames[tok1->str()]; + const Token * const fromStart = args[argnr].first; + const Token * const fromEnd = args[argnr].second->previous(); + TokenList::copyTokens(tok1, fromStart, fromEnd, true); + tok1->deleteThis(); + } else if (tok1->str() == "typename") + tok1->deleteThis(); + } + + // add new instantiations + for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { + if (!tok1->isName()) continue; if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) { // Create template instance.. @@ -1262,53 +1235,32 @@ void TemplateSimplifier::simplifyTemplateAliases() if (it != mTemplateInstantiations.end()) addInstantiation(tok2, it->scope); } - continue; } - const unsigned int argnr = aliasParameterNames[tok2->str()]; - const Token * const fromStart = args[argnr].first; - const Token * const fromEnd = args[argnr].second->previous(); - Token * const destToken = tok2; - tok2 = TokenList::copyTokens(tok2, fromStart, fromEnd, true); - if (tok2 == destToken->next()) - tok2 = destToken; - destToken->deleteThis(); } - endToken = endToken1->next(); - while (endToken->str() != ";") - endToken = endToken->next(); + // erase the instantiation tokens + eraseTokens(aliasUsage.token->previous(), dst->next()); + found = true; - // Remove alias usage code (parameters) - Token::eraseTokens(tok2->previous(), args.back().second); + // erase this instantiation + it2 = mTemplateInstantiations.erase(it2); } - if (endToken) { - // Remove all template instantiations in template alias - for (const Token *tok = aliasDeclaration.paramEnd->tokAt(4); tok != endToken; tok = tok->next()) { - if (!Token::Match(tok, "%name% <")) - continue; - std::list::iterator it = std::find_if(mTemplateInstantiations.begin(), - mTemplateInstantiations.end(), - FindToken(tok)); - if (it == mTemplateInstantiations.end()) - continue; - std::list::iterator next = it; - ++next; - if (it == it1) - it1 = next; - mTemplateInstantiations.erase(it,next); - } - if (startToken) - eraseTokens(startToken, endToken->next() ? endToken->next() : endToken); + if (found) { + Token *end = const_cast(aliasDeclaration.aliasEndToken()); + + // remove declaration tokens + if (aliasDeclaration.token->previous()) + eraseTokens(aliasDeclaration.token->previous(), end->next() ? end->next() : end); else { - eraseTokens(mTokenList.front(), endToken->next() ? endToken->next() : endToken); + eraseTokens(mTokenList.front(), end->next() ? end->next() : end); deleteToken(mTokenList.front()); } // remove declaration - mTemplateDeclarations.erase(it5); + it1 = mTemplateDeclarations.erase(it1); } else - mTemplateInstantiations.erase(it3); + ++it1; } } @@ -3298,7 +3250,7 @@ void TemplateSimplifier::simplifyTemplates( } } - // TODO: 2 is not the ideal number of loops. + // TODO: 3 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. // That will allow the uninstantiated template code to be removed from the symbol database. @@ -3306,7 +3258,7 @@ void TemplateSimplifier::simplifyTemplates( // the uninstantiated template code in the symbol database can't be removed until #8768 // is fixed. - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 3; ++i) { if (i) { // it may take more than one pass to simplify type aliases while (mTokenizer->simplifyUsing()) diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 827ca622b..54ccab07f 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -151,6 +151,7 @@ private: TEST_CASE(template111); // crash TEST_CASE(template112); // #9146 syntax error TEST_CASE(template113); + TEST_CASE(template114); // #9155 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) @@ -188,6 +189,7 @@ private: TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 TEST_CASE(templateAlias4); // #9070 + TEST_CASE(templateAlias5); // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatch); @@ -2555,7 +2557,7 @@ private: "template < typename > struct c ; " "struct e ; " "} " - "e foo ; " + "e :: g foo ; " "struct e { " "bool h ; h = a < b::g> > :: h ; " "} ; " @@ -2680,6 +2682,20 @@ private: } } + void template114() { // #9155 + const char code[] = "template struct b {};\n" + "template struct c;\n" + "template struct d : b{}> {};\n" + "template struct e;\n" + "template using f = typename e>::g>::h;"; + const char exp[] = "template < typename a , a > struct b { } ; " + "template < typename > struct c ; " + "template < typename > struct d : b < bool , std :: is_polymorphic < int > { } > { } ; " + "template < bool > struct e ; " + "template < typename a > using f = typename e < c < d < a > > :: g > :: h ;"; + ASSERT_EQUALS(exp, tok(code)); + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" @@ -3678,6 +3694,16 @@ private: ASSERT_EQUALS(expected, tok(code)); } + void templateAlias5() { + const char code[] = "template using A = int;\n" + "template using B = T;\n" + "A a;\n" + "B b;"; + const char expected[] = "int a ; " + "char b ;"; + ASSERT_EQUALS(expected, tok(code)); + } + unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) { Tokenizer tokenizer(&settings, this); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 83cd3c44f..1d9ea705b 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4858,7 +4858,6 @@ private: Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); - ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> > ;")->link()); ASSERT(nullptr != Token::findsimplematch(tokenizer.tokens(), "> ;")->link()); }