diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index cfb19afe5..cb291acd0 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -497,6 +497,9 @@ bool TemplateSimplifier::getTemplateDeclarations() tok2 = tok2->link(); else if (tok2->str() == ")") break; + // skip decltype(...) + else if (Token::Match(tok2, "decltype (")) + tok2 = tok2->linkAt(1); // Declaration => add to mTemplateForwardDeclarations else if (tok2->str() == ";") { const int namepos = getTemplateNamePosition(parmEnd, true); @@ -891,17 +894,24 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *to while (tok && tok->next()) { if (Token::Match(tok->next(), ";|{")) return false; - else if (Token::Match(tok->next(), "%type% <")) { + // skip decltype(...) + else if (Token::Match(tok, "decltype (")) { + const Token * end = tok->linkAt(1); + while (tok && tok != end) { + tok = tok->next(); + namepos++; + } + } else if (Token::Match(tok->next(), "%type% <")) { const Token *closing = tok->tokAt(2)->findClosingBracket(); if (closing) { - if (closing->strAt(1) == "(" && mTokenizer->isFunctionHead(closing->tokAt(2), ";|{|:")) + if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:", true)) return true; while (tok && tok->next() && tok->next() != closing) { tok = tok->next(); namepos++; } } - } else if (Token::Match(tok->next(), "%type% (") && mTokenizer->isFunctionHead(tok->tokAt(2), ";|{|:")) { + } else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:", true)) { return true; } tok = tok->next(); @@ -984,15 +994,19 @@ void TemplateSimplifier::expandTemplate( const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); const bool isClass = Token::Match(templateDeclarationToken->next(), "class|struct|union %name% <|{|:"); - const bool isFunction = templateDeclarationNameToken->strAt(1) == "("; + const bool isFunction = templateDeclarationNameToken->strAt(1) == "(" || + (templateDeclarationNameToken->strAt(1) == "<" && templateDeclarationNameToken->next()->findClosingBracket()->strAt(1) == "("); + const bool isSpecialization = Token::Match(templateDeclaration.token, "template < >"); // add forward declarations if (copy && isClass) { templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true); templateDeclaration.token->insertToken(newName, "", true); templateDeclaration.token->insertToken(";", "", true); - } else if (copy && isFunction) { + } else if (isFunction && (copy || (!copy && isSpecialization))) { Token * dst = templateDeclaration.token; + bool isStatic = false; + std::string scope; Token * start; Token * end; auto it = mTemplateForwardDeclarationsMap.find(dst); @@ -1003,8 +1017,21 @@ void TemplateSimplifier::expandTemplate( start = temp1->next(); end = temp2->linkAt(1)->next(); } else { + auto it2 = mTemplateUserSpecializationMap.find(dst); + if (it2 != mTemplateUserSpecializationMap.end()) { + dst = it2->second; + isStatic = dst->next()->findClosingBracket()->strAt(1) == "static"; + const Token * temp = templateDeclarationNameToken; + while (Token::Match(temp->tokAt(-2), "%name% ::")) { + scope.insert(0, temp->strAt(-2) + " :: "); + temp = temp->tokAt(-2); + } + } start = templateDeclarationToken->next(); - end = templateDeclarationNameToken->linkAt(1)->next(); + if (templateDeclarationNameToken->strAt(1) == "(") + end = templateDeclarationNameToken->linkAt(1)->next(); + else + end = templateDeclarationNameToken->next()->findClosingBracket()->linkAt(1)->next(); } unsigned int typeindentlevel = 0; while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { @@ -1015,6 +1042,9 @@ void TemplateSimplifier::expandTemplate( end = end->next(); } + if (isStatic) + dst->insertToken("static", "", true); + std::map links; while (start && start != end) { unsigned int itype = 0; @@ -1041,7 +1071,11 @@ void TemplateSimplifier::expandTemplate( dst->previous()->isLong(typetok->isLong()); } } else { - if (start->str() == templateDeclarationNameToken->str()) { + if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) { + // skip scope + while (start->strAt(1) != templateDeclarationNameToken->str()) + start = start->next(); + } else if (start->str() == templateDeclarationNameToken->str()) { dst->insertToken(newName, "", true); if (start->strAt(1) == "<") start = start->next()->findClosingBracket(); @@ -1133,6 +1167,8 @@ void TemplateSimplifier::expandTemplate( // Start of template.. if (tok3 == templateDeclarationToken) { tok3 = tok3->next(); + if (tok3->str() == "static") + tok3 = tok3->next(); } // member function implemented outside class definition @@ -1907,23 +1943,12 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( return false; // already simplified - if (!Token::Match(startToken, "%name% <")) + if (!Token::Match(tok2, "%name% <")) return false; - if (templateDeclaration.scope.empty()) { - if (startToken->str() != templateDeclaration.name) - return false; - } else { - std::string name = templateDeclaration.scope + " :: " + startToken->str(); - if (name != templateDeclaration.name) - return false; - } - - if (!matchSpecialization(tok->tokAt(namepos), startToken, specializations)) + if (!matchSpecialization(tok->tokAt(namepos), tok2, specializations)) return false; - tok2 = startToken; - // New type.. mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; @@ -2051,6 +2076,61 @@ void TemplateSimplifier::replaceTemplateUsage( } } +static std::string getPathName(const TemplateSimplifier::TokenAndName & decl) +{ + std::string name = decl.scope; + if (!name.empty()) + name += " :: "; + if (decl.nameToken->strAt(-1) == "::") { + const Token * start = decl.nameToken; + + while (Token::Match(start->tokAt(-2), "%name% ::")) + start = start->tokAt(-2); + + while (start != decl.nameToken) { + name += (start->str() + " "); + start = start->next(); + } + } + name += decl.name; + return name; +} + +void TemplateSimplifier::getUserDefinedSpecializations() +{ + // try to locate a matching declaration for each user defined specialization + for (auto & spec : mTemplateDeclarations) { + if (Token::Match(spec.token, "template < >")) { + std::string specName = getPathName(spec); + + bool found = false; + for (auto & decl : mTemplateDeclarations) { + if (Token::Match(decl.token, "template < >")) + continue; + std::string declName = getPathName(decl); + + // make sure the scopes and names match + if (specName == declName) { + // @todo make sure function parameters also match + mTemplateUserSpecializationMap[spec.token] = decl.token; + } + } + + if (!found) { + for (auto & decl : mTemplateForwardDeclarations) { + std::string declName = getPathName(decl); + + // make sure the scopes and names match + if (specName == declName) { + // @todo make sure function parameters also match + mTemplateUserSpecializationMap[spec.token] = decl.token; + } + } + } + } + } +} + void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() { // try to locate a matching declaration for each forward declaration @@ -2066,26 +2146,8 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() // make sure the number of arguments match if (params1.size() == params2.size()) { - std::string declName = decl.scope; - if (!declName.empty()) - declName += " :: "; - if (decl.nameToken->strAt(-1) == "::") { - const Token * start = decl.nameToken; - - while (Token::Match(start->tokAt(-2), "%name% ::")) - start = start->tokAt(-2); - - while (start != decl.nameToken) { - declName += (start->str() + " "); - start = start->next(); - } - } - declName += decl.name; - - std::string forwardDeclName = forwardDecl.scope; - if (!forwardDeclName.empty()) - forwardDeclName += " :: "; - forwardDeclName += forwardDecl.name; + std::string declName = getPathName(decl); + std::string forwardDeclName = getPathName(forwardDecl); // make sure the scopes and names match if (forwardDeclName == declName) { @@ -2124,6 +2186,7 @@ void TemplateSimplifier::simplifyTemplates( mTemplateDeclarations.clear(); mTemplateForwardDeclarations.clear(); mTemplateForwardDeclarationsMap.clear(); + mTemplateUserSpecializationMap.clear(); mTemplateInstantiations.clear(); mInstantiatedTemplates.clear(); mExplicitInstantiationsToDelete.clear(); @@ -2157,6 +2220,9 @@ void TemplateSimplifier::simplifyTemplates( // Copy default argument values from forward declaration to declaration fixForwardDeclaredDefaultArgumentValues(); + // Locate user defined specializations. + getUserDefinedSpecializations(); + // Locate possible instantiations of templates.. getTemplateInstantiations(); diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 5f42cf68d..4e0ea0663 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -156,6 +156,12 @@ private: */ void useDefaultArgumentValues(); + /** + * Try to locate a matching declaration for each user defined + * specialization. + */ + void getUserDefinedSpecializations(); + /** * simplify template aliases */ @@ -278,6 +284,7 @@ private: std::list mTemplateDeclarations; std::list mTemplateForwardDeclarations; std::map mTemplateForwardDeclarationsMap; + std::map mTemplateUserSpecializationMap; std::list mTemplateInstantiations; std::list mInstantiatedTemplates; std::list mMemberFunctionsToDelete; diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 518b9c27a..08a2dbd26 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -125,6 +125,10 @@ private: TEST_CASE(template85); // #8902 crash TEST_CASE(template86); // crash TEST_CASE(template87); + TEST_CASE(template88); // #6183 + TEST_CASE(template89); // #8917 + TEST_CASE(template90); // crash + TEST_CASE(template91); 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) @@ -479,7 +483,8 @@ private: "}\n"; // The expected result.. - const char expected[] = "void foo ( ) " + const char expected[] = "void foo ( ) ; " + "void foo ( ) " "{ x ( ) ; } " "int main ( ) " "{ foo ( ) ; }"; @@ -505,6 +510,7 @@ private: // The expected result.. const char expected[] = "void a<2> ( ) ; " "void a<1> ( ) ; " + "void a<0> ( ) ; " "void a<0> ( ) { } " "int main ( ) " "{ a<2> ( ) ; return 0 ; } " @@ -1664,6 +1670,155 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template88() { // #6183.cpp + const char code[] = "class CTest {\n" + "public:\n" + " template \n" + " static void Greeting(T val) {\n" + " std::cout << val << std::endl;\n" + " }\n" + "private:\n" + " static void SayHello() {\n" + " std::cout << \"Hello World!\" << std::endl;\n" + " }\n" + "};\n" + "template<>\n" + "void CTest::Greeting(bool) {\n" + " CTest::SayHello();\n" + "}\n" + "int main() {\n" + " CTest::Greeting(true);\n" + " return 0;\n" + "}"; + const char exp[] = "class CTest { " + "public: " + "static void Greeting ( bool ) ; " + "template < typename T > " + "static void Greeting ( T val ) { " + "std :: cout << val << std :: endl ; " + "} " + "private: " + "static void SayHello ( ) { " + "std :: cout << \"Hello World!\" << std :: endl ; " + "} " + "} ; " + "void CTest :: Greeting ( bool ) { " + "CTest :: SayHello ( ) ; " + "} " + "int main ( ) { " + "CTest :: Greeting ( true ) ; " + "return 0 ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template89() { // #8917 + const char code[] = "struct Fred {\n" + " template static void foo() { }\n" + "};\n" + "template void Fred::foo();\n" + "template void Fred::foo();\n" + "template <> void Fred::foo() { }\n" + "template <> void Fred::foo() { }"; + const char exp[] = "struct Fred { " + "static void foo ( ) ; " + "static void foo ( ) ; " + "static void foo ( ) ; " + "static void foo ( ) ; " + "} ; " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { }"; + const char act[] = "struct Fred { " + "static void foo ( ) ; " + "static void foo ( ) ; " + "} ; " + "void Fred :: foo ( ) ; " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) ; " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { }"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + + void template90() { // crash + const char code[] = "template struct S1 {};\n" + "void f(S1) {}\n" + "template \n" + "decltype(S1().~S1()) fun1() {};"; + const char exp[] = "struct S1 ; " + "void f ( S1 ) { } " + "template < typename T > " + "decltype ( S1 < T > ( ) . ~ S1 < T > ( ) ) fun1 ( ) { } ; " + "struct S1 { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template91() { + { + const char code[] = "template T foo(T t) { return t; }\n" + "template<> char foo(char a) { return a; }\n" + "template<> int foo(int a) { return a; }\n" + "template float foo(float);\n" + "template double foo(double);"; + const char exp[] = "float foo ( float t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) ; " + "int foo ( int a ) { return a ; } " + "float foo ( float t ) { return t ; } " + "double foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "struct Fred {\n" + " template T foo(T t) { return t; }\n" + " template<> char foo(char a) { return a; }\n" + " template<> int foo(int a) { return a; }\n" + "};\n" + "template float Fred::foo(float);\n" + "template double Fred::foo(double);"; + const char exp[] = "struct Fred { " + "float foo ( float t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) ; " + "int foo ( int a ) { return a ; } " + "} ; " + "float Fred :: foo ( float t ) { return t ; } " + "double Fred :: foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "namespace NS1 {\n" + " namespace NS2 {\n" + " template T foo(T t) { return t; }\n" + " template<> char foo(char a) { return a; }\n" + " template<> int foo(int a) { return a; }\n" + " }\n" + " template float NS2::foo(float);\n" + "}\n" + "template double NS1::NS2::foo(double);"; + const char exp[] = "namespace NS1 { " + "namespace NS2 { " + "float foo ( float t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) ; " + "int foo ( int a ) { return a ; } " + "} " + "} " + "float NS1 :: NS2 :: foo ( float t ) { return t ; } " + "double NS1 :: NS2 :: foo ( double t ) { return t ; }"; + 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" diff --git a/test/testunusedprivfunc.cpp b/test/testunusedprivfunc.cpp index 6770cdb03..aa1e13cd2 100644 --- a/test/testunusedprivfunc.cpp +++ b/test/testunusedprivfunc.cpp @@ -80,6 +80,8 @@ private: TEST_CASE(hierarchie_loop); // ticket 5590 TEST_CASE(staticVariable); //ticket #5566 + + TEST_CASE(templateSimplification); //ticket #6183 } @@ -794,6 +796,29 @@ private: "int i = F();"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused private function: 'Foo::F'\n", errout.str()); } + + void templateSimplification() { //ticket #6183 + check("class CTest {\n" + "public:\n" + " template \n" + " static void Greeting(T val) {\n" + " std::cout << val << std::endl;\n" + " }\n" + "private:\n" + " static void SayHello() {\n" + " std::cout << \"Hello World!\" << std::endl;\n" + " }\n" + "};\n" + "template<>\n" + "void CTest::Greeting(bool) {\n" + " CTest::SayHello();\n" + "}\n" + "int main() {\n" + " CTest::Greeting(true);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestUnusedPrivateFunction)