This fixes simplifyUsing to remove 'typename' and 'template' from type aliases of the form: using T3 = typename T1::template T3<T2>; This lets the template simplifier instantiate the type alias which will then remove the using type alias. The crash will still happen if there is no instantiation because the type alias will not be removed. The type alias is what cppcheck is crashing on after the template simplifier and that still needs fixing.
3165 lines
128 KiB
C++
3165 lines
128 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() == "<") {
|
|
const Token *closing = tok1->findClosingBracket();
|
|
if (closing)
|
|
tok1 = closing->next();
|
|
else
|
|
throw InternalError(tok, "unsupported syntax", InternalError::SYNTAX);
|
|
}
|
|
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;
|
|
if (Token::Match(tok->previous(), "%var% <"))
|
|
return 0;
|
|
tok = tok->next();
|
|
|
|
unsigned int level = 0;
|
|
|
|
while (tok) {
|
|
// skip template template
|
|
if (level == 0 && Token::simpleMatch(tok, "template <")) {
|
|
const Token *closing = tok->next()->findClosingBracket();
|
|
if (closing)
|
|
tok = closing->next();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// 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();
|
|
if (!tok)
|
|
return 0;
|
|
if (tok->str() == ">" && level == 0)
|
|
return numberOfParameters;
|
|
else if (tok->str() == "," && level == 0) {
|
|
++numberOfParameters;
|
|
tok = tok->next();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// 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() == ">" && !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0;
|
|
--level;
|
|
if (tok->str() == ">>") {
|
|
if (level == 0)
|
|
return !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0;
|
|
--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;
|
|
// ignore template template parameter
|
|
if (tok->strAt(-1) == "<")
|
|
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;
|
|
if (!startToken)
|
|
continue;
|
|
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.token || 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();
|
|
else if (tok2->str() == "<")
|
|
tok2 = tok2->findClosingBracket();
|
|
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();
|
|
if (closing) {
|
|
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)
|
|
continue;
|
|
|
|
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)
|
|
{
|
|
// start could be erased so use the token before start if available
|
|
Token * first = (start && start->previous()) ? start->previous() : mTokenList.front();
|
|
bool again = true;
|
|
|
|
while (again) {
|
|
again = false;
|
|
|
|
for (Token *tok = first->next(); 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(first->next(), end))
|
|
again = true;
|
|
|
|
for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) {
|
|
if (tok->str() == "?" && (tok->previous()->isNumber() || tok->previous()->isBoolean())) {
|
|
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 = first->next(); tok && tok != end; tok = tok->next()) {
|
|
if (Token::Match(tok, "( %num%|%bool% )") &&
|
|
(tok->previous() && !Token::Match(tok->previous(), "%name%"))) {
|
|
tok->deleteThis();
|
|
tok->deleteNext();
|
|
again = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool validTokenStart(bool bounded, const Token *tok, const Token *frontToken, int offset)
|
|
{
|
|
if (!bounded)
|
|
return true;
|
|
|
|
if (frontToken)
|
|
frontToken = frontToken->previous();
|
|
|
|
while (tok && offset <= 0) {
|
|
if (tok == frontToken)
|
|
return false;
|
|
++offset;
|
|
tok = tok->previous();
|
|
}
|
|
|
|
return tok && offset > 0;
|
|
}
|
|
|
|
static bool validTokenEnd(bool bounded, const Token *tok, const Token *backToken, int offset)
|
|
{
|
|
if (!bounded)
|
|
return true;
|
|
|
|
while (tok && offset >= 0) {
|
|
if (tok == backToken)
|
|
return false;
|
|
--offset;
|
|
tok = tok->next();
|
|
}
|
|
|
|
return tok && offset < 0;
|
|
}
|
|
|
|
// 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;
|
|
const bool bounded = frontToken || backToken;
|
|
if (!frontToken) {
|
|
frontToken = mTokenList.front();
|
|
}
|
|
for (Token *tok = frontToken; tok && 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 (validTokenEnd(bounded, tok, backToken, 4) &&
|
|
(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 (validTokenEnd(bounded, tok, backToken, 3) &&
|
|
Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) {
|
|
tok->str(MathLib::toString(MathLib::toLongNumber(tok->str())));
|
|
}
|
|
|
|
if (tok && tok->isNumber()) {
|
|
if (validTokenEnd(bounded, tok, backToken, 2) &&
|
|
simplifyNumericCalculations(tok)) {
|
|
ret = true;
|
|
Token *prev = tok->tokAt(-2);
|
|
while (validTokenStart(bounded, tok, frontToken, -2) &&
|
|
prev && simplifyNumericCalculations(prev)) {
|
|
tok = prev;
|
|
prev = prev->tokAt(-2);
|
|
}
|
|
}
|
|
|
|
// Remove redundant conditions (0&&x) (1||x)
|
|
if (validTokenStart(bounded, tok, frontToken, -1) &&
|
|
validTokenEnd(bounded, tok, backToken, 1) &&
|
|
(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" && validTokenStart(bounded, tok, frontToken, -1)) {
|
|
if (validTokenEnd(bounded, tok, backToken, 1) &&
|
|
((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 (validTokenEnd(bounded, tok, backToken, 1) &&
|
|
(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(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%")) &&
|
|
validTokenEnd(bounded, tok, backToken, 3)) ||
|
|
(((Token::Match(tok->previous(), "[=[(,] 0 * (") ||
|
|
Token::Match(tok->previous(), "return|case 0 *|&& (")) &&
|
|
validTokenEnd(bounded, tok, backToken, 2))))) {
|
|
tok->deleteNext();
|
|
if (tok->next()->str() == "(")
|
|
eraseTokens(tok, tok->next()->link());
|
|
tok->deleteNext();
|
|
ret = true;
|
|
} else if (validTokenEnd(bounded, tok, backToken, 4) &&
|
|
(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" && validTokenStart(bounded, tok, frontToken, -1)) {
|
|
if (validTokenEnd(bounded, tok, backToken, 3) &&
|
|
(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 (validTokenEnd(bounded, tok, backToken, 4) &&
|
|
(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") &&
|
|
validTokenStart(bounded, tok, frontToken, -2)) ||
|
|
(Token::Match(tok->previous(), "%any% 1 *") &&
|
|
validTokenStart(bounded, tok, frontToken, -1))) {
|
|
tok = tok->previous();
|
|
if (tok->str() == "*")
|
|
tok = tok->previous();
|
|
tok->deleteNext(2);
|
|
ret = true;
|
|
}
|
|
|
|
// Remove parentheses around number..
|
|
if (validTokenStart(bounded, tok, frontToken, -2) &&
|
|
Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") &&
|
|
tok->strAt(-2) != ">") {
|
|
tok = tok->previous();
|
|
tok->deleteThis();
|
|
tok->deleteNext();
|
|
ret = true;
|
|
}
|
|
|
|
if (validTokenStart(bounded, tok, frontToken, -1) &&
|
|
validTokenEnd(bounded, tok, backToken, 1) &&
|
|
(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 (validTokenEnd(bounded, tok, backToken, 2) &&
|
|
Token::Match(tok, "%num% %comp% %num%") &&
|
|
MathLib::isInt(tok->str()) &&
|
|
MathLib::isInt(tok->strAt(2))) {
|
|
if (validTokenStart(bounded, tok, frontToken, -1) &&
|
|
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();
|
|
const Token *end = tok->previous()->findClosingBracket();
|
|
for (; tok && tok!= end; tok = tok->next()) {
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
const Token *closing = tok->next()->findClosingBracket();
|
|
if (closing)
|
|
tok = closing->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();
|
|
if (!endToken)
|
|
continue;
|
|
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 && instToken && 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);
|
|
}
|
|
}
|
|
|
|
void TemplateSimplifier::simplifyTemplates(
|
|
const std::time_t maxtime,
|
|
bool &codeWithTemplates)
|
|
{
|
|
// Remove "typename" unless used in template arguments or using type alias..
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% ="))
|
|
tok->deleteThis();
|
|
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
tok = tok->next()->findClosingBracket();
|
|
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) {
|
|
if (i) {
|
|
// it may take more than one pass to simplify type aliases
|
|
while (mTokenizer->simplifyUsing())
|
|
;
|
|
|
|
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);
|
|
}
|