From 0a30768b593f35a6e607b9f1ac21f2fc804e2fbe Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Sun, 14 Oct 2018 10:57:07 -0400 Subject: [PATCH] =?UTF-8?q?Fixed=20#8693=20(Template=20specialization:=20C?= =?UTF-8?q?onstructor=20detected=20as=20normal=20=E2=80=A6=20(#1418)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed #8693 (Template specialization: Constructor detected as normal function (functionStatic error)) Refactor template simplifier to remove the existing full specialization function expandSpecialized and allow full specializations to use the existing function expandTemplate. The function expandTemplate was modified to either expand the template like it originally did by copying it or to modify the template in place. Both instantiated and uninstantiated full specializations are modified in place. This also fixes #8692 and probably other related tickets as well. The function simplifyTemplates now tries twice to simplify templates so more templates can be simplified. We should try as many times as necessary to find all possible templates. We can't do that now because uninstantiated templates are left unchanged. It is relatively straight forward to have the new code also expand in place uninstantiated templates with their symbolic types but namespaces are not handled properly (ticket #8671) and it would introduce regressions. * Fix travis warnings. --- lib/templatesimplifier.cpp | 459 +++++++++++++++++++++------------- lib/templatesimplifier.h | 33 +-- lib/token.cpp | 32 +++ lib/token.h | 3 + test/testclass.cpp | 20 ++ test/testsimplifytemplate.cpp | 125 ++++++++- 6 files changed, 478 insertions(+), 194 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index aac15c165..68371b549 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -352,6 +352,20 @@ void TemplateSimplifier::eraseTokens(Token *begin, const Token *end) it->token->hasTemplateSimplifierPointer(false); it->token = nullptr; } + const std::list::iterator it2 = std::find_if(mTemplateDeclarations.begin(), + mTemplateDeclarations.end(), + FindToken(begin->next())); + if (it2 != mTemplateDeclarations.end()) { + // remove the pointer flag and prevent the deleted pointer from being used + it2->token->hasTemplateSimplifierPointer(false); + it2->token = nullptr; + } + for (size_t i = 0; i < mTypesUsedInTemplateInstantiation.size(); ++i) { + if (mTypesUsedInTemplateInstantiation[i] == begin->next()) { + mTypesUsedInTemplateInstantiation[i]->hasTemplateSimplifierPointer(false); + mTypesUsedInTemplateInstantiation[i] = nullptr; + } + } } begin->deleteNext(); } @@ -377,20 +391,20 @@ bool TemplateSimplifier::removeTemplate(Token *tok) if (tok2->str() == "(") { tok2 = tok2->link(); } else if (tok2->str() == ")") { // garbage code! (#3504) - Token::eraseTokens(tok,tok2); + eraseTokens(tok,tok2); deleteToken(tok); return false; } else if (tok2->str() == "{") { tok2 = tok2->link()->next(); - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); if (tok2 && tok2->str() == ";" && tok2->next()) - tok->deleteNext(); + deleteToken(tok->next()); deleteToken(tok); return true; } else if (tok2->str() == "}") { // garbage code! (#3449) - Token::eraseTokens(tok,tok2); + eraseTokens(tok,tok2); deleteToken(tok); return false; } @@ -403,14 +417,14 @@ bool TemplateSimplifier::removeTemplate(Token *tok) if (tok2->str() == "explicit" || (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) { - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } if (tok2->str() == ";") { tok2 = tok2->next(); - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } @@ -423,7 +437,7 @@ bool TemplateSimplifier::removeTemplate(Token *tok) else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) { tok2 = tok2->next(); - Token::eraseTokens(tok, tok2); + eraseTokens(tok, tok2); deleteToken(tok); return true; } @@ -432,64 +446,6 @@ bool TemplateSimplifier::removeTemplate(Token *tok) return false; } -std::set TemplateSimplifier::expandSpecialized() -{ - std::set expandedtemplates; - - // Locate specialized templates.. - for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { - if (!Token::simpleMatch(tok, "template < >")) - continue; - - // what kind of template is this? - Token *tok2 = tok->tokAt(3); - while (tok2 && (tok2->isName() || tok2->str() == "*")) - tok2 = tok2->next(); - - if (!templateParameters(tok2)) - continue; - - // unknown template.. bail out - if (!tok2->previous()->isName()) - continue; - - tok2 = tok2->previous(); - std::string s; - { - std::ostringstream ostr; - const Token *tok3 = tok2; - for (; tok3 && tok3->str() != ">"; tok3 = tok3->next()) { - if (tok3 != tok2) - ostr << " "; - ostr << tok3->str(); - } - if (!Token::Match(tok3, "> (|{|:")) - continue; - s = ostr.str(); - } - - // remove spaces to create new name - const std::string name(s + " >"); - expandedtemplates.insert(name); - - // Rename template.. - Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); - tok2->str(name); - - // delete the "template < >" - tok->deleteNext(2); - tok->deleteThis(); - - // Use this special template in the code.. - while (nullptr != (tok2 = const_cast(Token::findsimplematch(tok2, name.c_str())))) { - Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); - tok2->str(name); - } - } - - return expandedtemplates; -} - /// TODO: This is copy pasted from Tokenizer. We should reuse this code. namespace { struct ScopeInfo2 { @@ -777,7 +733,15 @@ void TemplateSimplifier::useDefaultArgumentValues() if (!tok2) continue; - Token::eraseTokens(eqtok, tok2); + // don't strip args from uninstantiated templates + std::list::iterator ti2 = std::find_if(mTemplateInstantiations.begin(), + mTemplateInstantiations.end(), + FindName(template1.name)); + + if (ti2 == mTemplateInstantiations.end()) + continue; + + eraseTokens(eqtok, tok2); eqtok->deleteThis(); } } @@ -959,6 +923,9 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok) namepos = 2; else if (Token::Match(tok, "> %type% *|&| %type% (")) namepos = 2; + else if (Token::Match(tok, "> %type% %type% <") && + Token::simpleMatch(tok->tokAt(3)->findClosingBracket(), "> (")) + namepos = 2; else if (Token::Match(tok, "> %type% %type% *|&| %type% (")) namepos = 3; else if (getTemplateNamePositionTemplateMember(tok, namepos)) @@ -985,14 +952,14 @@ void TemplateSimplifier::expandTemplate( const std::string &fullName, const std::vector &typeParametersInDeclaration, const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation) + bool copy) { std::list scopeInfo; bool inTemplateDefinition = false; const Token *startOfTemplateDeclaration = nullptr; const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); - for (const Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { + for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { if (Token::Match(tok3, "}|namespace|class|struct|union")) { setScopeInfo(tok3, &scopeInfo); continue; @@ -1034,8 +1001,8 @@ void TemplateSimplifier::expandTemplate( instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; - const Token * tok5 = nullptr; // start of function return type - for (const Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { + Token * tok5 = nullptr; // start of function return type + for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->str() == "template") { istemplate = true; tok5 = prev; @@ -1058,13 +1025,20 @@ void TemplateSimplifier::expandTemplate( while (tok5 && tok5 != tok3) { // replace name if found if (Token::Match(tok5, "%name% <") && tok5->str() == fullName) { - mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); - tok5 = tok5->next()->findClosingBracket(); - } else + if (copy) { + mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); + tok5 = tok5->next()->findClosingBracket(); + } else { + tok5->str(newName); + eraseTokens(tok5, tok5->next()->findClosingBracket()->next()); + } + } else if (copy) mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex()); + tok5 = tok5->next(); } - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + if (copy) + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); while (tok3 && tok3->str() != "::") tok3 = tok3->next(); @@ -1094,7 +1068,7 @@ void TemplateSimplifier::expandTemplate( // replace type with given type.. if (itype < typeParametersInDeclaration.size()) { unsigned int typeindentlevel = 0; - for (const Token *typetok = typesUsedInTemplateInstantiation[itype]; + for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype]; typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (Token::simpleMatch(typetok, ". . .")) { @@ -1105,8 +1079,10 @@ void TemplateSimplifier::expandTemplate( ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; - mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); - mTokenList.back()->isTemplateArg(true); + if (copy) { + mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); + mTokenList.back()->isTemplateArg(true); + } } continue; } @@ -1115,17 +1091,29 @@ void TemplateSimplifier::expandTemplate( // replace name.. if (tok3->str() == lastName) { if (!Token::simpleMatch(tok3->next(), "<")) { - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + if (copy) + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + else if (tok3->strAt(1) != ":") + tok3->str(newName); + else + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); continue; } else if (tok3 == templateDeclarationNameToken) { - mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); - tok3 = tok3->next()->findClosingBracket(); + if (copy) { + mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); + tok3 = tok3->next()->findClosingBracket(); + } else { + tok3->str(newName); + eraseTokens(tok3, tok3->next()->findClosingBracket()->next()); + } continue; } } // copy - mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); + if (copy) + mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); + if (Token::Match(tok3, "%type% <") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { const Token *closingBracket = tok3->next()->findClosingBracket(); if (Token::simpleMatch(closingBracket->next(), "&")) { @@ -1149,32 +1137,34 @@ void TemplateSimplifier::expandTemplate( } // link() newly tokens manually - else if (tok3->str() == "{") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "(") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "[") { - brackets.push(mTokenList.back()); - } else if (tok3->str() == "}") { - assert(brackets.empty() == false && brackets.top()->str() == "{"); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - if (tok3->strAt(1) == ";") { - const Token * tokSemicolon = tok3->next(); - mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); + else if (copy) { + if (tok3->str() == "{") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "(") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "[") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "}") { + assert(brackets.empty() == false && brackets.top()->str() == "{"); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + if (tok3->strAt(1) == ";") { + const Token * tokSemicolon = tok3->next(); + mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); + } + brackets.pop(); + if (brackets.empty()) { + inTemplateDefinition = false; + break; + } + } else if (tok3->str() == ")") { + assert(brackets.empty() == false && brackets.top()->str() == "("); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); + } else if (tok3->str() == "]") { + assert(brackets.empty() == false && brackets.top()->str() == "["); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); } - brackets.pop(); - if (brackets.empty()) { - inTemplateDefinition = false; - break; - } - } else if (tok3->str() == ")") { - assert(brackets.empty() == false && brackets.top()->str() == "("); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - brackets.pop(); - } else if (tok3->str() == "]") { - assert(brackets.empty() == false && brackets.top()->str() == "["); - Token::createMutualLinks(brackets.top(), mTokenList.back()); - brackets.pop(); } } @@ -1541,6 +1531,52 @@ bool TemplateSimplifier::matchSpecialization( return Token::Match(templateDeclarationNameToken, "%name% !!<"); } +std::string TemplateSimplifier::getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation) +{ + std::string typeForNewName; + unsigned int indentlevel = 0; + for (Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { + // #2648 - unhandled parentheses => bail out + // #2721 - unhandled [ => bail out + if (Token::Match(tok3, "(|[")) { + typeForNewName.clear(); + break; + } + if (!tok3->next()) { + typeForNewName.clear(); + break; + } + if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && templateParameters(tok3) > 0) + ++indentlevel; + else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) + --indentlevel; + if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { + tok3->hasTemplateSimplifierPointer(true); + mTypesUsedInTemplateInstantiation.push_back(tok3); + } + const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; + if (!constconst) { + typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); + } + // add additional type information + if (!constconst && !Token::Match(tok3, "class|struct|enum")) { + if (tok3->isUnsigned()) + typeForNewName += "unsigned"; + else if (tok3->isSigned()) + typeForNewName += "signed"; + if (tok3->isLong()) + typeForNewName += "long"; + if (!typeForNewName.empty()) + typeForNewName += ' '; + typeForNewName += tok3->str(); + } + } + + return typeForNewName; +} + bool TemplateSimplifier::simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, @@ -1572,7 +1608,16 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( return false; } - const bool isfunc(tok->strAt(namepos + 1) == "("); + const bool specialized = Token::simpleMatch(templateDeclaration.token, "template < >"); + bool isfunc = false; + + if (tok->strAt(namepos + 1) == "(") + isfunc = true; + else if (tok->strAt(namepos + 1) == "<") { + const Token *tok1 = tok->tokAt(namepos + 1)->findClosingBracket(); + if (tok1 && tok1->strAt(1) == "(") + isfunc = true; + } // locate template usage.. std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); @@ -1613,54 +1658,28 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 const Token *startToken = tok2; - while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) - startToken = startToken->tokAt(-2); + while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || + Token::Match(startToken->tokAt(-2), "> :: %name%")) { + if (startToken->strAt(-2) == ">") { + const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } if (Token::Match(startToken->previous(), "[;{}=]") && - !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")) + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%"))) continue; // New type.. - std::vector typesUsedInTemplateInstantiation; - std::string typeForNewName; + mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; - unsigned int indentlevel = 0; - for (const Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { - // #2648 - unhandled parentheses => bail out - // #2721 - unhandled [ => bail out - if (Token::Match(tok3, "(|[")) { - typeForNewName.clear(); - break; - } - if (!tok3->next()) { - typeForNewName.clear(); - break; - } - if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && templateParameters(tok3) > 0) - ++indentlevel; - else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) - --indentlevel; - if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) - typesUsedInTemplateInstantiation.push_back(tok3); - const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; - if (!constconst) { - typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); - } - // add additional type information - if (!constconst && !Token::Match(tok3, "class|struct|union|enum")) { - if (tok3->isUnsigned()) - typeForNewName += "unsigned"; - else if (tok3->isSigned()) - typeForNewName += "signed"; - if (tok3->isLong()) - typeForNewName += "long"; - if (!typeForNewName.empty()) - typeForNewName += ' '; - typeForNewName += tok3->str(); - } - } + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); - if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { + if (typeForNewName.empty() || (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", @@ -1676,12 +1695,89 @@ bool TemplateSimplifier::simplifyTemplateInstantiations( if (expandedtemplates.find(newName) == expandedtemplates.end()) { expandedtemplates.insert(newName); - expandTemplate(tok, instantiation.name, typeParametersInDeclaration, newName, typesUsedInTemplateInstantiation); + expandTemplate(tok, instantiation.name, typeParametersInDeclaration, newName, !specialized); instantiated = true; } // Replace all these template usages.. - replaceTemplateUsage(tok2, instantiation.name, typeStringsUsedInTemplateInstantiation, newName, typesUsedInTemplateInstantiation); + replaceTemplateUsage(tok2, instantiation.name, typeStringsUsedInTemplateInstantiation, newName); + } + + // process uninstantiated templates + // TODO: remove the specialized check and handle all uninstantiated templates someday. + if (mTemplateInstantiations.empty() && specialized) { + simplifyCalculations(); + + Token * tok2 = const_cast(tok->tokAt(namepos)); + if (mErrorLogger && !mTokenList.getFiles().empty()) + mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); +#ifdef MAXTIME + if (std::time(0) > maxtime) + return false; +#else + (void)maxtime; +#endif + assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 + + Token *startToken = tok2; + while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || + Token::Match(startToken->tokAt(-2), "> :: %name%")) { + if (startToken->strAt(-2) == ">") { + const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } + + if (Token::Match(startToken->previous(), "[;{}=]") && + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%"))) + return false; + + // already simplified + if (!Token::Match(startToken, "%name% <")) + return false; + + if (templateDeclaration.scope.empty()) { + 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; + + tok2 = startToken; + + // New type.. + mTypesUsedInTemplateInstantiation.clear(); + std::list typeStringsUsedInTemplateInstantiation; + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); + + if (typeForNewName.empty()) { + if (printDebug && mErrorLogger) { + std::list callstack(1, tok2); + mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", + "Failed to instantiate template \"" + templateDeclaration.name + "\". The checking continues anyway.", false)); + } + return false; + } + + // New classname/funcname.. + const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); + if (expandedtemplates.find(newName) == expandedtemplates.end()) { + expandedtemplates.insert(newName); + expandTemplate(tok, templateDeclaration.name, typeParametersInDeclaration, newName, !specialized); + instantiated = true; + } + + // Replace all these template usages.. + replaceTemplateUsage(startToken, templateDeclaration.name, typeStringsUsedInTemplateInstantiation, newName); } // Template has been instantiated .. then remove the template declaration @@ -1702,8 +1798,7 @@ static bool matchTemplateParameters(const Token *nameTok, const std::list &typeStringsUsedInTemplateInstantiation, - const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation) + const std::string &newName) { std::list scopeInfo; std::list< std::pair > removeTokens; @@ -1725,7 +1820,7 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, // match parameters Token * tok2 = nameTok->tokAt(2); unsigned int typeCountInInstantiation = 1U; // There is always at least one type - const Token *typetok = (!typesUsedInTemplateInstantiation.empty()) ? typesUsedInTemplateInstantiation[0] : nullptr; + const Token *typetok = (!mTypesUsedInTemplateInstantiation.empty()) ? mTypesUsedInTemplateInstantiation[0] : nullptr; unsigned int indentlevel2 = 0; // indentlevel for tokgt while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) { if (tok2->str() == "<" && templateParameters(tok2) > 0) @@ -1743,8 +1838,8 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, typetok = typetok->next(); } else { - if (typeCountInInstantiation < typesUsedInTemplateInstantiation.size()) - typetok = typesUsedInTemplateInstantiation[typeCountInInstantiation++]; + if (typeCountInInstantiation < mTypesUsedInTemplateInstantiation.size()) + typetok = mTypesUsedInTemplateInstantiation[typeCountInInstantiation++]; else typetok = nullptr; } @@ -1757,7 +1852,7 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, // matching template usage => replace tokens.. // Foo < int > => Foo - if (tok2->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) { + if (tok2->str() == ">" && typeCountInInstantiation == mTypesUsedInTemplateInstantiation.size()) { const Token * const nameTok1 = nameTok; while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%")) nameTok = nameTok->tokAt(-2); @@ -1794,7 +1889,7 @@ void TemplateSimplifier::simplifyTemplates( bool &codeWithTemplates ) { - std::set expandedtemplates(expandSpecialized()); + std::set expandedtemplates; if (getTemplateDeclarations(codeWithTemplates).empty()) return; @@ -1813,25 +1908,31 @@ void TemplateSimplifier::simplifyTemplates( } } - mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates); - - // Locate possible instantiations of templates.. - getTemplateInstantiations(); - - // No template instantiations? Then return. - if (mTemplateInstantiations.empty()) - return; - - // Template arguments with default values - useDefaultArgumentValues(); - - simplifyTemplateAliases(); - // expand templates - //bool done = false; - //while (!done) - { - //done = true; + // 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. + // That will allow the uninstantiated template code to be removed from the symbol database. + // Unfortunately the template simplifier doesn't handle namespaces properly so + // the uninstantiated template code in the symbol database can't be removed until #8768 + // is fixed. + for (int i = 0; i < 2; ++i) { + if (i) { + expandedtemplates.clear(); + mTemplateInstantiations.clear(); + mInstantiatedTemplates.clear(); + } + + mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates); + + // Locate possible instantiations of templates.. + getTemplateInstantiations(); + + // Template arguments with default values + useDefaultArgumentValues(); + + simplifyTemplateAliases(); + for (std::list::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { // get specializations.. std::list specializations; @@ -1860,8 +1961,14 @@ void TemplateSimplifier::simplifyTemplates( break; } if (decl != mTemplateDeclarations.end()) { + if (Token::simpleMatch(it->token, "template < >")) { + // delete the "template < >" + Token * tok = it->token; + tok->deleteNext(2); + tok->deleteThis(); + } else + removeTemplate(it->token); mTemplateDeclarations.erase(decl); - removeTemplate(it->token); } } diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 4a9c4d944..63d93b8e5 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -109,7 +109,7 @@ public: * @return true if modifications to token-list are done. * false if no modifications are done. */ - bool simplifyNumericCalculations(Token *tok); + static bool simplifyNumericCalculations(Token *tok); /** * Simplify constant calculations such as "1+2" => "3". @@ -161,14 +161,14 @@ private: * @param fullName Full name of template * @param typeParametersInDeclaration The type parameters of the template * @param newName New name of class/function. - * @param typesUsedInTemplateInstantiation Type parameters in instantiation + * @param copy copy or expand in place */ void expandTemplate( const Token *templateDeclarationToken, const std::string &fullName, const std::vector &typeParametersInDeclaration, const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation); + bool copy); /** * Replace all matching template usages 'Foo < int >' => 'Foo' @@ -176,19 +176,11 @@ private: * @param templateName full template name with scope info * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param newName The new type name - * @param typesUsedInTemplateInstantiation template instantiation parameters */ void replaceTemplateUsage(Token *const instantiationToken, const std::string &templateName, const std::list &typeStringsUsedInTemplateInstantiation, - const std::string &newName, - const std::vector &typesUsedInTemplateInstantiation); - - /** - * Expand specialized templates : "template<>.." - * @return names of expanded templates - */ - std::set expandSpecialized(); + const std::string &newName); /** * @brief TemplateParametersInDeclaration @@ -199,7 +191,7 @@ private: * @return template < typename T, typename S > * ^ return */ - const Token * getTemplateParametersInDeclaration( + static const Token * getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration); @@ -211,7 +203,7 @@ private: /** Syntax error */ static void syntaxError(const Token *tok); - bool matchSpecialization( + static bool matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations); @@ -228,7 +220,17 @@ private: * tok will be invalidated. * @param tok token to delete */ - void deleteToken(Token *tok); + static void deleteToken(Token *tok); + + /** + * Get the new token name. + * @param tok name token + * @param &typeStringsUsedInTemplateInstantiation type strings use in template instantiation + * @return new token name + */ + std::string getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation); TokenList &mTokenList; const Settings *mSettings; @@ -238,6 +240,7 @@ private: std::list mTemplateInstantiations; std::list mInstantiatedTemplates; std::list mMemberFunctionsToDelete; + std::vector mTypesUsedInTemplateInstantiation; }; /// @} diff --git a/lib/token.cpp b/lib/token.cpp index b12888252..6e3b018f8 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -878,6 +878,38 @@ Token * Token::findClosingBracket() return const_cast(const_cast(this)->findClosingBracket()); } +const Token * Token::findOpeningBracket() const +{ + if (mStr != ">") + return nullptr; + + const Token *opening = nullptr; + + unsigned int depth = 0; + for (opening = this; opening != nullptr; opening = opening->previous()) { + if (Token::Match(opening, "}|]|)")) { + opening = opening->link(); + if (!opening) + return nullptr; + } else if (Token::Match(opening, "{|{|(|;")) + return nullptr; + else if (opening->str() == ">") + ++depth; + else if (opening->str() == "<") { + if (--depth == 0) + return opening; + } + } + + return opening; +} + +Token * Token::findOpeningBracket() +{ + // return value of const function + return const_cast(const_cast(this)->findOpeningBracket()); +} + //--------------------------------------------------------------------------- const Token *Token::findsimplematch(const Token * const startTok, const char pattern[]) diff --git a/lib/token.h b/lib/token.h index 7db1022c4..9cf9d5886 100644 --- a/lib/token.h +++ b/lib/token.h @@ -812,6 +812,9 @@ public: const Token* findClosingBracket() const; Token* findClosingBracket(); + const Token* findOpeningBracket() const; + Token* findOpeningBracket(); + /** * @return the original name. */ diff --git a/test/testclass.cpp b/test/testclass.cpp index b914fc602..816e93a9e 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -171,6 +171,7 @@ private: TEST_CASE(const62); // ticket #5701 TEST_CASE(const63); // ticket #5983 TEST_CASE(const64); // ticket #6268 + TEST_CASE(const65); // ticket #8693 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); @@ -5444,6 +5445,25 @@ private: ASSERT_EQUALS("", errout.str()); } + void const65() { + checkConst("template \n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "template <>\n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "int main() {\n" + " TemplateClass a;\n" + " TemplateClass b;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n" diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 5d4dad52d..f3c29e042 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -127,7 +127,9 @@ private: TEST_CASE(templateNamePosition); - TEST_CASE(expandSpecialized); + TEST_CASE(expandSpecialized1); + TEST_CASE(expandSpecialized2); + TEST_CASE(expandSpecialized3); // #8671 TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); @@ -1100,7 +1102,7 @@ private: "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " - "struct Factorial<1> { enum FacHelper { value = Factorial < 0 > :: value } ; } ;"; + "struct Factorial<1> { enum FacHelper { value = Factorial<0> :: value } ; } ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -1694,9 +1696,126 @@ private: "template<> unsigned A >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); } - void expandSpecialized() { + void expandSpecialized1() { ASSERT_EQUALS("class A { } ;", tok("template<> class A {};")); ASSERT_EQUALS("class A : public B { } ;", tok("template<> class A : public B {};")); + ASSERT_EQUALS("class A { A ( ) ; ~ A ( ) ; } ;", tok("template<> class A { A(); ~A(); };")); + ASSERT_EQUALS("class A { A ( ) { } ~ A ( ) { } } ;", tok("template<> class A { A() {} ~A() {} };")); + ASSERT_EQUALS("class A { A ( ) ; ~ A ( ) ; } ; A :: A ( ) { } ~ A :: A ( ) { }", + tok("template<> class A { A(); ~A(); }; A::A() { } ~A::A() {}")); + ASSERT_EQUALS("class A { A ( ) ; A ( const A & ) ; A foo ( ) ; } ; A :: A ( ) { } A :: A ( const A & ) { } A A :: foo ( ) { A a ; return a ; }", + tok("template<> class A { A(); A(const A &) ; A foo(); }; A::A() { } A::A(const A &) { } A A::foo() { A a; return a; }")); + } + + void expandSpecialized2() { + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C() { }\n" + " C(const C &) { }\n" + " ~C() { }\n" + " C & operator=(const C &) { return *this; }\n" + "};\n" + "C b;\n"; + const char expected[] = "class C { " + "public: " + "C ( ) { } " + "C ( const C & ) { } " + "~ C ( ) { } " + "C & operator= ( const C & ) { return * this ; } " + "} ; " + "C b ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C() { }\n" + " C(const C &) { }\n" + " ~C() { }\n" + " C & operator=(const C &) { return *this; }\n" + "};"; + const char expected[] = "class C { " + "public: " + "C ( ) { } " + "C ( const C & ) { } " + "~ C ( ) { } " + "C & operator= ( const C & ) { return * this ; } " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C();\n" + " C(const C &);\n" + " ~C();\n" + " C & operator=(const C &);\n" + "};\n" + "C::C() { }\n" + "C::C(const C &) { }\n" + "C::~C() { }\n" + "C & C::operator=(const C &) { return *this; }\n" + "C b;\n"; + const char expected[] = "class C { " + "public: " + "C ( ) ; " + "C ( const C & ) ; " + "~ C ( ) ; " + "C & operator= ( const C & ) ; " + "} ; " + "C :: C ( ) { } " + "C :: C ( const C & ) { } " + "C :: ~ C ( ) { } " + "C & C :: operator= ( const C & ) { return * this ; } " + "C b ;"; + ASSERT_EQUALS(expected, tok(code)); + } + { + const char code[] = "template <>\n" + "class C {\n" + "public:\n" + " C();\n" + " C(const C &);\n" + " ~C();\n" + " C & operator=(const C &);\n" + "};\n" + "C::C() { }\n" + "C::C(const C &) { }\n" + "C::~C() { }\n" + "C & C::operator=(const C &) { return *this; }"; + const char expected[] = "class C { " + "public: " + "C ( ) ; " + "C ( const C & ) ; " + "~ C ( ) ; " + "C & operator= ( const C & ) ; " + "} ; " + "C :: C ( ) { } " + "C :: C ( const C & ) { } " + "C :: ~ C ( ) { } " + "C & C :: operator= ( const C & ) { return * this ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + } + + void expandSpecialized3() { // #8671 + const char code[] = "template <> struct OutputU16 final {\n" + " explicit OutputU16(std::basic_ostream &t) : outputStream_(t) {}\n" + " void operator()(unsigned short) const;\n" + "private:\n" + " std::basic_ostream &outputStream_;\n" + "};"; + const char expected[] = "struct OutputU16 final { " + "explicit OutputU16 ( std :: basic_ostream < char > & t ) : outputStream_ ( t ) { } " + "void operator() ( short ) const ; " + "private: " + "std :: basic_ostream < char > & outputStream_ ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); } void templateAlias1() {