cppcheck/lib/templatesimplifier.cpp

3465 lines
140 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2019 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "templatesimplifier.h"
#include "errorlogger.h"
#include "mathlib.h"
#include "settings.h"
#include "token.h"
#include "tokenize.h"
#include "tokenlist.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
#include <stack>
#include <utility>
namespace {
class FindToken {
public:
explicit FindToken(const Token *token) : mToken(token) {}
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
return tokenAndName.token == mToken;
}
private:
const Token * const mToken;
};
class FindName {
public:
explicit FindName(const std::string &name) : mName(name) {}
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
return tokenAndName.name == mName;
}
private:
const std::string mName;
};
class FindFullName {
public:
explicit FindFullName(const std::string &fullName) : mFullName(fullName) {}
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
return tokenAndName.fullName == mFullName;
}
private:
const std::string mFullName;
};
}
TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s) :
token(tok), scope(s), name(tok ? tok->str() : ""), fullName(s.empty() ? name : (s + " :: " + name)),
nameToken(nullptr), paramEnd(nullptr), flags(0)
{
if (token)
token->templateSimplifierPointer(this);
}
TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, const Token *nt, const Token *pe) :
token(tok), scope(s), name(nt->str()), fullName(s.empty() ? name : (s + " :: " + name)),
nameToken(nt), paramEnd(pe), flags(0)
{
// only set flags for declaration
if (token && nameToken && paramEnd) {
isSpecialization(Token::simpleMatch(token, "template < >"));
isPartialSpecialization(!isSpecialization() && nameToken->strAt(1) == "<");
isAlias(paramEnd->strAt(1) == "using");
isClass(Token::Match(paramEnd->next(), "class|struct|union %name% <|{|:|;"));
const Token *tok1 = nameToken->next();
if (tok1->str() == "<")
tok1 = tok1->findClosingBracket()->next();
isFunction(tok1->str() == "(");
isVariable(!isClass() && Token::Match(tok1, "=|;"));
if (isVariable())
isForwardDeclaration(tok1->str() == ";");
else {
if (isFunction())
tok1 = tok1->link()->next();
tok1 = Token::findmatch(tok1, "{|;");
if (tok1)
isForwardDeclaration(tok1->str() == ";");
}
// check for member function and adjust scope
if (isFunction() && nameToken->strAt(-1) == "::") {
const Token * start = nameToken;
while (Token::Match(start->tokAt(-2), "%name% ::") ||
(Token::simpleMatch(start->tokAt(-2), "> ::") &&
start->tokAt(-2)->findOpeningBracket() &&
Token::Match(start->tokAt(-2)->findOpeningBracket()->previous(), "%name% <"))) {
if (start->strAt(-2) == ">")
start = start->tokAt(-2)->findOpeningBracket()->previous();
else
start = start->tokAt(-2);
}
if (start && start != nameToken) {
if (!scope.empty())
scope += " ::";
while (start && start->next() != nameToken) {
if (start->str() == "<")
start = start->findClosingBracket();
else {
if (!scope.empty())
scope += " ";
scope += start->str();
}
start = start->next();
}
if (start)
fullName = scope.empty() ? name : (scope + " :: " + name);
}
}
}
if (token)
token->templateSimplifierPointer(this);
}
TemplateSimplifier::TokenAndName::TokenAndName(const TokenAndName& otherTok) :
token(otherTok.token), scope(otherTok.scope), name(otherTok.name), fullName(otherTok.fullName),
nameToken(otherTok.nameToken), paramEnd(otherTok.paramEnd), flags(otherTok.flags)
{
if (token)
token->templateSimplifierPointer(this);
}
TemplateSimplifier::TokenAndName::~TokenAndName()
{
if (token)
token->templateSimplifierPointers().erase(this);
}
TemplateSimplifier::TemplateSimplifier(Tokenizer *tokenizer)
: mTokenizer(tokenizer), mTokenList(tokenizer->list), mSettings(tokenizer->mSettings), mErrorLogger(tokenizer->mErrorLogger)
{
}
TemplateSimplifier::~TemplateSimplifier()
{
}
void TemplateSimplifier::cleanupAfterSimplify()
{
bool goback = false;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (goback) {
tok = tok->previous();
goback = false;
}
if (tok->str() == "(")
tok = tok->link();
else if (Token::Match(tok, "template < > %name%")) {
const Token *end = tok;
while (end) {
if (end->str() == ";")
break;
if (end->str() == "{") {
end = end->link()->next();
break;
}
if (!Token::Match(end, "%name%|::|<|>|,")) {
end = nullptr;
break;
}
end = end->next();
}
if (end) {
Token::eraseTokens(tok,end);
tok->deleteThis();
}
}
else if (Token::Match(tok, "%type% <") &&
(!tok->previous() || tok->previous()->str() == ";")) {
const Token *tok2 = tok->tokAt(2);
std::string type;
while (Token::Match(tok2, "%type%|%num% ,")) {
type += tok2->str() + ",";
tok2 = tok2->tokAt(2);
}
if (Token::Match(tok2, "%type%|%num% > (")) {
type += tok2->str();
tok->str(tok->str() + "<" + type + ">");
Token::eraseTokens(tok, tok2->tokAt(2));
if (tok == mTokenList.front())
goback = true;
}
}
}
}
void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates()
{
// check for more complicated syntax errors when using templates..
for (const Token *tok = mTokenList.front(); tok; tok = tok->next()) {
// skip executing scopes (ticket #3183)..
if (Token::simpleMatch(tok, "( {")) {
tok = tok->link();
if (!tok)
syntaxError(nullptr);
}
// skip executing scopes..
const Token *start = Tokenizer::startOfExecutableScope(tok);
if (start) {
tok = start->link();
}
// skip executing scopes (ticket #1985)..
else if (Token::simpleMatch(tok, "try {")) {
tok = tok->next()->link();
while (Token::simpleMatch(tok, "} catch (")) {
tok = tok->linkAt(2);
if (Token::simpleMatch(tok, ") {"))
tok = tok->next()->link();
}
}
if (!tok)
syntaxError(nullptr);
// not start of statement?
if (tok->previous() && !Token::Match(tok, "[;{}]"))
continue;
// skip starting tokens.. ;;; typedef typename foo::bar::..
while (Token::simpleMatch(tok, ";"))
tok = tok->next();
while (Token::Match(tok, "typedef|typename"))
tok = tok->next();
while (Token::Match(tok, "%type% ::"))
tok = tok->tokAt(2);
if (!tok)
break;
// template variable or type..
if (Token::Match(tok, "%type% <")) {
// these are used types..
std::set<std::string> usedtypes;
// parse this statement and see if the '<' and '>' are matching
unsigned int level = 0;
for (const Token *tok2 = tok; tok2 && !Token::simpleMatch(tok2, ";"); tok2 = tok2->next()) {
if (Token::simpleMatch(tok2, "{") && (!Token::Match(tok2->previous(), ">|%type%") || Token::simpleMatch(tok2->link(), "} ;")))
break;
if (tok2->str() == "(")
tok2 = tok2->link();
else if (tok2->str() == "<") {
bool inclevel = false;
if (Token::simpleMatch(tok2->previous(), "operator <"))
;
else if (level == 0)
inclevel = true;
else if (tok2->next() && tok2->next()->isStandardType())
inclevel = true;
else if (Token::simpleMatch(tok2, "< typename"))
inclevel = true;
else if (Token::Match(tok2->tokAt(-2), "<|, %type% <") && usedtypes.find(tok2->previous()->str()) != usedtypes.end())
inclevel = true;
else if (Token::Match(tok2, "< %type%") && usedtypes.find(tok2->next()->str()) != usedtypes.end())
inclevel = true;
else if (Token::Match(tok2, "< %type%")) {
// is the next token a type and not a variable/constant?
// assume it's a type if there comes another "<"
const Token *tok3 = tok2->next();
while (Token::Match(tok3, "%type% ::"))
tok3 = tok3->tokAt(2);
if (Token::Match(tok3, "%type% <"))
inclevel = true;
}
if (inclevel) {
++level;
if (Token::Match(tok2->tokAt(-2), "<|, %type% <"))
usedtypes.insert(tok2->previous()->str());
}
} else if (tok2->str() == ">") {
if (level > 0)
--level;
} else if (tok2->str() == ">>") {
if (level > 0)
--level;
if (level > 0)
--level;
}
}
if (level > 0)
syntaxError(tok);
}
}
}
unsigned int TemplateSimplifier::templateParameters(const Token *tok)
{
unsigned int numberOfParameters = 1;
if (!tok)
return 0;
if (tok->str() != "<")
return 0;
tok = tok->next();
unsigned int level = 0;
while (tok) {
// skip const/volatile
if (Token::Match(tok, "const|volatile"))
tok = tok->next();
// skip struct/union
if (Token::Match(tok, "struct|union"))
tok = tok->next();
// Skip '&'
if (Token::Match(tok, "& ::| %name%"))
tok = tok->next();
// Skip variadic types (Ticket #5774, #6059, #6172)
if (Token::Match(tok, "%type% . . .")) {
tok = tok->tokAt(4);
continue;
}
// Skip '=', '?', ':'
if (Token::Match(tok, "=|?|:"))
tok = tok->next();
if (!tok)
return 0;
// Skip casts
if (tok->str() == "(") {
tok = tok->link();
if (tok)
tok = tok->next();
}
// skip std::
if (tok && tok->str() == "::")
tok = tok->next();
while (Token::Match(tok, "%name% ::")) {
tok = tok->tokAt(2);
if (tok && tok->str() == "*") // Ticket #5759: Class member pointer as a template argument; skip '*'
tok = tok->next();
}
if (!tok)
return 0;
// num/type ..
if (!tok->isNumber() && tok->tokType() != Token::eChar && !tok->isName() && !tok->isOp())
return 0;
tok = tok->next();
if (!tok)
return 0;
// * / const
while (Token::Match(tok, "*|&|&&|const"))
tok = tok->next();
if (!tok)
return 0;
// Function pointer or prototype..
while (Token::Match(tok, "(|[")) {
if (!tok->link())
syntaxError(tok);
tok = tok->link()->next();
while (Token::Match(tok, "const|volatile")) // Ticket #5786: Skip function cv-qualifiers
tok = tok->next();
}
if (!tok)
return 0;
// inner template
if (tok->str() == "<") {
++level;
tok = tok->next();
}
if (!tok)
return 0;
// ,/>
while (Token::Match(tok, ">|>>")) {
if (level == 0)
return ((tok->str() == ">") ? numberOfParameters : 0);
--level;
if (tok->str() == ">>") {
if (level == 0)
return numberOfParameters;
--level;
}
tok = tok->next();
if (Token::simpleMatch(tok,"("))
tok = tok->link()->next();
if (!tok)
return 0;
}
if (tok->str() != ",")
continue;
if (level == 0)
++numberOfParameters;
tok = tok->next();
}
return 0;
}
void TemplateSimplifier::eraseTokens(Token *begin, const Token *end)
{
if (!begin || begin == end)
return;
while (begin->next() && begin->next() != end) {
begin->deleteNext();
}
}
void TemplateSimplifier::deleteToken(Token *tok)
{
if (tok->next())
tok->next()->deletePrevious();
else
tok->deleteThis();
}
bool TemplateSimplifier::removeTemplate(Token *tok)
{
if (!Token::simpleMatch(tok, "template <"))
return false;
int indentlevel = 0;
unsigned int countgt = 0; // Counter for ">"
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
if (tok2->str() == "(") {
tok2 = tok2->link();
} else if (tok2->str() == ")") { // garbage code! (#3504)
eraseTokens(tok,tok2);
deleteToken(tok);
return false;
}
else if (tok2->str() == "{") {
tok2 = tok2->link()->next();
if (tok2 && tok2->str() == ";" && tok2->next())
tok2 = tok2->next();
eraseTokens(tok, tok2);
deleteToken(tok);
return true;
} else if (tok2->str() == "}") { // garbage code! (#3449)
eraseTokens(tok,tok2);
deleteToken(tok);
return false;
}
// Count ">"
if (tok2->str() == ">")
countgt++;
// don't remove constructor
if (tok2->str() == "explicit" ||
(countgt == 1 && Token::Match(tok2->previous(), "> %type% (") &&
Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) {
eraseTokens(tok, tok2);
deleteToken(tok);
return true;
}
if (tok2->str() == ";") {
tok2 = tok2->next();
eraseTokens(tok, tok2);
deleteToken(tok);
return true;
}
if (tok2->str() == "<")
++indentlevel;
else if (indentlevel >= 2 && tok2->str() == ">")
--indentlevel;
else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) {
tok2 = tok2->next();
eraseTokens(tok, tok2);
deleteToken(tok);
return true;
}
}
return false;
}
/// TODO: This is copy pasted from Tokenizer. We should reuse this code.
namespace {
struct ScopeInfo2 {
ScopeInfo2(const std::string &name_, const Token *bodyEnd_) : name(name_), bodyEnd(bodyEnd_) {}
const std::string name;
const Token * const bodyEnd;
std::set<std::string> usingNamespaces;
};
}
static std::string getScopeName(const std::list<ScopeInfo2> &scopeInfo)
{
std::string ret;
for (const ScopeInfo2 &i : scopeInfo) {
if (!i.name.empty())
ret += (ret.empty() ? "" : " :: ") + i.name;
}
return ret;
}
static void setScopeInfo(Token *tok, std::list<ScopeInfo2> *scopeInfo, bool all = false)
{
while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd)
scopeInfo->pop_back();
if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::")) {
// check for using namespace
if (Token::Match(tok, "using namespace %name% ;|::")) {
const Token * tok1 = tok->tokAt(2);
std::string nameSpace;
while (tok1 && tok1->str() != ";") {
if (!nameSpace.empty())
nameSpace += " ";
nameSpace += tok1->str();
tok1 = tok1->next();
}
scopeInfo->back().usingNamespaces.insert(nameSpace);
}
// check for member function
else if (tok->str() == "{") {
bool added = false;
Token *tok1 = tok;
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
tok1 = tok1->previous();
if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") {
tok1 = tok1->linkAt(-1);
if (Token::Match(tok1->previous(), "throw|noexcept")) {
tok1 = tok1->previous();
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
tok1 = tok1->previous();
if (tok1->strAt(-1) != ")")
return;
} else if (Token::Match(tok->tokAt(-2), ":|, %name%")) {
tok1 = tok1->tokAt(-2);
if (tok1->strAt(-1) != ")")
return;
}
if (tok1->strAt(-1) == ">")
tok1 = tok1->previous()->findOpeningBracket();
if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) {
tok1 = tok1->tokAt(-2);
std::string scope = tok1->strAt(-1);
while (Token::Match(tok1->tokAt(-2), ":: %name%")) {
scope = tok1->strAt(-3) + " :: " + scope;
tok1 = tok1->tokAt(-2);
}
scopeInfo->emplace_back(scope, tok->link());
added = true;
}
}
if (all && !added)
scopeInfo->emplace_back("", tok->link());
}
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() == ":") {
while (tok && !Token::Match(tok, ";|{"))
tok = tok->next();
}
if (tok && tok->str() == "{") {
scopeInfo->emplace_back(classname,tok->link());
}
}
bool TemplateSimplifier::getTemplateDeclarations()
{
bool codeWithTemplates = false;
std::list<ScopeInfo2> scopeInfo;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "{|}|namespace|class|struct|union")) {
setScopeInfo(tok, &scopeInfo);
continue;
}
if (!Token::simpleMatch(tok, "template <"))
continue;
// Some syntax checks, see #6865
if (!tok->tokAt(2))
syntaxError(tok->next());
if (tok->strAt(2)=="typename" &&
!Token::Match(tok->tokAt(3), "%name%|.|,|=|>"))
syntaxError(tok->next());
codeWithTemplates = true;
const Token * const parmEnd = tok->next()->findClosingBracket();
for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) {
if (tok2->str() == "(" && tok2->link())
tok2 = tok2->link();
else if (tok2->str() == ")")
break;
// skip decltype(...)
else if (Token::simpleMatch(tok2, "decltype ("))
tok2 = tok2->linkAt(1);
else if (Token::Match(tok2, "{|=|;")) {
const int namepos = getTemplateNamePosition(parmEnd);
if (namepos > 0) {
TokenAndName decl(tok, getScopeName(scopeInfo), parmEnd->tokAt(namepos), parmEnd);
if (decl.isForwardDeclaration()) {
// Declaration => add to mTemplateForwardDeclarations
mTemplateForwardDeclarations.emplace_back(decl);
} else {
// Implementation => add to mTemplateDeclarations
mTemplateDeclarations.emplace_back(decl);
}
break;
}
}
}
}
return codeWithTemplates;
}
void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope)
{
simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket());
mTemplateInstantiations.emplace_back(token, scope);
}
void TemplateSimplifier::getTemplateInstantiations()
{
std::multimap<std::string, const TokenAndName *> functionNameMap;
for (const auto & decl : mTemplateDeclarations) {
if (decl.isFunction())
functionNameMap.insert(std::make_pair(decl.name, &decl));
}
for (const auto & decl : mTemplateForwardDeclarations) {
if (decl.isFunction())
functionNameMap.insert(std::make_pair(decl.name, &decl));
}
std::list<ScopeInfo2> scopeList;
const Token *skip = nullptr;
scopeList.emplace_back("", nullptr);
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "{|}|namespace|class|struct|union") ||
Token::Match(tok, "using namespace %name% ;|::")) {
setScopeInfo(tok, &scopeList);
continue;
}
// template definition.. skip it
if (Token::simpleMatch(tok, "template <")) {
tok = tok->next()->findClosingBracket();
if (!tok)
break;
const bool isUsing = tok->strAt(1) == "using";
if (tok->strAt(-1) == "<") {
// Don't ignore user specialization but don't consider it an instantiation.
// Instantiations in return type, function parameters, and executable code
// are not ignored.
unsigned int pos = getTemplateNamePosition(tok);
if (pos > 0)
skip = tok->tokAt(pos);
} else {
// #7914
// Ignore template instantiations within template definitions: they will only be
// handled if the definition is actually instantiated
const Token *tok2 = Token::findmatch(tok, "{|;");
if (tok2 && tok2->str() == "{")
tok = tok2->link();
else if (!isUsing && tok2 && tok2->str() == ";")
tok = const_cast<Token *>(tok2);
}
} else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|< %name% ::|<|(") ||
Token::Match(tok->previous(), "%type% %name% ::|<") ||
Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) {
std::string scopeName = getScopeName(scopeList);
std::string qualification;
Token * qualificationTok = tok;
while (Token::Match(tok, "%name% :: %name%")) {
// ignore redundant namespaces
if (scopeName.find(tok->str()) == std::string::npos)
qualification += (qualification.empty() ? "" : " :: ") + tok->str();
tok = tok->tokAt(2);
}
// look for function instantiation with type deduction
// fixme: only single argument functions supported
if (tok->strAt(1) == "(") {
std::string fullName = qualification + (qualification.empty() ? "" : " :: ") + tok->str();
// get all declarations with this name
for (auto pos = functionNameMap.lower_bound(tok->str());
pos != functionNameMap.upper_bound(tok->str()); ++pos) {
// look for declaration with same qualification
if (pos->second->fullName == fullName) {
// make sure it is a single argument function
if (Token::Match(pos->second->token->tokAt(2), "typename|class %name% >") &&
Token::Match(pos->second->nameToken->tokAt(2), "const| %type% &| %name%| )") &&
Token::Match(tok->tokAt(2), "%num%|%str%|%char%|%bool% )")) {
tok->insertToken(">");
switch (tok->tokAt(3)->tokType()) {
case Token::eBoolean:
tok->insertToken("bool");
break;
case Token::eChar:
if (tok->tokAt(3)->isLong())
tok->insertToken("wchar_t");
else
tok->insertToken("char");
break;
case Token::eString:
tok->insertToken("*");
if (tok->tokAt(4)->isLong())
tok->insertToken("wchar_t");
else
tok->insertToken("char");
tok->insertToken("const");
break;
case Token::eNumber: {
MathLib::value num(tok->strAt(3));
if (num.isFloat()) {
// MathLib::getSuffix doesn't work for floating point numbers
char suffix = tok->strAt(3).back();
if (suffix == 'f' || suffix == 'F')
tok->insertToken("float");
else if (suffix == 'l' || suffix == 'L') {
tok->insertToken("double");
tok->insertToken("long");
} else
tok->insertToken("double");
} else if (num.isInt()) {
std::string suffix = MathLib::getSuffix(tok->strAt(3));
if (suffix.find("LL") != std::string::npos) {
tok->insertToken("long");
tok->insertToken("long");
} else if (suffix.find('L') != std::string::npos)
tok->insertToken("long");
else
tok->insertToken("int");
if (suffix.find('U') != std::string::npos)
tok->insertToken("unsigned");
}
break;
}
default:
break;
}
tok->insertToken("<");
break;
}
}
}
}
if (!Token::Match(tok, "%name% <") ||
Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast"))
continue;
if (tok == skip) {
skip = nullptr;
continue;
}
// Add inner template instantiations first => go to the ">"
// and then parse backwards, adding all seen instantiations
const Token *tok2 = tok->next()->findClosingBracket();
// parse backwards and add template instantiations
// TODO
for (; tok2 && tok2 != tok; tok2 = tok2->previous()) {
if (Token::Match(tok2, ", %name% <") &&
templateParameters(tok2->tokAt(2))) {
addInstantiation(tok2->next(), getScopeName(scopeList));
} else if (Token::Match(tok2->next(), "class|struct"))
const_cast<Token *>(tok2)->deleteNext();
}
// Add outer template..
if (templateParameters(tok->next())) {
const std::string scopeName1(scopeName);
while (true) {
const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") +
qualification + (qualification.empty()?"":" :: ") + tok->str();
const std::list<TokenAndName>::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(fullName));
if (it != mTemplateDeclarations.end()) {
// full name matches
addInstantiation(tok, it->scope);
break;
} else {
// full name doesn't match so try with using namespaces if available
bool found = false;
for (const auto & nameSpace : scopeList.back().usingNamespaces) {
std::string fullNameSpace = scopeName + (scopeName.empty()?"":" :: ") +
nameSpace + (qualification.empty()?"":" :: ") + qualification;
std::string newFullName = fullNameSpace + " :: " + tok->str();
const std::list<TokenAndName>::const_iterator it1 = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(newFullName));
if (it1 != mTemplateDeclarations.end()) {
// insert using namespace into token stream
std::string::size_type offset = 0;
std::string::size_type pos = 0;
while ((pos = nameSpace.substr(offset).find(' ')) != std::string::npos) {
qualificationTok->insertToken(nameSpace.substr(offset, pos), "", true);
offset = offset + pos + 1;
}
qualificationTok->insertToken(nameSpace.substr(offset), "", true);
qualificationTok->insertToken("::", "", true);
addInstantiation(tok, it1->scope);
found = true;
break;
}
}
if (found)
break;
if (scopeName.empty()) {
if (!qualification.empty())
addInstantiation(tok, qualification);
else
addInstantiation(tok, getScopeName(scopeList));
break;
}
const std::string::size_type pos = scopeName.rfind(" :: ");
scopeName = (pos == std::string::npos) ? std::string() : scopeName.substr(0,pos);
}
}
}
}
}
}
void TemplateSimplifier::useDefaultArgumentValues()
{
for (TokenAndName &template1 : mTemplateDeclarations) {
// template parameters with default value has syntax such as:
// x = y
// this list will contain all the '=' tokens for such arguments
std::list<Token *> eq;
// and this set the position of parameters with a default value
std::set<std::size_t> defaultedArgPos;
// parameter number. 1,2,3,..
std::size_t templatepar = 1;
// parameter depth
std::size_t templateParmDepth = 0;
// the template classname. This will be empty for template functions
std::string classname;
// Scan template declaration..
for (Token *tok = template1.token; tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "template < >")) { // Ticket #5762: Skip specialization tokens
tok = tok->tokAt(2);
if (0 == templateParmDepth)
break;
continue;
}
if (tok->str() == "(") { // Ticket #6835
tok = tok->link();
continue;
}
if (tok->str() == "<" && templateParameters(tok))
++templateParmDepth;
// end of template parameters?
if (tok->str() == ">") {
if (Token::Match(tok, "> class|struct|union %name%"))
classname = tok->strAt(2);
if (templateParmDepth<2)
break;
else
--templateParmDepth;
}
// next template parameter
if (tok->str() == "," && (1 == templateParmDepth)) // Ticket #5823: Properly count parameters
++templatepar;
// default parameter value?
else if (Token::Match(tok, "= !!>")) {
if (defaultedArgPos.insert(templatepar).second) {
eq.push_back(tok);
} else {
// Ticket #5605: Syntax error (two equal signs for the same parameter), bail out
eq.clear();
break;
}
}
}
if (eq.empty() || classname.empty())
continue;
// iterate through all template instantiations
for (const TokenAndName &templateInst : mTemplateInstantiations) {
Token *tok = templateInst.token;
if (!Token::simpleMatch(tok, (classname + " <").c_str()))
continue;
// count the parameters..
tok = tok->next();
const unsigned int usedpar = templateParameters(tok);
tok = tok->findClosingBracket();
if (tok && tok->str() == ">") {
tok = tok->previous();
std::list<Token *>::const_iterator it = eq.begin();
for (std::size_t i = (templatepar - eq.size()); it != eq.end() && i < usedpar; ++i)
++it;
while (it != eq.end()) {
int indentlevel = 0;
tok->insertToken(",");
tok = tok->next();
const Token *from = (*it)->next();
std::stack<Token *> links;
while (from && (!links.empty() || indentlevel || !Token::Match(from, ",|>"))) {
if (from->str() == "<")
++indentlevel;
else if (from->str() == ">")
--indentlevel;
tok->insertToken(from->str(), from->originalName());
tok = tok->next();
if (Token::Match(tok, "(|["))
links.push(tok);
else if (!links.empty() && Token::Match(tok, ")|]")) {
Token::createMutualLinks(links.top(), tok);
links.pop();
}
from = from->next();
}
++it;
}
}
}
for (Token * const eqtok : eq) {
Token *tok2;
int indentlevel = 0;
for (tok2 = eqtok->next(); tok2; tok2 = tok2->next()) {
if (Token::Match(tok2, ";|)|}|]")) { // bail out #6607
tok2 = nullptr;
break;
}
if (Token::Match(tok2, "(|{|["))
tok2 = tok2->link();
else if (Token::Match(tok2, "%type% <") && templateParameters(tok2->next())) {
std::list<TokenAndName>::iterator ti = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindToken(tok2));
if (ti != mTemplateInstantiations.end())
mTemplateInstantiations.erase(ti);
++indentlevel;
} else if (indentlevel > 0 && tok2->str() == ">")
--indentlevel;
else if (indentlevel == 0 && Token::Match(tok2, ",|>"))
break;
if (indentlevel < 0)
break;
}
// something went wrong, don't call eraseTokens()
// with a nullptr "end" parameter (=all remaining tokens).
if (!tok2)
continue;
// 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();
// update parameter end pointer
template1.paramEnd = template1.token->next()->findClosingBracket();
}
}
}
void TemplateSimplifier::simplifyTemplateAliases()
{
std::list<TokenAndName>::iterator it1, it2;
for (it1 = mTemplateInstantiations.begin(); it1 != mTemplateInstantiations.end();) {
TokenAndName &templateAlias = *it1;
++it1;
Token *startToken = templateAlias.token;
while (Token::Match(startToken->tokAt(-2), "%name% :: %name%"))
startToken = startToken->tokAt(-2);
if (!Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<"))
continue;
const std::string aliasName(startToken->strAt(-2));
const Token * const aliasToken1 = startToken;
// Get start token for alias
startToken = startToken->tokAt(-5);
while (Token::Match(startToken, "%name%|<|>|>>|,"))
startToken = startToken->previous();
// handle case where 'template' is first token
if (!startToken) {
if (!Token::simpleMatch(mTokenList.front(), "template <"))
continue;
} else if (!Token::Match(startToken, "[;{}] template <"))
continue;
// alias parameters..
std::vector<const Token *> aliasParameters;
getTemplateParametersInDeclaration(startToken ? startToken->tokAt(3) : mTokenList.front()->tokAt(2), aliasParameters);
std::map<std::string, unsigned int> aliasParameterNames;
for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr)
aliasParameterNames[aliasParameters[argnr]->str()] = argnr;
// Look for alias usages..
const Token *endToken = nullptr;
for (it2 = it1; it2 != mTemplateInstantiations.end(); ++it2) {
TokenAndName &aliasUsage = *it2;
if (aliasUsage.name != aliasName)
continue;
std::vector<std::pair<Token *, Token *>> args;
Token *tok2 = aliasUsage.token->tokAt(2);
while (tok2) {
Token * const start = tok2;
while (tok2 && !Token::Match(tok2, "[,>;{}]")) {
if (tok2->link() && Token::Match(tok2, "(|<|["))
tok2 = tok2->link();
tok2 = tok2->next();
}
args.emplace_back(start, tok2);
if (tok2 && tok2->str() == ",") {
tok2 = tok2->next();
} else {
break;
}
}
if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size())
continue;
// Replace template alias code..
aliasUsage.name = templateAlias.name;
if (aliasUsage.name.find(' ') == std::string::npos) {
const Token *temp = aliasToken1;
while (temp && temp != templateAlias.token) {
aliasUsage.token->insertToken(temp->str(), "", true);
temp = temp->next();
}
aliasUsage.token->str(templateAlias.token->str());
} else {
tok2 = TokenList::copyTokens(aliasUsage.token, aliasToken1, templateAlias.token, true);
deleteToken(aliasUsage.token);
aliasUsage.token = tok2;
}
tok2 = aliasUsage.token->next(); // the '<'
const Token * const endToken1 = templateAlias.token->next()->findClosingBracket();
const Token * const endToken2 = TokenList::copyTokens(tok2, templateAlias.token->tokAt(2), endToken1->previous(), false);
for (const Token *tok1 = templateAlias.token->next(); tok2 != endToken2; tok1 = tok1->next(), tok2 = tok2->next()) {
if (!tok2->isName())
continue;
if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) {
// Create template instance..
if (Token::Match(tok1, "%name% <")) {
const std::list<TokenAndName>::iterator it = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindToken(tok1));
if (it != mTemplateInstantiations.end())
addInstantiation(tok2, it->scope);
}
continue;
}
const unsigned int argnr = aliasParameterNames[tok2->str()];
const Token * const fromStart = args[argnr].first;
const Token * const fromEnd = args[argnr].second->previous();
Token * const destToken = tok2;
tok2 = TokenList::copyTokens(tok2, fromStart, fromEnd, true);
if (tok2 == destToken->next())
tok2 = destToken;
destToken->deleteThis();
}
endToken = endToken1->next();
// Remove alias usage code (parameters)
Token::eraseTokens(tok2, args.back().second);
}
if (endToken) {
// Remove all template instantiations in template alias
for (const Token *tok = startToken ? startToken : mTokenList.front(); tok != endToken; tok = tok->next()) {
if (!Token::Match(tok, "%name% <"))
continue;
std::list<TokenAndName>::iterator it = std::find_if(mTemplateInstantiations.begin(),
mTemplateInstantiations.end(),
FindToken(tok));
if (it == mTemplateInstantiations.end())
continue;
std::list<TokenAndName>::iterator next = it;
++next;
if (it == it1)
it1 = next;
mTemplateInstantiations.erase(it,next);
}
// find declaration
const std::list<TokenAndName>::iterator it3 = std::find_if(mTemplateDeclarations.begin(),
mTemplateDeclarations.end(),
FindToken(startToken ? startToken->next() : mTokenList.front()));
if (startToken)
eraseTokens(startToken, endToken);
else {
eraseTokens(mTokenList.front(), endToken);
deleteToken(mTokenList.front());
}
// remove declaration
if (it3 != mTemplateDeclarations.end())
mTemplateDeclarations.erase(it3);
}
}
}
bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[])
{
// if (!Token::simpleMatch(instance, (name + " <").c_str()))
// return false;
if (numberOfArguments != templateParameters(instance->next()))
return false;
if (patternAfter) {
const Token *tok = instance;
unsigned int indentlevel = 0;
for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0); tok = tok->next()) {
if (Token::Match(tok, "<|,|(|:: %name% <") && templateParameters(tok->tokAt(2)) > 0)
++indentlevel;
if (indentlevel > 0 && tok->str() == ">")
--indentlevel;
}
if (!tok || !Token::Match(tok->next(), patternAfter))
return false;
}
// nothing mismatching was found..
return true;
}
// Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template functions
bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos)
{
namepos = 1;
while (tok && tok->next()) {
if (Token::Match(tok->next(), ";|{"))
return false;
// skip decltype(...)
else if (Token::simpleMatch(tok, "decltype (")) {
const Token * end = tok->linkAt(1);
while (tok && tok != end) {
tok = tok->next();
namepos++;
}
} else if (Token::Match(tok->next(), "%type% <")) {
const Token *closing = tok->tokAt(2)->findClosingBracket();
if (closing) {
if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:", true))
return true;
while (tok && tok->next() && tok->next() != closing) {
tok = tok->next();
namepos++;
}
}
} else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:", true)) {
return true;
}
tok = tok->next();
namepos++;
}
return false;
}
bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos)
{
namepos = 1;
while (tok && tok->next()) {
if (Token::Match(tok->next(), ";|{|(|using"))
return false;
else if (Token::Match(tok->next(), "%type% <")) {
const Token *closing = tok->tokAt(2)->findClosingBracket();
if (closing) {
if (Token::Match(closing->next(), "=|;"))
return true;
while (tok && tok->next() && tok->next() != closing) {
tok = tok->next();
namepos++;
}
}
} else if (Token::Match(tok->next(), "%type% =|;")) {
return true;
}
tok = tok->next();
namepos++;
}
return false;
}
int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
{
// get the position of the template name
int namepos = 0;
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{"))
namepos = 2;
else if (Token::Match(tok, "> using %name% ="))
namepos = 2;
else if (getTemplateNamePositionTemplateVariable(tok, namepos))
;
else if (!getTemplateNamePositionTemplateFunction(tok, namepos))
return -1; // Name not found
return namepos;
}
void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok)
{
// find start of qualification
const Token * tokStart = tok;
int offset = 0;
while (Token::Match(tokStart->tokAt(-2), "%name% ::")) {
tokStart = tokStart->tokAt(-2);
offset -= 2;
}
// decide if namespace needs to be inserted in or appended to token list
const bool insert = tokStart != tok;
std::string::size_type start = 0;
std::string::size_type end = 0;
while ((end = templateDeclaration.scope.find(" ", start)) != std::string::npos) {
std::string token = templateDeclaration.scope.substr(start, end - start);
// done if scopes overlap
if (token == tokStart->str() && tok->strAt(-1) != "::")
break;
if (insert)
mTokenList.back()->tokAt(offset)->insertToken(token, "");
else
mTokenList.addtoken(token, tok->linenr(), tok->fileIndex());
start = end + 1;
}
// don't add if it already exists
std::string token = templateDeclaration.scope.substr(start, end - start);
if (token != tokStart->str() || tok->strAt(-1) != "::") {
if (insert) {
mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope.substr(start), "");
mTokenList.back()->tokAt(offset)->insertToken("::", "");
} else {
mTokenList.addtoken(templateDeclaration.scope.substr(start), tok->linenr(), tok->fileIndex());
mTokenList.addtoken("::", tok->linenr(), tok->fileIndex());
}
}
}
bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok)
{
std::string scope = templateDeclaration.scope;
// get the length in tokens of the namespace
std::string::size_type pos = 0;
int offset = -2;
while ((pos = scope.find("::", pos)) != std::string::npos) {
offset -= 2;
pos += 2;
}
return Token::simpleMatch(tok->tokAt(offset), scope.c_str()) ;
}
void TemplateSimplifier::expandTemplate(
const TokenAndName &templateDeclaration,
const TokenAndName &templateInstantiation,
const std::vector<const Token *> &typeParametersInDeclaration,
const std::string &newName,
bool copy)
{
std::list<ScopeInfo2> scopeInfo;
bool inTemplateDefinition = false;
const Token *startOfTemplateDeclaration = nullptr;
const Token *endOfTemplateDefinition = nullptr;
const Token * const templateDeclarationNameToken = templateDeclaration.nameToken;
const Token * const templateDeclarationToken = templateDeclaration.paramEnd;
const bool isClass = templateDeclaration.isClass();
const bool isFunction = templateDeclaration.isFunction();
const bool isSpecialization = templateDeclaration.isSpecialization();
const bool isVariable = templateDeclaration.isVariable();
struct newInstantiation {
newInstantiation(Token *t, const std::string &s) : token(t), scope(s) { }
Token *token;
std::string scope;
};
std::vector<newInstantiation> newInstantiations;
// add forward declarations
if (copy && isClass) {
templateDeclaration.token->insertToken(templateDeclarationToken->strAt(1), "", true);
templateDeclaration.token->insertToken(newName, "", true);
templateDeclaration.token->insertToken(";", "", true);
} else if ((isFunction && (copy || isSpecialization)) || (isVariable && !isSpecialization)) {
Token * dst = templateDeclaration.token;
Token * dstStart = dst->previous();
bool isStatic = false;
std::string scope;
Token * start;
Token * end;
auto it = mTemplateForwardDeclarationsMap.find(dst);
if (it != mTemplateForwardDeclarationsMap.end()) {
dst = it->second;
dstStart = dst->previous();
const Token * temp1 = dst->tokAt(1)->findClosingBracket();
const Token * temp2 = temp1->tokAt(getTemplateNamePosition(temp1));
start = temp1->next();
end = temp2->linkAt(1)->next();
} else {
auto it2 = mTemplateSpecializationMap.find(dst);
if (it2 != mTemplateSpecializationMap.end()) {
dst = it2->second;
dstStart = dst->previous();
isStatic = dst->next()->findClosingBracket()->strAt(1) == "static";
const Token * temp = templateDeclarationNameToken;
while (Token::Match(temp->tokAt(-2), "%name% ::")) {
scope.insert(0, temp->strAt(-2) + " :: ");
temp = temp->tokAt(-2);
}
}
start = templateDeclarationToken->next();
end = templateDeclarationNameToken->next();
if (end->str() == "<")
end = end->findClosingBracket()->next();
if (end->str() == "(")
end = end->link()->next();
else if (isVariable && end->str() == "=")
end = const_cast<Token *>(Token::findsimplematch(templateDeclarationNameToken, ";"));
}
unsigned int typeindentlevel = 0;
while (!(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) {
if (Token::Match(end, "<|(|{"))
++typeindentlevel;
else if (Token::Match(end, ">|)|}"))
--typeindentlevel;
end = end->next();
}
if (isStatic)
dst->insertToken("static", "", true);
std::map<const Token *, Token *> links;
bool inAssignment = false;
while (start && start != end) {
if (isVariable && start->str() == "=")
inAssignment = true;
unsigned int itype = 0;
while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != start->str())
++itype;
if (itype < typeParametersInDeclaration.size()) {
typeindentlevel = 0;
for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token;
typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>"));
typetok = typetok->next()) {
if (Token::simpleMatch(typetok, ". . .")) {
typetok = typetok->tokAt(2);
continue;
}
if (Token::Match(typetok, "%name% <") && templateParameters(typetok->next()) > 0)
++typeindentlevel;
else if (typeindentlevel > 0 && typetok->str() == ">")
--typeindentlevel;
dst->insertToken(typetok->str(), typetok->originalName(), true);
dst->previous()->isTemplateArg(true);
dst->previous()->isSigned(typetok->isSigned());
dst->previous()->isUnsigned(typetok->isUnsigned());
dst->previous()->isLong(typetok->isLong());
}
} else {
if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) {
// skip scope
while (start->strAt(1) != templateDeclarationNameToken->str())
start = start->next();
} else if (start->str() == templateDeclarationNameToken->str()) {
if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) {
dst->insertToken(newName, "", true);
if (start->strAt(1) == "<")
start = start->next()->findClosingBracket();
} else {
dst->insertToken(start->str(), "", true);
newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope);
}
} else {
// check if type is a template
if (start->strAt(1) == "<") {
// get the instantiated name
Token * closing = start->next()->findClosingBracket();
std::string name;
const Token * type = start;
while (type && type != closing->next()) {
if (!name.empty())
name += " ";
name += type->str();
type = type->next();
}
// check if type is instantiated
for (const auto & inst : mTemplateInstantiations) {
if (Token::simpleMatch(inst.token, name.c_str())) {
// use the instantiated name
dst->insertToken(name, "", true);
start = closing;
break;
}
}
// just copy the token if it wasn't instantiated
if (start != closing) {
dst->insertToken(start->str(), start->originalName(), true);
dst->previous()->isSigned(start->isSigned());
dst->previous()->isUnsigned(start->isUnsigned());
dst->previous()->isLong(start->isLong());
}
} else {
dst->insertToken(start->str(), start->originalName(), true);
dst->previous()->isSigned(start->isSigned());
dst->previous()->isUnsigned(start->isUnsigned());
dst->previous()->isLong(start->isLong());
}
}
if (start->link()) {
if (Token::Match(start, "[|{|(")) {
links[start->link()] = dst->previous();
} else if (Token::Match(start, "]|}|)")) {
Token::createMutualLinks(links[start], dst->previous());
links.erase(start);
}
}
}
start = start->next();
}
dst->insertToken(";", "", true);
if (isVariable)
simplifyTemplateArgs(dstStart, dst);
}
if (copy && (isClass || isFunction)) {
// check if this is an explicit instantiation
Token * start = templateInstantiation.token;
while (start && !Token::Match(start->previous(), "}|;|extern"))
start = start->previous();
if (Token::Match(start, "template !!<")) {
if (start->strAt(-1) == "extern")
start = start->previous();
mExplicitInstantiationsToDelete.emplace_back(start, "");
}
}
for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) {
if (Token::Match(tok3, "{|}|namespace|class|struct|union")) {
setScopeInfo(tok3, &scopeInfo);
continue;
}
if (inTemplateDefinition) {
if (!endOfTemplateDefinition) {
if (isVariable)
endOfTemplateDefinition = Token::findsimplematch(tok3, ";");
else if (tok3->str() == "{")
endOfTemplateDefinition = tok3->link();
}
if (tok3 == endOfTemplateDefinition) {
inTemplateDefinition = false;
startOfTemplateDeclaration = nullptr;
}
}
if (tok3->str()=="template") {
if (tok3->next() && tok3->next()->str()=="<") {
std::vector<const Token *> localTypeParametersInDeclaration;
getTemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration);
if (localTypeParametersInDeclaration.size() != typeParametersInDeclaration.size())
inTemplateDefinition = false; // Partial specialization
else
inTemplateDefinition = true;
} else {
inTemplateDefinition = false; // Only template instantiation
}
startOfTemplateDeclaration = tok3;
}
if (Token::Match(tok3, "(|["))
tok3 = tok3->link();
// Start of template..
if (tok3 == templateDeclarationToken) {
tok3 = tok3->next();
if (tok3->str() == "static")
tok3 = tok3->next();
}
// member function implemented outside class definition
else if (inTemplateDefinition &&
Token::Match(tok3, "%name% <") &&
templateInstantiation.name == tok3->str() &&
instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) {
// there must be template..
bool istemplate = false;
Token * tok5 = nullptr; // start of function return type
for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) {
if (prev->str() == "template") {
istemplate = true;
tok5 = prev;
break;
}
}
if (!istemplate)
continue;
const Token *tok4 = tok3->next()->findClosingBracket();
while (tok4 && tok4->str() != "(")
tok4 = tok4->next();
if (!Tokenizer::isFunctionHead(tok4, ":{", true))
continue;
// find function return type start
tok5 = tok5->next()->findClosingBracket();
if (tok5)
tok5 = tok5->next();
// copy return type
while (tok5 && tok5 != tok3) {
// replace name if found
if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name) {
if (copy) {
if (!templateDeclaration.scope.empty() && tok5->strAt(-1) != "::")
addNamespace(templateDeclaration, tok5);
mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex());
tok5 = tok5->next()->findClosingBracket();
} else {
tok5->str(newName);
eraseTokens(tok5, tok5->next()->findClosingBracket()->next());
}
} else if (copy) {
bool added = false;
if (tok5->isName()) {
// search for this token in the type vector
unsigned int itype = 0;
while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str())
++itype;
// replace type with given type..
if (itype < typeParametersInDeclaration.size()) {
unsigned int typeindentlevel = 0;
for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token;
typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>"));
typetok = typetok->next()) {
if (Token::simpleMatch(typetok, ". . .")) {
typetok = typetok->tokAt(2);
} else {
if (Token::Match(typetok, "%name% <") && templateParameters(typetok->next()) > 0)
++typeindentlevel;
else if (typeindentlevel > 0 && typetok->str() == ">")
--typeindentlevel;
mTokenList.addtoken(typetok, tok5->linenr(), tok5->fileIndex());
mTokenList.back()->isTemplateArg(true);
added = true;
break;
}
}
}
}
if (!added)
mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex());
}
tok5 = tok5->next();
}
if (copy) {
if (!templateDeclaration.scope.empty() && tok3->strAt(-1) != "::")
addNamespace(templateDeclaration, tok3);
mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
}
while (tok3 && tok3->str() != "::")
tok3 = tok3->next();
std::list<TokenAndName>::iterator it = std::find_if(mTemplateDeclarations.begin(),
mTemplateDeclarations.end(),
FindToken(startOfTemplateDeclaration));
if (it != mTemplateDeclarations.end())
mMemberFunctionsToDelete.push_back(*it);
}
// not part of template.. go on to next token
else
continue;
std::stack<Token *> brackets; // holds "(", "[" and "{" tokens
// FIXME use full name matching somehow
const std::string lastName = (templateInstantiation.name.find(' ') != std::string::npos) ? templateInstantiation.name.substr(templateInstantiation.name.rfind(' ')+1) : templateInstantiation.name;
for (; tok3; tok3 = tok3->next()) {
if (tok3->isName()) {
// search for this token in the type vector
unsigned int itype = 0;
while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str())
++itype;
// replace type with given type..
if (itype < typeParametersInDeclaration.size()) {
unsigned int typeindentlevel = 0;
for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token;
typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>"));
typetok = typetok->next()) {
if (Token::simpleMatch(typetok, ". . .")) {
typetok = typetok->tokAt(2);
continue;
}
if (Token::Match(typetok, "%name% <") && templateParameters(typetok->next()) > 0)
++typeindentlevel;
else if (typeindentlevel > 0 && typetok->str() == ">")
--typeindentlevel;
if (copy) {
mTokenList.addtoken(typetok, tok3->linenr(), tok3->fileIndex());
mTokenList.back()->isTemplateArg(true);
}
}
continue;
}
}
// replace name..
if (tok3->str() == lastName) {
if (Token::simpleMatch(tok3->next(), "<")) {
Token *closingBracket = tok3->next()->findClosingBracket();
if (closingBracket) {
// replace multi token name with single token name
if (tok3 == templateDeclarationNameToken ||
Token::Match(tok3, newName.c_str())) {
if (copy) {
mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
tok3 = closingBracket;
} else {
tok3->str(newName);
eraseTokens(tok3, closingBracket->next());
}
continue;
} else if (!templateDeclaration.scope.empty() &&
!alreadyHasNamespace(templateDeclaration, tok3) &&
!Token::Match(closingBracket->next(), "(|::")) {
if (copy)
addNamespace(templateDeclaration, tok3);
}
}
} else {
if (copy) {
// add namespace if necessary
if (!templateDeclaration.scope.empty() &&
(isClass ? tok3->strAt(1) != "(" : true)) {
addNamespace(templateDeclaration, tok3);
}
mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex());
} else if (!Token::Match(tok3->next(), ":|{|="))
tok3->str(newName);
continue;
}
}
// copy
if (copy)
mTokenList.addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <") &&
!Token::Match(tok3, "template|static_cast|const_cast|reinterpret_cast|dynamic_cast") &&
Token::Match(tok3->next()->findClosingBracket(), ">|>>")) {
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 scope;
for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) {
if (scope.empty())
scope = prev->str();
else
scope = prev->str() + " :: " + scope;
}
if (copy)
newInstantiations.emplace_back(mTokenList.back(), scope);
else if (!inTemplateDefinition)
newInstantiations.emplace_back(tok3, scope);
}
// link() newly tokens manually
else if (copy) {
if (tok3->str() == "{") {
brackets.push(mTokenList.back());
} else if (tok3->str() == "(") {
brackets.push(mTokenList.back());
} else if (tok3->str() == "[") {
brackets.push(mTokenList.back());
} else if (tok3->str() == "}") {
assert(brackets.empty() == false && brackets.top()->str() == "{");
Token::createMutualLinks(brackets.top(), mTokenList.back());
if (tok3->strAt(1) == ";") {
const Token * tokSemicolon = tok3->next();
mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex());
}
brackets.pop();
if (brackets.empty()) {
inTemplateDefinition = false;
break;
}
} else if (tok3->str() == ")") {
assert(brackets.empty() == false && brackets.top()->str() == "(");
Token::createMutualLinks(brackets.top(), mTokenList.back());
brackets.pop();
} else if (tok3->str() == "]") {
assert(brackets.empty() == false && brackets.top()->str() == "[");
Token::createMutualLinks(brackets.top(), mTokenList.back());
brackets.pop();
}
}
}
assert(brackets.empty());
}
// add new instantiations
for (const auto & inst : newInstantiations)
addInstantiation(inst.token, inst.scope);
}
static bool isLowerThanLogicalAnd(const Token *lower)
{
return lower->isAssignmentOp() || Token::Match(lower, "}|;|(|[|]|)|,|?|:|%oror%|return|throw|case");
}
static bool isLowerThanOr(const Token* lower)
{
return isLowerThanLogicalAnd(lower) || lower->str() == "&&";
}
static bool isLowerThanXor(const Token* lower)
{
return isLowerThanOr(lower) || lower->str() == "|";
}
static bool isLowerThanAnd(const Token* lower)
{
return isLowerThanXor(lower) || lower->str() == "^";
}
static bool isLowerThanShift(const Token* lower)
{
return isLowerThanAnd(lower) || lower->str() == "&";
}
static bool isLowerThanPlusMinus(const Token* lower)
{
return isLowerThanShift(lower) || Token::Match(lower, "%comp%|<<|>>");
}
static bool isLowerThanMulDiv(const Token* lower)
{
return isLowerThanPlusMinus(lower) || Token::Match(lower, "+|-");
}
static bool isLowerEqualThanMulDiv(const Token* lower)
{
return isLowerThanMulDiv(lower) || Token::Match(lower, "[*/%]");
}
bool TemplateSimplifier::simplifyNumericCalculations(Token *tok)
{
bool ret = false;
// (1-2)
while (tok->tokAt(3) && tok->isNumber() && tok->tokAt(2)->isNumber()) { // %any% %num% %any% %num% %any%
const Token *before = tok->previous();
if (!before)
break;
const Token* op = tok->next();
const Token* after = tok->tokAt(3);
const std::string &num1 = op->previous()->str();
const std::string &num2 = op->next()->str();
if (Token::Match(before, "* %num% /") && (num2 != "0") && num1 == MathLib::multiply(num2, MathLib::divide(num1, num2))) {
// Division where result is a whole number
} else if (!((op->str() == "*" && (isLowerThanMulDiv(before) || before->str() == "*") && isLowerEqualThanMulDiv(after)) || // associative
(Token::Match(op, "[/%]") && isLowerThanMulDiv(before) && isLowerEqualThanMulDiv(after)) || // NOT associative
(Token::Match(op, "[+-]") && isLowerThanMulDiv(before) && isLowerThanMulDiv(after)) || // Only partially (+) associative, but handled later
(Token::Match(op, ">>|<<") && isLowerThanShift(before) && isLowerThanPlusMinus(after)) || // NOT associative
(op->str() == "&" && isLowerThanShift(before) && isLowerThanShift(after)) || // associative
(op->str() == "^" && isLowerThanAnd(before) && isLowerThanAnd(after)) || // associative
(op->str() == "|" && isLowerThanXor(before) && isLowerThanXor(after)) || // associative
(op->str() == "&&" && isLowerThanOr(before) && isLowerThanOr(after)) ||
(op->str() == "||" && isLowerThanLogicalAnd(before) && isLowerThanLogicalAnd(after))))
break;
// Don't simplify "%num% / 0"
if (Token::Match(op, "[/%] 0"))
break;
// Integer operations
if (Token::Match(op, ">>|<<|&|^|%or%")) {
// Don't simplify if operand is negative, shifting with negative
// operand is UB. Bitmasking with negative operand is implementation
// defined behaviour.
if (MathLib::isNegative(num1) || MathLib::isNegative(num2))
break;
const MathLib::value v1(num1);
const MathLib::value v2(num2);
if (!v1.isInt() || !v2.isInt())
break;
switch (op->str()[0]) {
case '<':
tok->str((v1 << v2).str());
break;
case '>':
tok->str((v1 >> v2).str());
break;
case '&':
tok->str((v1 & v2).str());
break;
case '|':
tok->str((v1 | v2).str());
break;
case '^':
tok->str((v1 ^ v2).str());
break;
};
}
// Logical operations
else if (Token::Match(op, "%oror%|&&")) {
const bool op1 = !MathLib::isNullValue(num1);
const bool op2 = !MathLib::isNullValue(num2);
const bool result = (op->str() == "||") ? (op1 || op2) : (op1 && op2);
tok->str(result ? "1" : "0");
}
else if (Token::Match(tok->previous(), "- %num% - %num%"))
tok->str(MathLib::add(num1, num2));
else if (Token::Match(tok->previous(), "- %num% + %num%"))
tok->str(MathLib::subtract(num1, num2));
else {
try {
tok->str(MathLib::calculate(num1, num2, op->str()[0]));
} catch (InternalError &e) {
e.token = tok;
throw;
}
}
tok->deleteNext(2);
ret = true;
}
return ret;
}
static Token *skipTernaryOp(Token *tok, Token *backToken)
{
unsigned int colonLevel = 1;
while (nullptr != (tok = tok->next())) {
if (tok->str() == "?") {
++colonLevel;
} else if (tok->str() == ":") {
--colonLevel;
if (colonLevel == 0) {
tok = tok->next();
break;
}
}
if (tok->link() && tok->str() == "(")
tok = tok->link();
else if (Token::Match(tok->next(), "[{};)]") || tok->next() == backToken)
break;
}
if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?'
return nullptr;
return tok;
}
void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end)
{
bool again = true;
while (again) {
again = false;
for (Token *tok = start; tok && tok != end; tok = tok->next()) {
if (tok->str() == "sizeof") {
// sizeof('x')
if (Token::Match(tok->next(), "( %char% )")) {
tok->deleteNext();
tok->deleteThis();
tok->deleteNext();
std::ostringstream sz;
sz << 1;
tok->str(sz.str());
again = true;
}
// sizeof ("text")
else if (Token::Match(tok->next(), "( %str% )")) {
tok->deleteNext();
tok->deleteThis();
tok->deleteNext();
std::ostringstream ostr;
ostr << (Token::getStrLength(tok) + 1);
tok->str(ostr.str());
again = true;
}
else if (Token::Match(tok->next(), "( %type% * )")) {
tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(3))));
tok->deleteNext(4);
again = true;
} else if (Token::simpleMatch(tok->next(), "( * )")) {
tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(2))));
tok->deleteNext(3);
again = true;
} else if (Token::Match(tok->next(), "( %type% )")) {
const unsigned int size = mTokenizer->sizeOfType(tok->tokAt(2));
if (size > 0) {
tok->str(MathLib::toString(size));
tok->deleteNext(3);
again = true;
}
} else if (tok->strAt(1) == "(") {
tok = tok->linkAt(1);
}
}
}
if (simplifyCalculations(start, end))
again = true;
for (Token *tok = start; tok && tok != end; tok = tok->next()) {
if (tok->str() == "?" && tok->previous()->isNumber()) {
const int offset = (tok->previous()->str() == ")") ? 2 : 1;
// Find the token ":" then go to the next token
Token *colon = skipTernaryOp(tok, end);
if (!colon || colon->previous()->str() != ":" || !colon->next())
continue;
//handle the GNU extension: "x ? : y" <-> "x ? x : y"
if (colon->previous() == tok->next())
tok->insertToken(tok->strAt(-offset));
// go back before the condition, if possible
tok = tok->tokAt(-2);
if (offset == 2) {
// go further back before the "("
tok = tok->tokAt(-2);
//simplify the parentheses
tok->deleteNext();
tok->next()->deleteNext();
}
if (Token::Match(tok->next(), "false|0")) {
// Use code after colon, remove code before it.
Token::eraseTokens(tok, colon);
tok = tok->next();
again = true;
}
// The condition is true. Delete the operator after the ":"..
else {
// delete the condition token and the "?"
tok->deleteNext(2);
unsigned int ternaryOplevel = 0;
for (const Token *endTok = colon; endTok; endTok = endTok->next()) {
if (Token::Match(endTok, "(|[|{")) {
endTok = endTok->link();
}
else if (endTok->str() == "?")
++ternaryOplevel;
else if (Token::Match(endTok, ")|}|]|;|,|:|>")) {
if (endTok->str() == ":" && ternaryOplevel)
--ternaryOplevel;
else if (endTok->str() == ">" && !end)
;
else {
Token::eraseTokens(colon->tokAt(-2), endTok);
again = true;
break;
}
}
}
}
}
}
for (Token *tok = start; tok && tok != end; tok = tok->next()) {
if (Token::Match(tok, "( %num% )") && !Token::Match(tok->previous(), "%name%")) {
tok->deleteThis();
tok->deleteNext();
}
}
}
}
// TODO: This is not the correct class for simplifyCalculations(), so it
// should be moved away.
bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToken)
{
bool ret = false;
if (!frontToken) {
frontToken = mTokenList.front();
}
for (Token *tok = frontToken; tok != backToken; tok = tok->next()) {
// Remove parentheses around variable..
// keep parentheses here: dynamic_cast<Fred *>(p);
// keep parentheses here: A operator * (int);
// keep parentheses here: int ( * ( * f ) ( ... ) ) (int) ;
// keep parentheses here: int ( * * ( * compilerHookVector ) (void) ) ( ) ;
// keep parentheses here: operator new [] (size_t);
// keep parentheses here: Functor()(a ... )
// keep parentheses here: ) ( var ) ;
if ((Token::Match(tok->next(), "( %name% ) ;|)|,|]") ||
(Token::Match(tok->next(), "( %name% ) %cop%") && (tok->tokAt(2)->varId()>0 || !Token::Match(tok->tokAt(4), "[*&+-~]")))) &&
!tok->isName() &&
tok->str() != ">" &&
tok->str() != ")" &&
tok->str() != "]") {
tok->deleteNext();
tok = tok->next();
tok->deleteNext();
ret = true;
}
if (Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) {
tok->str(MathLib::toString(MathLib::toLongNumber(tok->str())));
}
if (tok->isNumber()) {
if (simplifyNumericCalculations(tok)) {
ret = true;
Token *prev = tok->tokAt(-2);
while (prev && simplifyNumericCalculations(prev)) {
tok = prev;
prev = prev->tokAt(-2);
}
}
// Remove redundant conditions (0&&x) (1||x)
if (Token::Match(tok->previous(), "[(=,] 0 &&") ||
Token::Match(tok->previous(), "[(=,] 1 %oror%")) {
unsigned int par = 0;
const Token *tok2 = tok;
const bool andAnd = (tok->next()->str() == "&&");
for (; tok2; tok2 = tok2->next()) {
if (tok2->str() == "(" || tok2->str() == "[")
++par;
else if (tok2->str() == ")" || tok2->str() == "]") {
if (par == 0)
break;
--par;
} else if (par == 0 && isLowerThanLogicalAnd(tok2) && (andAnd || tok2->str() != "||"))
break;
}
if (tok2) {
eraseTokens(tok, tok2);
ret = true;
}
continue;
}
if (tok->str() == "0") {
if ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) ||
(Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next()))) {
tok = tok->previous();
if (Token::Match(tok->tokAt(-4), "[;{}] %name% = %name% [+-|] 0 ;") &&
tok->strAt(-3) == tok->previous()->str()) {
tok = tok->tokAt(-4);
tok->deleteNext(5);
} else {
tok = tok->previous();
tok->deleteNext(2);
}
ret = true;
} else if (Token::Match(tok->previous(), "[=([,] 0 [+|]") ||
Token::Match(tok->previous(), "return|case 0 [+|]")) {
tok = tok->previous();
tok->deleteNext(2);
ret = true;
} else if (Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") ||
Token::Match(tok->previous(), "[=[(,] 0 * (") ||
Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%") ||
Token::Match(tok->previous(), "return|case 0 *|&& (")) {
tok->deleteNext();
if (tok->next()->str() == "(")
eraseTokens(tok, tok->next()->link());
tok->deleteNext();
ret = true;
} else if (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") ||
Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%")) {
tok->deleteNext();
tok->deleteNext();
if (tok->next()->str() == "(")
eraseTokens(tok, tok->next()->link());
tok->deleteNext();
ret = true;
}
}
if (tok->str() == "1") {
if (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") ||
Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%")) {
tok->deleteNext();
if (tok->next()->str() == "(")
eraseTokens(tok, tok->next()->link());
tok->deleteNext();
ret = true;
} else if (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") ||
Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%")) {
tok->deleteNext();
tok->deleteNext();
if (tok->next()->str() == "(")
eraseTokens(tok, tok->next()->link());
tok->deleteNext();
ret = true;
}
}
if (Token::Match(tok->tokAt(-2), "%any% * 1") || Token::Match(tok->previous(), "%any% 1 *")) {
tok = tok->previous();
if (tok->str() == "*")
tok = tok->previous();
tok->deleteNext(2);
ret = true;
}
// Remove parentheses around number..
if (Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && tok->strAt(-2) != ">") {
tok = tok->previous();
tok->deleteThis();
tok->deleteNext();
ret = true;
}
if (Token::Match(tok->previous(), "( 0 [|+]") ||
Token::Match(tok->previous(), "[|+-] 0 )")) {
tok = tok->previous();
if (Token::Match(tok, "[|+-]"))
tok = tok->previous();
tok->deleteNext(2);
ret = true;
}
if (Token::Match(tok, "%num% %comp% %num%") &&
MathLib::isInt(tok->str()) &&
MathLib::isInt(tok->strAt(2))) {
if (Token::Match(tok->previous(), "(|&&|%oror%") && Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) {
const MathLib::bigint op1(MathLib::toLongNumber(tok->str()));
const std::string &cmp(tok->next()->str());
const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2)));
std::string result;
if (cmp == "==")
result = (op1 == op2) ? "1" : "0";
else if (cmp == "!=")
result = (op1 != op2) ? "1" : "0";
else if (cmp == "<=")
result = (op1 <= op2) ? "1" : "0";
else if (cmp == ">=")
result = (op1 >= op2) ? "1" : "0";
else if (cmp == "<")
result = (op1 < op2) ? "1" : "0";
else
result = (op1 > op2) ? "1" : "0";
tok->str(result);
tok->deleteNext(2);
ret = true;
tok = tok->previous();
}
}
}
}
return ret;
}
const Token * TemplateSimplifier::getTemplateParametersInDeclaration(
const Token * tok,
std::vector<const Token *> & typeParametersInDeclaration)
{
typeParametersInDeclaration.clear();
for (; tok && tok->str() != ">"; tok = tok->next()) {
if (Token::Match(tok, "%name% ,|>|="))
typeParametersInDeclaration.push_back(tok);
}
return tok;
}
bool TemplateSimplifier::matchSpecialization(
const Token *templateDeclarationNameToken,
const Token *templateInstantiationNameToken,
const std::list<const Token *> & specializations)
{
// Is there a matching specialization?
for (std::list<const Token *>::const_iterator it = specializations.begin(); it != specializations.end(); ++it) {
if (!Token::Match(*it, "%name% <"))
continue;
const Token *startToken = (*it);
while (startToken->previous() && !Token::Match(startToken->previous(), "[;{}]"))
startToken = startToken->previous();
if (!Token::simpleMatch(startToken, "template <"))
continue;
std::vector<const Token *> templateParameters;
getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters);
const Token *instToken = templateInstantiationNameToken->tokAt(2);
const Token *declToken = (*it)->tokAt(2);
const Token * const endToken = (*it)->next()->findClosingBracket();
while (declToken != endToken) {
if (declToken->str() != instToken->str()) {
int nr = 0;
while (nr < templateParameters.size() && templateParameters[nr]->str() != declToken->str())
++nr;
if (nr == templateParameters.size())
break;
}
declToken = declToken->next();
instToken = instToken->next();
}
if (declToken == endToken && instToken->str() == ">") {
// specialization matches.
return templateDeclarationNameToken == *it;
}
}
// No specialization matches. Return true if the declaration is not a specialization.
return Token::Match(templateDeclarationNameToken, "%name% !!<") &&
(templateDeclarationNameToken->str().find('<') == std::string::npos);
}
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(), "[<,]")) {
mTypesUsedInTemplateInstantiation.emplace_back(tok3, "");
}
const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const";
if (!constconst) {
typeStringsUsedInTemplateInstantiation.push_back(tok3->str());
}
// add additional type information
if (!constconst && !Token::Match(tok3, "class|struct|enum")) {
if (tok3->isUnsigned())
typeForNewName += "unsigned";
else if (tok3->isSigned())
typeForNewName += "signed";
if (tok3->isLong())
typeForNewName += "long";
if (!typeForNewName.empty())
typeForNewName += ' ';
typeForNewName += tok3->str();
}
}
return typeForNewName;
}
bool TemplateSimplifier::simplifyTemplateInstantiations(
const TokenAndName &templateDeclaration,
const std::list<const Token *> &specializations,
const std::time_t maxtime,
std::set<std::string> &expandedtemplates)
{
// this variable is not used at the moment. The intention was to
// allow continuous instantiations until all templates has been expanded
//bool done = false;
// Contains tokens such as "T"
std::vector<const Token *> typeParametersInDeclaration;
getTemplateParametersInDeclaration(templateDeclaration.token->tokAt(2), typeParametersInDeclaration);
const bool printDebug = mSettings->debugwarnings;
const bool specialized = templateDeclaration.isSpecialization();
const bool isfunc = templateDeclaration.isFunction();
const bool isVar = templateDeclaration.isVariable();
// locate template usage..
std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size();
unsigned int recursiveCount = 0;
bool instantiated = false;
for (const TokenAndName &instantiation : mTemplateInstantiations) {
if (numberOfTemplateInstantiations != mTemplateInstantiations.size()) {
numberOfTemplateInstantiations = mTemplateInstantiations.size();
++recursiveCount;
if (recursiveCount > 100) {
// bail out..
break;
}
}
// already simplified
if (!Token::Match(instantiation.token, "%name% <"))
continue;
if (instantiation.fullName != templateDeclaration.fullName) {
// FIXME: fallback to not matching scopes until type deduction works
// names must match
if (instantiation.name != templateDeclaration.name)
continue;
// scopes must match when present
if (!instantiation.scope.empty() && !templateDeclaration.scope.empty())
continue;
}
if (!matchSpecialization(templateDeclaration.nameToken, instantiation.token, specializations))
continue;
Token * const tok2 = instantiation.token;
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
const 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(), ";|{|}|=|const") &&
(!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%" : "*|&|::| %name%")))
continue;
// New type..
mTypesUsedInTemplateInstantiation.clear();
std::list<std::string> typeStringsUsedInTemplateInstantiation;
std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation);
if (typeForNewName.empty() || (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) {
if (printDebug && mErrorLogger) {
std::list<const Token *> callstack(1, tok2);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
"Failed to instantiate template \"" + instantiation.name + "\". The checking continues anyway.", false));
}
if (typeForNewName.empty())
continue;
break;
}
// New classname/funcname..
const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >");
const std::string newFullName(templateDeclaration.scope + (templateDeclaration.scope.empty() ? "" : " :: ") + newName);
if (expandedtemplates.find(newFullName) == expandedtemplates.end()) {
expandedtemplates.insert(newFullName);
expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized && !isVar);
instantiated = true;
}
// Replace all these template usages..
replaceTemplateUsage(instantiation, typeStringsUsedInTemplateInstantiation, newName);
}
// process uninstantiated templates
// TODO: remove the specialized check and handle all uninstantiated templates someday.
if (!instantiated && specialized) {
Token * tok2 = const_cast<Token *>(templateDeclaration.nameToken);
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(), ";|{|}|=|const") &&
(!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%" : "*|&|::| %name%")))
return false;
// already simplified
if (!Token::Match(tok2, "%name% <"))
return false;
if (!matchSpecialization(templateDeclaration.nameToken, tok2, specializations))
return false;
// 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 + " >");
const std::string newFullName(templateDeclaration.scope + (templateDeclaration.scope.empty() ? "" : " :: ") + newName);
if (expandedtemplates.find(newFullName) == expandedtemplates.end()) {
expandedtemplates.insert(newFullName);
expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, !specialized && !isVar);
instantiated = true;
}
// Replace all these template usages..
replaceTemplateUsage(templateDeclaration, typeStringsUsedInTemplateInstantiation, newName);
}
// Template has been instantiated .. then remove the template declaration
return instantiated;
}
static bool matchTemplateParameters(const Token *nameTok, const std::list<std::string> &strings)
{
std::list<std::string>::const_iterator it = strings.begin();
const Token *tok = nameTok->tokAt(2);
while (tok && it != strings.end() && *it == tok->str()) {
tok = tok->next();
++it;
}
return it == strings.end() && tok && tok->str() == ">";
}
void TemplateSimplifier::replaceTemplateUsage(
const TokenAndName &instantiation,
const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
const std::string &newName)
{
std::list<ScopeInfo2> scopeInfo;
std::list< std::pair<Token *, Token *> > removeTokens;
for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) {
if (Token::Match(nameTok, "{|}|namespace|class|struct|union")) {
setScopeInfo(nameTok, &scopeInfo);
continue;
}
if (!Token::Match(nameTok, "%name% <") ||
Token::Match(nameTok, "template|const_cast|dynamic_cast|reinterpret_cast|static_cast"))
continue;
std::set<TemplateSimplifier::TokenAndName*> & pointers = nameTok->templateSimplifierPointers();
// check if instantiation matches token instantiation from pointer
if (pointers.size()) {
// check full name
if (instantiation.fullName != (*pointers.begin())->fullName) {
// FIXME: fallback to just matching name
if (nameTok->str() != instantiation.name)
continue;
}
}
// no pointer available look at tokens directly
else {
// FIXME: fallback to just matching name
if (nameTok->str() != instantiation.name)
continue;
}
if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation))
continue;
// match parameters
Token * tok2 = nameTok->tokAt(2);
unsigned int typeCountInInstantiation = 1U; // There is always at least one type
const Token *typetok = (!mTypesUsedInTemplateInstantiation.empty()) ? mTypesUsedInTemplateInstantiation[0].token : nullptr;
unsigned int indentlevel2 = 0; // indentlevel for tokgt
while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) {
if (tok2->str() == "<" && templateParameters(tok2) > 0)
++indentlevel2;
else if (indentlevel2 > 0 && Token::Match(tok2, "> [,>]"))
--indentlevel2;
else if (indentlevel2 == 0) {
if (tok2->str() != ",") {
if (!typetok ||
tok2->isUnsigned() != typetok->isUnsigned() ||
tok2->isSigned() != typetok->isSigned() ||
tok2->isLong() != typetok->isLong()) {
break;
}
typetok = typetok->next();
} else {
if (typeCountInInstantiation < mTypesUsedInTemplateInstantiation.size())
typetok = mTypesUsedInTemplateInstantiation[typeCountInInstantiation++].token;
else
typetok = nullptr;
}
}
tok2 = tok2->next();
}
if (!tok2)
break;
// matching template usage => replace tokens..
// Foo < int > => Foo<int>
if (tok2->str() == ">" && typeCountInInstantiation == mTypesUsedInTemplateInstantiation.size()) {
const Token * const nameTok1 = nameTok;
nameTok->str(newName);
for (Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) {
if (tok->isName() && !tok->templateSimplifierPointers().empty()) {
std::list<TokenAndName>::iterator ti;
for (ti = mTemplateInstantiations.begin(); ti != mTemplateInstantiations.end();) {
if (ti->token == tok) {
mTemplateInstantiations.erase(ti++);
break;
} else {
++ti;
}
}
}
}
removeTokens.emplace_back(nameTok, tok2->next());
}
nameTok = tok2;
}
while (!removeTokens.empty()) {
eraseTokens(removeTokens.back().first, removeTokens.back().second);
removeTokens.pop_back();
}
}
void TemplateSimplifier::getSpecializations()
{
// try to locate a matching declaration for each user defined specialization
for (auto & spec : mTemplateDeclarations) {
if (spec.isSpecialization()) {
bool found = false;
for (auto & decl : mTemplateDeclarations) {
if (decl.isSpecialization())
continue;
// make sure the scopes and names match
if (spec.fullName == decl.fullName) {
// @todo make sure function parameters also match
mTemplateSpecializationMap[spec.token] = decl.token;
found = true;
}
}
if (!found) {
for (auto & decl : mTemplateForwardDeclarations) {
// make sure the scopes and names match
if (spec.fullName == decl.fullName) {
// @todo make sure function parameters also match
mTemplateSpecializationMap[spec.token] = decl.token;
}
}
}
}
}
}
void TemplateSimplifier::getPartialSpecializations()
{
// try to locate a matching declaration for each user defined partial specialization
for (auto & spec : mTemplateDeclarations) {
if (spec.isPartialSpecialization()) {
bool found = false;
for (auto & decl : mTemplateDeclarations) {
if (decl.isPartialSpecialization())
continue;
// make sure the scopes and names match
if (spec.fullName == decl.fullName) {
// @todo make sure function parameters also match
mTemplatePartialSpecializationMap[spec.token] = decl.token;
found = true;
}
}
if (!found) {
for (auto & decl : mTemplateForwardDeclarations) {
// make sure the scopes and names match
if (spec.fullName == decl.fullName) {
// @todo make sure function parameters also match
mTemplatePartialSpecializationMap[spec.token] = decl.token;
}
}
}
}
}
}
void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
{
// try to locate a matching declaration for each forward declaration
for (const auto & forwardDecl : mTemplateForwardDeclarations) {
std::vector<const Token *> params1;
getTemplateParametersInDeclaration(forwardDecl.token->tokAt(2), params1);
for (auto & decl : mTemplateDeclarations) {
// skip partializations
if (decl.isPartialSpecialization())
continue;
std::vector<const Token *> params2;
getTemplateParametersInDeclaration(decl.token->tokAt(2), params2);
// make sure the number of arguments match
if (params1.size() == params2.size()) {
// make sure the scopes and names match
if (forwardDecl.fullName == decl.fullName) {
// save forward declaration for lookup later
if ((decl.nameToken->strAt(1) == "(" && forwardDecl.nameToken->strAt(1) == "(") ||
(decl.nameToken->strAt(1) == "{" && forwardDecl.nameToken->strAt(1) == ";")) {
mTemplateForwardDeclarationsMap[decl.token] = forwardDecl.token;
}
for (size_t k = 0; k < params1.size(); k++) {
// copy default value to declaration if not present
if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") {
int level = 0;
const Token *end = params1[k]->next();
while (end && !(level == 0 && Token::Match(end, ",|>"))) {
if (Token::Match(end, "{|(|<"))
level++;
else if (Token::Match(end, "}|)|>"))
level--;
end = end->next();
}
if (end)
TokenList::copyTokens(const_cast<Token *>(params2[k]), params1[k]->next(), end->previous());
break;
}
}
// update parameter end pointer
decl.paramEnd = decl.token->next()->findClosingBracket();
}
}
}
}
}
void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::string &indent) const
{
std::cout << indent << "token: ";
if (tokenAndName.token)
std::cout << "\"" << tokenAndName.token->str() << "\" " << mTokenList.fileLine(tokenAndName.token);
else
std::cout << "nullptr";
std::cout << std::endl;
std::cout << indent << "scope: \"" << tokenAndName.scope << "\"" << std::endl;
std::cout << indent << "name: \"" << tokenAndName.name << "\"" << std::endl;
std::cout << indent << "fullName: \"" << tokenAndName.fullName << "\"" << std::endl;
std::cout << indent << "nameToken: ";
if (tokenAndName.nameToken)
std::cout << "\"" << tokenAndName.nameToken->str() << "\" " << mTokenList.fileLine(tokenAndName.nameToken);
else
std::cout << "nullptr";
std::cout << std::endl;
std::cout << indent << "paramEnd: ";
if (tokenAndName.paramEnd)
std::cout << "\"" << tokenAndName.paramEnd->str() << "\" " << mTokenList.fileLine(tokenAndName.paramEnd);
else
std::cout << "nullptr";
std::cout << std::endl;
std::cout << indent << "flags: ";
if (tokenAndName.isClass())
std::cout << " isClass";
if (tokenAndName.isFunction())
std::cout << " isFunction";
if (tokenAndName.isVariable())
std::cout << " isVariable";
if (tokenAndName.isAlias())
std::cout << " isAlias";
if (tokenAndName.isSpecialization())
std::cout << " isSpecialization";
if (tokenAndName.isPartialSpecialization())
std::cout << " isPartialSpecialization";
if (tokenAndName.isForwardDeclaration())
std::cout << " isForwardDeclaration";
std::cout << std::endl;
if (tokenAndName.token && !tokenAndName.paramEnd && tokenAndName.token->strAt(1) == "<") {
const Token *end = tokenAndName.token->next()->findClosingBracket();
if (end) {
const Token *start = tokenAndName.token->next();
std::cout << indent << "type: ";
while (start && start != end) {
std::cout << start->str();
start = start->next();
}
std::cout << end->str() << std::endl;
}
}
}
void TemplateSimplifier::printOut(const std::string & text) const
{
std::cout << std::endl;
std::cout << text << std::endl;
std::cout << std::endl;
std::cout << "mTemplateDeclarations: " << mTemplateDeclarations.size() << std::endl;
int count = 0;
for (const auto & decl : mTemplateDeclarations) {
std::cout << "mTemplateDeclarations[" << count++ << "]:" << std::endl;
printOut(decl);
}
std::cout << "mTemplateForwardDeclarations: " << mTemplateForwardDeclarations.size() << std::endl;
count = 0;
for (const auto & decl : mTemplateForwardDeclarations) {
std::cout << "mTemplateForwardDeclarations[" << count++ << "]:" << std::endl;
printOut(decl);
}
std::cout << "mTemplateForwardDeclarationsMap: " << mTemplateForwardDeclarationsMap.size() << std::endl;
unsigned int mapIndex = 0;
for (const auto & mapItem : mTemplateForwardDeclarationsMap) {
unsigned int declIndex = 0;
for (const auto & decl : mTemplateDeclarations) {
if (mapItem.first == decl.token) {
unsigned int forwardIndex = 0;
for (const auto & forwardDecl : mTemplateForwardDeclarations) {
if (mapItem.second == forwardDecl.token) {
std::cout << "mTemplateForwardDeclarationsMap[" << mapIndex << "]:" << std::endl;
std::cout << " mTemplateDeclarations[" << declIndex
<< "] => mTemplateForwardDeclarations[" << forwardIndex << "]" << std::endl;
break;
}
forwardIndex++;
}
break;
}
declIndex++;
}
mapIndex++;
}
std::cout << "mTemplateSpecializationMap: " << mTemplateSpecializationMap.size() << std::endl;
for (const auto & mapItem : mTemplateSpecializationMap) {
unsigned int decl1Index = 0;
for (const auto & decl1 : mTemplateDeclarations) {
if (decl1.isSpecialization() && mapItem.first == decl1.token) {
bool found = 0;
unsigned int decl2Index = 0;
for (const auto & decl2 : mTemplateDeclarations) {
if (mapItem.second == decl2.token) {
std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl;
std::cout << " mTemplateDeclarations[" << decl1Index
<< "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl;
found = true;
break;
}
decl2Index++;
}
if (!found) {
decl2Index = 0;
for (const auto & decl2 : mTemplateForwardDeclarations) {
if (mapItem.second == decl2.token) {
std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl;
std::cout << " mTemplateDeclarations[" << decl1Index
<< "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl;
break;
}
decl2Index++;
}
}
break;
}
decl1Index++;
}
mapIndex++;
}
std::cout << "mTemplatePartialSpecializationMap: " << mTemplatePartialSpecializationMap.size() << std::endl;
for (const auto & mapItem : mTemplatePartialSpecializationMap) {
unsigned int decl1Index = 0;
for (const auto & decl1 : mTemplateDeclarations) {
if (mapItem.first == decl1.token) {
bool found = 0;
unsigned int decl2Index = 0;
for (const auto & decl2 : mTemplateDeclarations) {
if (mapItem.second == decl2.token) {
std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl;
std::cout << " mTemplateDeclarations[" << decl1Index
<< "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl;
found = true;
break;
}
decl2Index++;
}
if (!found) {
decl2Index = 0;
for (const auto & decl2 : mTemplateForwardDeclarations) {
if (mapItem.second == decl2.token) {
std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl;
std::cout << " mTemplateDeclarations[" << decl1Index
<< "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl;
break;
}
decl2Index++;
}
}
break;
}
decl1Index++;
}
mapIndex++;
}
std::cout << "mTemplateInstantiations: " << mTemplateInstantiations.size() << std::endl;
count = 0;
for (const auto & decl : mTemplateInstantiations) {
std::cout << "mTemplateInstantiations[" << count++ << "]:" << std::endl;
printOut(decl);
}
}
static Token *findSemicolon(Token *tok)
{
unsigned int level = 0;
for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) {
if (tok->str() == "{")
++level;
else if (level > 0 && tok->str() == "}")
--level;
}
return tok;
}
static bool usingMatch(
const Token *nameToken,
const std::string &scope,
Token **tok,
const std::string &scope1,
const std::list<ScopeInfo2> &scopeList1)
{
Token *tok1 = *tok;
if (tok1 && tok1->str() != nameToken->str())
return false;
// skip this using
if (tok1 == nameToken) {
*tok = findSemicolon(tok1);
return false;
}
// skip other using with this name
if (tok1->strAt(-1) == "using") {
// fixme: this is wrong
// skip to end of scope
if (scopeList1.back().bodyEnd)
*tok = scopeList1.back().bodyEnd->previous();
return false;
}
if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) {
// fixme
return false;
}
// get qualification
std::string qualification;
const Token* tok2 = tok1;
std::string::size_type index = scope.size();
std::string::size_type new_index = std::string::npos;
bool match = true;
while (tok2->strAt(-1) == "::") {
std::string last;
if (match && !scope1.empty()) {
new_index = scope1.rfind(' ', index - 1);
if (new_index != std::string::npos)
last = scope1.substr(new_index, index - new_index);
else if (!qualification.empty())
last.clear();
else
last = scope1;
} else
match = false;
if (match && tok2->strAt(-2) == last)
index = new_index;
else {
if (!qualification.empty())
qualification = " :: " + qualification;
qualification = tok2->strAt(-2) + qualification;
}
tok2 = tok2->tokAt(-2);
}
// todo: check using namespace
std::string fullScope1 = scope1;
if (!scope1.empty() && !qualification.empty())
fullScope1 += " :: ";
fullScope1 += qualification;
if (scope == fullScope1)
return true;
std::string newScope1 = scope1;
// scopes didn't match so try higher scopes
while (!newScope1.empty()) {
std::string::size_type separator = newScope1.rfind(" :: ", index - 1);
if (separator != std::string::npos)
newScope1 = newScope1.substr(0, separator);
else
newScope1.clear();
std::string newFullScope1 = newScope1;
if (!newScope1.empty() && !qualification.empty())
newFullScope1 += " :: ";
newFullScope1 += qualification;
if (scope == newFullScope1)
return true;
}
return false;
}
bool TemplateSimplifier::simplifyUsing()
{
bool substitute = false;
std::list<ScopeInfo2> scopeList;
bool inTemplateDefinition = false;
const Token *endOfTemplateDefinition = nullptr;
bool isVariable = false;
struct Using {
Using(Token *start, Token *end) : startTok(start), endTok(end) { }
Token *startTok;
Token *endTok;
};
std::list<Using> usingList;
scopeList.emplace_back("", nullptr);
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (mErrorLogger && !mTokenList.getFiles().empty())
mErrorLogger->reportProgress(mTokenList.getFiles()[0], "Tokenize (using)", tok->progressValue());
if (mSettings->terminated())
return substitute;
if (Token::Match(tok, "{|}|namespace|class|struct|union") ||
Token::Match(tok, "using namespace %name% ;|::")) {
setScopeInfo(tok, &scopeList);
}
if (inTemplateDefinition) {
if (!endOfTemplateDefinition) {
if (isVariable)
endOfTemplateDefinition = findSemicolon(tok);
else if (tok->str() == "{")
endOfTemplateDefinition = tok->link();
}
if (tok == endOfTemplateDefinition) {
inTemplateDefinition = false;
continue;
}
}
if (tok->str()=="template") {
if (Token::Match(tok->next(), "< !!>"))
inTemplateDefinition = true;
else
inTemplateDefinition = false;
}
if (!inTemplateDefinition) {
// look for non-template type aliases
if (tok->strAt(-1) != ">" &&
(Token::Match(tok, "using %name% = ::| %name%") ||
(Token::Match(tok, "using %name% [ [") &&
Token::Match(tok->linkAt(2), "] ] = ::| %name%")))) {
std::list<ScopeInfo2> scopeList1;
scopeList1.emplace_back("", nullptr);
std::string name = tok->strAt(1);
const Token *nameToken = tok->next();
std::string scope = getScopeName(scopeList);
Token *usingStart = tok;
Token *start;
if (tok->strAt(2) == "=")
start = tok->tokAt(3);
else
start = tok->linkAt(2)->tokAt(3);
Token *usingEnd = findSemicolon(start);
if (!usingEnd)
continue;
// Move struct defined in using out of using.
// using T = struct t { }; => struct t { }; using T = struct t;
// fixme: this doesn't handle attributes
if (Token::Match(start, "struct|union|enum %name%| {")) {
if (start->strAt(1) != "{") {
Token *structEnd = start->linkAt(2);
structEnd->insertToken(";", "");
mTokenList.copyTokens(structEnd->next(), tok, start->next());
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
} else {
Token *structEnd = start->linkAt(1);
structEnd->insertToken(";", "");
std::string newName;
if (structEnd->strAt(2) == ";")
newName = name;
else
newName = "Unnamed" + MathLib::toString(mTokenizer->mUnnamedCount++);
mTokenList.copyTokens(structEnd->next(), tok, start);
structEnd->tokAt(5)->insertToken(newName, "");
start->insertToken(newName, "");
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
}
}
// Unfortunately we have to start searching from the beginning
// of the token stream because templates are instantiated at
// the end of the token stream and it may be used before then.
std::string scope1;
bool skip = false; // don't erase type aliases we can't parse
for (Token* tok1 = mTokenList.front(); tok1; tok1 = tok1->next()) {
if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") ||
Token::Match(tok1, "using namespace %name% ;|::")) {
setScopeInfo(tok1, &scopeList1, true);
scope1 = getScopeName(scopeList1);
continue;
}
if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
continue;
// remove the qualification
while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) {
tok1->deletePrevious();
tok1->deletePrevious();
}
Token * arrayStart = nullptr;
// parse the type
Token *type = start;
if (type->str() == "::") {
type = type->next();
while (Token::Match(type, "%type% ::"))
type = type->tokAt(2);
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type% ::")) {
do {
type = type->tokAt(2);
} while (Token::Match(type, "%type% ::"));
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType()))
type = type->next();
type = type->next();
while (Token::Match(type, "%type%") &&
(type->isStandardType() || Token::Match(type, "unsigned|signed"))) {
type = type->next();
}
bool atEnd = false;
while (!atEnd) {
if (type && type->str() == "::") {
type = type->next();
}
if (Token::Match(type, "%type%") &&
type->next() && !Token::Match(type->next(), "[|;|,|(")) {
type = type->next();
} else if (Token::simpleMatch(type, "const (")) {
type = type->next();
atEnd = true;
} else
atEnd = true;
}
} else
syntaxError(type);
// check for invalid input
if (!type)
syntaxError(tok1);
// check for template
if (type->str() == "<") {
type = type->findClosingBracket();
while (type && Token::Match(type->next(), ":: %type%"))
type = type->tokAt(2);
if (!type) {
syntaxError(tok1);
}
while (Token::Match(type->next(), "const|volatile"))
type = type->next();
type = type->next();
}
// check for pointers and references
std::list<std::string> pointers;
while (Token::Match(type, "*|&|&&|const")) {
pointers.push_back(type->str());
type = type->next();
}
// check for array
if (type && type->str() == "[") {
do {
if (!arrayStart)
arrayStart = type;
bool atEnd = false;
while (!atEnd) {
while (type->next() && !Token::Match(type->next(), ";|,")) {
type = type->next();
}
if (!type->next())
syntaxError(type); // invalid input
else if (type->next()->str() == ";")
atEnd = true;
else if (type->str() == "]")
atEnd = true;
else
type = type->next();
}
type = type->next();
} while (type && type->str() == "[");
}
Token* after = tok1->next();
// check if type was parsed
if (type && type == usingEnd) {
// check for array syntax and add type around variable
if (arrayStart) {
if (Token::Match(tok1->next(), "%name%")) {
mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
mTokenList.copyTokens(tok1, start, arrayStart->previous());
tok1->deleteThis();
substitute = true;
}
} else {
// just replace simple type aliases
mTokenList.copyTokens(tok1, start, usingEnd->previous());
tok1->deleteThis();
substitute = true;
}
} else {
skip = true;
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
if (!str.empty())
str += ' ';
str += tok3->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
"Failed to parse \'" + str + "\'. The checking continues anyway.", false));
}
}
tok1 = after;
}
if (!skip)
usingList.emplace_back(usingStart, usingEnd);
}
}
}
// delete all used type alias definitions
for (std::list<Using>::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) {
Token *usingStart = it->startTok;
Token *usingEnd = it->endTok;
if (usingStart->previous()) {
if (usingEnd->next())
Token::eraseTokens(usingStart->previous(), usingEnd->next());
else {
Token::eraseTokens(usingStart, usingEnd);
usingEnd->deleteThis();
}
} else {
if (usingEnd->next()) {
Token::eraseTokens(usingStart, usingEnd->next());
usingStart->deleteThis();
} else {
// this is the only code being checked so leave ';'
Token::eraseTokens(usingStart, usingEnd);
usingStart->deleteThis();
}
}
}
return substitute;
}
void TemplateSimplifier::simplifyTemplates(
const std::time_t maxtime,
bool &codeWithTemplates)
{
// Remove "typename" unless used in template arguments..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "typename %name%"))
tok->deleteThis();
if (Token::simpleMatch(tok, "template <")) {
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
// 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) {
// it may take more than one pass to simplify type aliases
while (simplifyUsing())
;
if (i) {
mTemplateDeclarations.clear();
mTemplateForwardDeclarations.clear();
mTemplateForwardDeclarationsMap.clear();
mTemplateSpecializationMap.clear();
mTemplatePartialSpecializationMap.clear();
mTemplateInstantiations.clear();
mInstantiatedTemplates.clear();
mExplicitInstantiationsToDelete.clear();
}
bool hasTemplates = getTemplateDeclarations();
if (i == 0)
codeWithTemplates = hasTemplates;
// Make sure there is something to simplify.
if (mTemplateDeclarations.empty())
return;
// Copy default argument values from forward declaration to declaration
fixForwardDeclaredDefaultArgumentValues();
// Locate user defined specializations.
getSpecializations();
// Locate user defined partial specializations.
getPartialSpecializations();
// Locate possible instantiations of templates..
getTemplateInstantiations();
// Template arguments with default values
useDefaultArgumentValues();
simplifyTemplateAliases();
if (mSettings->debugtemplate)
printOut("### Template Simplifier pass " + std::to_string(i + 1) + " ###");
std::set<std::string> expandedtemplates;
for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) {
// get specializations..
std::list<const Token *> specializations;
for (std::list<TokenAndName>::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) {
if (iter1->fullName == iter2->fullName)
specializations.push_back(iter2->nameToken);
}
const bool instantiated = simplifyTemplateInstantiations(
*iter1,
specializations,
maxtime,
expandedtemplates);
if (instantiated)
mInstantiatedTemplates.push_back(*iter1);
}
for (std::list<TokenAndName>::const_iterator it = mInstantiatedTemplates.begin(); it != mInstantiatedTemplates.end(); ++it) {
std::list<TokenAndName>::iterator decl;
for (decl = mTemplateDeclarations.begin(); decl != mTemplateDeclarations.end(); ++decl) {
if (decl->token == it->token)
break;
}
if (decl != mTemplateDeclarations.end()) {
if (it->isSpecialization()) {
// delete the "template < >"
Token * tok = it->token;
tok->deleteNext(2);
tok->deleteThis();
} else {
// remove forward declaration if found
auto it1 = mTemplateForwardDeclarationsMap.find(it->token);
if (it1 != mTemplateForwardDeclarationsMap.end())
removeTemplate(it1->second);
removeTemplate(it->token);
}
mTemplateDeclarations.erase(decl);
}
}
// remove out of line member functions
while (!mMemberFunctionsToDelete.empty()) {
const std::list<TokenAndName>::iterator it = std::find_if(mTemplateDeclarations.begin(),
mTemplateDeclarations.end(),
FindToken(mMemberFunctionsToDelete.begin()->token));
// multiple functions can share the same declaration so make sure it hasn't already been deleted
if (it != mTemplateDeclarations.end()) {
removeTemplate(it->token);
mTemplateDeclarations.erase(it);
}
mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin());
}
// remove explicit instantiations
for (size_t j = 0; j < mExplicitInstantiationsToDelete.size(); ++j) {
Token * start = mExplicitInstantiationsToDelete[j].token;
if (start) {
Token * end = start->next();
while (end && end->str() != ";")
end = end->next();
if (start->previous())
start = start->previous();
if (end && end->next())
end = end->next();
eraseTokens(start, end);
}
}
}
}
void TemplateSimplifier::syntaxError(const Token *tok)
{
throw InternalError(tok, "syntax error", InternalError::SYNTAX);
}