Fixed #4349 (Support C++11 variadic templates)

This commit is contained in:
Daniel Marjamäki 2021-04-17 21:56:13 +02:00
parent 3bc71f982a
commit 9a9043a07e
3 changed files with 50 additions and 22 deletions

View File

@ -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<Token *> 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<Token *> 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<const Token *> callstack(1, tok2);
mErrorLogger->reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",

View File

@ -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

View File

@ -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<int N, int ... M> struct A { "
" static void foo() { "
" int i = N; "
" } "
"}; "
"void bar() { "
" A<0>::foo(); "
"}");
}
void template52() { // #6437
const char code[] = "template <int value> int sum() { "
" return value + sum<value/2>(); "
@ -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<typename T, typename... Args>\n"
"void printf(const char *s, T value, Args... args) {}\n"
"\n"
"int main() {\n"
" printf<int, float>(\"\", foo, bar);\n"
"}";
const char expected[] = "void printf<int,float> ( const char * s , int value , float ) ; "
"int main ( ) { printf<int,float> ( \"\" , foo , bar ) ; } "
"void printf<int,float> ( const char * s , int value , float ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void template_variadic_3() { // #6172
const char code[] = "template<int N, int ... M> 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 <int N> const int foo = N*N;\n"