diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index d519b6772..d98006d15 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -906,6 +906,7 @@ void TemplateSimplifier::expandTemplate( std::list scopeInfo; bool inTemplateDefinition = false; const Token *endOfTemplateDefinition = nullptr; + const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { setScopeInfo(const_cast(tok3), &scopeInfo); if (inTemplateDefinition) { @@ -987,12 +988,16 @@ void TemplateSimplifier::expandTemplate( } // replace name.. - if (Token::Match(tok3, (lastName + " !!<").c_str())) { + if (tok3 && tok3->str() == lastName) { if (Token::Match(tok3->tokAt(-2), "> :: %name% ( )")) { ; // Ticket #7942: Replacing for out-of-line constructors generates invalid syntax - } else { + } else if (!Token::simpleMatch(tok3->next(), "<")) { tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); continue; + } else if (tok3 == templateDeclarationNameToken) { + tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + tok3 = tok3->next()->findClosingBracket(); + continue; } } @@ -1383,11 +1388,52 @@ const Token * TemplateSimplifier::getTemplateParametersInDeclaration( return tok; } +static bool matchSpecialization(const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations) +{ + // Is there a matching specialization? + for (std::list::const_iterator it = specializations.begin(); it != specializations.end(); ++it) { + if (!Token::Match(*it, "%name% <")) + continue; + const Token *startToken = (*it); + while (startToken->previous() && !Token::Match(startToken->previous(), "[;{}]")) + startToken = startToken->previous(); + if (!Token::Match(startToken, "template <")) + continue; + std::vector templateParameters; + TemplateSimplifier::getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters); + + const Token *instToken = templateInstantiationNameToken->tokAt(2); + const Token *declToken = (*it)->tokAt(2); + const Token * const endToken = (*it)->next()->findClosingBracket(); + while (declToken != endToken) { + if (declToken->str() != instToken->str()) { + int nr = 0; + while (nr < templateParameters.size() && templateParameters[nr]->str() != declToken->str()) + ++nr; + + if (nr == templateParameters.size()) + break; + } + declToken = declToken->next(); + instToken = instToken->next(); + } + + if (declToken == endToken && instToken->str() == ">") { + // specialization matches. + return templateDeclarationNameToken == *it; + } + } + + // No specialization matches. Return true if the declaration is not a specialization. + return Token::Match(templateDeclarationNameToken, "%name% !!<"); +} + bool TemplateSimplifier::simplifyTemplateInstantiations( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, const TokenAndName &templateDeclaration, + const std::list &specializations, const std::time_t maxtime, std::list &templateInstantiations, std::set &expandedtemplates) @@ -1407,7 +1453,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( const bool printDebug = _settings->debugwarnings; // get the position of the template name - int namepos = TemplateSimplifier::getTemplateNamePosition(tok); + const int namepos = TemplateSimplifier::getTemplateNamePosition(tok); if (namepos == -1) { // debug message that we bail out.. if (printDebug && errorlogger) { @@ -1442,6 +1488,9 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( if (iter2->name != templateDeclaration.name) continue; + if (!matchSpecialization(tok->tokAt(namepos), iter2->token, specializations)) + continue; + Token * const tok2 = iter2->token; if (errorlogger && !tokenlist.getFiles().empty()) errorlogger->reportProgress(tokenlist.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); @@ -1676,10 +1725,22 @@ void TemplateSimplifier::simplifyTemplates( //done = true; std::list instantiatedTemplates; for (std::list::reverse_iterator iter1 = templateDeclarations.rbegin(); iter1 != templateDeclarations.rend(); ++iter1) { + // get specializations.. + std::list specializations; + for (std::list::const_iterator iter2 = templateDeclarations.begin(); iter2 != templateDeclarations.end(); ++iter2) { + if (iter1->name == iter2->name) { + const Token *tok = iter2->token->next()->findClosingBracket(); + int namepos = getTemplateNamePosition(tok); + if (namepos > 0) + specializations.push_back(tok->tokAt(namepos)); + } + } + bool instantiated = TemplateSimplifier::simplifyTemplateInstantiations(tokenlist, errorlogger, _settings, *iter1, + specializations, maxtime, templateInstantiations, expandedtemplates); diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index a05fec68c..22731c88f 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -166,6 +166,7 @@ public: * @param errorlogger error logger * @param _settings settings * @param templateDeclaration template declaration + * @param specializations template specializations (list each template name token) * @param maxtime time when the simplification will stop * @param templateInstantiations a list of template usages (not necessarily just for this template) * @param expandedtemplates all templates that has been expanded so far. The full names are stored. @@ -176,6 +177,7 @@ public: ErrorLogger* errorlogger, const Settings *_settings, const TokenAndName &templateDeclaration, + const std::list &specializations, const std::time_t maxtime, std::list &templateInstantiations, std::set &expandedtemplates); diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index d76600f49..7f2a078e4 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -98,6 +98,8 @@ private: TEST_CASE(template58); // #6021 - use after free (deleted tokens in simplifyCalculations) TEST_CASE(template59); // #8051 - TemplateSimplifier::simplifyTemplateInstantiation failure TEST_CASE(template60); // handling of methods outside template definition + 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) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); @@ -1117,6 +1119,24 @@ private: 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" + "template struct S> {b};\n" + "S s;"; + const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S < C < T > > { b } ; S s ; struct S { a } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template_specialization_2() { // #7868 - template specialization template struct S> {..}; + const char code[] = "template struct C {};\n" + "template struct S {a};\n" + "template struct S> {b};\n" + "S> s;"; + const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S { a } ; S> s ; struct S> { b } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + void template_enum() { const char code1[] = "template \n" "struct Unconst {\n"