template simplifier: various small fixes (#1916)

* fix adding instantiation of first argument to an instantiation

* add support for function pointer template variables

* fix more cases where templates ending in ">>" are changed to end in "> >"

* fix travis build

* standard types can't be a template parameter name

* remove redundant level == 0 checks

* fix lambda in template variable

* fix a test
This commit is contained in:
IOBYTE 2019-06-28 05:14:20 -04:00 committed by Daniel Marjamäki
parent ec4e43767e
commit 16788df055
4 changed files with 194 additions and 49 deletions

View File

@ -206,6 +206,26 @@ TemplateSimplifier::~TemplateSimplifier()
{ {
} }
void TemplateSimplifier::fixAngleBrackets()
{
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
// Ticket #6181: normalize C++11 template parameter list closing syntax
if (tok->str() == "<" && templateParameters(tok)) {
Token *endTok = tok->findClosingBracket();
if (endTok && endTok->str() == ">>") {
endTok->str(">");
endTok->insertToken(">");
}
} else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <")) {
Token *endTok = tok->tokAt(2)->findClosingBracket();
if (Token::Match(endTok, ">> ;|{")) {
endTok->str(">");
endTok->insertToken(">");
}
}
}
}
void TemplateSimplifier::cleanupAfterSimplify() void TemplateSimplifier::cleanupAfterSimplify()
{ {
bool goback = false; bool goback = false;
@ -368,6 +388,8 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
if (Token::Match(tok->previous(), "%var% <")) if (Token::Match(tok->previous(), "%var% <"))
return 0; return 0;
tok = tok->next(); tok = tok->next();
if (tok->str() == ">")
return 0;
unsigned int level = 0; unsigned int level = 0;
@ -375,9 +397,18 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
// skip template template // skip template template
if (level == 0 && Token::simpleMatch(tok, "template <")) { if (level == 0 && Token::simpleMatch(tok, "template <")) {
const Token *closing = tok->next()->findClosingBracket(); const Token *closing = tok->next()->findClosingBracket();
if (closing) if (closing) {
if (closing->str() == ">>")
return numberOfParameters;
tok = closing->next(); tok = closing->next();
else if (tok->str() == ">" || tok->str() == ">>")
return numberOfParameters;
else if (tok->str() == ",") {
++numberOfParameters;
tok = tok->next();
continue;
}
} else
return 0; return 0;
} }
@ -394,9 +425,25 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
tok = tok->next(); tok = tok->next();
// Skip variadic types (Ticket #5774, #6059, #6172) // Skip variadic types (Ticket #5774, #6059, #6172)
if (Token::Match(tok, "%type% . . .")) { if (Token::simpleMatch(tok, ". . .")) {
tok = tok->tokAt(4); if ((tok->previous()->isName() && !Token::Match(tok->tokAt(-2), "<|,")) ||
continue; (!tok->previous()->isName() && tok->strAt(-1) != ">"))
return 0; // syntax error
tok = tok->tokAt(3);
if (!tok)
return 0;
if (tok->str() == ">") {
if (level == 0)
return numberOfParameters;
--level;
} else if (tok->str() == ">>") {
if (level == 1)
return numberOfParameters;
} else if (tok->str() == "," && level == 0) {
++numberOfParameters;
tok = tok->next();
continue;
}
} }
// Skip '=', '?', ':' // Skip '=', '?', ':'
@ -414,6 +461,8 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
return 0; return 0;
if (tok->str() == ">" && level == 0) if (tok->str() == ">" && level == 0)
return numberOfParameters; return numberOfParameters;
else if (tok->str() == ">>" && level == 1)
return numberOfParameters;
else if (tok->str() == "," && level == 0) { else if (tok->str() == "," && level == 0) {
++numberOfParameters; ++numberOfParameters;
tok = tok->next(); tok = tok->next();
@ -900,8 +949,8 @@ void TemplateSimplifier::getTemplateInstantiations()
// parse backwards and add template instantiations // parse backwards and add template instantiations
// TODO // TODO
for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { for (; tok2 && tok2 != tok; tok2 = tok2->previous()) {
if (Token::Match(tok2, ", %name% <") && if (Token::Match(tok2, ",|< %name% <") &&
templateParameters(tok2->tokAt(2))) { (tok2->strAt(3) == ">" || templateParameters(tok2->tokAt(2)))) {
addInstantiation(tok2->next(), getScopeName(scopeList)); addInstantiation(tok2->next(), getScopeName(scopeList));
} else if (Token::Match(tok2->next(), "class|struct")) } else if (Token::Match(tok2->next(), "class|struct"))
tok2->deleteNext(); tok2->deleteNext();
@ -1351,7 +1400,14 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
while (tok && tok->next()) { while (tok && tok->next()) {
if (Token::Match(tok->next(), ";|{|(|using")) if (Token::Match(tok->next(), ";|{|(|using"))
return false; return false;
else if (Token::Match(tok->next(), "%type% <")) { // skip decltype(...)
else if (Token::simpleMatch(tok->next(), "decltype (")) {
const Token * end = tok->linkAt(2);
while (tok && tok->next() && tok != end) {
tok = tok->next();
namepos++;
}
} else if (Token::Match(tok->next(), "%type% <")) {
const Token *closing = tok->tokAt(2)->findClosingBracket(); const Token *closing = tok->tokAt(2)->findClosingBracket();
if (closing) { if (closing) {
if (Token::Match(closing->next(), "=|;")) if (Token::Match(closing->next(), "=|;"))
@ -1401,8 +1457,7 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok,
int TemplateSimplifier::getTemplateNamePosition(const Token *tok) int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
{ {
// FIXME: tok == ">>" is a tokenizer bug that needs to be fixed assert(tok && tok->str() == ">");
assert(tok && (tok->str() == ">" || tok->str() == ">>"));
auto it = mTemplateNamePos.find(tok); auto it = mTemplateNamePos.find(tok);
if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) { if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) {
@ -1540,8 +1595,15 @@ void TemplateSimplifier::expandTemplate(
end = end->findClosingBracket()->next(); end = end->findClosingBracket()->next();
if (end->str() == "(") if (end->str() == "(")
end = end->link()->next(); end = end->link()->next();
else if (isVariable && end->str() == "=") else if (isVariable && end->str() == "=") {
end = const_cast<Token *>(Token::findsimplematch(templateDeclarationNameToken, ";")); Token *temp = end->next();
while (temp && temp->str() != ";") {
if (temp->link() && Token::Match(temp, "{|[|("))
temp = temp->link();
temp = temp->next();
}
end = temp;
}
} }
unsigned int typeindentlevel = 0; unsigned int typeindentlevel = 0;
while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) {
@ -1697,9 +1759,17 @@ void TemplateSimplifier::expandTemplate(
} }
if (inTemplateDefinition) { if (inTemplateDefinition) {
if (!endOfTemplateDefinition) { if (!endOfTemplateDefinition) {
if (isVariable) if (isVariable) {
endOfTemplateDefinition = Token::findsimplematch(tok3, ";"); Token *temp = tok3->findClosingBracket();
else if (tok3->str() == "{") if (temp) {
while (temp && temp->str() != ";") {
if (temp->link() && Token::Match(temp, "{|[|("))
temp = temp->link();
temp = temp->next();
}
endOfTemplateDefinition = temp;
}
} else if (tok3->str() == "{")
endOfTemplateDefinition = tok3->link(); endOfTemplateDefinition = tok3->link();
} }
if (tok3 == endOfTemplateDefinition) { if (tok3 == endOfTemplateDefinition) {
@ -1773,7 +1843,7 @@ void TemplateSimplifier::expandTemplate(
} }
} else if (copy) { } else if (copy) {
bool added = false; bool added = false;
if (tok5->isName() && !Token::Match(tok5, "class|typename|struct")) { if (tok5->isName() && !Token::Match(tok5, "class|typename|struct") && !tok5->isStandardType()) {
// search for this token in the type vector // search for this token in the type vector
unsigned int itype = 0; unsigned int itype = 0;
while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str()) while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str())
@ -1852,7 +1922,7 @@ void TemplateSimplifier::expandTemplate(
const std::string lastName = (templateInstantiation.name.find(' ') != std::string::npos) ? templateInstantiation.name.substr(templateInstantiation.name.rfind(' ')+1) : templateInstantiation.name; const std::string lastName = (templateInstantiation.name.find(' ') != std::string::npos) ? templateInstantiation.name.substr(templateInstantiation.name.rfind(' ')+1) : templateInstantiation.name;
for (; tok3; tok3 = tok3->next()) { for (; tok3; tok3 = tok3->next()) {
if (tok3->isName() && !Token::Match(tok3, "class|typename|struct")) { if (tok3->isName() && !Token::Match(tok3, "class|typename|struct") && !tok3->isStandardType()) {
// search for this token in the type vector // search for this token in the type vector
unsigned int itype = 0; unsigned int itype = 0;
while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str()) while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str())
@ -2746,7 +2816,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
} }
if (Token::Match(startToken->previous(), ";|{|}|=|const") && if (Token::Match(startToken->previous(), ";|{|}|=|const") &&
(!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%" : "*|&|::| %name%"))) (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%")))
continue; continue;
// New type.. // New type..
@ -2808,7 +2878,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
} }
if (Token::Match(startToken->previous(), ";|{|}|=|const") && if (Token::Match(startToken->previous(), ";|{|}|=|const") &&
(!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%" : "*|&|::| %name%"))) (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%")))
return false; return false;
// already simplified // already simplified
@ -3282,6 +3352,9 @@ void TemplateSimplifier::simplifyTemplates(
const std::time_t maxtime, const std::time_t maxtime,
bool &codeWithTemplates) bool &codeWithTemplates)
{ {
// split ">>" into "> >"
fixAngleBrackets();
// Remove "typename" unless used in template arguments or using type alias.. // Remove "typename" unless used in template arguments or using type alias..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% =")) if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% ="))

View File

@ -294,6 +294,11 @@ public:
*/ */
void simplifyTemplateArgs(Token *start, Token *end); void simplifyTemplateArgs(Token *start, Token *end);
/** Fix angle brackets.
* foo < bar < >> => foo < bar < > >
*/
void fixAngleBrackets();
private: private:
/** /**
* Get template declarations * Get template declarations

View File

@ -2817,23 +2817,6 @@ void Tokenizer::simplifyTemplates()
if (isC()) if (isC())
return; return;
for (Token *tok = list.front(); tok; tok = tok->next()) {
// Ticket #6181: normalize C++11 template parameter list closing syntax
if (tok->str() == "<" && mTemplateSimplifier->templateParameters(tok)) {
Token *endTok = tok->findClosingBracket();
if (endTok && endTok->str() == ">>") {
endTok->str(">");
endTok->insertToken(">");
}
} else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <")) {
Token *endTok = tok->tokAt(2)->findClosingBracket();
if (Token::Match(endTok, ">> ;|{")) {
endTok->str(">");
endTok->insertToken(">");
}
}
}
mTemplateSimplifier->simplifyTemplates( mTemplateSimplifier->simplifyTemplates(
#ifdef MAXTIME #ifdef MAXTIME
mMaxTime, mMaxTime,

View File

@ -154,6 +154,9 @@ private:
TEST_CASE(template114); // #9155 TEST_CASE(template114); // #9155
TEST_CASE(template115); // #9153 TEST_CASE(template115); // #9153
TEST_CASE(template116); // #9178 TEST_CASE(template116); // #9178
TEST_CASE(template117);
TEST_CASE(template118);
TEST_CASE(template119); // #9186
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -207,6 +210,7 @@ private:
TEST_CASE(template_variable_1); TEST_CASE(template_variable_1);
TEST_CASE(template_variable_2); TEST_CASE(template_variable_2);
TEST_CASE(template_variable_3); TEST_CASE(template_variable_3);
TEST_CASE(template_variable_4);
} }
std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) {
@ -945,19 +949,19 @@ private:
" A<A<BLA>> gna1;\n" " A<A<BLA>> gna1;\n"
" A<BLA> gna2;\n" " A<BLA> gna2;\n"
"}\n"; "}\n";
const char expected[] = "class A<A<BLA>> ; " const char expected[] = "class A<BLA> ; "
"class A<BLA> ; " "class A<A<BLA>> ; "
"int main ( ) { " "int main ( ) { "
"A<A<BLA>> gna1 ; " "A<A<BLA>> gna1 ; "
"A<BLA> gna2 ; " "A<BLA> gna2 ; "
"} " "} "
"class A<A<BLA>> { " "class A<BLA> { "
"A<BLA> mT ; " "BLA mT ; "
"public: " "public: "
"void foo ( ) { } " "void foo ( ) { } "
"} ; " "} ; "
"class A<BLA> { " "class A<A<BLA>> { "
"BLA mT ; " "A<BLA> mT ; "
"public: " "public: "
"void foo ( ) { } " "void foo ( ) { } "
"} ;"; "} ;";
@ -2763,6 +2767,58 @@ private:
ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS(exp, tok(code));
} }
void template117() {
const char code[] = "template<typename T = void> struct X {};\n"
"X<X<>> x;";
const char exp[] = "struct X<void> ; "
"struct X<X<void>> ; "
"X<X<void>> x ; "
"struct X<void> { } ; "
"struct X<X<void>> { } ;";
ASSERT_EQUALS(exp, tok(code));
}
void template118() {
const char code[] = "template<int> struct S { void f(int i); };\n"
"S<1> s;";
const char exp[] = "struct S<1> ; "
"S<1> s ; struct S<1> { "
"void f ( int i ) ; "
"} ;";
ASSERT_EQUALS(exp, tok(code));
}
void template119() { // #9186
{
const char code[] = "template <typename T>\n"
"constexpr auto func = [](auto x){ return T(x);};\n"
"template <typename T>\n"
"constexpr auto funcBraced = [](auto x){ return T{x};};\n"
"double f(int x) { return func<double>(x); }\n"
"double fBraced(int x) { return funcBraced<int>(x); }";
const char exp[] = "const auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
"const auto funcBraced<int> = [ ] ( auto x ) { return int { x } ; } ; "
"double f ( int x ) { return func<double> ( x ) ; } "
"double fBraced ( int x ) { return funcBraced<int> ( x ) ; }";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "template <typename T>\n"
"constexpr auto func = [](auto x){ return T(x);};\n"
"void foo() {\n"
" func<int>(x);\n"
" func<double>(x);\n"
"}";
const char exp[] = "const auto func<int> = [ ] ( auto x ) { return int ( x ) ; } ; "
"const auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
"void foo ( ) { "
"func<int> ( x ) ; "
"func<double> ( x ) ; "
"}";
ASSERT_EQUALS(exp, tok(code));
}
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..}; void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n" const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n" "template <typename T> struct S {a};\n"
@ -3433,11 +3489,13 @@ private:
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp", ""); tokenizer.createTokens(istr, "test.cpp");
tokenizer.createLinks();
tokenizer.mTemplateSimplifier->fixAngleBrackets();
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { for (const Token *tok1 = tokenizer.tokens(); tok1; tok1 = tok1->next()) {
if (tok->str() == "var1") if (tok1->str() == "var1")
(const_cast<Token *>(tok))->varId(1); (const_cast<Token *>(tok1))->varId(1);
} }
return TemplateSimplifier::templateParameters(tokenizer.tokens()->next()); return TemplateSimplifier::templateParameters(tokenizer.tokens()->next());
@ -3459,14 +3517,28 @@ private:
ASSERT_EQUALS(2U, templateParameters("X<class, typename... T> x;")); ASSERT_EQUALS(2U, templateParameters("X<class, typename... T> x;"));
ASSERT_EQUALS(2U, templateParameters("X<int(&)(), class> x;")); ASSERT_EQUALS(2U, templateParameters("X<int(&)(), class> x;"));
ASSERT_EQUALS(3U, templateParameters("X<char, int(*)(), bool> x;")); ASSERT_EQUALS(3U, templateParameters("X<char, int(*)(), bool> x;"));
TODO_ASSERT_EQUALS(1U, 0U, templateParameters("X<int...> x;")); // Mishandled valid syntax ASSERT_EQUALS(1U, templateParameters("X<int...> x;"));
TODO_ASSERT_EQUALS(2U, 0U, templateParameters("X<class, typename...> x;")); // Mishandled valid syntax ASSERT_EQUALS(2U, templateParameters("X<class, typename...> x;"));
ASSERT_EQUALS(2U, templateParameters("X<1, T> x;")); ASSERT_EQUALS(2U, templateParameters("X<1, T> x;"));
ASSERT_EQUALS(1U, templateParameters("X<i == 0> x;")); ASSERT_EQUALS(1U, templateParameters("X<i == 0> x;"));
ASSERT_EQUALS(2U, templateParameters("X<int, i>=0> x;")); ASSERT_EQUALS(2U, templateParameters("X<int, i>=0> x;"));
ASSERT_EQUALS(3U, templateParameters("X<int, i>=0, i - 2> x;")); ASSERT_EQUALS(3U, templateParameters("X<int, i>=0, i - 2> x;"));
ASSERT_EQUALS(0U, templateParameters("var1<1> x;")); ASSERT_EQUALS(0U, templateParameters("var1<1> x;"));
ASSERT_EQUALS(0U, templateParameters("X<1>2;")); ASSERT_EQUALS(0U, templateParameters("X<1>2;"));
ASSERT_EQUALS(2U, templateParameters("template<typename...B,typename=SameSize<B...>> x;"));
ASSERT_EQUALS(2U, templateParameters("template<typename...B,typename=SameSize<B...> > x;"));
ASSERT_EQUALS(1U, templateParameters("template<template<typename>...Foo> x;"));
ASSERT_EQUALS(1U, templateParameters("template<template<typename>> x;"));
ASSERT_EQUALS(1U, templateParameters("template<template<template<typename>>> x;"));
ASSERT_EQUALS(1U, templateParameters("template<template<template<template<typename>>>> x;"));
ASSERT_EQUALS(1U, templateParameters("template<template<template<template<template<typename>>>>> x;"));
ASSERT_EQUALS(2U, templateParameters("template<template<typename>,int> x;"));
ASSERT_EQUALS(2U, templateParameters("template<template<template<typename>>,int> x;"));
ASSERT_EQUALS(2U, templateParameters("template<template<template<template<typename>>>,int> x;"));
ASSERT_EQUALS(2U, templateParameters("template<template<template<template<template<typename>>>>,int> x;"));
ASSERT_EQUALS(2U, templateParameters("template<template<typename>...Foo,template<template<template<typename>>>> x;"));
ASSERT_EQUALS(3U, templateParameters("template<template<typename>...Foo,int,template<template<template<typename>>>> x;"));
ASSERT_EQUALS(4U, templateParameters("template<template<typename>...Foo,int,template<template<template<typename>>>,int> x;"));
} }
// Helper function to unit test TemplateSimplifier::getTemplateNamePosition // Helper function to unit test TemplateSimplifier::getTemplateNamePosition
@ -3474,7 +3546,9 @@ private:
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp", emptyString); tokenizer.createTokens(istr, "test.cpp");
tokenizer.createLinks();
tokenizer.mTemplateSimplifier->fixAngleBrackets();
const Token *_tok = tokenizer.tokens(); const Token *_tok = tokenizer.tokens();
for (unsigned i = 0 ; i < offset ; ++i) for (unsigned i = 0 ; i < offset ; ++i)
@ -4040,6 +4114,16 @@ private:
} }
} }
void template_variable_4() {
const char code[] = "template<typename T> void test() { }\n"
"template<typename T> decltype(test<T>)* foo = &(test<T>);\n"
"void bar() { foo<int>(); }";
const char expected[] = "void test<int> ( ) ; "
"decltype ( test<int> ) * foo<int> = & ( test<int> ) ; "
"void bar ( ) { foo<int> ( ) ; } "
"void test<int> ( ) { }";
ASSERT_EQUALS(expected, tok(code));
}
}; };
REGISTER_TEST(TestSimplifyTemplate) REGISTER_TEST(TestSimplifyTemplate)