template simplifier: specialized member class not recognized causing wrong instantiation (#1876)

Specialized member classes declared outsize the class were not
recognized. This caused the the member class to be instantiated rather
than the specialized class. We already had a test for this but it was
wrong so it went unnoticed.
This commit is contained in:
IOBYTE 2019-06-09 02:11:59 -04:00 committed by Daniel Marjamäki
parent 1f24aa778b
commit 5af8beecf6
3 changed files with 81 additions and 16 deletions

View File

@ -89,7 +89,7 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s,
throw InternalError(tok, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX);
}
isClass(Token::Match(paramEnd->next(), "class|struct|union %name% <|{|:|;"));
isClass(Token::Match(paramEnd->next(), "class|struct|union %name% <|{|:|;|::"));
if (token->strAt(1) == "<" && !isSpecialization()) {
const Token *end = token->next()->findClosingBracket();
isVariadic(end && Token::findmatch(token->tokAt(2), "typename|class . . .", end));
@ -113,8 +113,8 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s,
if (tok1)
isForwardDeclaration(tok1->str() == ";");
}
// check for member function and adjust scope
if (isFunction() && nameToken->strAt(-1) == "::") {
// check for member class or function and adjust scope
if ((isFunction() || isClass()) && nameToken->strAt(-1) == "::") {
const Token * start = nameToken;
while (Token::Match(start->tokAt(-2), "%name% ::") ||
@ -1397,16 +1397,48 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
return false;
}
bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos)
{
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{|::")) {
namepos = 2;
tok = tok->tokAt(2);
while (Token::Match(tok, "%type% :: %type%") ||
(Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) {
if (tok->strAt(1) == "::") {
tok = tok->tokAt(2);
namepos += 2;
} else {
const Token *end = tok->next()->findClosingBracket();
if (!end || !end->tokAt(2)) {
// syntax error
namepos = -1;
return true;
}
end = end->tokAt(2);
do {
tok = tok->next();
namepos += 1;
} while (tok && tok != end);
}
}
return true;
}
return false;
}
int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
{
// FIXME: tok == ">>" is a tokenizer bug that needs to be fixed
assert(tok && (tok->str() == ">" || tok->str() == ">>"));
auto it = mTemplateNamePos.find(tok);
if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) {
return it->second;
}
// get the position of the template name
int namepos = 0;
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{"))
namepos = 2;
if (getTemplateNamePositionTemplateClass(tok, namepos))
;
else if (Token::Match(tok, "> using %name% ="))
namepos = 2;
else if (getTemplateNamePositionTemplateVariable(tok, namepos))
@ -1500,7 +1532,9 @@ void TemplateSimplifier::expandTemplate(
templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true);
templateDeclaration.token->insertToken(newName, "", true);
templateDeclaration.token->insertToken(";", "", true);
} else if ((isFunction && (copy || isSpecialization)) || (isVariable && !isSpecialization)) {
} else if ((isFunction && (copy || isSpecialization)) ||
(isVariable && !isSpecialization) ||
(isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token) != mTemplateSpecializationMap.end())) {
Token * dst = templateDeclaration.token;
Token * dstStart = dst->previous();
bool isStatic = false;

View File

@ -240,11 +240,19 @@ public:
int getTemplateNamePosition(const Token *tok);
/**
* Get function template name position
* Get class template name position
* @param tok The ">" token e.g. before "class"
* @param namepos return offset to name
* @return true if name found, false if not
* */
static bool getTemplateNamePositionTemplateClass(const Token *tok, int &namepos);
/**
* Get function template name position
* @param tok The ">" token
* @param namepos return offset to name
* @return true if name found, false if not
* */
static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos);
/**

View File

@ -976,7 +976,12 @@ private:
" template<typename T> struct X { T t; };"
"};"
"template<> struct A::X<int> { int *t; };";
ASSERT_EQUALS("struct A { struct X<int> ; } ; struct A :: X<int> { int t ; } ;", tok(code));
const char expected[] = "struct A { "
"struct X<int> ; "
"template < typename T > struct X { T t ; } ; "
"} ; "
"struct A :: X<int> { int * t ; } ;";
ASSERT_EQUALS(expected, tok(code));
}
void template41() { // #4710 - const in template instantiation not handled perfectly
@ -1021,6 +1026,7 @@ private:
" return 0;\n"
"}";
const char expected[] = "struct E<void*> ; "
"struct C<B<A>> ; "
"struct C<A> ; "
"struct D<B<A>> ; "
"int f2<B<A>> ( ) ; "
@ -1215,7 +1221,8 @@ private:
" enum { value = 1 }; "
"};"
"const int x = Factorial<4>::value;";
const char expected[] = "struct Factorial<4> ; "
const char expected[] = "struct Factorial<0> ; "
"struct Factorial<4> ; "
"struct Factorial<3> ; "
"struct Factorial<2> ; "
"struct Factorial<1> ; "
@ -1342,7 +1349,8 @@ private:
"int main () {\n"
" return diagonalGroupTest<4>();\n"
"}";
const char exp[] = "struct Factorial<4> ; "
const char exp[] = "struct Factorial<0> ; "
"struct Factorial<4> ; "
"struct Factorial<3> ; "
"struct Factorial<2> ; "
"struct Factorial<1> ; "
@ -1576,13 +1584,22 @@ private:
" void foo() { }\n"
"};";
const char exp [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; "
"class Bar<void,void> ; "
"class Bar<void,void> { "
"} ; "
"template < typename K , typename V , int KeySize = 0 > "
"class Bar : private Bar<void,void> { "
"void foo ( ) { } "
"} ;";
ASSERT_EQUALS(exp, tok(code));
const char act [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; "
"class Bar<void,void> { "
"} ; "
"class Bar<void,void> ; "
"template < typename K , typename V , int KeySize = 0 > "
"class Bar : private Bar<void,void> { "
"void foo ( ) { } "
"} ;";
TODO_ASSERT_EQUALS(exp, act, tok(code));
}
void template71() { // #8821
@ -1679,7 +1696,8 @@ private:
" std::cout << is_void<char>::value << std::endl;\n"
" std::cout << is_void<void>::value << std::endl;\n"
"}";
const char exp[] = "struct is_void<char> ; "
const char exp[] = "struct is_void<void> ; "
"struct is_void<char> ; "
"struct is_void<void> : std :: true_type { } ; "
"int main ( ) { "
"std :: cout << is_void<char> :: value << std :: endl ; "
@ -2846,7 +2864,8 @@ private:
" b.A<>::Print();\n"
" return 0;\n"
"}";
ASSERT_EQUALS("template < typename T0 > class A ; "
ASSERT_EQUALS("class A<void> ; "
"template < typename T0 > class A ; "
"class A<void> { "
"public: "
"A<void> ( ) { } "
@ -3261,6 +3280,7 @@ private:
"template class Fred<1>;\n"
"}\n";
ASSERT_EQUALS("namespace NS { "
"struct Barney<1> ; "
"template < int type > struct Barney ; "
"struct Barney<1> { } ; "
"class Fred<1> ; "
@ -3577,7 +3597,8 @@ private:
const char code[] = "template<typename T> class C { };\n"
"template<> class C<char> { };\n"
"map<int> m;\n";
const char expected[] = "template < typename T > class C { } ; "
const char expected[] = "class C<char> ; "
"template < typename T > class C { } ; "
"class C<char> { } ; "
"map < int > m ;";
ASSERT_EQUALS(expected, tok(code));
@ -3587,7 +3608,8 @@ private:
"template<> class C<char> { };\n"
"map<int> m;\n"
"C<int> i;";
const char expected[] = "class C<int> ; "
const char expected[] = "class C<char> ; "
"class C<int> ; "
"class C<char> { } ; "
"map < int > m ; "
"C<int> i ; "
@ -3600,7 +3622,8 @@ private:
"map<int> m;\n"
"C<int> i;\n"
"C<char> c;";
const char expected[] = "class C<int> ; "
const char expected[] = "class C<char> ; "
"class C<int> ; "
"class C<char> { } ; "
"map < int > m ; "
"C<int> i ; "