From 0763fdbfadb14f0b617a1dbef1f46271a25c886c Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Wed, 24 Oct 2018 08:38:59 -0400 Subject: [PATCH] Copy template default argument values from forward declaration to declaration. (#1447) It is possible to define default template parameter values in forward declarations and not define any in the actual declaration. Cppcheck ignores forward declarations and only uses the default values in the actual declaration so default values in forward declarations are copied to the actual declaration when necessary. --- lib/templatesimplifier.cpp | 61 ++++++++++++++++++++++++++---- lib/templatesimplifier.h | 13 ++++++- test/testsimplifytemplate.cpp | 70 +++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 10 deletions(-) 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"