diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 6755c007f..2c2e6d5e1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2791,6 +2791,325 @@ void Tokenizer::simplifyTemplatesUseDefaultArgumentValues(const std::list &used, + const Token *tok, + std::set &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 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 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::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 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 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 braces; // holds "{" tokens + std::stack brackets; // holds "(" tokens + std::stack 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 expandedtemplates(simplifyTemplatesExpandSpecialized()); @@ -2840,316 +3159,7 @@ void Tokenizer::simplifyTemplates() done = true; for (std::list::iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) { - Token *tok = *iter1; - std::vector 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 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::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 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 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 braces; // holds "{" tokens - std::stack brackets; // holds "(" tokens - std::stack 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); } } diff --git a/lib/tokenize.h b/lib/tokenize.h index d06843a01..579c233bd 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -423,6 +423,13 @@ public: void simplifyTemplatesUseDefaultArgumentValues(const std::list &templates, const std::list &instantiations); + /** + * Simplify templates : expand all instantiatiations for a template + */ + void simplifyTemplatesInstantiate(std::list &used, + const Token *tok, + std::set &expandedtemplates); + /** * Used after simplifyTemplates to perform a little cleanup. * Sometimes the simplifyTemplates isn't fully successful and then