From 9a9043a07ed811082e8fe336d429ce2ad10029ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 17 Apr 2021 21:56:13 +0200 Subject: [PATCH] Fixed #4349 (Support C++11 variadic templates) --- lib/templatesimplifier.cpp | 25 +++++++++++++------- lib/templatesimplifier.h | 3 ++- test/testsimplifytemplate.cpp | 44 ++++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index c157178b0..1faf58e8b 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -120,7 +120,7 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string & isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::")); if (mToken->strAt(1) == "<" && !isSpecialization()) { const Token *end = mToken->next()->findClosingBracket(); - isVariadic(end && Token::findmatch(mToken->tokAt(2), "typename|class ...", end)); + isVariadic(end && Token::findmatch(mToken->tokAt(2), "%name% ...", end)); } const Token *tok1 = mNameToken->next(); if (tok1->str() == "<") { @@ -1324,11 +1324,12 @@ void TemplateSimplifier::simplifyTemplateAliases() } } -bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]) +bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]) { assert(instance->strAt(1) == "<"); - if (numberOfArguments != templateParameters(instance->next())) + auto n = templateParameters(instance->next()); + if (variadic ? (n + 1 < numberOfArguments) : (numberOfArguments != n)) return false; if (patternAfter) { @@ -1650,8 +1651,12 @@ void TemplateSimplifier::expandTemplate( std::stack brackets1; // holds "(" and "{" tokens bool pointerType = false; Token * const dst1 = dst->previous(); + const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); + if (isVariadicTemplateArg && Token::Match(start, "%name% ... %name%")) + start = start->tokAt(2); + const std::string endsWith(isVariadicTemplateArg ? ">" : ",>"); for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); - typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>")); + typetok && (typeindentlevel > 0 || endsWith.find(typetok->str()[0]) == std::string::npos); typetok = typetok->next()) { if (typeindentlevel == 0 && typetok->str() == "*") pointerType = true; @@ -1847,7 +1852,7 @@ void TemplateSimplifier::expandTemplate( else if (inTemplateDefinition && Token::Match(tok3, "%name% <") && templateInstantiation.name() == tok3->str() && - instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { + instantiateMatch(tok3, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; Token * tok5 = nullptr; // start of function return type @@ -2003,8 +2008,12 @@ void TemplateSimplifier::expandTemplate( std::stack brackets1; // holds "(" and "{" tokens Token * const beforeTypeToken = mTokenList.back(); bool pointerType = false; + const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); + if (isVariadicTemplateArg && Token::Match(tok3, "%name% ... %name%")) + tok3 = tok3->tokAt(2); + const std::string endsWith(isVariadicTemplateArg ? ">" : ",>"); for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); - typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>")); + typetok && (typeindentlevel > 0 || endsWith.find(typetok->str()[0]) == std::string::npos); typetok = typetok->next()) { if (typeindentlevel == 0 && typetok->str() == "*") pointerType = true; @@ -3091,7 +3100,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( } if (Token::Match(startToken->previous(), ";|{|}|=|const") && - (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) continue; // New type.. @@ -3100,7 +3109,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); if ((typeForNewName.empty() && !templateDeclaration.isVariadic()) || - (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) { + (!typeParametersInDeclaration.empty() && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), nullptr))) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 18a951200..04f528b86 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -258,10 +258,11 @@ public: * Match template declaration/instantiation * @param instance template instantiation * @param numberOfArguments number of template arguments + * @param variadic last template argument is variadic * @param patternAfter pattern that must match the tokens after the ">" * @return match => true */ - static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]); + static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]); /** * Match template declaration/instantiation diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 81d2479c9..f50687c79 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -93,7 +93,6 @@ private: TEST_CASE(template48); // #6134 - 100% CPU upon invalid code TEST_CASE(template49); // #6237 - template instantiation TEST_CASE(template50); // #4272 - simple partial specialization - TEST_CASE(template51); // #6172 - crash upon valid code TEST_CASE(template52); // #6437 - crash upon valid code TEST_CASE(template53); // #4335 - bail out for valid code TEST_CASE(template54); // #6587 - memory corruption upon valid code @@ -271,6 +270,8 @@ private: TEST_CASE(simplifyTemplateArgs2); TEST_CASE(template_variadic_1); // #9144 + TEST_CASE(template_variadic_2); // #4349 + TEST_CASE(template_variadic_3); // #6172 TEST_CASE(template_variable_1); TEST_CASE(template_variable_2); @@ -1252,17 +1253,6 @@ private: ASSERT_EQUALS(expected, tok(code)); } - void template51() { // #6172 - tok("template struct A { " - " static void foo() { " - " int i = N; " - " } " - "}; " - "void bar() { " - " A<0>::foo(); " - "}"); - } - void template52() { // #6437 const char code[] = "template int sum() { " " return value + sum(); " @@ -5493,7 +5483,7 @@ private: std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); - return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), numberOfArguments, patternAfter); + return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), numberOfArguments, false, patternAfter); } void instantiateMatch() { @@ -5841,6 +5831,34 @@ private: ASSERT_EQUALS(expected, tok(code)); } + void template_variadic_2() { // #4349 + const char code[] = "template\n" + "void printf(const char *s, T value, Args... args) {}\n" + "\n" + "int main() {\n" + " printf(\"\", foo, bar);\n" + "}"; + const char expected[] = "void printf ( const char * s , int value , float ) ; " + "int main ( ) { printf ( \"\" , foo , bar ) ; } " + "void printf ( const char * s , int value , float ) { }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template_variadic_3() { // #6172 + const char code[] = "template struct A { " + " static void foo() { " + " int i = N; " + " } " + "}; " + "void bar() { " + " A<0>::foo(); " + "}"; + const char expected[] = "struct A<0> ; " + "void bar ( ) { A<0> :: foo ( ) ; } " + "struct A<0> { static void foo ( ) { int i ; i = 0 ; } } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + void template_variable_1() { { const char code[] = "template const int foo = N*N;\n"