diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index a4b65e955..ac731869b 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -1583,6 +1583,12 @@ bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclara return Token::simpleMatch(tok->tokAt(offset), scope.c_str(), scope.size()); } +struct newInstantiation { + newInstantiation(Token* t, std::string s) : token(t), scope(std::move(s)) {} + Token* token; + std::string scope; +}; + void TemplateSimplifier::expandTemplate( const TokenAndName &templateDeclaration, const TokenAndName &templateInstantiation, @@ -1599,11 +1605,7 @@ void TemplateSimplifier::expandTemplate( const bool isFunction = templateDeclaration.isFunction(); const bool isSpecialization = templateDeclaration.isSpecialization(); const bool isVariable = templateDeclaration.isVariable(); - struct newInstantiation { - newInstantiation(Token *t, std::string s) : token(t), scope(std::move(s)) {} - Token *token; - std::string scope; - }; + std::vector newInstantiations; // add forward declarations @@ -2251,7 +2253,9 @@ void TemplateSimplifier::expandTemplate( // add new instantiations for (const auto & inst : newInstantiations) { - simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket()); + if (!inst.token) + continue; + simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket(), &newInstantiations); // only add recursive instantiation if its arguments are a constant expression if (templateDeclaration.name() != inst.token->str() || (inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType())) @@ -2409,7 +2413,17 @@ static Token *skipTernaryOp(Token *tok, const Token *backToken) return tok; } -void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end) +static void invalidateInst(const Token* beg, const Token* end, std::vector* newInst) { + if (!newInst) + return; + for (auto& inst : *newInst) { + for (const Token* tok = beg; tok != end; tok = tok->next()) + if (inst.token == tok) + inst.token = nullptr; + } +} + +void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end, std::vector* newInst) { // start could be erased so use the token before start if available Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); @@ -2517,6 +2531,7 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end) } if (Token::Match(tok->next(), "false|0")) { + invalidateInst(tok->next(), colon, newInst); // Use code after colon, remove code before it. Token::eraseTokens(tok, colon); @@ -2543,6 +2558,7 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end) else if (endTok->str() == ">" && !end) ; else { + invalidateInst(colon->tokAt(-1), endTok, newInst); Token::eraseTokens(colon->tokAt(-2), endTok); again = true; break; diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 186170a6c..a454b092e 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -37,6 +37,7 @@ class Settings; class Token; class Tokenizer; class TokenList; +struct newInstantiation; /// @addtogroup Core /// @{ @@ -333,7 +334,7 @@ public: * @param start first token of arguments * @param end token following last argument token */ - void simplifyTemplateArgs(Token *start, const Token *end); + void simplifyTemplateArgs(Token *start, const Token *end, std::vector* newInst = nullptr); private: /** diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index ff61b1b46..4307e45c7 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -278,6 +278,7 @@ private: TEST_CASE(simplifyTemplateArgs1); TEST_CASE(simplifyTemplateArgs2); + TEST_CASE(simplifyTemplateArgs3); TEST_CASE(template_variadic_1); // #9144 TEST_CASE(template_variadic_2); // #4349 @@ -6051,6 +6052,35 @@ private: ASSERT_EQUALS(expected, tok(code)); } + void simplifyTemplateArgs3() { // #11418 + const char code[] = "template struct S {};\n" + "template\n" + "T f() {}\n" + "template\n" + "void g() {\n" + " S() : f())> s1;\n" + " S() : f())> s2;\n" + "}\n" + "void h() {\n" + " g();\n" + "}\n"; + const char expected[] = "struct S()))> ; " + "struct S())> ; " + "int f ( ) ; " + "char f ( ) ; " + "void g ( ) ; " + "void h ( ) { g ( ) ; } " + "void g ( ) { " + "S()))> s1 ; " + "S())> s2 ; " + "} " + "int f ( ) { } " + "char f ( ) { } " + "struct S()))> { } ; " + "struct S())> { } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + void template_variadic_1() { // #9144 const char code[] = "template struct e {};\n" "static_assert(sizeof(e<>) == sizeof(e), \"\");";