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() {