Fixed #6183 (TemplateSimplifier: Does not handle methods) (#1540)

This commit is contained in:
IOBYTE 2018-12-24 01:25:11 -05:00 committed by Daniel Marjamäki
parent 54d6625ffb
commit 7875555b03
4 changed files with 228 additions and 39 deletions

View File

@ -497,6 +497,9 @@ bool TemplateSimplifier::getTemplateDeclarations()
tok2 = tok2->link(); tok2 = tok2->link();
else if (tok2->str() == ")") else if (tok2->str() == ")")
break; break;
// skip decltype(...)
else if (Token::Match(tok2, "decltype ("))
tok2 = tok2->linkAt(1);
// Declaration => add to mTemplateForwardDeclarations // Declaration => add to mTemplateForwardDeclarations
else if (tok2->str() == ";") { else if (tok2->str() == ";") {
const int namepos = getTemplateNamePosition(parmEnd, true); const int namepos = getTemplateNamePosition(parmEnd, true);
@ -891,7 +894,14 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *to
while (tok && tok->next()) { while (tok && tok->next()) {
if (Token::Match(tok->next(), ";|{")) if (Token::Match(tok->next(), ";|{"))
return false; return false;
else if (Token::Match(tok->next(), "%type% <")) { // skip decltype(...)
else if (Token::Match(tok, "decltype (")) {
const Token * end = tok->linkAt(1);
while (tok && 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 (closing->strAt(1) == "(" && mTokenizer->isFunctionHead(closing->tokAt(2), ";|{|:")) if (closing->strAt(1) == "(" && mTokenizer->isFunctionHead(closing->tokAt(2), ";|{|:"))
@ -984,15 +994,19 @@ void TemplateSimplifier::expandTemplate(
const Token *endOfTemplateDefinition = nullptr; const Token *endOfTemplateDefinition = nullptr;
const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken));
const bool isClass = Token::Match(templateDeclarationToken->next(), "class|struct|union %name% <|{|:"); const bool isClass = Token::Match(templateDeclarationToken->next(), "class|struct|union %name% <|{|:");
const bool isFunction = templateDeclarationNameToken->strAt(1) == "("; const bool isFunction = templateDeclarationNameToken->strAt(1) == "(" ||
(templateDeclarationNameToken->strAt(1) == "<" && templateDeclarationNameToken->next()->findClosingBracket()->strAt(1) == "(");
const bool isSpecialization = Token::Match(templateDeclaration.token, "template < >");
// add forward declarations // add forward declarations
if (copy && isClass) { if (copy && isClass) {
templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true); templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true);
templateDeclaration.token->insertToken(newName, "", true); templateDeclaration.token->insertToken(newName, "", true);
templateDeclaration.token->insertToken(";", "", true); templateDeclaration.token->insertToken(";", "", true);
} else if (copy && isFunction) { } else if (isFunction && (copy || (!copy && isSpecialization))) {
Token * dst = templateDeclaration.token; Token * dst = templateDeclaration.token;
bool isStatic = false;
std::string scope;
Token * start; Token * start;
Token * end; Token * end;
auto it = mTemplateForwardDeclarationsMap.find(dst); auto it = mTemplateForwardDeclarationsMap.find(dst);
@ -1003,8 +1017,21 @@ void TemplateSimplifier::expandTemplate(
start = temp1->next(); start = temp1->next();
end = temp2->linkAt(1)->next(); end = temp2->linkAt(1)->next();
} else { } else {
auto it2 = mTemplateUserSpecializationMap.find(dst);
if (it2 != mTemplateUserSpecializationMap.end()) {
dst = it2->second;
isStatic = dst->next()->findClosingBracket()->strAt(1) == "static";
const Token * temp = templateDeclarationNameToken;
while (Token::Match(temp->tokAt(-2), "%name% ::")) {
scope.insert(0, temp->strAt(-2) + " :: ");
temp = temp->tokAt(-2);
}
}
start = templateDeclarationToken->next(); start = templateDeclarationToken->next();
end = templateDeclarationNameToken->linkAt(1)->next(); if (templateDeclarationNameToken->strAt(1) == "(")
end = templateDeclarationNameToken->linkAt(1)->next();
else
end = templateDeclarationNameToken->next()->findClosingBracket()->linkAt(1)->next();
} }
unsigned int typeindentlevel = 0; unsigned int typeindentlevel = 0;
while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) {
@ -1015,6 +1042,9 @@ void TemplateSimplifier::expandTemplate(
end = end->next(); end = end->next();
} }
if (isStatic)
dst->insertToken("static", "", true);
std::map<const Token *, Token *> links; std::map<const Token *, Token *> links;
while (start && start != end) { while (start && start != end) {
unsigned int itype = 0; unsigned int itype = 0;
@ -1041,7 +1071,11 @@ void TemplateSimplifier::expandTemplate(
dst->previous()->isLong(typetok->isLong()); dst->previous()->isLong(typetok->isLong());
} }
} else { } else {
if (start->str() == templateDeclarationNameToken->str()) { if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) {
// skip scope
while (start->strAt(1) != templateDeclarationNameToken->str())
start = start->next();
} else if (start->str() == templateDeclarationNameToken->str()) {
dst->insertToken(newName, "", true); dst->insertToken(newName, "", true);
if (start->strAt(1) == "<") if (start->strAt(1) == "<")
start = start->next()->findClosingBracket(); start = start->next()->findClosingBracket();
@ -1133,6 +1167,8 @@ void TemplateSimplifier::expandTemplate(
// Start of template.. // Start of template..
if (tok3 == templateDeclarationToken) { if (tok3 == templateDeclarationToken) {
tok3 = tok3->next(); tok3 = tok3->next();
if (tok3->str() == "static")
tok3 = tok3->next();
} }
// member function implemented outside class definition // member function implemented outside class definition
@ -1907,23 +1943,12 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
return false; return false;
// already simplified // already simplified
if (!Token::Match(startToken, "%name% <")) if (!Token::Match(tok2, "%name% <"))
return false; return false;
if (templateDeclaration.scope.empty()) { if (!matchSpecialization(tok->tokAt(namepos), tok2, specializations))
if (startToken->str() != templateDeclaration.name)
return false;
} else {
std::string name = templateDeclaration.scope + " :: " + startToken->str();
if (name != templateDeclaration.name)
return false;
}
if (!matchSpecialization(tok->tokAt(namepos), startToken, specializations))
return false; return false;
tok2 = startToken;
// New type.. // New type..
mTypesUsedInTemplateInstantiation.clear(); mTypesUsedInTemplateInstantiation.clear();
std::list<std::string> typeStringsUsedInTemplateInstantiation; std::list<std::string> typeStringsUsedInTemplateInstantiation;
@ -2051,6 +2076,61 @@ void TemplateSimplifier::replaceTemplateUsage(
} }
} }
static std::string getPathName(const TemplateSimplifier::TokenAndName & decl)
{
std::string name = decl.scope;
if (!name.empty())
name += " :: ";
if (decl.nameToken->strAt(-1) == "::") {
const Token * start = decl.nameToken;
while (Token::Match(start->tokAt(-2), "%name% ::"))
start = start->tokAt(-2);
while (start != decl.nameToken) {
name += (start->str() + " ");
start = start->next();
}
}
name += decl.name;
return name;
}
void TemplateSimplifier::getUserDefinedSpecializations()
{
// try to locate a matching declaration for each user defined specialization
for (auto & spec : mTemplateDeclarations) {
if (Token::Match(spec.token, "template < >")) {
std::string specName = getPathName(spec);
bool found = false;
for (auto & decl : mTemplateDeclarations) {
if (Token::Match(decl.token, "template < >"))
continue;
std::string declName = getPathName(decl);
// make sure the scopes and names match
if (specName == declName) {
// @todo make sure function parameters also match
mTemplateUserSpecializationMap[spec.token] = decl.token;
}
}
if (!found) {
for (auto & decl : mTemplateForwardDeclarations) {
std::string declName = getPathName(decl);
// make sure the scopes and names match
if (specName == declName) {
// @todo make sure function parameters also match
mTemplateUserSpecializationMap[spec.token] = decl.token;
}
}
}
}
}
}
void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
{ {
// try to locate a matching declaration for each forward declaration // try to locate a matching declaration for each forward declaration
@ -2066,26 +2146,8 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
// make sure the number of arguments match // make sure the number of arguments match
if (params1.size() == params2.size()) { if (params1.size() == params2.size()) {
std::string declName = decl.scope; std::string declName = getPathName(decl);
if (!declName.empty()) std::string forwardDeclName = getPathName(forwardDecl);
declName += " :: ";
if (decl.nameToken->strAt(-1) == "::") {
const Token * start = decl.nameToken;
while (Token::Match(start->tokAt(-2), "%name% ::"))
start = start->tokAt(-2);
while (start != decl.nameToken) {
declName += (start->str() + " ");
start = start->next();
}
}
declName += decl.name;
std::string forwardDeclName = forwardDecl.scope;
if (!forwardDeclName.empty())
forwardDeclName += " :: ";
forwardDeclName += forwardDecl.name;
// make sure the scopes and names match // make sure the scopes and names match
if (forwardDeclName == declName) { if (forwardDeclName == declName) {
@ -2124,6 +2186,7 @@ void TemplateSimplifier::simplifyTemplates(
mTemplateDeclarations.clear(); mTemplateDeclarations.clear();
mTemplateForwardDeclarations.clear(); mTemplateForwardDeclarations.clear();
mTemplateForwardDeclarationsMap.clear(); mTemplateForwardDeclarationsMap.clear();
mTemplateUserSpecializationMap.clear();
mTemplateInstantiations.clear(); mTemplateInstantiations.clear();
mInstantiatedTemplates.clear(); mInstantiatedTemplates.clear();
mExplicitInstantiationsToDelete.clear(); mExplicitInstantiationsToDelete.clear();
@ -2157,6 +2220,9 @@ void TemplateSimplifier::simplifyTemplates(
// Copy default argument values from forward declaration to declaration // Copy default argument values from forward declaration to declaration
fixForwardDeclaredDefaultArgumentValues(); fixForwardDeclaredDefaultArgumentValues();
// Locate user defined specializations.
getUserDefinedSpecializations();
// Locate possible instantiations of templates.. // Locate possible instantiations of templates..
getTemplateInstantiations(); getTemplateInstantiations();

View File

@ -156,6 +156,12 @@ private:
*/ */
void useDefaultArgumentValues(); void useDefaultArgumentValues();
/**
* Try to locate a matching declaration for each user defined
* specialization.
*/
void getUserDefinedSpecializations();
/** /**
* simplify template aliases * simplify template aliases
*/ */
@ -278,6 +284,7 @@ private:
std::list<TokenAndName> mTemplateDeclarations; std::list<TokenAndName> mTemplateDeclarations;
std::list<TokenAndName> mTemplateForwardDeclarations; std::list<TokenAndName> mTemplateForwardDeclarations;
std::map<Token *, Token *> mTemplateForwardDeclarationsMap; std::map<Token *, Token *> mTemplateForwardDeclarationsMap;
std::map<Token *, Token *> mTemplateUserSpecializationMap;
std::list<TokenAndName> mTemplateInstantiations; std::list<TokenAndName> mTemplateInstantiations;
std::list<TokenAndName> mInstantiatedTemplates; std::list<TokenAndName> mInstantiatedTemplates;
std::list<TokenAndName> mMemberFunctionsToDelete; std::list<TokenAndName> mMemberFunctionsToDelete;

View File

@ -125,6 +125,9 @@ private:
TEST_CASE(template85); // #8902 crash TEST_CASE(template85); // #8902 crash
TEST_CASE(template86); // crash TEST_CASE(template86); // crash
TEST_CASE(template87); TEST_CASE(template87);
TEST_CASE(template88); // #6183
TEST_CASE(template89); // #8917
TEST_CASE(template90); // crash
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)
@ -479,7 +482,8 @@ private:
"}\n"; "}\n";
// The expected result.. // The expected result..
const char expected[] = "void foo<int*> ( ) " const char expected[] = "void foo<int*> ( ) ; "
"void foo<int*> ( ) "
"{ x ( ) ; } " "{ x ( ) ; } "
"int main ( ) " "int main ( ) "
"{ foo<int*> ( ) ; }"; "{ foo<int*> ( ) ; }";
@ -505,6 +509,7 @@ private:
// The expected result.. // The expected result..
const char expected[] = "void a<2> ( ) ; " const char expected[] = "void a<2> ( ) ; "
"void a<1> ( ) ; " "void a<1> ( ) ; "
"void a<0> ( ) ; "
"void a<0> ( ) { } " "void a<0> ( ) { } "
"int main ( ) " "int main ( ) "
"{ a<2> ( ) ; return 0 ; } " "{ a<2> ( ) ; return 0 ; } "
@ -1664,6 +1669,92 @@ private:
ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS(exp, tok(code));
} }
void template88() { // #6183.cpp
const char code[] = "class CTest {\n"
"public:\n"
" template <typename T>\n"
" static void Greeting(T val) {\n"
" std::cout << val << std::endl;\n"
" }\n"
"private:\n"
" static void SayHello() {\n"
" std::cout << \"Hello World!\" << std::endl;\n"
" }\n"
"};\n"
"template<>\n"
"void CTest::Greeting(bool) {\n"
" CTest::SayHello();\n"
"}\n"
"int main() {\n"
" CTest::Greeting<bool>(true);\n"
" return 0;\n"
"}";
const char exp[] = "class CTest { "
"public: "
"static void Greeting<bool> ( bool ) ; "
"template < typename T > "
"static void Greeting ( T val ) { "
"std :: cout << val << std :: endl ; "
"} "
"private: "
"static void SayHello ( ) { "
"std :: cout << \"Hello World!\" << std :: endl ; "
"} "
"} ; "
"void CTest :: Greeting<bool> ( bool ) { "
"CTest :: SayHello ( ) ; "
"} "
"int main ( ) { "
"CTest :: Greeting<bool> ( true ) ; "
"return 0 ; "
"}";
ASSERT_EQUALS(exp, tok(code));
}
void template89() { // #8917
const char code[] = "struct Fred {\n"
" template <typename T> static void foo() { }\n"
"};\n"
"template void Fred::foo<char>();\n"
"template void Fred::foo<float>();\n"
"template <> void Fred::foo<bool>() { }\n"
"template <> void Fred::foo<int>() { }";
const char exp[] = "struct Fred { "
"static void foo<char> ( ) ; "
"static void foo<float> ( ) ; "
"static void foo<bool> ( ) ; "
"static void foo<int> ( ) ; "
"} ; "
"void Fred :: foo<char> ( ) { } "
"void Fred :: foo<float> ( ) { } "
"void Fred :: foo<bool> ( ) { } "
"void Fred :: foo<int> ( ) { }";
const char act[] = "struct Fred { "
"static void foo<char> ( ) ; "
"static void foo<float> ( ) ; "
"} ; "
"void Fred :: foo<bool> ( ) ; "
"void Fred :: foo<bool> ( ) { } "
"void Fred :: foo<int> ( ) ; "
"void Fred :: foo<int> ( ) { } "
"void Fred :: foo<char> ( ) { } "
"void Fred :: foo<float> ( ) { }";
TODO_ASSERT_EQUALS(exp, act, tok(code));
}
void template90() { // crash
const char code[] = "template <typename T> struct S1 {};\n"
"void f(S1<double>) {}\n"
"template <typename T>\n"
"decltype(S1<T>().~S1<T>()) fun1() {};";
const char exp[] = "struct S1<double> ; "
"void f ( S1<double> ) { } "
"template < typename T > "
"decltype ( S1 < T > ( ) . ~ S1 < T > ( ) ) fun1 ( ) { } ; "
"struct S1<double> { } ;";
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"

View File

@ -80,6 +80,8 @@ private:
TEST_CASE(hierarchie_loop); // ticket 5590 TEST_CASE(hierarchie_loop); // ticket 5590
TEST_CASE(staticVariable); //ticket #5566 TEST_CASE(staticVariable); //ticket #5566
TEST_CASE(templateSimplification); //ticket #6183
} }
@ -794,6 +796,29 @@ private:
"int i = F();"); "int i = F();");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused private function: 'Foo::F'\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Unused private function: 'Foo::F'\n", errout.str());
} }
void templateSimplification() { //ticket #6183
check("class CTest {\n"
"public:\n"
" template <typename T>\n"
" static void Greeting(T val) {\n"
" std::cout << val << std::endl;\n"
" }\n"
"private:\n"
" static void SayHello() {\n"
" std::cout << \"Hello World!\" << std::endl;\n"
" }\n"
"};\n"
"template<>\n"
"void CTest::Greeting(bool) {\n"
" CTest::SayHello();\n"
"}\n"
"int main() {\n"
" CTest::Greeting<bool>(true);\n"
" return 0;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
}; };
REGISTER_TEST(TestUnusedPrivateFunction) REGISTER_TEST(TestUnusedPrivateFunction)