Fixed #8693 (Template specialization: Constructor detected as normal … (#1418)

* 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.
This commit is contained in:
IOBYTE 2018-10-14 10:57:07 -04:00 committed by Daniel Marjamäki
parent 12c81ac0bf
commit 0a30768b59
6 changed files with 478 additions and 194 deletions

View File

@ -352,6 +352,20 @@ void TemplateSimplifier::eraseTokens(Token *begin, const Token *end)
it->token->hasTemplateSimplifierPointer(false); it->token->hasTemplateSimplifierPointer(false);
it->token = nullptr; it->token = nullptr;
} }
const std::list<TokenAndName>::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(); begin->deleteNext();
} }
@ -377,20 +391,20 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
if (tok2->str() == "(") { if (tok2->str() == "(") {
tok2 = tok2->link(); tok2 = tok2->link();
} else if (tok2->str() == ")") { // garbage code! (#3504) } else if (tok2->str() == ")") { // garbage code! (#3504)
Token::eraseTokens(tok,tok2); eraseTokens(tok,tok2);
deleteToken(tok); deleteToken(tok);
return false; return false;
} }
else if (tok2->str() == "{") { else if (tok2->str() == "{") {
tok2 = tok2->link()->next(); tok2 = tok2->link()->next();
Token::eraseTokens(tok, tok2); eraseTokens(tok, tok2);
if (tok2 && tok2->str() == ";" && tok2->next()) if (tok2 && tok2->str() == ";" && tok2->next())
tok->deleteNext(); deleteToken(tok->next());
deleteToken(tok); deleteToken(tok);
return true; return true;
} else if (tok2->str() == "}") { // garbage code! (#3449) } else if (tok2->str() == "}") { // garbage code! (#3449)
Token::eraseTokens(tok,tok2); eraseTokens(tok,tok2);
deleteToken(tok); deleteToken(tok);
return false; return false;
} }
@ -403,14 +417,14 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
if (tok2->str() == "explicit" || if (tok2->str() == "explicit" ||
(countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") &&
Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) { Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) {
Token::eraseTokens(tok, tok2); eraseTokens(tok, tok2);
deleteToken(tok); deleteToken(tok);
return true; return true;
} }
if (tok2->str() == ";") { if (tok2->str() == ";") {
tok2 = tok2->next(); tok2 = tok2->next();
Token::eraseTokens(tok, tok2); eraseTokens(tok, tok2);
deleteToken(tok); deleteToken(tok);
return true; return true;
} }
@ -423,7 +437,7 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) { else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) {
tok2 = tok2->next(); tok2 = tok2->next();
Token::eraseTokens(tok, tok2); eraseTokens(tok, tok2);
deleteToken(tok); deleteToken(tok);
return true; return true;
} }
@ -432,64 +446,6 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
return false; return false;
} }
std::set<std::string> TemplateSimplifier::expandSpecialized()
{
std::set<std::string> 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 *>(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. /// TODO: This is copy pasted from Tokenizer. We should reuse this code.
namespace { namespace {
struct ScopeInfo2 { struct ScopeInfo2 {
@ -777,7 +733,15 @@ void TemplateSimplifier::useDefaultArgumentValues()
if (!tok2) if (!tok2)
continue; continue;
Token::eraseTokens(eqtok, tok2); // don't strip args from uninstantiated templates
std::list<TokenAndName>::iterator ti2 = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindName(template1.name));
if (ti2 == mTemplateInstantiations.end())
continue;
eraseTokens(eqtok, tok2);
eqtok->deleteThis(); eqtok->deleteThis();
} }
} }
@ -959,6 +923,9 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
namepos = 2; namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% (")) else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2; 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% (")) else if (Token::Match(tok, "> %type% %type% *|&| %type% ("))
namepos = 3; namepos = 3;
else if (getTemplateNamePositionTemplateMember(tok, namepos)) else if (getTemplateNamePositionTemplateMember(tok, namepos))
@ -985,14 +952,14 @@ void TemplateSimplifier::expandTemplate(
const std::string &fullName, const std::string &fullName,
const std::vector<const Token *> &typeParametersInDeclaration, const std::vector<const Token *> &typeParametersInDeclaration,
const std::string &newName, const std::string &newName,
const std::vector<const Token *> &typesUsedInTemplateInstantiation) bool copy)
{ {
std::list<ScopeInfo2> scopeInfo; std::list<ScopeInfo2> scopeInfo;
bool inTemplateDefinition = false; bool inTemplateDefinition = false;
const Token *startOfTemplateDeclaration = nullptr; const Token *startOfTemplateDeclaration = nullptr;
const Token *endOfTemplateDefinition = nullptr; const Token *endOfTemplateDefinition = nullptr;
const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); 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")) { if (Token::Match(tok3, "}|namespace|class|struct|union")) {
setScopeInfo(tok3, &scopeInfo); setScopeInfo(tok3, &scopeInfo);
continue; continue;
@ -1034,8 +1001,8 @@ void TemplateSimplifier::expandTemplate(
instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) {
// there must be template.. // there must be template..
bool istemplate = false; bool istemplate = false;
const Token * tok5 = nullptr; // start of function return type Token * tok5 = nullptr; // start of function return type
for (const Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) {
if (prev->str() == "template") { if (prev->str() == "template") {
istemplate = true; istemplate = true;
tok5 = prev; tok5 = prev;
@ -1058,12 +1025,19 @@ void TemplateSimplifier::expandTemplate(
while (tok5 && tok5 != tok3) { while (tok5 && tok5 != tok3) {
// replace name if found // replace name if found
if (Token::Match(tok5, "%name% <") && tok5->str() == fullName) { if (Token::Match(tok5, "%name% <") && tok5->str() == fullName) {
if (copy) {
mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex());
tok5 = tok5->next()->findClosingBracket(); tok5 = tok5->next()->findClosingBracket();
} else } else {
tok5->str(newName);
eraseTokens(tok5, tok5->next()->findClosingBracket()->next());
}
} else if (copy)
mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex()); mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex());
tok5 = tok5->next(); tok5 = tok5->next();
} }
if (copy)
mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
while (tok3 && tok3->str() != "::") while (tok3 && tok3->str() != "::")
tok3 = tok3->next(); tok3 = tok3->next();
@ -1094,7 +1068,7 @@ void TemplateSimplifier::expandTemplate(
// replace type with given type.. // replace type with given type..
if (itype < typeParametersInDeclaration.size()) { if (itype < typeParametersInDeclaration.size()) {
unsigned int typeindentlevel = 0; unsigned int typeindentlevel = 0;
for (const Token *typetok = typesUsedInTemplateInstantiation[itype]; for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype];
typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>"));
typetok = typetok->next()) { typetok = typetok->next()) {
if (Token::simpleMatch(typetok, ". . .")) { if (Token::simpleMatch(typetok, ". . .")) {
@ -1105,9 +1079,11 @@ void TemplateSimplifier::expandTemplate(
++typeindentlevel; ++typeindentlevel;
else if (typeindentlevel > 0 && typetok->str() == ">") else if (typeindentlevel > 0 && typetok->str() == ">")
--typeindentlevel; --typeindentlevel;
if (copy) {
mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex());
mTokenList.back()->isTemplateArg(true); mTokenList.back()->isTemplateArg(true);
} }
}
continue; continue;
} }
} }
@ -1115,17 +1091,29 @@ void TemplateSimplifier::expandTemplate(
// replace name.. // replace name..
if (tok3->str() == lastName) { if (tok3->str() == lastName) {
if (!Token::simpleMatch(tok3->next(), "<")) { if (!Token::simpleMatch(tok3->next(), "<")) {
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()); mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
continue; continue;
} else if (tok3 == templateDeclarationNameToken) { } else if (tok3 == templateDeclarationNameToken) {
if (copy) {
mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
tok3 = tok3->next()->findClosingBracket(); tok3 = tok3->next()->findClosingBracket();
} else {
tok3->str(newName);
eraseTokens(tok3, tok3->next()->findClosingBracket()->next());
}
continue; continue;
} }
} }
// copy // copy
if (copy)
mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { if (Token::Match(tok3, "%type% <") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) {
const Token *closingBracket = tok3->next()->findClosingBracket(); const Token *closingBracket = tok3->next()->findClosingBracket();
if (Token::simpleMatch(closingBracket->next(), "&")) { if (Token::simpleMatch(closingBracket->next(), "&")) {
@ -1149,7 +1137,8 @@ void TemplateSimplifier::expandTemplate(
} }
// link() newly tokens manually // link() newly tokens manually
else if (tok3->str() == "{") { else if (copy) {
if (tok3->str() == "{") {
brackets.push(mTokenList.back()); brackets.push(mTokenList.back());
} else if (tok3->str() == "(") { } else if (tok3->str() == "(") {
brackets.push(mTokenList.back()); brackets.push(mTokenList.back());
@ -1177,6 +1166,7 @@ void TemplateSimplifier::expandTemplate(
brackets.pop(); brackets.pop();
} }
} }
}
assert(brackets.empty()); assert(brackets.empty());
} }
@ -1541,6 +1531,52 @@ bool TemplateSimplifier::matchSpecialization(
return Token::Match(templateDeclarationNameToken, "%name% !!<"); return Token::Match(templateDeclarationNameToken, "%name% !!<");
} }
std::string TemplateSimplifier::getNewName(
Token *tok2,
std::list<std::string> &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( bool TemplateSimplifier::simplifyTemplateInstantiations(
const TokenAndName &templateDeclaration, const TokenAndName &templateDeclaration,
const std::list<const Token *> &specializations, const std::list<const Token *> &specializations,
@ -1572,7 +1608,16 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
return false; 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.. // locate template usage..
std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); 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 assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021
const Token *startToken = tok2; const Token *startToken = tok2;
while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) 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); startToken = startToken->tokAt(-2);
}
if (Token::Match(startToken->previous(), "[;{}=]") && if (Token::Match(startToken->previous(), "[;{}=]") &&
!instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")) (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")))
continue; continue;
// New type.. // New type..
std::vector<const Token *> typesUsedInTemplateInstantiation; mTypesUsedInTemplateInstantiation.clear();
std::string typeForNewName;
std::list<std::string> typeStringsUsedInTemplateInstantiation; std::list<std::string> typeStringsUsedInTemplateInstantiation;
unsigned int indentlevel = 0; std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation);
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();
}
}
if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { if (typeForNewName.empty() || (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) {
if (printDebug && mErrorLogger) { if (printDebug && mErrorLogger) {
std::list<const Token *> callstack(1, tok2); std::list<const Token *> callstack(1, tok2);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
@ -1676,12 +1695,89 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
if (expandedtemplates.find(newName) == expandedtemplates.end()) { if (expandedtemplates.find(newName) == expandedtemplates.end()) {
expandedtemplates.insert(newName); expandedtemplates.insert(newName);
expandTemplate(tok, instantiation.name, typeParametersInDeclaration, newName, typesUsedInTemplateInstantiation); expandTemplate(tok, instantiation.name, typeParametersInDeclaration, newName, !specialized);
instantiated = true; instantiated = true;
} }
// Replace all these template usages.. // 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<Token *>(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<std::string> typeStringsUsedInTemplateInstantiation;
std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation);
if (typeForNewName.empty()) {
if (printDebug && mErrorLogger) {
std::list<const Token *> 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 // Template has been instantiated .. then remove the template declaration
@ -1702,8 +1798,7 @@ static bool matchTemplateParameters(const Token *nameTok, const std::list<std::s
void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
const std::string &templateName, const std::string &templateName,
const std::list<std::string> &typeStringsUsedInTemplateInstantiation, const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
const std::string &newName, const std::string &newName)
const std::vector<const Token *> &typesUsedInTemplateInstantiation)
{ {
std::list<ScopeInfo2> scopeInfo; std::list<ScopeInfo2> scopeInfo;
std::list< std::pair<Token *, Token *> > removeTokens; std::list< std::pair<Token *, Token *> > removeTokens;
@ -1725,7 +1820,7 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
// match parameters // match parameters
Token * tok2 = nameTok->tokAt(2); Token * tok2 = nameTok->tokAt(2);
unsigned int typeCountInInstantiation = 1U; // There is always at least one type 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 unsigned int indentlevel2 = 0; // indentlevel for tokgt
while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) { while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) {
if (tok2->str() == "<" && templateParameters(tok2) > 0) if (tok2->str() == "<" && templateParameters(tok2) > 0)
@ -1743,8 +1838,8 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
typetok = typetok->next(); typetok = typetok->next();
} else { } else {
if (typeCountInInstantiation < typesUsedInTemplateInstantiation.size()) if (typeCountInInstantiation < mTypesUsedInTemplateInstantiation.size())
typetok = typesUsedInTemplateInstantiation[typeCountInInstantiation++]; typetok = mTypesUsedInTemplateInstantiation[typeCountInInstantiation++];
else else
typetok = nullptr; typetok = nullptr;
} }
@ -1757,7 +1852,7 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
// matching template usage => replace tokens.. // matching template usage => replace tokens..
// Foo < int > => Foo<int> // Foo < int > => Foo<int>
if (tok2->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) { if (tok2->str() == ">" && typeCountInInstantiation == mTypesUsedInTemplateInstantiation.size()) {
const Token * const nameTok1 = nameTok; const Token * const nameTok1 = nameTok;
while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%")) while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%"))
nameTok = nameTok->tokAt(-2); nameTok = nameTok->tokAt(-2);
@ -1794,7 +1889,7 @@ void TemplateSimplifier::simplifyTemplates(
bool &codeWithTemplates bool &codeWithTemplates
) )
{ {
std::set<std::string> expandedtemplates(expandSpecialized()); std::set<std::string> expandedtemplates;
if (getTemplateDeclarations(codeWithTemplates).empty()) if (getTemplateDeclarations(codeWithTemplates).empty())
return; return;
@ -1813,25 +1908,31 @@ void TemplateSimplifier::simplifyTemplates(
} }
} }
// 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.
// 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); mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates);
// Locate possible instantiations of templates.. // Locate possible instantiations of templates..
getTemplateInstantiations(); getTemplateInstantiations();
// No template instantiations? Then return.
if (mTemplateInstantiations.empty())
return;
// Template arguments with default values // Template arguments with default values
useDefaultArgumentValues(); useDefaultArgumentValues();
simplifyTemplateAliases(); simplifyTemplateAliases();
// expand templates
//bool done = false;
//while (!done)
{
//done = true;
for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) {
// get specializations.. // get specializations..
std::list<const Token *> specializations; std::list<const Token *> specializations;
@ -1860,8 +1961,14 @@ void TemplateSimplifier::simplifyTemplates(
break; break;
} }
if (decl != mTemplateDeclarations.end()) { if (decl != mTemplateDeclarations.end()) {
mTemplateDeclarations.erase(decl); if (Token::simpleMatch(it->token, "template < >")) {
// delete the "template < >"
Token * tok = it->token;
tok->deleteNext(2);
tok->deleteThis();
} else
removeTemplate(it->token); removeTemplate(it->token);
mTemplateDeclarations.erase(decl);
} }
} }

View File

@ -109,7 +109,7 @@ public:
* @return true if modifications to token-list are done. * @return true if modifications to token-list are done.
* false if no modifications 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". * Simplify constant calculations such as "1+2" => "3".
@ -161,14 +161,14 @@ private:
* @param fullName Full name of template * @param fullName Full name of template
* @param typeParametersInDeclaration The type parameters of the template * @param typeParametersInDeclaration The type parameters of the template
* @param newName New name of class/function. * @param newName New name of class/function.
* @param typesUsedInTemplateInstantiation Type parameters in instantiation * @param copy copy or expand in place
*/ */
void expandTemplate( void expandTemplate(
const Token *templateDeclarationToken, const Token *templateDeclarationToken,
const std::string &fullName, const std::string &fullName,
const std::vector<const Token *> &typeParametersInDeclaration, const std::vector<const Token *> &typeParametersInDeclaration,
const std::string &newName, const std::string &newName,
const std::vector<const Token *> &typesUsedInTemplateInstantiation); bool copy);
/** /**
* Replace all matching template usages 'Foo < int >' => 'Foo<int>' * Replace all matching template usages 'Foo < int >' => 'Foo<int>'
@ -176,19 +176,11 @@ private:
* @param templateName full template name with scope info * @param templateName full template name with scope info
* @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings.
* @param newName The new type name * @param newName The new type name
* @param typesUsedInTemplateInstantiation template instantiation parameters
*/ */
void replaceTemplateUsage(Token *const instantiationToken, void replaceTemplateUsage(Token *const instantiationToken,
const std::string &templateName, const std::string &templateName,
const std::list<std::string> &typeStringsUsedInTemplateInstantiation, const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
const std::string &newName, const std::string &newName);
const std::vector<const Token *> &typesUsedInTemplateInstantiation);
/**
* Expand specialized templates : "template<>.."
* @return names of expanded templates
*/
std::set<std::string> expandSpecialized();
/** /**
* @brief TemplateParametersInDeclaration * @brief TemplateParametersInDeclaration
@ -199,7 +191,7 @@ private:
* @return template < typename T, typename S > * @return template < typename T, typename S >
* ^ return * ^ return
*/ */
const Token * getTemplateParametersInDeclaration( static const Token * getTemplateParametersInDeclaration(
const Token * tok, const Token * tok,
std::vector<const Token *> & typeParametersInDeclaration); std::vector<const Token *> & typeParametersInDeclaration);
@ -211,7 +203,7 @@ private:
/** Syntax error */ /** Syntax error */
static void syntaxError(const Token *tok); static void syntaxError(const Token *tok);
bool matchSpecialization( static bool matchSpecialization(
const Token *templateDeclarationNameToken, const Token *templateDeclarationNameToken,
const Token *templateInstantiationNameToken, const Token *templateInstantiationNameToken,
const std::list<const Token *> & specializations); const std::list<const Token *> & specializations);
@ -228,7 +220,17 @@ private:
* tok will be invalidated. * tok will be invalidated.
* @param tok token to delete * @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<std::string> &typeStringsUsedInTemplateInstantiation);
TokenList &mTokenList; TokenList &mTokenList;
const Settings *mSettings; const Settings *mSettings;
@ -238,6 +240,7 @@ private:
std::list<TokenAndName> mTemplateInstantiations; std::list<TokenAndName> mTemplateInstantiations;
std::list<TokenAndName> mInstantiatedTemplates; std::list<TokenAndName> mInstantiatedTemplates;
std::list<TokenAndName> mMemberFunctionsToDelete; std::list<TokenAndName> mMemberFunctionsToDelete;
std::vector<Token *> mTypesUsedInTemplateInstantiation;
}; };
/// @} /// @}

View File

@ -878,6 +878,38 @@ Token * Token::findClosingBracket()
return const_cast<Token*>(const_cast<const Token*>(this)->findClosingBracket()); return const_cast<Token*>(const_cast<const Token*>(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<Token*>(const_cast<const Token*>(this)->findOpeningBracket());
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const Token *Token::findsimplematch(const Token * const startTok, const char pattern[]) const Token *Token::findsimplematch(const Token * const startTok, const char pattern[])

View File

@ -812,6 +812,9 @@ public:
const Token* findClosingBracket() const; const Token* findClosingBracket() const;
Token* findClosingBracket(); Token* findClosingBracket();
const Token* findOpeningBracket() const;
Token* findOpeningBracket();
/** /**
* @return the original name. * @return the original name.
*/ */

View File

@ -171,6 +171,7 @@ private:
TEST_CASE(const62); // ticket #5701 TEST_CASE(const62); // ticket #5701
TEST_CASE(const63); // ticket #5983 TEST_CASE(const63); // ticket #5983
TEST_CASE(const64); // ticket #6268 TEST_CASE(const64); // ticket #6268
TEST_CASE(const65); // ticket #8693
TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_handleDefaultParameters);
TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(const_passThisToMemberOfOtherClass);
TEST_CASE(assigningPointerToPointerIsNotAConstOperation); TEST_CASE(assigningPointerToPointerIsNotAConstOperation);
@ -5444,6 +5445,25 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void const65() {
checkConst("template <typename T>\n"
"class TemplateClass {\n"
"public:\n"
" TemplateClass() { }\n"
"};\n"
"template <>\n"
"class TemplateClass<float> {\n"
"public:\n"
" TemplateClass() { }\n"
"};\n"
"int main() {\n"
" TemplateClass<int> a;\n"
" TemplateClass<float> b;\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void const_handleDefaultParameters() { void const_handleDefaultParameters() {
checkConst("struct Foo {\n" checkConst("struct Foo {\n"
" void foo1(int i, int j = 0) {\n" " void foo1(int i, int j = 0) {\n"

View File

@ -127,7 +127,9 @@ private:
TEST_CASE(templateNamePosition); TEST_CASE(templateNamePosition);
TEST_CASE(expandSpecialized); TEST_CASE(expandSpecialized1);
TEST_CASE(expandSpecialized2);
TEST_CASE(expandSpecialized3); // #8671
TEST_CASE(templateAlias1); TEST_CASE(templateAlias1);
TEST_CASE(templateAlias2); TEST_CASE(templateAlias2);
@ -1694,9 +1696,126 @@ private:
"template<> unsigned A<int, v<char> >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); "template<> unsigned A<int, v<char> >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true));
} }
void expandSpecialized() { void expandSpecialized1() {
ASSERT_EQUALS("class A<int> { } ;", tok("template<> class A<int> {};")); ASSERT_EQUALS("class A<int> { } ;", tok("template<> class A<int> {};"));
ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};")); ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};"));
ASSERT_EQUALS("class A<int> { A<int> ( ) ; ~ A<int> ( ) ; } ;", tok("template<> class A<int> { A(); ~A(); };"));
ASSERT_EQUALS("class A<int> { A<int> ( ) { } ~ A<int> ( ) { } } ;", tok("template<> class A<int> { A() {} ~A() {} };"));
ASSERT_EQUALS("class A<int> { A<int> ( ) ; ~ A<int> ( ) ; } ; A<int> :: A<int> ( ) { } ~ A<int> :: A<int> ( ) { }",
tok("template<> class A<int> { A(); ~A(); }; A<int>::A() { } ~A<int>::A() {}"));
ASSERT_EQUALS("class A<int> { A<int> ( ) ; A<int> ( const A<int> & ) ; A<int> foo ( ) ; } ; A<int> :: A<int> ( ) { } A<int> :: A<int> ( const A<int> & ) { } A<int> A<int> :: foo ( ) { A<int> a ; return a ; }",
tok("template<> class A<int> { A(); A(const A &) ; A foo(); }; A<int>::A() { } A<int>::A(const A &) { } A<int> A<int>::foo() { A a; return a; }"));
}
void expandSpecialized2() {
{
const char code[] = "template <>\n"
"class C<float> {\n"
"public:\n"
" C() { }\n"
" C(const C &) { }\n"
" ~C() { }\n"
" C & operator=(const C &) { return *this; }\n"
"};\n"
"C<float> b;\n";
const char expected[] = "class C<float> { "
"public: "
"C<float> ( ) { } "
"C<float> ( const C<float> & ) { } "
"~ C<float> ( ) { } "
"C<float> & operator= ( const C<float> & ) { return * this ; } "
"} ; "
"C<float> b ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template <>\n"
"class C<float> {\n"
"public:\n"
" C() { }\n"
" C(const C &) { }\n"
" ~C() { }\n"
" C & operator=(const C &) { return *this; }\n"
"};";
const char expected[] = "class C<float> { "
"public: "
"C<float> ( ) { } "
"C<float> ( const C<float> & ) { } "
"~ C<float> ( ) { } "
"C<float> & operator= ( const C<float> & ) { return * this ; } "
"} ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template <>\n"
"class C<float> {\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<float> b;\n";
const char expected[] = "class C<float> { "
"public: "
"C<float> ( ) ; "
"C<float> ( const C<float> & ) ; "
"~ C<float> ( ) ; "
"C<float> & operator= ( const C<float> & ) ; "
"} ; "
"C<float> :: C<float> ( ) { } "
"C<float> :: C<float> ( const C<float> & ) { } "
"C<float> :: ~ C<float> ( ) { } "
"C<float> & C<float> :: operator= ( const C<float> & ) { return * this ; } "
"C<float> b ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template <>\n"
"class C<float> {\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<float> { "
"public: "
"C<float> ( ) ; "
"C<float> ( const C<float> & ) ; "
"~ C<float> ( ) ; "
"C<float> & operator= ( const C<float> & ) ; "
"} ; "
"C<float> :: C<float> ( ) { } "
"C<float> :: C<float> ( const C<float> & ) { } "
"C<float> :: ~ C<float> ( ) { } "
"C<float> & C<float> :: operator= ( const C<float> & ) { return * this ; }";
ASSERT_EQUALS(expected, tok(code));
}
}
void expandSpecialized3() { // #8671
const char code[] = "template <> struct OutputU16<unsigned char> final {\n"
" explicit OutputU16(std::basic_ostream<unsigned char> &t) : outputStream_(t) {}\n"
" void operator()(unsigned short) const;\n"
"private:\n"
" std::basic_ostream<unsigned char> &outputStream_;\n"
"};";
const char expected[] = "struct OutputU16<unsignedchar> final { "
"explicit OutputU16<unsignedchar> ( std :: basic_ostream < char > & t ) : outputStream_ ( t ) { } "
"void operator() ( short ) const ; "
"private: "
"std :: basic_ostream < char > & outputStream_ ; "
"} ;";
ASSERT_EQUALS(expected, tok(code));
} }
void templateAlias1() { void templateAlias1() {