diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 5f444113c..465a28207 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -959,6 +959,34 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok, bool forward) return namepos; } +void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok) +{ + std::string::size_type start = 0; + std::string::size_type end = 0; + while ((end = templateDeclaration.scope.find(" ", start)) != std::string::npos) { + std::string token = templateDeclaration.scope.substr(start, end - start); + mTokenList.addtoken(token, tok->linenr(), tok->fileIndex()); + start = end + 1; + } + mTokenList.addtoken(templateDeclaration.scope.substr(start), tok->linenr(), tok->fileIndex()); + mTokenList.addtoken("::", tok->linenr(), tok->fileIndex()); +} + +bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) const +{ + std::string scope = templateDeclaration.scope; + + // get the length in tokens of the namespace + std::string::size_type pos = 0; + int offset = -2; + + while ((pos = scope.find("::", pos)) != std::string::npos) { + offset -= 2; + pos += 2; + } + + return Token::simpleMatch(tok->tokAt(offset), scope.c_str()) ; +} void TemplateSimplifier::expandTemplate( const TokenAndName &templateDeclaration, @@ -1049,6 +1077,8 @@ void TemplateSimplifier::expandTemplate( // replace name if found if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name) { if (copy) { + if (!templateDeclaration.scope.empty() && tok5->strAt(-1) != "::") + addNamespace(templateDeclaration, tok5); mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); tok5 = tok5->next()->findClosingBracket(); } else { @@ -1060,8 +1090,11 @@ void TemplateSimplifier::expandTemplate( tok5 = tok5->next(); } - if (copy) + if (copy) { + if (!templateDeclaration.scope.empty() && tok3->strAt(-1) != "::") + addNamespace(templateDeclaration, tok3); mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + } while (tok3 && tok3->str() != "::") tok3 = tok3->next(); @@ -1129,21 +1162,17 @@ void TemplateSimplifier::expandTemplate( } continue; } + } else if (!templateDeclaration.scope.empty() && + !alreadyHasNamespace(templateDeclaration, tok3)) { + if (copy) + addNamespace(templateDeclaration, tok3); } } else { if (copy) { // add namespace if necessary - if (!templateDeclaration.scope.empty() && (isClass ? tok3->strAt(1) != "(" : true)) { - std::string::size_type start = 0; - std::string::size_type end = 0; - std::string temp; - while ((end = templateDeclaration.scope.find(" ", start)) != std::string::npos) { - std::string token = templateDeclaration.scope.substr(start, end - start); - mTokenList.addtoken(token, tok3->linenr(), tok3->fileIndex()); - start = end + 1; - } - mTokenList.addtoken(templateDeclaration.scope.substr(start), tok3->linenr(), tok3->fileIndex()); - mTokenList.addtoken("::", tok3->linenr(), tok3->fileIndex()); + if (!templateDeclaration.scope.empty() && + (isClass ? tok3->strAt(1) != "(" : true)) { + addNamespace(templateDeclaration, tok3); } mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); } else if (!Token::Match(tok3->next(), ":|{")) @@ -1818,7 +1847,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); if (expandedtemplates.find(newName) == expandedtemplates.end()) { expandedtemplates.insert(newName); - expandTemplate(templateDeclaration, tok, templateDeclaration, typeParametersInDeclaration, newName, !specialized); + expandTemplate(templateDeclaration, tok, templateDeclaration, typeParametersInDeclaration, newName, false); instantiated = true; } diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index aa48467f0..b586a846b 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -165,6 +165,21 @@ private: const std::time_t maxtime, std::set &expandedtemplates); + /** + * Simplify templates : add namespace to template name + * @param templateDeclaration template declaration + * @param tok place to insert namespace + */ + void addNamespace(const TokenAndName &templateDeclaration, const Token *tok); + + /** + * Simplify templates : check if namespace already present + * @param templateDeclaration template declaration + * @param tok place to start looking for namespace + * @return true if namespace already present + */ + bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) const; + /** * Expand a template. Create "expanded" class/function at end of tokenlist. * @param templateDeclaration Template declaration information diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 2e30971a4..08d77bc76 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -127,6 +127,8 @@ private: TEST_CASE(template_namespace_5); TEST_CASE(template_namespace_6); TEST_CASE(template_namespace_7); // #8768 + TEST_CASE(template_namespace_8); + TEST_CASE(template_namespace_9); // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); @@ -1824,6 +1826,71 @@ private: "N1 :: N2 :: CT ct3 ; struct N1 :: N2 :: CT { } ;", tok(code)); } + void template_namespace_8() { // #8768 + const char code[] = "namespace NS1 {\n" + "namespace NS2 {\n" + " template \n" + " struct Fred {\n" + " Fred();\n" + " Fred(const Fred &);\n" + " Fred & operator = (const Fred &);\n" + " ~Fred();\n" + " };\n" + " template \n" + " Fred::Fred() { }\n" + " template \n" + " Fred::Fred(const Fred & f) { }\n" + " template \n" + " Fred & Fred::operator = (const Fred & f) { }\n" + " template \n" + " Fred::~Fred() { }\n" + "}\n" + "}\n" + "NS1::NS2::Fred fred;"; + ASSERT_EQUALS("namespace NS1 { " + "namespace NS2 { " + "struct Fred ; " + "} " + "} " + "NS1 :: NS2 :: Fred fred ; struct NS1 :: NS2 :: Fred { " + "Fred ( ) ; " + "Fred ( const NS1 :: NS2 :: Fred & ) ; " + "NS1 :: NS2 :: Fred & operator= ( const NS1 :: NS2 :: Fred & ) ; " + "~ Fred ( ) ; " + "} ; " + "NS1 :: NS2 :: Fred :: Fred ( ) { } " + "NS1 :: NS2 :: Fred :: Fred ( const NS1 :: NS2 :: Fred & f ) { } " + "NS1 :: NS2 :: Fred & NS1 :: NS2 :: Fred :: operator= ( const NS1 :: NS2 :: Fred & f ) { } " + "NS1 :: NS2 :: Fred :: ~ Fred ( ) { }", tok(code)); + } + + void template_namespace_9() { + const char code[] = "namespace NS {\n" + "template struct Barney;\n" + "template<> struct Barney<1> { };\n" + "template\n" + "class Fred {\n" + "public:\n" + " Fred();\n" + "private:\n" + " Barney m_data;\n" + "};\n" + "template class Fred<1>;\n" + "}\n"; + ASSERT_EQUALS("namespace NS { " + "template < int type > struct Barney ; " + "struct Barney<1> { } ; " + "class Fred<1> ; " + "template class Fred<1> ; " + "} " + "class NS :: Fred<1> { " + "public: " + "Fred<1> ( ) ; " + "private: " + "Barney<1> m_data ; " + "} ;", tok(code)); + } + unsigned int templateParameters(const char code[]) { Tokenizer tokenizer(&settings, this);