TemplateSimplifier: Improved handling of scopes

This commit is contained in:
Daniel Marjamäki 2017-12-26 10:55:18 +01:00
parent 5a642ac35e
commit 2c69f2b226
3 changed files with 270 additions and 135 deletions

View File

@ -31,38 +31,6 @@
#include <stack> #include <stack>
#include <utility> #include <utility>
#ifdef GDB_HELPERS
static void printlist(const std::list<Token *> &list)
{
for (std::list<Token *>::const_iterator it = list.begin(); it != list.end(); ++it) {
const Token *token = *it;
std::cout << " ";
while (token && !Token::Match(token, "[{};]")) {
std::cout << " " << token->str();
token = token->next();
}
std::cout << std::endl;
}
}
static void printvector(const std::vector<const Token *> &v)
{
for (std::size_t i = 0; i < v.size(); i++) {
const Token *token = v[i];
std::cout << " " << i << ":";
while (token && !Token::Match(token, "[{};]")) {
std::cout << " " << token->str();
token = token->next();
}
std::cout << std::endl;
}
}
#endif
//---------------------------------------------------------------------------
void TemplateSimplifier::cleanupAfterSimplify(Token *tokens) void TemplateSimplifier::cleanupAfterSimplify(Token *tokens)
{ {
bool goback = false; bool goback = false;
@ -455,14 +423,54 @@ std::set<std::string> TemplateSimplifier::expandSpecialized(Token *tokens)
return expandedtemplates; return expandedtemplates;
} }
std::list<Token *> TemplateSimplifier::getTemplateDeclarations(Token *tokens, bool &codeWithTemplates) /// TODO: This is copy pasted from Tokenizer. We should reuse this code.
namespace {
struct ScopeInfo2 {
ScopeInfo2(const std::string &name_, const Token *classEnd_) : name(name_), classEnd(classEnd_) {}
const std::string name;
const Token * const classEnd;
};
}
static std::string getScopeName(const std::list<ScopeInfo2> &scopeInfo)
{ {
std::list<Token *> templates; std::string ret;
for (Token *tok = tokens; tok; tok = tok->next()) { for (std::list<ScopeInfo2>::const_iterator it = scopeInfo.begin(); it != scopeInfo.end(); ++it)
// TODO: handle namespaces. Right now we don't instantiate templates that are defined in namespaces. ret += (ret.empty() ? "" : " :: ") + (it->name);
if (Token::Match(tok, "namespace %type% {")) return ret;
tok = tok->linkAt(2); }
static std::string getFullName(const std::list<ScopeInfo2> &scopeInfo, const std::string &name)
{
const std::string &scopeName = getScopeName(scopeInfo);
return scopeName + (scopeName.empty() ? "" : " :: ") + name;
}
static void setScopeInfo(const Token *tok, std::list<ScopeInfo2> *scopeInfo)
{
while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().classEnd)
scopeInfo->pop_back();
if (!Token::Match(tok, "namespace|class|struct %name% {|:|::"))
return;
tok = tok->next();
std::string classname = tok->str();
while (Token::Match(tok, "%name% :: %name%")) {
tok = tok->tokAt(2);
classname += " :: " + tok->str();
}
tok = tok->next();
if (tok && tok->str() == ":") {
// ...
}
if (tok && tok->str() == "{") {
scopeInfo->push_back(ScopeInfo2(classname,tok->link()));
}
}
std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDeclarations(Token *tokens, bool &codeWithTemplates)
{
std::list<ScopeInfo2> scopeInfo;
std::list<TokenAndName> declarations;
for (Token *tok = tokens; tok; tok = tok->next()) {
setScopeInfo(tok, &scopeInfo);
if (Token::simpleMatch(tok, "template <")) { if (Token::simpleMatch(tok, "template <")) {
// Some syntax checks, see #6865 // Some syntax checks, see #6865
if (!tok->tokAt(2)) if (!tok->tokAt(2))
@ -472,36 +480,35 @@ std::list<Token *> TemplateSimplifier::getTemplateDeclarations(Token *tokens, bo
syntaxError(tok->next()); syntaxError(tok->next());
codeWithTemplates = true; codeWithTemplates = true;
Token *parmEnd = tok->next()->findClosingBracket(); Token *parmEnd = tok->next()->findClosingBracket();
int indentlevel = 0;
for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) {
if (tok2->str() == "(") if (tok2->str() == "(")
++indentlevel; tok2 = tok2->link();
else if (tok2->str() == ")") else if (tok2->str() == ")")
--indentlevel; break;
if (indentlevel) // In an argument list; move to the next token
continue;
// Just a declaration => ignore this // Just a declaration => ignore this
if (tok2->str() == ";") else if (tok2->str() == ";")
break; break;
// Implementation => add to "templates" // Implementation => add to "templates"
if (tok2->str() == "{") { else if (tok2->str() == "{") {
templates.push_back(tok); int namepos = getTemplateNamePosition(parmEnd);
if (namepos > 0)
declarations.push_back(TokenAndName(tok, getFullName(scopeInfo, parmEnd->strAt(namepos))));
break; break;
} }
} }
} }
} }
return templates; return declarations;
} }
std::list<Token *> TemplateSimplifier::getTemplateInstantiations(Token *tokens) std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateInstantiations(Token *tokens)
{ {
std::list<Token *> used; std::list<TokenAndName> instantiations;
std::list<ScopeInfo2> scopeList;
for (Token *tok = tokens; tok; tok = tok->next()) { for (Token *tok = tokens; tok; tok = tok->next()) {
setScopeInfo(tok, &scopeList);
// template definition.. skip it // template definition.. skip it
if (Token::simpleMatch(tok, "template <")) { if (Token::simpleMatch(tok, "template <")) {
tok = tok->next()->findClosingBracket(); tok = tok->next()->findClosingBracket();
@ -513,36 +520,45 @@ std::list<Token *> TemplateSimplifier::getTemplateInstantiations(Token *tokens)
const Token *tok2 = Token::findmatch(tok, "{|;"); const Token *tok2 = Token::findmatch(tok, "{|;");
if (tok2 && tok2->str() == "{") if (tok2 && tok2->str() == "{")
tok = tok2->link(); tok = tok2->link();
} else if (Token::Match(tok->previous(), "[({};=] %name% <") || } else if (Token::Match(tok->previous(), "[({};=] %name% ::|<") ||
Token::Match(tok->previous(), "%type% %name% <") || Token::Match(tok->previous(), "%type% %name% ::|<") ||
Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% <")) { Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) {
std::string scopeName = getScopeName(scopeList);
while (Token::Match(tok, "%name% :: %name%")) {
scopeName += (scopeName.empty() ? "" : " :: ") + tok->str();
tok = tok->tokAt(2);
}
if (!Token::Match(tok, "%name% <"))
continue;
// Add inner template instantiations first => go to the ">" // Add inner template instantiations first => go to the ">"
// and then parse backwards, adding all seen instantiations // and then parse backwards, adding all seen instantiations
const Token *tok2 = tok->next()->findClosingBracket(); const Token *tok2 = tok->next()->findClosingBracket();
// parse backwards and add template instantiations // parse backwards and add template instantiations
// TODO
for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { for (; tok2 && tok2 != tok; tok2 = tok2->previous()) {
if (Token::Match(tok2, ", %name% <") && if (Token::Match(tok2, ", %name% <") &&
TemplateSimplifier::templateParameters(tok2->tokAt(2))) { TemplateSimplifier::templateParameters(tok2->tokAt(2))) {
used.push_back(tok2->next()); instantiations.push_back(TokenAndName(tok2->next(),getFullName(scopeList, tok2->strAt(1))));
} }
} }
// Add outer template.. // Add outer template..
if (TemplateSimplifier::templateParameters(tok->next())) if (TemplateSimplifier::templateParameters(tok->next()))
used.push_back(tok); instantiations.push_back(TokenAndName(tok, scopeName + (scopeName.empty()?"":" :: ") + tok->str()));
} }
} }
return used; return instantiations;
} }
void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &templates, void TemplateSimplifier::useDefaultArgumentValues(const std::list<TokenAndName> &templates,
std::list<Token *> * const templateInstantiations) std::list<TokenAndName> * const templateInstantiations)
{ {
for (std::list<Token *>::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) { for (std::list<TokenAndName>::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) {
// template parameters with default value has syntax such as: // template parameters with default value has syntax such as:
// x = y // x = y
// this list will contain all the '=' tokens for such arguments // this list will contain all the '=' tokens for such arguments
@ -560,7 +576,7 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &temp
std::string classname; std::string classname;
// Scan template declaration.. // Scan template declaration..
for (Token *tok = *iter1; tok; tok = tok->next()) { for (Token *tok = iter1->token; tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "template < >")) { // Ticket #5762: Skip specialization tokens if (Token::simpleMatch(tok, "template < >")) { // Ticket #5762: Skip specialization tokens
tok = tok->tokAt(2); tok = tok->tokAt(2);
if (0 == templateParmDepth) if (0 == templateParmDepth)
@ -605,8 +621,8 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &temp
continue; continue;
// iterate through all template instantiations // iterate through all template instantiations
for (std::list<Token *>::const_iterator iter2 = templateInstantiations->begin(); iter2 != templateInstantiations->end(); ++iter2) { for (std::list<TokenAndName>::const_iterator iter2 = templateInstantiations->begin(); iter2 != templateInstantiations->end(); ++iter2) {
Token *tok = *iter2; Token *tok = iter2->token;
if (!Token::simpleMatch(tok, (classname + " <").c_str())) if (!Token::simpleMatch(tok, (classname + " <").c_str()))
continue; continue;
@ -659,9 +675,11 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &temp
if (Token::Match(tok2, "(|{|[")) if (Token::Match(tok2, "(|{|["))
tok2 = tok2->link(); tok2 = tok2->link();
else if (Token::Match(tok2, "%type% <") && templateParameters(tok2->next())) { else if (Token::Match(tok2, "%type% <") && templateParameters(tok2->next())) {
std::list<Token*>::iterator ti = std::find(templateInstantiations->begin(), std::list<TokenAndName>::iterator ti;
templateInstantiations->end(), for (ti = templateInstantiations->begin(); ti != templateInstantiations->end(); ++ti) {
tok2); if (ti->token == tok2)
break;
}
if (ti != templateInstantiations->end()) if (ti != templateInstantiations->end())
templateInstantiations->erase(ti); templateInstantiations->erase(ti);
++indentlevel; ++indentlevel;
@ -683,10 +701,10 @@ void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &temp
} }
} }
bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::string &name, const std::size_t numberOfArguments, const char patternAfter[]) bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[])
{ {
if (!Token::simpleMatch(instance, (name + " <").c_str())) // if (!Token::simpleMatch(instance, (name + " <").c_str()))
return false; // return false;
if (numberOfArguments != TemplateSimplifier::templateParameters(instance->next())) if (numberOfArguments != TemplateSimplifier::templateParameters(instance->next()))
return false; return false;
@ -766,17 +784,18 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
void TemplateSimplifier::expandTemplate( void TemplateSimplifier::expandTemplate(
TokenList& tokenlist, TokenList& tokenlist,
const Token *tok, const Token *templateDeclarationToken,
const std::string &name, 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, const std::vector<const Token *> &typesUsedInTemplateInstantiation,
std::list<Token *> &templateInstantiations) std::list<TemplateSimplifier::TokenAndName> &templateInstantiations)
{ {
std::list<ScopeInfo2> scopeInfo;
bool inTemplateDefinition = false; bool inTemplateDefinition = false;
const Token *endOfTemplateDefinition = nullptr; const Token *endOfTemplateDefinition = nullptr;
std::vector<const Token *> localTypeParametersInDeclaration;
for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) {
setScopeInfo(const_cast<Token *>(tok3), &scopeInfo);
if (inTemplateDefinition) { if (inTemplateDefinition) {
if (!endOfTemplateDefinition && tok3->str() == "{") if (!endOfTemplateDefinition && tok3->str() == "{")
endOfTemplateDefinition = tok3->link(); endOfTemplateDefinition = tok3->link();
@ -786,31 +805,33 @@ void TemplateSimplifier::expandTemplate(
if (tok3->str()=="template") { if (tok3->str()=="template") {
if (tok3->next() && tok3->next()->str()=="<") { if (tok3->next() && tok3->next()->str()=="<") {
std::vector<const Token *> localTypeParametersInDeclaration;
TemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration); TemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration);
if (localTypeParametersInDeclaration.size() != typeParametersInDeclaration.size()) if (localTypeParametersInDeclaration.size() != typeParametersInDeclaration.size())
inTemplateDefinition = false; // Partial specialization inTemplateDefinition = false; // Partial specialization
else else
inTemplateDefinition = true; inTemplateDefinition = true;
} else } else {
inTemplateDefinition = false; // Only template instantiation inTemplateDefinition = false; // Only template instantiation
} }
if (Token::Match(tok3, "{|(|[")) }
if (Token::Match(tok3, "(|["))
tok3 = tok3->link(); tok3 = tok3->link();
else if (Token::simpleMatch(tok3, "namespace {"))
tok3 = tok3->tokAt(2);
// Start of template.. // Start of template..
if (tok3 == tok) { if (tok3 == templateDeclarationToken) {
tok3 = tok3->next(); tok3 = tok3->next();
} }
// member function implemented outside class definition // member function implemented outside class definition
else if (inTemplateDefinition && else if (inTemplateDefinition &&
TemplateSimplifier::instantiateMatch(tok3, name, typeParametersInDeclaration.size(), ":: ~| %name% (")) { Token::Match(tok3, "%name% <") &&
fullName == getFullName(scopeInfo, tok3->str()) &&
TemplateSimplifier::instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) {
const Token *tok4 = tok3->next()->findClosingBracket(); const Token *tok4 = tok3->next()->findClosingBracket();
while (tok4 && tok4->str() != "(") while (tok4 && tok4->str() != "(")
tok4 = tok4->next(); tok4 = tok4->next();
if (!Tokenizer::isFunctionHead(tok4, "{:", true)) if (!Tokenizer::isFunctionHead(tok4, ":{", true))
continue; continue;
tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex());
while (tok3 && tok3->str() != "::") while (tok3 && tok3->str() != "::")
@ -824,6 +845,9 @@ void TemplateSimplifier::expandTemplate(
int indentlevel = 0; int indentlevel = 0;
std::stack<Token *> brackets; // holds "(", "[" and "{" tokens std::stack<Token *> brackets; // holds "(", "[" and "{" tokens
// FIXME use full name matching somehow
const std::string lastName = (fullName.find(" ") != std::string::npos) ? fullName.substr(fullName.rfind(" ")+1) : fullName;
for (; tok3; tok3 = tok3->next()) { for (; tok3; tok3 = tok3->next()) {
if (tok3->isName()) { if (tok3->isName()) {
// search for this token in the type vector // search for this token in the type vector
@ -852,7 +876,7 @@ void TemplateSimplifier::expandTemplate(
} }
// replace name.. // replace name..
if (Token::Match(tok3, (name + " !!<").c_str())) { if (Token::Match(tok3, (lastName + " !!<").c_str())) {
if (Token::Match(tok3->tokAt(-2), "> :: %name% ( )")) { if (Token::Match(tok3->tokAt(-2), "> :: %name% ( )")) {
; // Ticket #7942: Replacing for out-of-line constructors generates invalid syntax ; // Ticket #7942: Replacing for out-of-line constructors generates invalid syntax
} else { } else {
@ -863,8 +887,26 @@ void TemplateSimplifier::expandTemplate(
// copy // copy
tokenlist.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); tokenlist.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(), ">|>>")) {
templateInstantiations.push_back(tokenlist.back()); const Token *closingBracket = tok3->next()->findClosingBracket();
if (Token::simpleMatch(closingBracket->next(), "&")) {
int num = 0;
const Token *par = tok3->next();
while (num < typeParametersInDeclaration.size() && par != closingBracket) {
const std::string pattern("[<,] " + typeParametersInDeclaration[num]->str() + " [,>]");
if (!Token::Match(par, pattern.c_str()))
break;
++num;
par = par->tokAt(2);
}
if (num < typeParametersInDeclaration.size() || par != closingBracket)
continue;
}
std::string name = tok3->str();
for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2))
name = prev->str() + " :: " + name;
templateInstantiations.push_back(TokenAndName(tokenlist.back(), getFullName(scopeInfo, name)));
} }
// link() newly tokens manually // link() newly tokens manually
@ -888,6 +930,7 @@ void TemplateSimplifier::expandTemplate(
// the "}" token should only be added if indentlevel is 1 but I add it always intentionally // the "}" token should only be added if indentlevel is 1 but I add it always intentionally
// if indentlevel ever becomes 0, cppcheck will write: // if indentlevel ever becomes 0, cppcheck will write:
// ### Error: Invalid number of character { // ### Error: Invalid number of character {
inTemplateDefinition = false;
break; break;
} }
--indentlevel; --indentlevel;
@ -1239,9 +1282,9 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
TokenList& tokenlist, TokenList& tokenlist,
ErrorLogger* errorlogger, ErrorLogger* errorlogger,
const Settings *_settings, const Settings *_settings,
const Token *tok, const TokenAndName &templateDeclaration,
const std::time_t maxtime, const std::time_t maxtime,
std::list<Token *> &templateInstantiations, std::list<TokenAndName> &templateInstantiations,
std::set<std::string> &expandedtemplates) std::set<std::string> &expandedtemplates)
{ {
// this variable is not used at the moment. The intention was to // this variable is not used at the moment. The intention was to
@ -1250,7 +1293,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
// Contains tokens such as "T" // Contains tokens such as "T"
std::vector<const Token *> typeParametersInDeclaration; std::vector<const Token *> typeParametersInDeclaration;
tok = TemplateParametersInDeclaration(tok->tokAt(2), typeParametersInDeclaration); const Token * const tok = TemplateParametersInDeclaration(templateDeclaration.token->tokAt(2), typeParametersInDeclaration);
// bail out if the end of the file was reached // bail out if the end of the file was reached
if (!tok) if (!tok)
@ -1280,7 +1323,7 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
bool instantiated = false; bool instantiated = false;
for (std::list<Token *>::const_iterator iter2 = templateInstantiations.begin(); iter2 != templateInstantiations.end(); ++iter2) { for (std::list<TokenAndName>::const_iterator iter2 = templateInstantiations.begin(); iter2 != templateInstantiations.end(); ++iter2) {
if (numberOfTemplateInstantiations != templateInstantiations.size()) { if (numberOfTemplateInstantiations != templateInstantiations.size()) {
numberOfTemplateInstantiations = templateInstantiations.size(); numberOfTemplateInstantiations = templateInstantiations.size();
simplifyCalculations(tokenlist.front()); simplifyCalculations(tokenlist.front());
@ -1290,7 +1333,11 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
break; break;
} }
} }
Token * const tok2 = *iter2;
if (iter2->name != templateDeclaration.name)
continue;
Token * const tok2 = iter2->token;
if (errorlogger && !tokenlist.getFiles().empty()) if (errorlogger && !tokenlist.getFiles().empty())
errorlogger->reportProgress(tokenlist.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); errorlogger->reportProgress(tokenlist.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue());
#ifdef MAXTIME #ifdef MAXTIME
@ -1300,11 +1347,13 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
(void)maxtime; (void)maxtime;
#endif #endif
assert(tokenlist.validateToken(tok2)); // that assertion fails on examples from #6021 assert(tokenlist.validateToken(tok2)); // that assertion fails on examples from #6021
if (tok2->str() != name)
continue;
if (Token::Match(tok2->previous(), "[;{}=]") && const Token *startToken = tok2;
!TemplateSimplifier::instantiateMatch(*iter2, name, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")) while (Token::Match(startToken->tokAt(-2), "%name% :: %name%"))
startToken = startToken->tokAt(-2);
if (Token::Match(startToken->previous(), "[;{}=]") &&
!TemplateSimplifier::instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%"))
continue; continue;
// New type.. // New type..
@ -1348,13 +1397,13 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
typeForNewName += tok3->str(); typeForNewName += tok3->str();
} }
} }
std::string templateMatchPattern(name + " < " + typeForPatternMatch + " >"); const std::string templateParametersMatchPattern("< " + typeForPatternMatch + " >");
if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) {
if (printDebug && errorlogger) { if (printDebug && errorlogger) {
std::list<const Token *> callstack(1, tok2); std::list<const Token *> callstack(1, tok2);
errorlogger->reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug", errorlogger->reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug",
"Failed to instantiate template \"" + name + "\". The checking continues anyway.", false)); "Failed to instantiate template \"" + iter2->name + "\". The checking continues anyway.", false));
} }
if (typeForNewName.empty()) if (typeForNewName.empty())
continue; continue;
@ -1362,16 +1411,16 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
} }
// New classname/funcname.. // New classname/funcname..
const std::string newName(name + " < " + typeForNewName + " >"); const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >");
if (expandedtemplates.find(newName) == expandedtemplates.end()) { if (expandedtemplates.find(newName) == expandedtemplates.end()) {
expandedtemplates.insert(newName); expandedtemplates.insert(newName);
TemplateSimplifier::expandTemplate(tokenlist, tok,name,typeParametersInDeclaration,newName,typesUsedInTemplateInstantiation,templateInstantiations); TemplateSimplifier::expandTemplate(tokenlist, tok, iter2->name, typeParametersInDeclaration, newName, typesUsedInTemplateInstantiation, templateInstantiations);
instantiated = true; instantiated = true;
} }
// Replace all these template usages.. // Replace all these template usages..
replaceTemplateUsage(tok2, templateMatchPattern, newName, typesUsedInTemplateInstantiation, templateInstantiations); replaceTemplateUsage(tok2, iter2->name, templateParametersMatchPattern, newName, typesUsedInTemplateInstantiation, templateInstantiations);
} }
// Template has been instantiated .. then remove the template declaration // Template has been instantiated .. then remove the template declaration
@ -1380,14 +1429,24 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken, void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
const std::string &templateMatchPattern, const std::string &templateName,
const std::string &templateParametersMatchPattern,
const std::string &newName, const std::string &newName,
const std::vector<const Token *> &typesUsedInTemplateInstantiation, const std::vector<const Token *> &typesUsedInTemplateInstantiation,
std::list<Token *> &templateInstantiations) std::list<TokenAndName> &templateInstantiations)
{ {
std::list<ScopeInfo2> scopeInfo;
std::list< std::pair<Token *, Token *> > removeTokens; std::list< std::pair<Token *, Token *> > removeTokens;
for (Token *nameTok = instantiationToken; nameTok; nameTok = nameTok->next()) { for (Token *nameTok = instantiationToken; nameTok; nameTok = nameTok->next()) {
if (!Token::simpleMatch(nameTok, templateMatchPattern.c_str())) setScopeInfo(nameTok, &scopeInfo);
if (!Token::Match(nameTok, "%name% <"))
continue;
if (!Token::simpleMatch(nameTok->next(), templateParametersMatchPattern.c_str()))
continue;
// FIXME Proper name matching
const std::string lastName(templateName.find(" ") == std::string::npos ? templateName : templateName.substr(templateName.rfind(" ") + 1));
if (lastName != nameTok->str())
continue; continue;
// match parameters // match parameters
@ -1426,10 +1485,24 @@ 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 == typesUsedInTemplateInstantiation.size()) {
Token * const nameTok1 = nameTok;
while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%"))
nameTok = nameTok->tokAt(-2);
nameTok->str(newName); nameTok->str(newName);
for (Token *tok = nameTok->next(); tok != tok2; tok = tok->next()) { for (std::list<TokenAndName>::iterator it = templateInstantiations.begin(); it != templateInstantiations.end(); ++it) {
if (tok->isName()) if (it->token == nameTok1)
templateInstantiations.remove(tok); it->token = nameTok;
}
for (Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) {
if (tok->isName()) {
std::list<TokenAndName>::iterator ti;
for (ti = templateInstantiations.begin(); ti != templateInstantiations.end();) {
if (ti->token == tok)
templateInstantiations.erase(ti++);
else
++ti;
}
}
} }
removeTokens.push_back(std::pair<Token*,Token*>(nameTok, tok2->next())); removeTokens.push_back(std::pair<Token*,Token*>(nameTok, tok2->next()));
} }
@ -1470,10 +1543,10 @@ void TemplateSimplifier::simplifyTemplates(
} }
} }
std::list<Token *> templates(TemplateSimplifier::getTemplateDeclarations(tokenlist.front(), _codeWithTemplates)); std::list<TokenAndName> templates(TemplateSimplifier::getTemplateDeclarations(tokenlist.front(), _codeWithTemplates));
// Locate possible instantiations of templates.. // Locate possible instantiations of templates..
std::list<Token *> templateInstantiations(TemplateSimplifier::getTemplateInstantiations(tokenlist.front())); std::list<TokenAndName> templateInstantiations(TemplateSimplifier::getTemplateInstantiations(tokenlist.front()));
// No template instantiations? Then return. // No template instantiations? Then return.
if (templateInstantiations.empty()) if (templateInstantiations.empty())
@ -1487,8 +1560,8 @@ void TemplateSimplifier::simplifyTemplates(
//while (!done) //while (!done)
{ {
//done = true; //done = true;
std::list<Token *> templates2; std::list<TokenAndName> templates2;
for (std::list<Token *>::reverse_iterator iter1 = templates.rbegin(); iter1 != templates.rend(); ++iter1) { for (std::list<TokenAndName>::reverse_iterator iter1 = templates.rbegin(); iter1 != templates.rend(); ++iter1) {
bool instantiated = TemplateSimplifier::simplifyTemplateInstantiations(tokenlist, bool instantiated = TemplateSimplifier::simplifyTemplateInstantiations(tokenlist,
errorlogger, errorlogger,
_settings, _settings,
@ -1500,11 +1573,15 @@ void TemplateSimplifier::simplifyTemplates(
templates2.push_back(*iter1); templates2.push_back(*iter1);
} }
for (std::list<Token *>::const_iterator it = templates2.begin(); it != templates2.end(); ++it) { for (std::list<TokenAndName>::const_iterator it = templates2.begin(); it != templates2.end(); ++it) {
std::list<Token *>::iterator it1 = std::find(templates.begin(), templates.end(), *it); std::list<TokenAndName>::iterator it1;
for (it1 = templates.begin(); it1 != templates.end(); ++it1) {
if (it1->token == it->token)
break;
}
if (it1 != templates.end()) { if (it1 != templates.end()) {
templates.erase(it1); templates.erase(it1);
removeTemplate(*it); removeTemplate(it->token);
} }
} }
} }

View File

@ -72,35 +72,43 @@ public:
*/ */
static std::set<std::string> expandSpecialized(Token *tokens); static std::set<std::string> expandSpecialized(Token *tokens);
/**
* Token and its full scopename
*/
struct TokenAndName {
TokenAndName(Token *tok, const std::string &scopeName) : token(tok), name(scopeName) {}
Token *token;
std::string name;
};
/** /**
* Get template declarations * Get template declarations
* @return list of template declarations * @return list of template declarations
*/ */
static std::list<Token *> getTemplateDeclarations(Token *tokens, bool &codeWithTemplates); static std::list<TokenAndName> getTemplateDeclarations(Token *tokens, bool &codeWithTemplates);
/** /**
* Get template instantiations * Get template instantiations
* @return list of template instantiations * @return list of template instantiations
*/ */
static std::list<Token *> getTemplateInstantiations(Token *tokens); static std::list<TokenAndName> getTemplateInstantiations(Token *tokens);
/** /**
* simplify template instantiations (use default argument values) * simplify template instantiations (use default argument values)
* @param templates list of template declarations * @param templates list of template declarations
* @param templateInstantiations list of template instantiations * @param templateInstantiations list of template instantiations
*/ */
static void useDefaultArgumentValues(const std::list<Token *> &templates, static void useDefaultArgumentValues(const std::list<TokenAndName> &templates,
std::list<Token *> *templateInstantiations); std::list<TokenAndName> *templateInstantiations);
/** /**
* Match template declaration/instantiation * Match template declaration/instantiation
* @param instance template instantiation * @param instance template instantiation
* @param name name of template
* @param numberOfArguments number of template arguments * @param numberOfArguments number of template arguments
* @param patternAfter pattern that must match the tokens after the ">" * @param patternAfter pattern that must match the tokens after the ">"
* @return match => true * @return match => true
*/ */
static bool instantiateMatch(const Token *instance, const std::string &name, const std::size_t numberOfArguments, const char patternAfter[]); static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]);
/** /**
* Match template declaration/instantiation * Match template declaration/instantiation
@ -112,12 +120,12 @@ public:
static void expandTemplate( static void expandTemplate(
TokenList& tokenlist, TokenList& tokenlist,
const Token *tok, const Token *templateDeclarationToken,
const std::string &name, 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, const std::vector<const Token *> &typesUsedInTemplateInstantiation,
std::list<Token *> &templateInstantiations); std::list<TokenAndName> &templateInstantiations);
/** /**
* @brief TemplateParametersInDeclaration * @brief TemplateParametersInDeclaration
@ -138,7 +146,7 @@ public:
* @param tokenlist token list * @param tokenlist token list
* @param errorlogger error logger * @param errorlogger error logger
* @param _settings settings * @param _settings settings
* @param tok token where the template declaration begins * @param templateDeclaration template declaration
* @param maxtime time when the simplification will stop * @param maxtime time when the simplification will stop
* @param templateInstantiations a list of template usages (not necessarily just for this template) * @param templateInstantiations a list of template usages (not necessarily just for this template)
* @param expandedtemplates all templates that has been expanded so far. The full names are stored. * @param expandedtemplates all templates that has been expanded so far. The full names are stored.
@ -148,24 +156,26 @@ public:
TokenList& tokenlist, TokenList& tokenlist,
ErrorLogger* errorlogger, ErrorLogger* errorlogger,
const Settings *_settings, const Settings *_settings,
const Token *tok, const TokenAndName &templateDeclaration,
const std::time_t maxtime, const std::time_t maxtime,
std::list<Token *> &templateInstantiations, std::list<TokenAndName> &templateInstantiations,
std::set<std::string> &expandedtemplates); std::set<std::string> &expandedtemplates);
/** /**
* Replace all matching template usages 'Foo < int >' => 'Foo<int>' * Replace all matching template usages 'Foo < int >' => 'Foo<int>'
* @param instantiationToken Template instantiation token * @param instantiationToken Template instantiation token
* @param templateMatchPattern Pattern compatible with Token::simpleMatch * @param templateName full template name with scope info
* @param templateParametersMatchPattern template parameters, Token::simpleMatch compatible pattern
* @param newName The new type name * @param newName The new type name
* @param typesUsedInTemplateInstantiation template instantiation parameters * @param typesUsedInTemplateInstantiation template instantiation parameters
* @param templateInstantiations All seen instantiations * @param templateInstantiations All seen instantiations
*/ */
static void replaceTemplateUsage(Token *const instantiationToken, static void replaceTemplateUsage(Token *const instantiationToken,
const std::string &templateMatchPattern, const std::string &templateName,
const std::string &templateParametersMatchPattern,
const std::string &newName, const std::string &newName,
const std::vector<const Token *> &typesUsedInTemplateInstantiation, const std::vector<const Token *> &typesUsedInTemplateInstantiation,
std::list<Token *> &templateInstantiations); std::list<TokenAndName> &templateInstantiations);
/** /**
* Simplify templates * Simplify templates
@ -200,7 +210,6 @@ public:
static bool simplifyCalculations(Token *_tokens); static bool simplifyCalculations(Token *_tokens);
private: private:
/** /**
* Remove a specific "template < ..." template class/function * Remove a specific "template < ..." template class/function
*/ */

View File

@ -106,7 +106,10 @@ private:
TEST_CASE(template_constructor); // #3152 - template constructor is removed TEST_CASE(template_constructor); // #3152 - template constructor is removed
TEST_CASE(syntax_error_templates_1); TEST_CASE(syntax_error_templates_1);
TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code
TEST_CASE(template_namespace); TEST_CASE(template_namespace_1);
TEST_CASE(template_namespace_2);
TEST_CASE(template_namespace_3);
TEST_CASE(template_namespace_4);
// Test TemplateSimplifier::templateParameters // Test TemplateSimplifier::templateParameters
TEST_CASE(templateParameters); TEST_CASE(templateParameters);
@ -1392,7 +1395,7 @@ private:
"};"); "};");
} }
void template_namespace() { void template_namespace_1() {
// #6570 // #6570
const char code[] = "namespace {\n" const char code[] = "namespace {\n"
" template<class T> void Fred(T value) { }\n" " template<class T> void Fred(T value) { }\n"
@ -1403,6 +1406,52 @@ private:
"void Fred<int> ( int value ) { }", tok(code)); "void Fred<int> ( int value ) { }", tok(code));
} }
void template_namespace_2() {
// #8283
const char code[] = "namespace X {\n"
" template<class T> struct S { };\n"
"}\n"
"X::S<int> s;";
ASSERT_EQUALS("X::S<int> s ; "
"struct X::S<int> { } ;", tok(code));
}
void template_namespace_3() {
const char code[] = "namespace test16 {\n"
" template <class T> struct foo {\n"
" static void *bar();\n"
" };\n"
" void *test() { return foo<int>::bar(); }\n"
"}";
ASSERT_EQUALS("namespace test16 {"
" void * test ( ) {"
" return test16::foo<int> :: bar ( ) ;"
" } "
"} "
"struct test16::foo<int> {"
" static void * bar ( ) ; "
"} ;", tok(code));
}
void template_namespace_4() {
const char code[] = "namespace foo {\n"
" template<class T> class A { void dostuff() {} };\n"
" struct S : public A<int> {\n"
" void f() {\n"
" A<int>::dostuff();\n"
" }\n"
" };\n"
"}";
ASSERT_EQUALS("namespace foo {"
" struct S : public foo::A<int> {"
" void f ( ) {"
" foo::A<int> :: dostuff ( ) ;"
" }"
" } ; "
"} "
"class foo::A<int> { void dostuff ( ) { } } ;", tok(code));
}
unsigned int templateParameters(const char code[]) { unsigned int templateParameters(const char code[]) {
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
@ -1491,29 +1540,29 @@ private:
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 {};"));
} }
unsigned int instantiateMatch(const char code[], const std::string& name, const std::size_t numberOfArguments, const char patternAfter[]) { unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) {
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp", ""); tokenizer.tokenize(istr, "test.cpp", "");
return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), name, numberOfArguments, patternAfter); return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), numberOfArguments, patternAfter);
} }
void instantiateMatch() { void instantiateMatch() {
// Ticket #8175 // Ticket #8175
ASSERT_EQUALS(false, ASSERT_EQUALS(false,
instantiateMatch("ConvertHelper < From, To > c ;", instantiateMatch("ConvertHelper < From, To > c ;",
"ConvertHelper", 2, ":: %name% (")); 2, ":: %name% ("));
ASSERT_EQUALS(true, ASSERT_EQUALS(true,
instantiateMatch("ConvertHelper < From, To > :: Create ( ) ;", instantiateMatch("ConvertHelper < From, To > :: Create ( ) ;",
"ConvertHelper", 2, ":: %name% (")); 2, ":: %name% ("));
ASSERT_EQUALS(false, ASSERT_EQUALS(false,
instantiateMatch("integral_constant < bool, sizeof ( ConvertHelper < From, To > :: Create ( ) ) > ;", instantiateMatch("integral_constant < bool, sizeof ( ConvertHelper < From, To > :: Create ( ) ) > ;",
"integral_constant", 2, ":: %name% (")); 2, ":: %name% ("));
ASSERT_EQUALS(false, ASSERT_EQUALS(false,
instantiateMatch("integral_constant < bool, sizeof ( ns :: ConvertHelper < From, To > :: Create ( ) ) > ;", instantiateMatch("integral_constant < bool, sizeof ( ns :: ConvertHelper < From, To > :: Create ( ) ) > ;",
"integral_constant", 2, ":: %name% (")); 2, ":: %name% ("));
} }
}; };