Tokenizer::simplifyTemplates: Broke out the functionality that instantiates a template

This commit is contained in:
Daniel Marjamäki 2011-02-12 20:58:45 +01:00
parent 9021f0f180
commit 4d1aae5859
2 changed files with 327 additions and 310 deletions

View File

@ -2791,6 +2791,325 @@ void Tokenizer::simplifyTemplatesUseDefaultArgumentValues(const std::list<Token
}
}
void Tokenizer::simplifyTemplatesInstantiate(std::list<Token *> &used,
const Token *tok,
std::set<std::string> &expandedtemplates)
{
// this variable is not used at the moment. the intention was to
// allow continous instantiations until all templates has been expanded
bool done = false;
std::vector<const Token *> type;
for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next())
{
if (Token::Match(tok, "%var% ,|>"))
type.push_back(tok);
}
// bail out if the end of the file was reached
if (!tok)
return;
// get the position of the template name
unsigned char namepos = 0;
if (Token::Match(tok, "> class|struct %type% {|:"))
namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2;
else if (Token::Match(tok, "> %type% %type% *|&| %type% ("))
namepos = 3;
else
{
// debug message that we bail out..
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok->linenr();
loc.setfile(file(tok));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"simplifyTemplates: bailing out",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
return;
}
if ((tok->tokAt(namepos)->str() == "*" || tok->tokAt(namepos)->str() == "&"))
++namepos;
// name of template function/class..
const std::string name(tok->strAt(namepos));
const bool isfunc(tok->strAt(namepos + 1) == "(");
// locate template usage..
std::string s(name + " <");
for (unsigned int i = 0; i < type.size(); ++i)
{
if (i > 0)
s += ",";
s += " %any% ";
}
const std::string pattern(s + "> ");
std::string::size_type sz1 = used.size();
unsigned int recursiveCount = 0;
for (std::list<Token *>::const_iterator iter2 = used.begin(); iter2 != used.end(); ++iter2)
{
// If the size of "used" has changed, simplify calculations
if (sz1 != used.size())
{
sz1 = used.size();
simplifyCalculations();
recursiveCount++;
if (recursiveCount > 100)
{
// bail out..
break;
}
}
Token * const tok2 = *iter2;
if (tok2->str() != name)
continue;
if (Token::Match(tok2->previous(), "[;{}=]") &&
!Token::Match(tok2, (pattern + (isfunc ? "(" : "*| %var%")).c_str()))
continue;
// New type..
std::vector<Token> types2;
s = "";
std::string s1(name + " < ");
for (const Token *tok3 = tok2->tokAt(2); tok3 && tok3->str() != ">"; tok3 = tok3->next())
{
if (!tok3->next())
{
s.clear();
break;
}
s1 += tok3->str();
s1 += " ";
if (tok3->str() != ",")
types2.push_back(*tok3);
// add additional type information
if (tok3->isUnsigned())
s += "unsigned";
else if (tok3->isSigned())
s += "signed";
if (tok3->isLong())
s += "long";
s += tok3->str();
}
s1 += ">";
const std::string type2(s);
if (type2.empty() || type.size() != types2.size())
{
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok2->linenr();
loc.setfile(file(tok2));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"Failed to instantiate template. The checking continues anyway.",
"debug");
_errorLogger->reportErr(errmsg);
}
if (type2.empty())
continue;
break;
}
// New classname/funcname..
const std::string name2(name + "<" + type2 + ">");
if (expandedtemplates.find(name2) == expandedtemplates.end())
{
expandedtemplates.insert(name2);
// Copy template..
int _indentlevel = 0;
int _parlevel = 0;
for (const Token *tok3 = _tokens; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++_indentlevel;
else if (tok3->str() == "}")
--_indentlevel;
else if (tok3->str() == "(")
++_parlevel;
else if (tok3->str() == ")")
--_parlevel;
// Start of template..
if (tok3 == tok)
{
tok3 = tok3->next();
}
// member function implemented outside class definition
else if (_indentlevel == 0 && _parlevel == 0 && Token::Match(tok3, (pattern + " :: ~| %var% (").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
while (tok3->str() != "::")
tok3 = tok3->next();
}
// not part of template.. go on to next token
else
continue;
int indentlevel = 0;
std::stack<Token *> braces; // holds "{" tokens
std::stack<Token *> brackets; // holds "(" tokens
std::stack<Token *> brackets2; // holds "[" tokens
for (; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++indentlevel;
else if (tok3->str() == "}")
{
if (indentlevel <= 1 && brackets.empty() && brackets2.empty())
{
// there is a bug if indentlevel is 0
// the "}" token should only be added if indentlevel is 1 but I add it always intentionally
// if indentlevel ever becomes 0, cppcheck will write:
// ### Error: Invalid number of character {
addtoken("}", tok3->linenr(), tok3->fileIndex());
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
break;
}
--indentlevel;
}
if (tok3->isName())
{
// search for this token in the type vector
unsigned int itype = 0;
while (itype < type.size() && type[itype]->str() != tok3->str())
++itype;
// replace type with given type..
if (itype < type.size())
{
addtoken(&types2[itype], tok3->linenr(), tok3->fileIndex());
continue;
}
}
// replace name..
if (Token::Match(tok3, (name + " !!<").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
continue;
}
// copy
addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <"))
{
if (!Token::Match(tok3, (name + " <").c_str()))
done = false;
used.push_back(_tokensBack);
}
// link() newly tokens manually
if (tok3->str() == "{")
{
braces.push(_tokensBack);
}
else if (tok3->str() == "}")
{
assert(braces.empty() == false);
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
}
else if (tok3->str() == "(")
{
brackets.push(_tokensBack);
}
else if (tok3->str() == "[")
{
brackets2.push(_tokensBack);
}
else if (tok3->str() == ")")
{
assert(brackets.empty() == false);
Token::createMutualLinks(brackets.top(), _tokensBack);
brackets.pop();
}
else if (tok3->str() == "]")
{
assert(brackets2.empty() == false);
Token::createMutualLinks(brackets2.top(), _tokensBack);
brackets2.pop();
}
}
assert(braces.empty());
assert(brackets.empty());
}
}
// Replace all these template usages..
for (Token *tok4 = tok2; tok4; tok4 = tok4->next())
{
if (Token::simpleMatch(tok4, s1.c_str()))
{
bool match = true;
Token * tok5 = tok4->tokAt(2);
unsigned int count = 0;
while (tok5->str() != ">")
{
if (tok5->str() != ",")
{
if (tok5->isUnsigned() != types2[count].isUnsigned() ||
tok5->isSigned() != types2[count].isSigned() ||
tok5->isLong() != types2[count].isLong())
{
match = false;
break;
}
count++;
}
tok5 = tok5->next();
}
if (match)
{
tok4->str(name2);
while (tok4->next()->str() != ">")
{
used.remove(tok4->next());
tok4->deleteNext();
}
used.remove(tok4->next());
tok4->deleteNext();
}
}
}
}
}
void Tokenizer::simplifyTemplates()
{
std::set<std::string> expandedtemplates(simplifyTemplatesExpandSpecialized());
@ -2840,316 +3159,7 @@ void Tokenizer::simplifyTemplates()
done = true;
for (std::list<Token *>::iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1)
{
Token *tok = *iter1;
std::vector<const Token *> type;
for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next())
{
if (Token::Match(tok, "%var% ,|>"))
type.push_back(tok);
}
// bail out if the end of the file was reached
if (!tok)
break;
// get the position of the template name
unsigned char namepos = 0;
if (Token::Match(tok, "> class|struct %type% {|:"))
namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2;
else if (Token::Match(tok, "> %type% %type% *|&| %type% ("))
namepos = 3;
else
{
// debug message that we bail out..
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok->linenr();
loc.setfile(file(tok));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"simplifyTemplates: bailing out",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
continue;
}
if ((tok->tokAt(namepos)->str() == "*" || tok->tokAt(namepos)->str() == "&"))
++namepos;
// name of template function/class..
const std::string name(tok->strAt(namepos));
const bool isfunc(tok->strAt(namepos + 1) == "(");
// locate template usage..
std::string s(name + " <");
for (unsigned int i = 0; i < type.size(); ++i)
{
if (i > 0)
s += ",";
s += " %any% ";
}
const std::string pattern(s + "> ");
std::string::size_type sz1 = used.size();
unsigned int recursiveCount = 0;
for (std::list<Token *>::iterator iter2 = used.begin(); iter2 != used.end(); ++iter2)
{
// If the size of "used" has changed, simplify calculations
if (sz1 != used.size())
{
sz1 = used.size();
simplifyCalculations();
recursiveCount++;
if (recursiveCount > 100)
{
// bail out..
break;
}
}
Token * const tok2 = *iter2;
if (tok2->str() != name)
continue;
if (Token::Match(tok2->previous(), "[;{}=]") &&
!Token::Match(tok2, (pattern + (isfunc ? "(" : "*| %var%")).c_str()))
continue;
// New type..
std::vector<Token> types2;
s = "";
std::string s1(name + " < ");
for (const Token *tok3 = tok2->tokAt(2); tok3 && tok3->str() != ">"; tok3 = tok3->next())
{
if (!tok3->next())
{
s.clear();
break;
}
s1 += tok3->str();
s1 += " ";
if (tok3->str() != ",")
types2.push_back(*tok3);
// add additional type information
if (tok3->isUnsigned())
s += "unsigned";
else if (tok3->isSigned())
s += "signed";
if (tok3->isLong())
s += "long";
s += tok3->str();
}
s1 += ">";
const std::string type2(s);
if (type2.empty() || type.size() != types2.size())
{
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok2->linenr();
loc.setfile(file(tok2));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"Failed to instantiate template. The checking continues anyway.",
"debug");
_errorLogger->reportErr(errmsg);
}
if (type2.empty())
continue;
break;
}
// New classname/funcname..
const std::string name2(name + "<" + type2 + ">");
if (expandedtemplates.find(name2) == expandedtemplates.end())
{
expandedtemplates.insert(name2);
// Copy template..
int _indentlevel = 0;
int _parlevel = 0;
for (const Token *tok3 = _tokens; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++_indentlevel;
else if (tok3->str() == "}")
--_indentlevel;
else if (tok3->str() == "(")
++_parlevel;
else if (tok3->str() == ")")
--_parlevel;
// Start of template..
if (tok3 == tok)
{
tok3 = tok3->next();
}
// member function implemented outside class definition
else if (_indentlevel == 0 && _parlevel == 0 && Token::Match(tok3, (pattern + " :: ~| %var% (").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
while (tok3->str() != "::")
tok3 = tok3->next();
}
// not part of template.. go on to next token
else
continue;
int indentlevel = 0;
std::stack<Token *> braces; // holds "{" tokens
std::stack<Token *> brackets; // holds "(" tokens
std::stack<Token *> brackets2; // holds "[" tokens
for (; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++indentlevel;
else if (tok3->str() == "}")
{
if (indentlevel <= 1 && brackets.empty() && brackets2.empty())
{
// there is a bug if indentlevel is 0
// the "}" token should only be added if indentlevel is 1 but I add it always intentionally
// if indentlevel ever becomes 0, cppcheck will write:
// ### Error: Invalid number of character {
addtoken("}", tok3->linenr(), tok3->fileIndex());
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
break;
}
--indentlevel;
}
if (tok3->isName())
{
// search for this token in the type vector
unsigned int itype = 0;
while (itype < type.size() && type[itype]->str() != tok3->str())
++itype;
// replace type with given type..
if (itype < type.size())
{
addtoken(&types2[itype], tok3->linenr(), tok3->fileIndex());
continue;
}
}
// replace name..
if (Token::Match(tok3, (name + " !!<").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
continue;
}
// copy
addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <"))
{
if (!Token::Match(tok3, (name + " <").c_str()))
done = false;
used.push_back(_tokensBack);
}
// link() newly tokens manually
if (tok3->str() == "{")
{
braces.push(_tokensBack);
}
else if (tok3->str() == "}")
{
assert(braces.empty() == false);
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
}
else if (tok3->str() == "(")
{
brackets.push(_tokensBack);
}
else if (tok3->str() == "[")
{
brackets2.push(_tokensBack);
}
else if (tok3->str() == ")")
{
assert(brackets.empty() == false);
Token::createMutualLinks(brackets.top(), _tokensBack);
brackets.pop();
}
else if (tok3->str() == "]")
{
assert(brackets2.empty() == false);
Token::createMutualLinks(brackets2.top(), _tokensBack);
brackets2.pop();
}
}
assert(braces.empty());
assert(brackets.empty());
}
}
// Replace all these template usages..
for (Token *tok4 = tok2; tok4; tok4 = tok4->next())
{
if (Token::simpleMatch(tok4, s1.c_str()))
{
bool match = true;
Token * tok5 = tok4->tokAt(2);
unsigned int count = 0;
while (tok5->str() != ">")
{
if (tok5->str() != ",")
{
if (tok5->isUnsigned() != types2[count].isUnsigned() ||
tok5->isSigned() != types2[count].isSigned() ||
tok5->isLong() != types2[count].isLong())
{
match = false;
break;
}
count++;
}
tok5 = tok5->next();
}
if (match)
{
tok4->str(name2);
while (tok4->next()->str() != ">")
{
used.remove(tok4->next());
tok4->deleteNext();
}
used.remove(tok4->next());
tok4->deleteNext();
}
}
}
}
simplifyTemplatesInstantiate(used, *iter1, expandedtemplates);
}
}

View File

@ -423,6 +423,13 @@ public:
void simplifyTemplatesUseDefaultArgumentValues(const std::list<Token *> &templates,
const std::list<Token *> &instantiations);
/**
* Simplify templates : expand all instantiatiations for a template
*/
void simplifyTemplatesInstantiate(std::list<Token *> &used,
const Token *tok,
std::set<std::string> &expandedtemplates);
/**
* Used after simplifyTemplates to perform a little cleanup.
* Sometimes the simplifyTemplates isn't fully successful and then