diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 15778fcfe..04c27cdb3 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -488,7 +488,7 @@ static void setScopeInfo(const Token *tok, std::list *scopeInfo) } } -std::list TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates) +std::list TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates, bool forward) { std::list scopeInfo; std::list declarations; @@ -513,13 +513,21 @@ std::list TemplateSimplifier::getTemplateDecla else if (tok2->str() == ")") break; // Just a declaration => ignore this - else if (tok2->str() == ";") + else if (tok2->str() == ";") { + if (forward) { + const int namepos = getTemplateNamePosition(parmEnd, forward); + if (namepos > 0) + declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos))); + } break; + } // Implementation => add to "templates" else if (tok2->str() == "{") { - const int namepos = getTemplateNamePosition(parmEnd); - if (namepos > 0) - declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos))); + if (!forward) { + const int namepos = getTemplateNamePosition(parmEnd, forward); + if (namepos > 0) + declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos))); + } break; } } @@ -915,11 +923,12 @@ static bool getTemplateNamePositionTemplateMember(const Token *tok, int &namepos return false; } -int TemplateSimplifier::getTemplateNamePosition(const Token *tok) +int TemplateSimplifier::getTemplateNamePosition(const Token *tok, bool forward) { // get the position of the template name int namepos = 0, starAmpPossiblePosition = 0; - if (Token::Match(tok, "> class|struct|union %type% {|:|<")) + if ((forward && Token::Match(tok, "> class|struct|union %type% :|<|;")) || + (!forward && Token::Match(tok, "> class|struct|union %type% {|:|<"))) namepos = 2; else if (Token::Match(tok, "> %type% *|&| %type% (")) namepos = 2; @@ -1482,7 +1491,7 @@ const Token * TemplateSimplifier::getTemplateParametersInDeclaration( { typeParametersInDeclaration.clear(); for (; tok && tok->str() != ">"; tok = tok->next()) { - if (Token::Match(tok, "%name% ,|>")) + if (Token::Match(tok, "%name% ,|>|=")) typeParametersInDeclaration.push_back(tok); } return tok; @@ -1887,6 +1896,39 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, } } +void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() +{ + // get all forward declarations + bool dummy; + std::list forwardTemplateDeclarations = getTemplateDeclarations(dummy, true); + + // try to locate a matching declaration for each forward declaration + for (const auto & forwardDecl : forwardTemplateDeclarations) { + std::vector params1; + + getTemplateParametersInDeclaration(forwardDecl.token, params1); + + for (auto & decl : mTemplateDeclarations) { + std::vector params2; + + getTemplateParametersInDeclaration(decl.token, params2); + + // make sure the number of arguments match + if (params1.size() == params2.size()) { + // make sure the scopes and names match + if (forwardDecl.scope == decl.scope && forwardDecl.name == decl.name) { + 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)); + } + } + } + } + } + } +} void TemplateSimplifier::simplifyTemplates( const std::time_t maxtime, @@ -1929,6 +1971,9 @@ void TemplateSimplifier::simplifyTemplates( mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates); + // Copy default argument values from forward declaration to declaration + fixForwardDeclaredDefaultArgumentValues(); + // Locate possible instantiations of templates.. getTemplateInstantiations(); diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 63d93b8e5..d5c1eecb5 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -89,10 +89,11 @@ public: /** * Match template declaration/instantiation * @param tok The ">" token e.g. before "class" + * @param forward declaration or forward declaration * @return -1 to bail out or positive integer to identity the position * of the template name. */ - static int getTemplateNamePosition(const Token *tok); + static int getTemplateNamePosition(const Token *tok, bool forward = false); /** * Simplify templates @@ -122,15 +123,23 @@ public: private: /** * Get template declarations + * @param codeWithTemplates set to true if code has templates + * @param forward declaration or forward declaration * @return list of template declarations */ - std::list getTemplateDeclarations(bool &codeWithTemplates); + std::list getTemplateDeclarations(bool &codeWithTemplates, bool forward = false); /** * Get template instantiations */ void getTemplateInstantiations(); + /** + * Fix forward declared default argument values by copying them + * when they are not present in the declaration. + */ + void fixForwardDeclaredDefaultArgumentValues(); + /** * simplify template instantiations (use default argument values) */ diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index cef7fa7f0..7f1692c9d 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -111,6 +111,7 @@ private: TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); + TEST_CASE(template_forward_declared_default_parameter); TEST_CASE(template_default_type); TEST_CASE(template_typename); TEST_CASE(template_constructor); // #3152 - template constructor is removed @@ -1412,6 +1413,75 @@ private: } } + void template_forward_declared_default_parameter() { + { + const char code[] = "template class A;\n" + "template \n" + "class A\n" + "{ T ar[n]; };\n" + "\n" + "void f()\n" + "{\n" + " A a1;\n" + " A a2;\n" + "}\n"; + + const char wanted[] = "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + const char current[] = "template < class T , int n = 3 > class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + TODO_ASSERT_EQUALS(wanted, current, tok(code)); + } + { + const char code[] = "template class A;\n" + "template \n" + "class A\n" + "{ T ar[n]; };\n" + "\n" + "void f()\n" + "{\n" + " A a1;\n" + " A a2;\n" + "}\n"; + + const char wanted[] = "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + const char current[] = "template < class , int = 3 > class A ; " + "void f ( ) " + "{" + " A a1 ;" + " A a2 ; " + "} " + "class A " + "{ int ar [ 2 ] ; } ; " + "class A " + "{ int ar [ 3 ] ; } ;"; + TODO_ASSERT_EQUALS(wanted, current, tok(code)); + } + } + void template_default_type() { const char code[] = "template \n" "class A\n"