Fix a template simplifier namespace bug in #7145 (#1473)

* Fix a template simplifier namespace bug in #7145

* Refactor template simplifier to only call getTemplateDeclarations once per loop.
This commit is contained in:
IOBYTE 2018-11-10 10:41:14 -05:00 committed by Daniel Marjamäki
parent 1ffcc6b730
commit 69e6e11844
3 changed files with 94 additions and 51 deletions

View File

@ -485,10 +485,10 @@ static void setScopeInfo(const Token *tok, std::list<ScopeInfo2> *scopeInfo)
}
}
std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates, bool forward)
bool TemplateSimplifier::getTemplateDeclarations()
{
bool codeWithTemplates = false;
std::list<ScopeInfo2> scopeInfo;
std::list<TokenAndName> declarations;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "}|namespace|class|struct|union")) {
setScopeInfo(tok, &scopeInfo);
@ -509,27 +509,23 @@ std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDecla
tok2 = tok2->link();
else if (tok2->str() == ")")
break;
// Just a declaration => ignore this
// Declaration => add to mTemplateForwardDeclarations
else if (tok2->str() == ";") {
if (forward) {
const int namepos = getTemplateNamePosition(parmEnd, forward);
if (namepos > 0)
declarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos));
}
const int namepos = getTemplateNamePosition(parmEnd, true);
if (namepos > 0)
mTemplateForwardDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos));
break;
}
// Implementation => add to "templates"
// Implementation => add to mTemplateDeclarations
else if (tok2->str() == "{") {
if (!forward) {
const int namepos = getTemplateNamePosition(parmEnd, forward);
if (namepos > 0)
declarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos));
}
const int namepos = getTemplateNamePosition(parmEnd, false);
if (namepos > 0)
mTemplateDeclarations.emplace_back(tok, getScopeName(scopeInfo), parmEnd->strAt(namepos));
break;
}
}
}
return declarations;
return codeWithTemplates;
}
@ -961,15 +957,33 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok, bool forward)
void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok)
{
// find start of qualification
const Token * tokStart = tok;
int offset = 0;
while (Token::Match(tokStart->tokAt(-2), "%name% ::")) {
tokStart = tokStart->tokAt(-2);
offset -= 2;
}
// decide if namespace needs to be inserted in or appended to token list
const bool insert = tokStart != tok;
std::string::size_type start = 0;
std::string::size_type end = 0;
while ((end = templateDeclaration.scope.find(" ", start)) != std::string::npos) {
std::string token = templateDeclaration.scope.substr(start, end - start);
mTokenList.addtoken(token, tok->linenr(), tok->fileIndex());
if (insert)
mTokenList.back()->tokAt(offset)->insertToken(token, "");
else
mTokenList.addtoken(token, tok->linenr(), tok->fileIndex());
start = end + 1;
}
mTokenList.addtoken(templateDeclaration.scope.substr(start), tok->linenr(), tok->fileIndex());
mTokenList.addtoken("::", tok->linenr(), tok->fileIndex());
if (insert) {
mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope.substr(start), "");
mTokenList.back()->tokAt(offset)->insertToken("::", "");
} else {
mTokenList.addtoken(templateDeclaration.scope.substr(start), tok->linenr(), tok->fileIndex());
mTokenList.addtoken("::", tok->linenr(), tok->fileIndex());
}
}
bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) const
@ -1961,12 +1975,8 @@ void TemplateSimplifier::replaceTemplateUsage(
void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
{
// get all forward declarations
bool dummy;
std::list<TokenAndName> forwardTemplateDeclarations = getTemplateDeclarations(dummy, true);
// try to locate a matching declaration for each forward declaration
for (const auto & forwardDecl : forwardTemplateDeclarations) {
for (const auto & forwardDecl : mTemplateForwardDeclarations) {
std::vector<const Token *> params1;
getTemplateParametersInDeclaration(forwardDecl.token, params1);
@ -1995,29 +2005,8 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
void TemplateSimplifier::simplifyTemplates(
const std::time_t maxtime,
bool &codeWithTemplates
)
bool &codeWithTemplates)
{
std::set<std::string> expandedtemplates;
if (getTemplateDeclarations(codeWithTemplates).empty())
return;
// There are templates..
// Remove "typename" unless used in template arguments..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (tok->str() == "typename")
tok->deleteThis();
if (Token::simpleMatch(tok, "template <")) {
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
// expand templates
// TODO: 2 is not the ideal number of loops.
// We should loop until the number of declarations is 0 but we can't
// do that until we instantiate unintstantiated templates with their symbolic types.
@ -2027,12 +2016,36 @@ void TemplateSimplifier::simplifyTemplates(
// is fixed.
for (int i = 0; i < 2; ++i) {
if (i) {
expandedtemplates.clear();
mTemplateDeclarations.clear();
mTemplateForwardDeclarations.clear();
mTemplateInstantiations.clear();
mInstantiatedTemplates.clear();
}
mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates);
bool hasTemplates = getTemplateDeclarations();
if (i == 0) {
codeWithTemplates = hasTemplates;
if (hasTemplates) {
// There are templates..
// Remove "typename" unless used in template arguments..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (tok->str() == "typename")
tok->deleteThis();
if (Token::simpleMatch(tok, "template <")) {
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
}
}
// Make sure there is something to simplify.
if (mTemplateDeclarations.empty())
return;
// Copy default argument values from forward declaration to declaration
fixForwardDeclaredDefaultArgumentValues();
@ -2045,6 +2058,8 @@ void TemplateSimplifier::simplifyTemplates(
simplifyTemplateAliases();
std::set<std::string> expandedtemplates;
for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) {
// get specializations..
std::list<const Token *> specializations;

View File

@ -123,11 +123,9 @@ public:
private:
/**
* Get template declarations
* @param codeWithTemplates set to true if code has templates
* @param forward declaration or forward declaration
* @return list of template declarations
* @return true if code has templates.
*/
std::list<TokenAndName> getTemplateDeclarations(bool &codeWithTemplates, bool forward = false);
bool getTemplateDeclarations();
/**
* Get template instantiations
@ -264,6 +262,7 @@ private:
ErrorLogger *mErrorLogger;
std::list<TokenAndName> mTemplateDeclarations;
std::list<TokenAndName> mTemplateForwardDeclarations;
std::list<TokenAndName> mTemplateInstantiations;
std::list<TokenAndName> mInstantiatedTemplates;
std::list<TokenAndName> mMemberFunctionsToDelete;

View File

@ -130,6 +130,7 @@ private:
TEST_CASE(template_namespace_8);
TEST_CASE(template_namespace_9);
TEST_CASE(template_namespace_10);
TEST_CASE(template_namespace_11); // #7145
// Test TemplateSimplifier::templateParameters
TEST_CASE(templateParameters);
@ -1974,6 +1975,34 @@ private:
"} ;", tok(code));
}
void template_namespace_11() {// #7145
const char code[] = "namespace MyNamespace {\n"
"class TestClass {\n"
"public:\n"
" TestClass() {\n"
" SomeFunction();\n"
" TemplatedMethod< int >();\n"
" }\n"
" void SomeFunction() { }\n"
"private:\n"
" template< typename T > void TemplatedMethod();\n"
"};\n"
"template< typename T > void TestClass::TemplatedMethod() { }\n"
"}";
ASSERT_EQUALS("namespace MyNamespace { "
"class TestClass { "
"public: "
"TestClass ( ) { "
"SomeFunction ( ) ; "
"TemplatedMethod<int> ( ) ; "
"} "
"void SomeFunction ( ) { } "
"private: "
"template < typename T > void TemplatedMethod ( ) ; "
"} ; "
"} void MyNamespace :: TestClass :: TemplatedMethod<int> ( ) { }", tok(code));
}
unsigned int templateParameters(const char code[]) {
Tokenizer tokenizer(&settings, this);