3946 lines
165 KiB
C++
3946 lines
165 KiB
C++
/*
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
|
* Copyright (C) 2007-2023 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 "errortypes.h"
|
|
#include "mathlib.h"
|
|
#include "settings.h"
|
|
#include "standards.h"
|
|
#include "token.h"
|
|
#include "tokenize.h"
|
|
#include "tokenlist.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <stack>
|
|
#include <utility>
|
|
|
|
static Token *skipRequires(Token *tok)
|
|
{
|
|
if (!Token::simpleMatch(tok, "requires"))
|
|
return tok;
|
|
|
|
while (Token::Match(tok, "%oror%|&&|requires %name%|(")) {
|
|
Token *after = tok->next();
|
|
if (after->str() == "(") {
|
|
tok = after->link()->next();
|
|
continue;
|
|
}
|
|
if (Token::simpleMatch(after, "requires (") && Token::simpleMatch(after->linkAt(1), ") {")) {
|
|
tok = after->linkAt(1)->linkAt(1)->next();
|
|
continue;
|
|
}
|
|
while (Token::Match(after, "%name% :: %name%"))
|
|
after = after->tokAt(2);
|
|
if (Token::Match(after, "%name% <")) {
|
|
after = after->next()->findClosingBracket();
|
|
tok = after ? after->next() : nullptr;
|
|
} else
|
|
break;
|
|
}
|
|
return tok;
|
|
}
|
|
|
|
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(std::string name) : mName(std::move(name)) {}
|
|
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
|
|
return tokenAndName.name() == mName;
|
|
}
|
|
private:
|
|
const std::string mName;
|
|
};
|
|
|
|
class FindFullName {
|
|
public:
|
|
explicit FindFullName(std::string fullName) : mFullName(std::move(fullName)) {}
|
|
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
|
|
return tokenAndName.fullName() == mFullName;
|
|
}
|
|
private:
|
|
const std::string mFullName;
|
|
};
|
|
}
|
|
|
|
TemplateSimplifier::TokenAndName::TokenAndName(Token *token, std::string scope) :
|
|
mToken(token), mScope(std::move(scope)), mName(mToken ? mToken->str() : ""),
|
|
mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)),
|
|
mNameToken(nullptr), mParamEnd(nullptr), mFlags(0)
|
|
{
|
|
if (mToken) {
|
|
if (mToken->strAt(1) == "<") {
|
|
const Token *end = mToken->next()->findClosingBracket();
|
|
if (end && end->strAt(1) == "(") {
|
|
isFunction(true);
|
|
}
|
|
}
|
|
mToken->templateSimplifierPointer(this);
|
|
}
|
|
}
|
|
|
|
TemplateSimplifier::TokenAndName::TokenAndName(Token *token, std::string scope, const Token *nameToken, const Token *paramEnd) :
|
|
mToken(token), mScope(std::move(scope)), mName(nameToken->str()),
|
|
mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)),
|
|
mNameToken(nameToken), mParamEnd(paramEnd), mFlags(0)
|
|
{
|
|
// only set flags for declaration
|
|
if (mToken && mNameToken && mParamEnd) {
|
|
isSpecialization(Token::simpleMatch(mToken, "template < >"));
|
|
|
|
if (!isSpecialization()) {
|
|
if (Token::simpleMatch(mToken->next()->findClosingBracket(), "> template <")) {
|
|
const Token * temp = mNameToken->tokAt(-2);
|
|
while (Token::Match(temp, ">|%name% ::")) {
|
|
if (temp->str() == ">")
|
|
temp = temp->findOpeningBracket()->previous();
|
|
else
|
|
temp = temp->tokAt(-2);
|
|
}
|
|
isPartialSpecialization(temp->strAt(1) == "<");
|
|
} else
|
|
isPartialSpecialization(mNameToken->strAt(1) == "<");
|
|
}
|
|
|
|
isAlias(mParamEnd->strAt(1) == "using");
|
|
|
|
if (isAlias() && isPartialSpecialization()) {
|
|
throw InternalError(mToken, "partial specialization of alias templates is not permitted", InternalError::SYNTAX);
|
|
}
|
|
if (isAlias() && isSpecialization()) {
|
|
throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX);
|
|
}
|
|
|
|
isFriend(mParamEnd->strAt(1) == "friend");
|
|
const Token *next = mParamEnd->next();
|
|
if (isFriend())
|
|
next = next->next();
|
|
|
|
isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::"));
|
|
if (mToken->strAt(1) == "<" && !isSpecialization()) {
|
|
const Token *end = mToken->next()->findClosingBracket();
|
|
isVariadic(end && Token::findmatch(mToken->tokAt(2), "%name% ...", end));
|
|
}
|
|
const Token *tok1 = mNameToken->next();
|
|
if (tok1->str() == "<") {
|
|
const Token *closing = tok1->findClosingBracket();
|
|
if (closing)
|
|
tok1 = closing->next();
|
|
else
|
|
throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX);
|
|
}
|
|
isFunction(tok1->str() == "(");
|
|
isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;"));
|
|
if (!isFriend()) {
|
|
if (isVariable())
|
|
isForwardDeclaration(tok1->str() == ";");
|
|
else if (!isAlias()) {
|
|
if (isFunction())
|
|
tok1 = tok1->link()->next();
|
|
while (tok1 && !Token::Match(tok1, ";|{")) {
|
|
if (tok1->str() == "<")
|
|
tok1 = tok1->findClosingBracket();
|
|
else if (Token::Match(tok1, "(|[") && tok1->link())
|
|
tok1 = tok1->link();
|
|
if (tok1)
|
|
tok1 = tok1->next();
|
|
}
|
|
if (tok1)
|
|
isForwardDeclaration(tok1->str() == ";");
|
|
}
|
|
}
|
|
// check for member class or function and adjust scope
|
|
if ((isFunction() || isClass()) &&
|
|
(mNameToken->strAt(-1) == "::" || Token::simpleMatch(mNameToken->tokAt(-2), ":: ~"))) {
|
|
const Token * start = mNameToken;
|
|
if (start->strAt(-1) == "~")
|
|
start = start->previous();
|
|
const Token *end = start;
|
|
|
|
while (start && (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 != end) {
|
|
if (!mScope.empty())
|
|
mScope += " ::";
|
|
while (start && start->next() != end) {
|
|
if (start->str() == "<")
|
|
start = start->findClosingBracket();
|
|
else {
|
|
if (!mScope.empty())
|
|
mScope += " ";
|
|
mScope += start->str();
|
|
}
|
|
start = start->next();
|
|
}
|
|
if (start)
|
|
mFullName = mScope.empty() ? mName : (mScope + " :: " + mName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// make sure at most only one family flag is set
|
|
assert(isClass() ? !(isFunction() || isVariable()) : true);
|
|
assert(isFunction() ? !(isClass() || isVariable()) : true);
|
|
assert(isVariable() ? !(isClass() || isFunction()) : true);
|
|
|
|
if (mToken)
|
|
mToken->templateSimplifierPointer(this);
|
|
}
|
|
|
|
TemplateSimplifier::TokenAndName::TokenAndName(const TokenAndName& other) :
|
|
mToken(other.mToken), mScope(other.mScope), mName(other.mName), mFullName(other.mFullName),
|
|
mNameToken(other.mNameToken), mParamEnd(other.mParamEnd), mFlags(other.mFlags)
|
|
{
|
|
if (mToken)
|
|
mToken->templateSimplifierPointer(this);
|
|
}
|
|
|
|
TemplateSimplifier::TokenAndName::TokenAndName(TokenAndName&& other) NOEXCEPT :
|
|
mToken(other.mToken), mScope(std::move(other.mScope)), mName(std::move(other.mName)), mFullName(std::move(other.mFullName)),
|
|
mNameToken(other.mNameToken), mParamEnd(other.mParamEnd), mFlags(other.mFlags)
|
|
{
|
|
if (mToken)
|
|
mToken->templateSimplifierPointer(this);
|
|
}
|
|
|
|
TemplateSimplifier::TokenAndName::~TokenAndName()
|
|
{
|
|
if (mToken && mToken->templateSimplifierPointers())
|
|
mToken->templateSimplifierPointers()->erase(this);
|
|
}
|
|
|
|
std::string TemplateSimplifier::TokenAndName::dump(const std::vector<std::string>& fileNames) const {
|
|
std::string ret = " <TokenAndName name=\"" + mName + "\" file=\"" + ErrorLogger::toxml(fileNames.at(mToken->fileIndex())) + "\" line=\"" + std::to_string(mToken->linenr()) + "\">\n";
|
|
for (const Token* tok = mToken; tok && !Token::Match(tok, "[;{}]"); tok = tok->next())
|
|
ret += " " + ErrorLogger::toxml(tok->str()) + "\n";
|
|
return ret + " </TokenAndName>\n";
|
|
}
|
|
|
|
const Token * TemplateSimplifier::TokenAndName::aliasStartToken() const
|
|
{
|
|
if (mParamEnd)
|
|
return mParamEnd->tokAt(4);
|
|
return nullptr;
|
|
}
|
|
|
|
const Token * TemplateSimplifier::TokenAndName::aliasEndToken() const
|
|
{
|
|
if (aliasStartToken())
|
|
return Token::findsimplematch(aliasStartToken(), ";");
|
|
return nullptr;
|
|
}
|
|
|
|
bool TemplateSimplifier::TokenAndName::isAliasToken(const Token *tok) const
|
|
{
|
|
const Token *end = aliasEndToken();
|
|
|
|
for (const Token *tok1 = aliasStartToken(); tok1 != end; tok1 = tok1->next()) {
|
|
if (tok1 == tok)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TemplateSimplifier::TemplateSimplifier(Tokenizer &tokenizer)
|
|
: mTokenizer(tokenizer), mTokenList(mTokenizer.list), mSettings(*mTokenizer.mSettings),
|
|
mErrorLogger(mTokenizer.mErrorLogger), mChanged(false)
|
|
{}
|
|
|
|
TemplateSimplifier::~TemplateSimplifier()
|
|
{}
|
|
|
|
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::Match(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% <") && !Token::simpleMatch(tok, "template")) {
|
|
// 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 && Token::Match(tok2->previous(), "%type%")) {
|
|
// @todo add better expression detection
|
|
if (!(Token::Match(tok2->next(), "*| %type%|%num% ;") ||
|
|
Token::Match(tok2->next(), "*| %type% . %type% ;"))) {
|
|
inclevel = true;
|
|
}
|
|
} else if (tok2->next() && tok2->next()->isStandardType() && !Token::Match(tok2->tokAt(2), "(|{"))
|
|
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;
|
|
} else if (tok2->strAt(-1) == ">")
|
|
syntaxError(tok);
|
|
|
|
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();
|
|
if (!tok || tok->str() == ">")
|
|
return 0;
|
|
|
|
unsigned int level = 0;
|
|
|
|
while (tok) {
|
|
// skip template template
|
|
if (level == 0 && Token::simpleMatch(tok, "template <")) {
|
|
const Token *closing = tok->next()->findClosingBracket();
|
|
if (closing) {
|
|
if (closing->str() == ">>")
|
|
return numberOfParameters;
|
|
tok = closing->next();
|
|
if (!tok)
|
|
syntaxError(tok);
|
|
if (Token::Match(tok, ">|>>|>>="))
|
|
return numberOfParameters;
|
|
else if (tok->str() == ",") {
|
|
++numberOfParameters;
|
|
tok = tok->next();
|
|
continue;
|
|
}
|
|
} 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::simpleMatch(tok, "...")) {
|
|
if ((tok->previous()->isName() && !Token::Match(tok->tokAt(-2), "<|,|::")) ||
|
|
(!tok->previous()->isName() && !Token::Match(tok->previous(), ">|&|&&|*")))
|
|
return 0; // syntax error
|
|
tok = tok->next();
|
|
if (!tok)
|
|
return 0;
|
|
if (tok->str() == ">") {
|
|
if (level == 0)
|
|
return numberOfParameters;
|
|
--level;
|
|
} else if (tok->str() == ">>" || tok->str() == ">>=") {
|
|
if (level == 1)
|
|
return numberOfParameters;
|
|
level -= 2;
|
|
} else if (tok->str() == "," && level == 0) {
|
|
++numberOfParameters;
|
|
tok = tok->next();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Skip '=', '?', ':'
|
|
if (Token::Match(tok, "=|?|:"))
|
|
tok = tok->next();
|
|
if (!tok)
|
|
return 0;
|
|
|
|
// Skip links
|
|
if (Token::Match(tok, "(|{")) {
|
|
tok = tok->link();
|
|
if (tok)
|
|
tok = tok->next();
|
|
if (!tok)
|
|
return 0;
|
|
if (tok->str() == ">" && level == 0)
|
|
return numberOfParameters;
|
|
else if ((tok->str() == ">>" || tok->str() == ">>=") && level == 1)
|
|
return numberOfParameters;
|
|
else if (tok->str() == ",") {
|
|
if (level == 0)
|
|
++numberOfParameters;
|
|
tok = tok->next();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// skip std::
|
|
if (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() == "<" && tok->previous()->isName()) {
|
|
++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() == ">>" || tok->str() == ">>=") {
|
|
if (level == 0)
|
|
return !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0;
|
|
--level;
|
|
}
|
|
tok = tok->next();
|
|
|
|
if (Token::Match(tok, "(|["))
|
|
tok = tok->link()->next();
|
|
|
|
if (!tok)
|
|
return 0;
|
|
}
|
|
|
|
if (tok->str() != ",")
|
|
continue;
|
|
if (level == 0)
|
|
++numberOfParameters;
|
|
tok = tok->next();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok)
|
|
{
|
|
return const_cast<const Token *>(findTemplateDeclarationEnd(const_cast<Token *>(tok)));
|
|
}
|
|
|
|
Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok)
|
|
{
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
tok = tok->next()->findClosingBracket();
|
|
if (tok)
|
|
tok = tok->next();
|
|
}
|
|
|
|
if (!tok)
|
|
return nullptr;
|
|
|
|
Token * tok2 = tok;
|
|
bool in_init = false;
|
|
while (tok2 && !Token::Match(tok2, ";|{")) {
|
|
if (tok2->str() == "<")
|
|
tok2 = tok2->findClosingBracket();
|
|
else if (Token::Match(tok2, "(|[") && tok2->link())
|
|
tok2 = tok2->link();
|
|
else if (tok2->str() == ":")
|
|
in_init = true;
|
|
else if (in_init && Token::Match(tok2, "%name% (|{")) {
|
|
tok2 = tok2->linkAt(1);
|
|
if (tok2->strAt(1) == "{")
|
|
in_init = false;
|
|
}
|
|
if (tok2)
|
|
tok2 = tok2->next();
|
|
}
|
|
if (tok2 && tok2->str() == "{") {
|
|
tok = tok2->link();
|
|
if (tok && tok->strAt(1) == ";")
|
|
tok = tok->next();
|
|
} else if (tok2 && tok2->str() == ";")
|
|
tok = tok2;
|
|
else
|
|
tok = nullptr;
|
|
|
|
return tok;
|
|
}
|
|
|
|
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;
|
|
|
|
Token *end = findTemplateDeclarationEnd(tok);
|
|
if (end && end->next()) {
|
|
eraseTokens(tok, end->next());
|
|
deleteToken(tok);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TemplateSimplifier::getTemplateDeclarations()
|
|
{
|
|
bool codeWithTemplates = false;
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (!Token::simpleMatch(tok, "template <"))
|
|
continue;
|
|
// ignore template template parameter
|
|
if (tok->strAt(-1) == "<" || tok->strAt(-1) == ",")
|
|
continue;
|
|
// ignore nested template
|
|
if (tok->strAt(-1) == ">")
|
|
continue;
|
|
// skip to last nested template parameter
|
|
const Token *tok1 = tok;
|
|
while (tok1 && tok1->next()) {
|
|
const Token *closing = tok1->next()->findClosingBracket();
|
|
if (!Token::simpleMatch(closing, "> template <"))
|
|
break;
|
|
tok1 = closing->next();
|
|
}
|
|
if (!Token::Match(tok, "%any% %any%"))
|
|
syntaxError(tok);
|
|
if (tok->strAt(2)=="typename" &&
|
|
!Token::Match(tok->tokAt(3), "%name%|...|,|=|>"))
|
|
syntaxError(tok->next());
|
|
codeWithTemplates = true;
|
|
const Token * const parmEnd = tok1->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, tok->scopeInfo()->name, parmEnd->tokAt(namepos), parmEnd);
|
|
if (decl.isForwardDeclaration()) {
|
|
// Declaration => add to mTemplateForwardDeclarations
|
|
mTemplateForwardDeclarations.emplace_back(std::move(decl));
|
|
} else {
|
|
// Implementation => add to mTemplateDeclarations
|
|
mTemplateDeclarations.emplace_back(std::move(decl));
|
|
}
|
|
Token *end = findTemplateDeclarationEnd(tok);
|
|
if (end)
|
|
tok = end;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return codeWithTemplates;
|
|
}
|
|
|
|
void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope)
|
|
{
|
|
simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket());
|
|
|
|
TokenAndName instantiation(token, scope);
|
|
|
|
// check if instantiation already exists before adding it
|
|
const std::list<TokenAndName>::const_iterator it = std::find(mTemplateInstantiations.cbegin(),
|
|
mTemplateInstantiations.cend(),
|
|
instantiation);
|
|
|
|
if (it == mTemplateInstantiations.cend())
|
|
mTemplateInstantiations.emplace_back(std::move(instantiation));
|
|
}
|
|
|
|
static void getFunctionArguments(const Token *nameToken, std::vector<const Token *> &args)
|
|
{
|
|
const Token *argToken;
|
|
|
|
if (nameToken->strAt(1) == "(")
|
|
argToken = nameToken->tokAt(2);
|
|
else if (nameToken->strAt(1) == "<") {
|
|
const Token *end = nameToken->next()->findClosingBracket();
|
|
if (end)
|
|
argToken = end->tokAt(2);
|
|
else
|
|
return;
|
|
} else
|
|
return;
|
|
|
|
if (argToken->str() == ")")
|
|
return;
|
|
|
|
args.push_back(argToken);
|
|
|
|
while ((argToken = argToken->nextArgumentBeforeCreateLinks2()))
|
|
args.push_back(argToken);
|
|
}
|
|
|
|
static bool areAllParamsTypes(const std::vector<const Token *> ¶ms)
|
|
{
|
|
if (params.empty())
|
|
return false;
|
|
|
|
return std::all_of(params.cbegin(), params.cend(), [](const Token* param) {
|
|
return Token::Match(param->previous(), "typename|class %name% ,|>");
|
|
});
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
const Token *skip = nullptr;
|
|
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
|
|
// template definition.. skip it
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
tok = tok->next()->findClosingBracket();
|
|
if (!tok)
|
|
break;
|
|
|
|
const bool isUsing = tok->strAt(1) == "using";
|
|
if (isUsing && Token::Match(tok->tokAt(2), "%name% <")) {
|
|
// Can't have specialized type alias so ignore it
|
|
Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";");
|
|
if (tok2)
|
|
tok = tok2;
|
|
} else 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.
|
|
const 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
|
|
|
|
Token * tok2 = findTemplateDeclarationEnd(tok->next());
|
|
if (tok2)
|
|
tok = tok2;
|
|
}
|
|
} else if (Token::Match(tok, "template using %name% <")) {
|
|
// Can't have specialized type alias so ignore it
|
|
Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";");
|
|
if (tok2)
|
|
tok = tok2;
|
|
} else if (Token::Match(tok, "using %name% <")) {
|
|
// Can't have specialized type alias so ignore it
|
|
Token *tok2 = Token::findsimplematch(tok->tokAt(2), ";");
|
|
if (tok2)
|
|
tok = 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 = tok->scopeInfo()->name;
|
|
std::string qualification;
|
|
Token * qualificationTok = tok;
|
|
while (Token::Match(tok, "%name% :: %name%")) {
|
|
qualification += (qualification.empty() ? "" : " :: ") + tok->str();
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
// skip specialization
|
|
if (tok == skip) {
|
|
skip = nullptr;
|
|
continue;
|
|
}
|
|
|
|
// look for function instantiation with type deduction
|
|
if (tok->strAt(1) == "(") {
|
|
std::vector<const Token *> instantiationArgs;
|
|
getFunctionArguments(tok, instantiationArgs);
|
|
|
|
std::string fullName;
|
|
if (!qualification.empty())
|
|
fullName = qualification + " :: " + tok->str();
|
|
else if (!scopeName.empty())
|
|
fullName = scopeName + " :: " + tok->str();
|
|
else
|
|
fullName = tok->str();
|
|
|
|
// get all declarations with this name
|
|
auto range = functionNameMap.equal_range(tok->str());
|
|
for (auto pos = range.first; pos != range.second; ++pos) {
|
|
// look for declaration with same qualification or constructor with same qualification
|
|
if (pos->second->fullName() == fullName ||
|
|
(pos->second->scope() == fullName && tok->str() == pos->second->name())) {
|
|
std::vector<const Token *> templateParams;
|
|
getTemplateParametersInDeclaration(pos->second->token()->tokAt(2), templateParams);
|
|
|
|
// todo: handle more than one template parameter
|
|
if (templateParams.size() != 1 || !areAllParamsTypes(templateParams))
|
|
continue;
|
|
|
|
std::vector<const Token *> declarationParams;
|
|
getFunctionArguments(pos->second->nameToken(), declarationParams);
|
|
|
|
// function argument counts must match
|
|
if (instantiationArgs.empty() || instantiationArgs.size() != declarationParams.size())
|
|
continue;
|
|
|
|
size_t match = 0;
|
|
size_t argMatch = 0;
|
|
for (size_t i = 0; i < declarationParams.size(); ++i) {
|
|
// fixme: only type deducton from literals is supported
|
|
const bool isArgLiteral = Token::Match(instantiationArgs[i], "%num%|%str%|%char%|%bool% ,|)");
|
|
if (isArgLiteral && Token::Match(declarationParams[i], "const| %type% &| %name%| ,|)")) {
|
|
match++;
|
|
|
|
// check if parameter types match
|
|
if (templateParams[0]->str() == declarationParams[i]->str())
|
|
argMatch = i;
|
|
else {
|
|
// todo: check if non-template args match for function overloads
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match == declarationParams.size()) {
|
|
const Token *arg = instantiationArgs[argMatch];
|
|
tok->insertToken(">");
|
|
switch (arg->tokType()) {
|
|
case Token::eBoolean:
|
|
tok->insertToken("bool");
|
|
break;
|
|
case Token::eChar:
|
|
if (arg->isLong())
|
|
tok->insertToken("wchar_t");
|
|
else
|
|
tok->insertToken("char");
|
|
break;
|
|
case Token::eString:
|
|
tok->insertToken("*");
|
|
if (arg->isLong())
|
|
tok->insertToken("wchar_t");
|
|
else
|
|
tok->insertToken("char");
|
|
tok->insertToken("const");
|
|
break;
|
|
case Token::eNumber: {
|
|
MathLib::value num(arg->str());
|
|
if (num.isFloat()) {
|
|
// MathLib::getSuffix doesn't work for floating point numbers
|
|
const char suffix = arg->str().back();
|
|
if (suffix == 'f' || suffix == 'F')
|
|
tok->insertToken("float");
|
|
else if (suffix == 'l' || suffix == 'L') {
|
|
tok->insertToken("double");
|
|
tok->next()->isLong(true);
|
|
} 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->next()->isLong(true);
|
|
} else if (suffix.find('L') != std::string::npos)
|
|
tok->insertToken("long");
|
|
else
|
|
tok->insertToken("int");
|
|
if (suffix.find('U') != std::string::npos)
|
|
tok->next()->isUnsigned(true);
|
|
}
|
|
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
|
|
Token *tok2 = tok->next()->findClosingBracket();
|
|
|
|
// parse backwards and add template instantiations
|
|
// TODO
|
|
for (; tok2 && tok2 != tok; tok2 = tok2->previous()) {
|
|
if (Token::Match(tok2, ",|< %name% <") &&
|
|
(tok2->strAt(3) == ">" || templateParameters(tok2->tokAt(2)))) {
|
|
addInstantiation(tok2->next(), tok->scopeInfo()->name);
|
|
} else if (Token::Match(tok2->next(), "class|struct"))
|
|
tok2->deleteNext();
|
|
}
|
|
|
|
// Add outer template..
|
|
if (templateParameters(tok->next()) || tok->strAt(2) == ">") {
|
|
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.cbegin(), mTemplateDeclarations.cend(), 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 : tok->scopeInfo()->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.cbegin(), mTemplateDeclarations.cend(), 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.find(' ', offset)) != std::string::npos) {
|
|
qualificationTok->insertToken(nameSpace.substr(offset, pos - offset), emptyString, true);
|
|
offset = pos + 1;
|
|
}
|
|
qualificationTok->insertToken(nameSpace.substr(offset), emptyString, true);
|
|
qualificationTok->insertToken("::", emptyString, true);
|
|
addInstantiation(tok, it1->scope());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
break;
|
|
|
|
if (scopeName.empty()) {
|
|
if (!qualification.empty())
|
|
addInstantiation(tok, qualification);
|
|
else
|
|
addInstantiation(tok, tok->scopeInfo()->name);
|
|
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 &declaration : mTemplateDeclarations)
|
|
useDefaultArgumentValues(declaration);
|
|
|
|
for (TokenAndName &declaration : mTemplateForwardDeclarations)
|
|
useDefaultArgumentValues(declaration);
|
|
}
|
|
|
|
void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration)
|
|
{
|
|
// Ticket #5762: Skip specialization tokens
|
|
if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend())
|
|
return;
|
|
|
|
// template parameters with default value has syntax such as:
|
|
// x = y
|
|
// this list will contain all the '=' tokens for such arguments
|
|
struct Default {
|
|
Token *eq;
|
|
Token *end;
|
|
};
|
|
std::list<Default> 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;
|
|
|
|
// map type parameter name to index
|
|
std::map<std::string, unsigned int> typeParameterNames;
|
|
|
|
// Scan template declaration..
|
|
for (Token *tok = declaration.token()->next(); tok; tok = tok->next()) {
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
Token* end = tok->next()->findClosingBracket();
|
|
if (end)
|
|
tok = end;
|
|
continue;
|
|
}
|
|
|
|
if (tok->link() && Token::Match(tok, "{|(|[")) { // Ticket #6835
|
|
tok = tok->link();
|
|
continue;
|
|
}
|
|
|
|
if (tok->str() == "<" &&
|
|
(tok->strAt(1) == ">" || (tok->previous()->isName() &&
|
|
typeParameterNames.find(tok->strAt(-1)) == typeParameterNames.end())))
|
|
++templateParmDepth;
|
|
|
|
// end of template parameters?
|
|
if (tok->str() == ">") {
|
|
if (templateParmDepth<2) {
|
|
if (!eq.empty())
|
|
eq.back().end = tok;
|
|
break;
|
|
} else
|
|
--templateParmDepth;
|
|
}
|
|
|
|
// map type parameter name to index
|
|
if (Token::Match(tok, "typename|class|%type% %name% ,|>"))
|
|
typeParameterNames[tok->strAt(1)] = templatepar - 1;
|
|
|
|
// next template parameter
|
|
if (tok->str() == "," && (1 == templateParmDepth)) { // Ticket #5823: Properly count parameters
|
|
if (!eq.empty())
|
|
eq.back().end = tok;
|
|
++templatepar;
|
|
}
|
|
|
|
// default parameter value?
|
|
else if (Token::Match(tok, "= !!>")) {
|
|
if (defaultedArgPos.insert(templatepar).second) {
|
|
eq.emplace_back(Default{tok, nullptr});
|
|
} else {
|
|
// Ticket #5605: Syntax error (two equal signs for the same parameter), bail out
|
|
eq.clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (eq.empty())
|
|
return;
|
|
|
|
// iterate through all template instantiations
|
|
for (const TokenAndName &instantiation : mTemplateInstantiations) {
|
|
if (declaration.fullName() != instantiation.fullName())
|
|
continue;
|
|
|
|
// instantiation arguments..
|
|
std::vector<std::vector<const Token *>> instantiationArgs;
|
|
std::size_t index = 0;
|
|
const Token *end = instantiation.token()->next()->findClosingBracket();
|
|
if (!end)
|
|
continue;
|
|
if (end != instantiation.token()->tokAt(2))
|
|
instantiationArgs.resize(1);
|
|
for (const Token *tok1 = instantiation.token()->tokAt(2); tok1 && tok1 != end; tok1 = tok1->next()) {
|
|
if (tok1->link() && Token::Match(tok1, "{|(|[")) {
|
|
const Token *endLink = tok1->link();
|
|
do {
|
|
instantiationArgs[index].push_back(tok1);
|
|
tok1 = tok1->next();
|
|
} while (tok1 && tok1 != endLink);
|
|
instantiationArgs[index].push_back(tok1);
|
|
} else if (tok1->str() == "<" &&
|
|
(tok1->strAt(1) == ">" || (tok1->previous()->isName() &&
|
|
typeParameterNames.find(tok1->strAt(-1)) == typeParameterNames.end()))) {
|
|
const Token *endLink = tok1->findClosingBracket();
|
|
do {
|
|
instantiationArgs[index].push_back(tok1);
|
|
tok1 = tok1->next();
|
|
} while (tok1 && tok1 != endLink);
|
|
instantiationArgs[index].push_back(tok1);
|
|
} else if (tok1->str() == ",") {
|
|
++index;
|
|
instantiationArgs.resize(index + 1);
|
|
} else
|
|
instantiationArgs[index].push_back(tok1);
|
|
}
|
|
|
|
// count the parameters..
|
|
Token *tok = instantiation.token()->next();
|
|
unsigned int usedpar = templateParameters(tok);
|
|
Token *instantiationEnd = tok->findClosingBracket();
|
|
tok = instantiationEnd;
|
|
|
|
if (tok && tok->str() == ">") {
|
|
tok = tok->previous();
|
|
std::list<Default>::const_iterator it = eq.cbegin();
|
|
for (std::size_t i = (templatepar - eq.size()); it != eq.cend() && i < usedpar; ++i)
|
|
++it;
|
|
int count = 0;
|
|
while (it != eq.cend()) {
|
|
// check for end
|
|
if (!it->end) {
|
|
if (mSettings.debugwarnings) {
|
|
const std::list<const Token*> locationList(1, it->eq);
|
|
const ErrorMessage errmsg(locationList, &mTokenizer.list,
|
|
Severity::debug,
|
|
"noparamend",
|
|
"TemplateSimplifier couldn't find end of template parameter.",
|
|
Certainty::normal);
|
|
if (mErrorLogger && mSettings.severity.isEnabled(Severity::debug))
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((usedpar + count) && usedpar <= (instantiationArgs.size() + count)) {
|
|
tok->insertToken(",");
|
|
tok = tok->next();
|
|
}
|
|
std::stack<Token *> links;
|
|
for (const Token* from = it->eq->next(); from && from != it->end; from = from->next()) {
|
|
auto entry = typeParameterNames.find(from->str());
|
|
if (entry != typeParameterNames.end() && entry->second < instantiationArgs.size()) {
|
|
for (const Token *tok1 : instantiationArgs[entry->second]) {
|
|
tok->insertToken(tok1->str(), tok1->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();
|
|
}
|
|
}
|
|
} else {
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
++it;
|
|
count++;
|
|
usedpar++;
|
|
}
|
|
}
|
|
|
|
simplifyTemplateArgs(instantiation.token()->next(), instantiationEnd);
|
|
}
|
|
|
|
for (const auto & entry : eq) {
|
|
Token *const eqtok = entry.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% <") && (tok2->strAt(2) == ">" || templateParameters(tok2->next()))) {
|
|
const 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
|
|
const std::list<TokenAndName>::iterator ti2 = std::find_if(mTemplateInstantiations.begin(),
|
|
mTemplateInstantiations.end(),
|
|
FindName(declaration.name()));
|
|
|
|
if (ti2 == mTemplateInstantiations.end())
|
|
continue;
|
|
|
|
eraseTokens(eqtok, tok2);
|
|
eqtok->deleteThis();
|
|
|
|
// update parameter end pointer
|
|
declaration.paramEnd(declaration.token()->next()->findClosingBracket());
|
|
}
|
|
}
|
|
|
|
void TemplateSimplifier::simplifyTemplateAliases()
|
|
{
|
|
for (std::list<TokenAndName>::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) {
|
|
const TokenAndName &aliasDeclaration = *it1;
|
|
|
|
if (!aliasDeclaration.isAlias()) {
|
|
++it1;
|
|
continue;
|
|
}
|
|
|
|
// alias parameters..
|
|
std::vector<const Token *> aliasParameters;
|
|
getTemplateParametersInDeclaration(aliasDeclaration.token()->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..
|
|
bool found = false;
|
|
for (std::list<TokenAndName>::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) {
|
|
const TokenAndName &aliasUsage = *it2;
|
|
if (!aliasUsage.token() || aliasUsage.fullName() != aliasDeclaration.fullName()) {
|
|
++it2;
|
|
continue;
|
|
}
|
|
|
|
// don't recurse
|
|
if (aliasDeclaration.isAliasToken(aliasUsage.token())) {
|
|
++it2;
|
|
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();
|
|
if (!tok2)
|
|
break;
|
|
}
|
|
tok2 = tok2->next();
|
|
}
|
|
|
|
args.emplace_back(start, tok2);
|
|
if (tok2 && tok2->str() == ",") {
|
|
tok2 = tok2->next();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!tok2 || tok2->str() != ">" ||
|
|
(!aliasDeclaration.isVariadic() && (args.size() != aliasParameters.size())) ||
|
|
(aliasDeclaration.isVariadic() && (args.size() < aliasParameters.size()))) {
|
|
++it2;
|
|
continue;
|
|
}
|
|
|
|
mChanged = true;
|
|
|
|
// copy template-id from declaration to after instantiation
|
|
Token * dst = aliasUsage.token()->next()->findClosingBracket();
|
|
Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next();
|
|
|
|
// replace parameters
|
|
for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
|
|
if (!tok1->isName())
|
|
continue;
|
|
if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) {
|
|
const unsigned int argnr = aliasParameterNames[tok1->str()];
|
|
const Token * const fromStart = args[argnr].first;
|
|
const Token * const fromEnd = args[argnr].second->previous();
|
|
Token *temp = TokenList::copyTokens(tok1, fromStart, fromEnd, true);
|
|
const bool tempOK(temp && temp != tok1->next());
|
|
tok1->deleteThis();
|
|
if (tempOK)
|
|
tok1 = temp; // skip over inserted parameters
|
|
} else if (tok1->str() == "typename")
|
|
tok1->deleteThis();
|
|
}
|
|
|
|
// add new instantiations
|
|
for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) {
|
|
if (!tok1->isName())
|
|
continue;
|
|
if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) {
|
|
// Create template instance..
|
|
if (Token::Match(tok1, "%name% <")) {
|
|
const std::list<TokenAndName>::const_iterator it = std::find_if(mTemplateInstantiations.cbegin(),
|
|
mTemplateInstantiations.cend(),
|
|
FindToken(tok1));
|
|
if (it != mTemplateInstantiations.cend())
|
|
addInstantiation(tok2, it->scope());
|
|
}
|
|
}
|
|
}
|
|
|
|
// erase the instantiation tokens
|
|
eraseTokens(aliasUsage.token()->previous(), dst->next());
|
|
found = true;
|
|
|
|
// erase this instantiation
|
|
it2 = mTemplateInstantiations.erase(it2);
|
|
}
|
|
|
|
if (found) {
|
|
Token *end = const_cast<Token *>(aliasDeclaration.aliasEndToken());
|
|
|
|
// remove declaration tokens
|
|
if (aliasDeclaration.token()->previous())
|
|
eraseTokens(aliasDeclaration.token()->previous(), end->next() ? end->next() : end);
|
|
else {
|
|
eraseTokens(mTokenList.front(), end->next() ? end->next() : end);
|
|
deleteToken(mTokenList.front());
|
|
}
|
|
|
|
// remove declaration
|
|
it1 = mTemplateDeclarations.erase(it1);
|
|
} else
|
|
++it1;
|
|
}
|
|
}
|
|
|
|
bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[])
|
|
{
|
|
assert(instance->strAt(1) == "<");
|
|
|
|
auto n = templateParameters(instance->next());
|
|
if (variadic ? (n + 1 < numberOfArguments) : (numberOfArguments != n))
|
|
return false;
|
|
|
|
if (patternAfter) {
|
|
const Token *tok = instance->next()->findClosingBracket();
|
|
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->next(), "decltype (")) {
|
|
const Token * end = tok->linkAt(2)->previous();
|
|
while (tok->next() && 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->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;
|
|
// skip decltype(...)
|
|
else if (Token::simpleMatch(tok->next(), "decltype (")) {
|
|
const Token * end = tok->linkAt(2);
|
|
while (tok->next() && tok != end) {
|
|
tok = tok->next();
|
|
namepos++;
|
|
}
|
|
} 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->next() && tok->next() != closing) {
|
|
tok = tok->next();
|
|
namepos++;
|
|
}
|
|
}
|
|
} else if (Token::Match(tok->next(), "%type% =|;")) {
|
|
return true;
|
|
}
|
|
tok = tok->next();
|
|
namepos++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos)
|
|
{
|
|
if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) {
|
|
namepos = tok->strAt(1) == "friend" ? 3 : 2;
|
|
tok = tok->tokAt(namepos);
|
|
while (Token::Match(tok, "%type% :: %type%") ||
|
|
(Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) {
|
|
if (tok->strAt(1) == "::") {
|
|
tok = tok->tokAt(2);
|
|
namepos += 2;
|
|
} else {
|
|
const Token *end = tok->next()->findClosingBracket();
|
|
if (!end || !end->tokAt(2)) {
|
|
// syntax error
|
|
namepos = -1;
|
|
return true;
|
|
}
|
|
end = end->tokAt(2);
|
|
do {
|
|
tok = tok->next();
|
|
namepos += 1;
|
|
} while (tok && tok != end);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
|
|
{
|
|
assert(tok && tok->str() == ">");
|
|
|
|
auto it = mTemplateNamePos.find(tok);
|
|
if (!mSettings.debugtemplate && it != mTemplateNamePos.end()) {
|
|
return it->second;
|
|
}
|
|
// get the position of the template name
|
|
int namepos = 0;
|
|
if (getTemplateNamePositionTemplateClass(tok, namepos))
|
|
;
|
|
else if (Token::Match(tok, "> using %name% =")) {
|
|
// types may not be defined in alias template declarations
|
|
if (!Token::Match(tok->tokAt(4), "class|struct|union|enum %name%| {"))
|
|
namepos = 2;
|
|
} else if (getTemplateNamePositionTemplateVariable(tok, namepos))
|
|
;
|
|
else if (!getTemplateNamePositionTemplateFunction(tok, namepos))
|
|
namepos = -1; // Name not found
|
|
mTemplateNamePos[tok] = namepos;
|
|
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;
|
|
bool inTemplate = false;
|
|
int level = 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 (token == "<") {
|
|
inTemplate = true;
|
|
++level;
|
|
}
|
|
if (inTemplate) {
|
|
if (insert)
|
|
mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + token);
|
|
else
|
|
mTokenList.back()->str(mTokenList.back()->str() + token);
|
|
if (token == ">") {
|
|
--level;
|
|
if (level == 0)
|
|
inTemplate = false;
|
|
}
|
|
} else {
|
|
if (insert)
|
|
mTokenList.back()->tokAt(offset)->insertToken(token, emptyString);
|
|
else
|
|
mTokenList.addtoken(token, tok->linenr(), tok->column(), 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) {
|
|
if (!inTemplate)
|
|
mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope().substr(start), emptyString);
|
|
else
|
|
mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + templateDeclaration.scope().substr(start));
|
|
mTokenList.back()->tokAt(offset)->insertToken("::", emptyString);
|
|
} else {
|
|
if (!inTemplate)
|
|
mTokenList.addtoken(templateDeclaration.scope().substr(start), tok->linenr(), tok->column(), tok->fileIndex());
|
|
else
|
|
mTokenList.back()->str(mTokenList.back()->str() + templateDeclaration.scope().substr(start));
|
|
mTokenList.addtoken("::", tok->linenr(), tok->column(), tok->fileIndex());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok)
|
|
{
|
|
const 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(), scope.size());
|
|
}
|
|
|
|
void TemplateSimplifier::expandTemplate(
|
|
const TokenAndName &templateDeclaration,
|
|
const TokenAndName &templateInstantiation,
|
|
const std::vector<const Token *> &typeParametersInDeclaration,
|
|
const std::string &newName,
|
|
bool copy)
|
|
{
|
|
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, std::string s) : token(t), scope(std::move(s)) {}
|
|
Token *token;
|
|
std::string scope;
|
|
};
|
|
std::vector<newInstantiation> newInstantiations;
|
|
|
|
// add forward declarations
|
|
if (copy && isClass) {
|
|
templateDeclaration.token()->insertToken(templateDeclarationToken->strAt(1), emptyString, true);
|
|
templateDeclaration.token()->insertToken(newName, emptyString, true);
|
|
templateDeclaration.token()->insertToken(";", emptyString, true);
|
|
} else if ((isFunction && (copy || isSpecialization)) ||
|
|
(isVariable && !isSpecialization) ||
|
|
(isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token()) != mTemplateSpecializationMap.end())) {
|
|
Token * dst = templateDeclaration.token();
|
|
Token * dstStart = dst->previous();
|
|
bool isStatic = false;
|
|
std::string scope;
|
|
const Token * start;
|
|
const Token * end;
|
|
auto it = mTemplateForwardDeclarationsMap.find(dst);
|
|
if (!isSpecialization && 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 {
|
|
if (it != mTemplateForwardDeclarationsMap.end()) {
|
|
const std::list<TokenAndName>::const_iterator it1 = std::find_if(mTemplateForwardDeclarations.cbegin(),
|
|
mTemplateForwardDeclarations.cend(),
|
|
FindToken(it->second));
|
|
if (it1 != mTemplateForwardDeclarations.cend())
|
|
mMemberFunctionsToDelete.push_back(*it1);
|
|
}
|
|
|
|
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() == "=") {
|
|
const Token *temp = end->next();
|
|
while (temp && temp->str() != ";") {
|
|
if (temp->link() && Token::Match(temp, "{|[|("))
|
|
temp = temp->link();
|
|
temp = temp->next();
|
|
}
|
|
end = temp;
|
|
}
|
|
}
|
|
unsigned int typeindentlevel = 0;
|
|
while (end && !(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) {
|
|
if (Token::Match(end, "<|(|{"))
|
|
++typeindentlevel;
|
|
else if (Token::Match(end, ">|)|}"))
|
|
--typeindentlevel;
|
|
end = end->next();
|
|
}
|
|
|
|
if (isStatic) {
|
|
dst->insertToken("static", emptyString, true);
|
|
if (start) {
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
}
|
|
}
|
|
|
|
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() && itype < mTypesUsedInTemplateInstantiation.size() &&
|
|
(!isVariable || !Token::Match(typeParametersInDeclaration[itype]->previous(), "<|, %type% >|,"))) {
|
|
typeindentlevel = 0;
|
|
std::stack<Token *> brackets1; // holds "(" and "{" tokens
|
|
bool pointerType = false;
|
|
Token * const dst1 = dst->previous();
|
|
const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size();
|
|
if (isVariadicTemplateArg && Token::Match(start, "%name% ... %name%"))
|
|
start = start->tokAt(2);
|
|
const std::string endStr(isVariadicTemplateArg ? ">" : ",>");
|
|
for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token();
|
|
typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos);
|
|
typetok = typetok->next()) {
|
|
if (typeindentlevel == 0 && typetok->str() == "*")
|
|
pointerType = true;
|
|
if (Token::simpleMatch(typetok, "..."))
|
|
continue;
|
|
if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next())))
|
|
++typeindentlevel;
|
|
else if (typeindentlevel > 0 && typetok->str() == ">")
|
|
--typeindentlevel;
|
|
else if (typetok->str() == "(")
|
|
++typeindentlevel;
|
|
else if (typetok->str() == ")")
|
|
--typeindentlevel;
|
|
dst->insertToken(typetok->str(), typetok->originalName(), true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
Token *previous = dst->previous();
|
|
previous->isTemplateArg(true);
|
|
previous->isSigned(typetok->isSigned());
|
|
previous->isUnsigned(typetok->isUnsigned());
|
|
previous->isLong(typetok->isLong());
|
|
if (Token::Match(previous, "{|(|[")) {
|
|
brackets1.push(previous);
|
|
} else if (previous->str() == "}") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "{");
|
|
Token::createMutualLinks(brackets1.top(), previous);
|
|
brackets1.pop();
|
|
} else if (previous->str() == ")") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "(");
|
|
Token::createMutualLinks(brackets1.top(), previous);
|
|
brackets1.pop();
|
|
} else if (previous->str() == "]") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "[");
|
|
Token::createMutualLinks(brackets1.top(), previous);
|
|
brackets1.pop();
|
|
}
|
|
}
|
|
if (pointerType && Token::simpleMatch(dst1, "const")) {
|
|
dst->insertToken("const", dst1->originalName(), true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
dst1->deleteThis();
|
|
}
|
|
} 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() &&
|
|
!(templateDeclaration.isFunction() && templateDeclaration.scope().empty() &&
|
|
(start->strAt(-1) == "." || Token::simpleMatch(start->tokAt(-2), ". template")))) {
|
|
if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) {
|
|
dst->insertToken(newName, emptyString, true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
if (start->strAt(1) == "<")
|
|
start = start->next()->findClosingBracket();
|
|
} else {
|
|
dst->insertToken(start->str(), emptyString, true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope());
|
|
}
|
|
} else {
|
|
// check if type is a template
|
|
if (start->strAt(1) == "<") {
|
|
// get the instantiated name
|
|
const 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
|
|
if (std::any_of(mTemplateInstantiations.cbegin(), mTemplateInstantiations.cend(), [&](const TokenAndName& inst) {
|
|
return Token::simpleMatch(inst.token(), name.c_str(), name.size());
|
|
})) {
|
|
// use the instantiated name
|
|
dst->insertToken(name, "", true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
start = closing;
|
|
}
|
|
}
|
|
// just copy the token if it wasn't instantiated
|
|
if (start != closing) {
|
|
dst->insertToken(start->str(), start->originalName(), true);
|
|
dst->previous()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
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()->linenr(start->linenr());
|
|
dst->previous()->column(start->column());
|
|
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, "]|}|)")) {
|
|
std::map<const Token *, Token *>::iterator link = links.find(start);
|
|
// make sure link is valid
|
|
if (link != links.end()) {
|
|
Token::createMutualLinks(link->second, dst->previous());
|
|
links.erase(start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
start = start->next();
|
|
}
|
|
dst->insertToken(";", emptyString, true);
|
|
dst->previous()->linenr(dst->tokAt(-2)->linenr());
|
|
dst->previous()->column(dst->tokAt(-2)->column() + 1);
|
|
|
|
if (isVariable || isFunction)
|
|
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 (inTemplateDefinition) {
|
|
if (!endOfTemplateDefinition) {
|
|
if (isVariable) {
|
|
Token *temp = tok3->findClosingBracket();
|
|
if (temp) {
|
|
while (temp && temp->str() != ";") {
|
|
if (temp->link() && Token::Match(temp, "{|[|("))
|
|
temp = temp->link();
|
|
temp = temp->next();
|
|
}
|
|
endOfTemplateDefinition = temp;
|
|
}
|
|
} 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(), templateDeclaration.isVariadic(), ":: ~| %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
|
|
std::stack<Token *> brackets2; // holds "(" and "{" tokens
|
|
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->column(), 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() && !Token::Match(tok5, "class|typename|struct") && !tok5->isStandardType()) {
|
|
// 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() && itype < mTypesUsedInTemplateInstantiation.size()) {
|
|
std::stack<Token *> brackets1; // holds "(" and "{" tokens
|
|
for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token();
|
|
typetok && !Token::Match(typetok, ",|>");
|
|
typetok = typetok->next()) {
|
|
if (!Token::simpleMatch(typetok, "...")) {
|
|
mTokenList.addtoken(typetok, tok5);
|
|
Token *back = mTokenList.back();
|
|
if (Token::Match(back, "{|(|[")) {
|
|
brackets1.push(back);
|
|
} else if (back->str() == "}") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "{");
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
} else if (back->str() == ")") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "(");
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
} else if (back->str() == "]") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "[");
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
}
|
|
back->isTemplateArg(true);
|
|
back->isUnsigned(typetok->isUnsigned());
|
|
back->isSigned(typetok->isSigned());
|
|
back->isLong(typetok->isLong());
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!added) {
|
|
mTokenList.addtoken(tok5);
|
|
Token *back = mTokenList.back();
|
|
if (Token::Match(back, "{|(|[")) {
|
|
brackets2.push(back);
|
|
} else if (back->str() == "}") {
|
|
assert(brackets2.empty() == false);
|
|
assert(brackets2.top()->str() == "{");
|
|
Token::createMutualLinks(brackets2.top(), back);
|
|
brackets2.pop();
|
|
} else if (back->str() == ")") {
|
|
assert(brackets2.empty() == false);
|
|
assert(brackets2.top()->str() == "(");
|
|
Token::createMutualLinks(brackets2.top(), back);
|
|
brackets2.pop();
|
|
} else if (back->str() == "]") {
|
|
assert(brackets2.empty() == false);
|
|
assert(brackets2.top()->str() == "[");
|
|
Token::createMutualLinks(brackets2.top(), back);
|
|
brackets2.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
tok5 = tok5->next();
|
|
}
|
|
if (copy) {
|
|
if (!templateDeclaration.scope().empty() && tok3->strAt(-1) != "::")
|
|
addNamespace(templateDeclaration, tok3);
|
|
mTokenList.addtoken(newName, tok3->linenr(), tok3->column(), tok3->fileIndex());
|
|
}
|
|
|
|
while (tok3 && tok3->str() != "::")
|
|
tok3 = tok3->next();
|
|
|
|
const std::list<TokenAndName>::const_iterator it = std::find_if(mTemplateDeclarations.cbegin(),
|
|
mTemplateDeclarations.cend(),
|
|
FindToken(startOfTemplateDeclaration));
|
|
if (it != mTemplateDeclarations.cend())
|
|
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();
|
|
|
|
std::stack<const Token *> templates;
|
|
for (; tok3; tok3 = tok3->next()) {
|
|
if (tok3->isName() && !Token::Match(tok3, "class|typename|struct") && !tok3->isStandardType()) {
|
|
// 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() && itype < mTypesUsedInTemplateInstantiation.size()) {
|
|
unsigned int typeindentlevel = 0;
|
|
std::stack<Token *> brackets1; // holds "(" and "{" tokens
|
|
Token * const beforeTypeToken = mTokenList.back();
|
|
bool pointerType = false;
|
|
const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size();
|
|
if (isVariadicTemplateArg && Token::Match(tok3, "%name% ... %name%"))
|
|
tok3 = tok3->tokAt(2);
|
|
const std::string endStr(isVariadicTemplateArg ? ">" : ",>");
|
|
for (Token *typetok = mTypesUsedInTemplateInstantiation[itype].token();
|
|
typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos);
|
|
typetok = typetok->next()) {
|
|
if (typeindentlevel == 0 && typetok->str() == "*")
|
|
pointerType = true;
|
|
if (Token::simpleMatch(typetok, "..."))
|
|
continue;
|
|
if (Token::Match(typetok, "%name% <") &&
|
|
(typetok->strAt(2) == ">" || templateParameters(typetok->next()))) {
|
|
brackets1.push(typetok->next());
|
|
++typeindentlevel;
|
|
} else if (typeindentlevel > 0 && typetok->str() == ">" && brackets1.top()->str() == "<") {
|
|
--typeindentlevel;
|
|
brackets1.pop();
|
|
} else if (Token::Match(typetok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) {
|
|
brackets1.push(typetok->next());
|
|
++typeindentlevel;
|
|
} else if (typetok->str() == "(")
|
|
++typeindentlevel;
|
|
else if (typetok->str() == ")")
|
|
--typeindentlevel;
|
|
Token *back;
|
|
if (copy) {
|
|
mTokenList.addtoken(typetok, tok3);
|
|
back = mTokenList.back();
|
|
} else
|
|
back = typetok;
|
|
if (Token::Match(back, "{|(|["))
|
|
brackets1.push(back);
|
|
else if (back->str() == "}") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "{");
|
|
if (copy)
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
} else if (back->str() == ")") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "(");
|
|
if (copy)
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
} else if (back->str() == "]") {
|
|
assert(brackets1.empty() == false);
|
|
assert(brackets1.top()->str() == "[");
|
|
if (copy)
|
|
Token::createMutualLinks(brackets1.top(), back);
|
|
brackets1.pop();
|
|
}
|
|
if (copy)
|
|
back->isTemplateArg(true);
|
|
}
|
|
if (pointerType && Token::simpleMatch(beforeTypeToken, "const")) {
|
|
mTokenList.addtoken(beforeTypeToken);
|
|
beforeTypeToken->deleteThis();
|
|
}
|
|
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);
|
|
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 {
|
|
// don't modify friend
|
|
if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) {
|
|
if (copy)
|
|
mTokenList.addtoken(tok3);
|
|
} else if (copy) {
|
|
// add namespace if necessary
|
|
if (!templateDeclaration.scope().empty() &&
|
|
(isClass ? tok3->strAt(1) != "(" : true)) {
|
|
addNamespace(templateDeclaration, tok3);
|
|
}
|
|
mTokenList.addtoken(newName, tok3);
|
|
} else if (!Token::Match(tok3->next(), ":|{|=|;|[|]"))
|
|
tok3->str(newName);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// copy
|
|
if (copy)
|
|
mTokenList.addtoken(tok3);
|
|
|
|
// look for template definitions
|
|
if (Token::simpleMatch(tok3, "template <")) {
|
|
Token * tok2 = findTemplateDeclarationEnd(tok3);
|
|
if (tok2)
|
|
templates.push(tok2);
|
|
} else if (!templates.empty() && templates.top() == tok3)
|
|
templates.pop();
|
|
|
|
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;
|
|
}
|
|
|
|
// don't add instantiations in template definitions
|
|
if (!templates.empty())
|
|
continue;
|
|
|
|
std::string scope;
|
|
const Token *prev = tok3;
|
|
for (; Token::Match(prev->tokAt(-2), "%name% ::"); prev = prev->tokAt(-2)) {
|
|
if (scope.empty())
|
|
scope = prev->strAt(-2);
|
|
else
|
|
scope = prev->strAt(-2) + " :: " + scope;
|
|
}
|
|
|
|
// check for global scope
|
|
if (prev->strAt(-1) != "::") {
|
|
// adjust for current scope
|
|
std::string token_scope = tok3->scopeInfo()->name;
|
|
const std::string::size_type end = token_scope.find_last_of(" :: ");
|
|
if (end != std::string::npos) {
|
|
token_scope.resize(end);
|
|
if (scope.empty())
|
|
scope = token_scope;
|
|
else
|
|
scope = token_scope + " :: " + scope;
|
|
}
|
|
}
|
|
|
|
if (copy)
|
|
newInstantiations.emplace_back(mTokenList.back(), std::move(scope));
|
|
else if (!inTemplateDefinition)
|
|
newInstantiations.emplace_back(tok3, std::move(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);
|
|
assert(brackets.top()->str() == "{");
|
|
Token::createMutualLinks(brackets.top(), mTokenList.back());
|
|
if (tok3->strAt(1) == ";") {
|
|
const Token * tokSemicolon = tok3->next();
|
|
mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->column(), tokSemicolon->fileIndex());
|
|
}
|
|
brackets.pop();
|
|
if (brackets.empty() && !Token::Match(tok3, "} >|,|{")) {
|
|
inTemplateDefinition = false;
|
|
break;
|
|
}
|
|
} else if (tok3->str() == ")") {
|
|
assert(brackets.empty() == false);
|
|
assert(brackets.top()->str() == "(");
|
|
Token::createMutualLinks(brackets.top(), mTokenList.back());
|
|
brackets.pop();
|
|
} else if (tok3->str() == "]") {
|
|
assert(brackets.empty() == false);
|
|
assert(brackets.top()->str() == "[");
|
|
Token::createMutualLinks(brackets.top(), mTokenList.back());
|
|
brackets.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(brackets.empty());
|
|
}
|
|
|
|
// add new instantiations
|
|
for (const auto & inst : newInstantiations) {
|
|
simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket());
|
|
// only add recursive instantiation if its arguments are a constant expression
|
|
if (templateDeclaration.name() != inst.token->str() ||
|
|
(inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType()))
|
|
mTemplateInstantiations.emplace_back(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 isTemplate)
|
|
{
|
|
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")) {
|
|
if (isTemplate) throw InternalError(op, "Instantiation error: Divide by zero in template instantiation.", InternalError::INSTANTIATION);
|
|
else return ret;
|
|
}
|
|
|
|
// 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, const 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();
|
|
tok->str(std::to_string(1));
|
|
again = true;
|
|
}
|
|
|
|
// sizeof ("text")
|
|
else if (Token::Match(tok->next(), "( %str% )")) {
|
|
tok->deleteNext();
|
|
tok->deleteThis();
|
|
tok->deleteNext();
|
|
tok->str(std::to_string(Token::getStrLength(tok) + 1));
|
|
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);
|
|
}
|
|
} else if (Token::Match(tok, "%num% %comp% %num%") &&
|
|
MathLib::isInt(tok->str()) &&
|
|
MathLib::isInt(tok->strAt(2))) {
|
|
if ((Token::Match(tok->previous(), "(|&&|%oror%|,") || tok == start) &&
|
|
(Token::Match(tok->tokAt(3), ")|&&|%oror%|?") || tok->tokAt(3) == end)) {
|
|
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) ? "true" : "false";
|
|
else if (cmp == "!=")
|
|
result = (op1 != op2) ? "true" : "false";
|
|
else if (cmp == "<=")
|
|
result = (op1 <= op2) ? "true" : "false";
|
|
else if (cmp == ">=")
|
|
result = (op1 >= op2) ? "true" : "false";
|
|
else if (cmp == "<")
|
|
result = (op1 < op2) ? "true" : "false";
|
|
else
|
|
result = (op1 > op2) ? "true" : "false";
|
|
|
|
tok->str(result);
|
|
tok->deleteNext(2);
|
|
again = true;
|
|
tok = tok->previous();
|
|
}
|
|
}
|
|
}
|
|
|
|
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()) ||
|
|
Token::Match(tok->tokAt(-3), "( %bool%|%num% )"))) {
|
|
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() == "<" && (endTok->strAt(1) == ">" || templateParameters(endTok)))
|
|
endTok = endTok->findClosingBracket();
|
|
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() && !tok->previous()->isName())) {
|
|
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 isTemplate)
|
|
{
|
|
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 (validTokenEnd(bounded, tok, backToken, 5) &&
|
|
Token::Match(tok, "decltype ( %type% { } )")) {
|
|
tok->deleteThis();
|
|
tok->deleteThis();
|
|
tok->deleteNext();
|
|
tok->deleteNext();
|
|
tok->deleteNext();
|
|
ret = true;
|
|
}
|
|
|
|
if (validTokenEnd(bounded, tok, backToken, 3) &&
|
|
Token::Match(tok, "decltype ( %bool%|%num% )")) {
|
|
tok->deleteThis();
|
|
tok->deleteThis();
|
|
if (tok->isBoolean())
|
|
tok->str("bool");
|
|
else if (MathLib::isFloat(tok->str())) {
|
|
// MathLib::getSuffix doesn't work for floating point numbers
|
|
const char suffix = tok->str().back();
|
|
if (suffix == 'f' || suffix == 'F')
|
|
tok->str("float");
|
|
else if (suffix == 'l' || suffix == 'L') {
|
|
tok->str("double");
|
|
tok->isLong(true);
|
|
} else
|
|
tok->str("double");
|
|
} else if (MathLib::isInt(tok->str())) {
|
|
std::string suffix = MathLib::getSuffix(tok->str());
|
|
if (suffix.find("LL") != std::string::npos) {
|
|
tok->str("long");
|
|
tok->isLong(true);
|
|
} else if (suffix.find('L') != std::string::npos)
|
|
tok->str("long");
|
|
else
|
|
tok->str("int");
|
|
tok->isUnsigned(suffix.find('U') != std::string::npos);
|
|
}
|
|
tok->deleteNext();
|
|
ret = true;
|
|
}
|
|
|
|
if (validTokenEnd(bounded, tok, backToken, 2) &&
|
|
(Token::Match(tok, "char|short|int|long { }") ||
|
|
Token::Match(tok, "char|short|int|long ( )"))) {
|
|
tok->str("0"); // FIXME add type suffix
|
|
tok->isSigned(false);
|
|
tok->isUnsigned(false);
|
|
tok->isLong(false);
|
|
tok->deleteNext();
|
|
tok->deleteNext();
|
|
ret = true;
|
|
}
|
|
|
|
if (tok && tok->isNumber()) {
|
|
if (validTokenEnd(bounded, tok, backToken, 2) &&
|
|
simplifyNumericCalculations(tok, isTemplate)) {
|
|
ret = true;
|
|
Token *prev = tok->tokAt(-2);
|
|
while (validTokenStart(bounded, tok, frontToken, -2) &&
|
|
prev && simplifyNumericCalculations(prev, isTemplate)) {
|
|
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;
|
|
}
|
|
|
|
void TemplateSimplifier::getTemplateParametersInDeclaration(
|
|
const Token * tok,
|
|
std::vector<const Token *> & typeParametersInDeclaration)
|
|
{
|
|
assert(tok->strAt(-1) == "<");
|
|
|
|
typeParametersInDeclaration.clear();
|
|
const Token *end = tok->previous()->findClosingBracket();
|
|
bool inDefaultValue = false;
|
|
for (; tok && tok!= end; tok = tok->next()) {
|
|
if (Token::simpleMatch(tok, "template <")) {
|
|
const Token *closing = tok->next()->findClosingBracket();
|
|
if (closing)
|
|
tok = closing->next();
|
|
} else if (tok->link() && Token::Match(tok, "{|(|["))
|
|
tok = tok->link();
|
|
else if (Token::Match(tok, "%name% ,|>|=")) {
|
|
if (!inDefaultValue) {
|
|
typeParametersInDeclaration.push_back(tok);
|
|
if (tok->strAt(1) == "=")
|
|
inDefaultValue = true;
|
|
}
|
|
} else if (inDefaultValue) {
|
|
if (tok->str() == ",")
|
|
inDefaultValue = false;
|
|
else if (tok->str() == "<") {
|
|
const Token *closing = tok->findClosingBracket();
|
|
if (closing)
|
|
tok = closing;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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.cbegin(); it != specializations.cend(); ++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() ||
|
|
declToken->isSigned() != instToken->isSigned() ||
|
|
declToken->isUnsigned() != instToken->isUnsigned() ||
|
|
declToken->isLong() != instToken->isLong()) {
|
|
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;
|
|
const Token * endToken = tok2->next()->findClosingBracket();
|
|
for (Token *tok3 = tok2->tokAt(2); tok3 != endToken && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) {
|
|
// #2721 - unhandled [ => bail out
|
|
if (tok3->str() == "[" && !Token::Match(tok3->next(), "%num%| ]")) {
|
|
typeForNewName.clear();
|
|
break;
|
|
}
|
|
if (!tok3->next()) {
|
|
typeForNewName.clear();
|
|
break;
|
|
}
|
|
if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && (tok3->strAt(1) == ">" || templateParameters(tok3)))
|
|
++indentlevel;
|
|
else if (indentlevel > 0 && Token::Match(tok3, "> ,|>|::"))
|
|
--indentlevel;
|
|
else if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) {
|
|
mTypesUsedInTemplateInstantiation.emplace_back(tok3, "");
|
|
}
|
|
if (Token::Match(tok3, "(|["))
|
|
++indentlevel;
|
|
else if (Token::Match(tok3, ")|]"))
|
|
--indentlevel;
|
|
const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const";
|
|
if (!constconst) {
|
|
if (tok3->isUnsigned())
|
|
typeStringsUsedInTemplateInstantiation.emplace_back("unsigned");
|
|
else if (tok3->isSigned())
|
|
typeStringsUsedInTemplateInstantiation.emplace_back("signed");
|
|
if (tok3->isLong())
|
|
typeStringsUsedInTemplateInstantiation.emplace_back("long");
|
|
typeStringsUsedInTemplateInstantiation.push_back(tok3->str());
|
|
}
|
|
// add additional type information
|
|
if (!constconst && !Token::Match(tok3, "class|struct|enum")) {
|
|
if (!typeForNewName.empty())
|
|
typeForNewName += ' ';
|
|
if (tok3->isUnsigned())
|
|
typeForNewName += "unsigned ";
|
|
else if (tok3->isSigned())
|
|
typeForNewName += "signed ";
|
|
if (tok3->isLong()) {
|
|
typeForNewName += "long ";
|
|
}
|
|
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) {
|
|
// skip deleted instantiations
|
|
if (!instantiation.token())
|
|
continue;
|
|
if (numberOfTemplateInstantiations != mTemplateInstantiations.size()) {
|
|
numberOfTemplateInstantiations = mTemplateInstantiations.size();
|
|
++recursiveCount;
|
|
if (recursiveCount > mSettings.maxTemplateRecursion) {
|
|
std::list<std::string> typeStringsUsedInTemplateInstantiation;
|
|
const std::string typeForNewName = templateDeclaration.name() + "<" + getNewName(instantiation.token(), typeStringsUsedInTemplateInstantiation) + ">";
|
|
|
|
const std::list<const Token *> callstack(1, instantiation.token());
|
|
const ErrorMessage errmsg(callstack,
|
|
&mTokenizer.list,
|
|
Severity::information,
|
|
"templateRecursion",
|
|
"TemplateSimplifier: max template recursion ("
|
|
+ MathLib::toString(mSettings.maxTemplateRecursion)
|
|
+ ") reached for template '"+typeForNewName+"'. You might want to limit Cppcheck recursion.",
|
|
Certainty::normal);
|
|
if (mErrorLogger && mSettings.severity.isEnabled(Severity::information))
|
|
mErrorLogger->reportErr(errmsg);
|
|
|
|
// bail out..
|
|
break;
|
|
}
|
|
}
|
|
|
|
// already simplified
|
|
if (!Token::Match(instantiation.token(), "%name% <"))
|
|
continue;
|
|
|
|
if (!((instantiation.fullName() == templateDeclaration.fullName()) ||
|
|
(instantiation.name() == templateDeclaration.name() &&
|
|
instantiation.fullName() == templateDeclaration.scope()))) {
|
|
// 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;
|
|
}
|
|
|
|
// make sure constructors and destructors don't match each other
|
|
if (templateDeclaration.nameToken()->strAt(-1) == "~" && instantiation.token()->strAt(-1) != "~")
|
|
continue;
|
|
|
|
// template families should match
|
|
if (!instantiation.isFunction() && templateDeclaration.isFunction()) {
|
|
// there are exceptions
|
|
if (!Token::simpleMatch(instantiation.token()->tokAt(-2), "decltype ("))
|
|
continue;
|
|
}
|
|
|
|
if (templateDeclaration.isFunction() && instantiation.isFunction()) {
|
|
std::vector<const Token*> declFuncArgs;
|
|
getFunctionArguments(templateDeclaration.nameToken(), declFuncArgs);
|
|
std::vector<const Token*> instFuncParams;
|
|
getFunctionArguments(instantiation.token(), instFuncParams);
|
|
|
|
if (declFuncArgs.size() != instFuncParams.size()) {
|
|
// check for default arguments
|
|
const Token* tok = templateDeclaration.nameToken()->tokAt(2);
|
|
const Token* end = templateDeclaration.nameToken()->linkAt(1);
|
|
size_t count = 0;
|
|
for (; tok != end; tok = tok->next()) {
|
|
if (tok->str() == "=")
|
|
count++;
|
|
}
|
|
|
|
if (instFuncParams.size() < (declFuncArgs.size() - count) || instFuncParams.size() > declFuncArgs.size())
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// A global function can't be called through a pointer.
|
|
if (templateDeclaration.isFunction() && templateDeclaration.scope().empty() &&
|
|
(instantiation.token()->strAt(-1) == "." ||
|
|
Token::simpleMatch(instantiation.token()->tokAt(-2), ". template")))
|
|
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());
|
|
|
|
if (maxtime > 0 && std::time(nullptr) > maxtime) {
|
|
if (mSettings.debugwarnings) {
|
|
ErrorMessage::FileLocation loc;
|
|
loc.setfile(mTokenList.getFiles()[0]);
|
|
ErrorMessage errmsg({std::move(loc)},
|
|
emptyString,
|
|
Severity::debug,
|
|
"Template instantiation maximum time exceeded",
|
|
"templateMaxTime",
|
|
Certainty::normal);
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021
|
|
|
|
const Token *startToken = tok2;
|
|
while (Token::Match(startToken->tokAt(-2), ">|%name% :: %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(), templateDeclaration.isVariadic(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%")))
|
|
continue;
|
|
|
|
// New type..
|
|
mTypesUsedInTemplateInstantiation.clear();
|
|
std::list<std::string> typeStringsUsedInTemplateInstantiation;
|
|
std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation);
|
|
|
|
if ((typeForNewName.empty() && !templateDeclaration.isVariadic()) ||
|
|
(!typeParametersInDeclaration.empty() && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), nullptr))) {
|
|
if (printDebug && mErrorLogger) {
|
|
std::list<const Token *> callstack(1, tok2);
|
|
mErrorLogger->reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
|
|
"Failed to instantiate template \"" + instantiation.name() + "\". The checking continues anyway.", Certainty::normal));
|
|
}
|
|
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.insert(newFullName).second) {
|
|
expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized && !isVar);
|
|
instantiated = true;
|
|
mChanged = 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());
|
|
|
|
if (maxtime > 0 && std::time(nullptr) > maxtime) {
|
|
if (mSettings.debugwarnings) {
|
|
ErrorMessage::FileLocation loc;
|
|
loc.setfile(mTokenList.getFiles()[0]);
|
|
ErrorMessage errmsg({std::move(loc)},
|
|
emptyString,
|
|
Severity::debug,
|
|
"Template instantiation maximum time exceeded",
|
|
"templateMaxTime",
|
|
Certainty::normal);
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021
|
|
|
|
Token *startToken = tok2;
|
|
while (Token::Match(startToken->tokAt(-2), ">|%name% :: %name%")) {
|
|
if (startToken->strAt(-2) == ">") {
|
|
Token * tok3 = startToken->tokAt(-2)->findOpeningBracket();
|
|
if (tok3)
|
|
startToken = tok3->previous();
|
|
else
|
|
break;
|
|
} else
|
|
startToken = startToken->tokAt(-2);
|
|
}
|
|
|
|
// TODO: re-enable when specialized check is removed
|
|
// 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(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
|
|
"Failed to instantiate template \"" + templateDeclaration.name() + "\". The checking continues anyway.", Certainty::normal));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// New classname/funcname..
|
|
const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >");
|
|
const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName);
|
|
|
|
if (expandedtemplates.insert(newFullName).second) {
|
|
expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, !specialized && !isVar);
|
|
instantiated = true;
|
|
mChanged = 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.cbegin();
|
|
const Token *tok = nameTok->tokAt(2);
|
|
const Token *end = nameTok->next()->findClosingBracket();
|
|
if (!end)
|
|
return false;
|
|
while (tok && tok != end && it != strings.cend()) {
|
|
if (tok->isUnsigned()) {
|
|
if (*it != "unsigned")
|
|
return false;
|
|
else {
|
|
++it;
|
|
if (it == strings.cend())
|
|
return false;
|
|
}
|
|
} else if (tok->isSigned()) {
|
|
if (*it != "signed")
|
|
return false;
|
|
else {
|
|
++it;
|
|
if (it == strings.cend())
|
|
return false;
|
|
}
|
|
}
|
|
if (tok->isLong()) {
|
|
if (*it != "long")
|
|
return false;
|
|
else {
|
|
++it;
|
|
if (it == strings.cend())
|
|
return false;
|
|
}
|
|
}
|
|
if (*it != tok->str())
|
|
return false;
|
|
tok = tok->next();
|
|
++it;
|
|
}
|
|
return it == strings.cend() && tok && tok->str() == ">";
|
|
}
|
|
|
|
void TemplateSimplifier::replaceTemplateUsage(
|
|
const TokenAndName &instantiation,
|
|
const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
|
|
const std::string &newName)
|
|
{
|
|
std::list<std::pair<Token *, Token *>> removeTokens;
|
|
for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) {
|
|
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 && 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;
|
|
|
|
Token *tok2 = nameTok->next()->findClosingBracket();
|
|
|
|
if (!tok2)
|
|
break;
|
|
|
|
const Token * const nameTok1 = nameTok;
|
|
nameTok->str(newName);
|
|
|
|
// matching template usage => replace tokens..
|
|
// Foo < int > => Foo<int>
|
|
for (const Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) {
|
|
if (tok->isName() && tok->templateSimplifierPointers() && !tok->templateSimplifierPointers()->empty()) {
|
|
std::list<TokenAndName>::iterator ti;
|
|
for (ti = mTemplateInstantiations.begin(); ti != mTemplateInstantiations.end();) {
|
|
if (ti->token() == tok) {
|
|
ti = mTemplateInstantiations.erase(ti);
|
|
break;
|
|
} else {
|
|
++ti;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Fix crash in #9007
|
|
if (Token::simpleMatch(nameTok->previous(), ">"))
|
|
mTemplateNamePos.erase(nameTok->previous());
|
|
removeTokens.emplace_back(nameTok, tok2->next());
|
|
|
|
nameTok = tok2;
|
|
}
|
|
while (!removeTokens.empty()) {
|
|
eraseTokens(removeTokens.back().first, removeTokens.back().second);
|
|
removeTokens.pop_back();
|
|
}
|
|
}
|
|
|
|
static bool specMatch(
|
|
const TemplateSimplifier::TokenAndName &spec,
|
|
const TemplateSimplifier::TokenAndName &decl)
|
|
{
|
|
// make sure decl is really a declaration
|
|
if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend())
|
|
return false;
|
|
|
|
if (!spec.isSameFamily(decl))
|
|
return false;
|
|
|
|
// make sure the scopes and names match
|
|
if (spec.fullName() == decl.fullName()) {
|
|
if (spec.isFunction()) {
|
|
std::vector<const Token*> specArgs;
|
|
std::vector<const Token*> declArgs;
|
|
getFunctionArguments(spec.nameToken(), specArgs);
|
|
getFunctionArguments(decl.nameToken(), declArgs);
|
|
|
|
if (specArgs.size() == declArgs.size()) {
|
|
// @todo make sure function parameters also match
|
|
return true;
|
|
}
|
|
} else
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TemplateSimplifier::getSpecializations()
|
|
{
|
|
// try to locate a matching declaration for each user defined specialization
|
|
for (const auto& spec : mTemplateDeclarations) {
|
|
if (spec.isSpecialization()) {
|
|
auto it = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), [&](const TokenAndName& decl) {
|
|
return specMatch(spec, decl);
|
|
});
|
|
if (it != mTemplateDeclarations.cend())
|
|
mTemplateSpecializationMap[spec.token()] = it->token();
|
|
else {
|
|
it = std::find_if(mTemplateForwardDeclarations.cbegin(), mTemplateForwardDeclarations.cend(), [&](const TokenAndName& decl) {
|
|
return specMatch(spec, decl);
|
|
});
|
|
if (it != mTemplateForwardDeclarations.cend())
|
|
mTemplateSpecializationMap[spec.token()] = it->token();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TemplateSimplifier::getPartialSpecializations()
|
|
{
|
|
// try to locate a matching declaration for each user defined partial specialization
|
|
for (const auto& spec : mTemplateDeclarations) {
|
|
if (spec.isPartialSpecialization()) {
|
|
auto it = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), [&](const TokenAndName& decl) {
|
|
return specMatch(spec, decl);
|
|
});
|
|
if (it != mTemplateDeclarations.cend())
|
|
mTemplatePartialSpecializationMap[spec.token()] = it->token();
|
|
else {
|
|
it = std::find_if(mTemplateForwardDeclarations.cbegin(), mTemplateForwardDeclarations.cend(), [&](const TokenAndName& decl) {
|
|
return specMatch(spec, decl);
|
|
});
|
|
if (it != mTemplateForwardDeclarations.cend())
|
|
mTemplatePartialSpecializationMap[spec.token()] = it->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, type aliases and friends
|
|
if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend())
|
|
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());
|
|
}
|
|
}
|
|
|
|
// 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";
|
|
if (tokenAndName.isVariadic())
|
|
std::cout << " isVariadic";
|
|
if (tokenAndName.isFriend())
|
|
std::cout << " isFriend";
|
|
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) {
|
|
if (start->isUnsigned())
|
|
std::cout << "unsigned";
|
|
else if (start->isSigned())
|
|
std::cout << "signed";
|
|
if (start->isLong())
|
|
std::cout << "long";
|
|
std::cout << start->str();
|
|
start = start->next();
|
|
}
|
|
std::cout << end->str() << std::endl;
|
|
}
|
|
} else if (tokenAndName.isAlias() && tokenAndName.paramEnd()) {
|
|
if (tokenAndName.aliasStartToken()) {
|
|
std::cout << indent << "aliasStartToken: \"" << tokenAndName.aliasStartToken()->str() << "\" "
|
|
<< mTokenList.fileLine(tokenAndName.aliasStartToken()) << std::endl;
|
|
}
|
|
if (tokenAndName.aliasEndToken()) {
|
|
std::cout << indent << "aliasEndToken: \"" << tokenAndName.aliasEndToken()->str() << "\" "
|
|
<< mTokenList.fileLine(tokenAndName.aliasEndToken()) << 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 = false;
|
|
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 = false;
|
|
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)
|
|
{
|
|
// convert "sizeof ..." to "sizeof..."
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (Token::simpleMatch(tok, "sizeof ...")) {
|
|
tok->str("sizeof...");
|
|
tok->deleteNext();
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
if (mSettings.standards.cpp >= Standards::CPP20) {
|
|
// Remove concepts/requires
|
|
// TODO concepts are not removed yet
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (!Token::Match(tok, ")|>|>> requires %name%|("))
|
|
continue;
|
|
Token *end = skipRequires(tok->next());
|
|
if (end)
|
|
Token::eraseTokens(tok, end);
|
|
}
|
|
|
|
// explicit(bool)
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (Token::simpleMatch(tok, "explicit (")) {
|
|
const bool isFalse = Token::simpleMatch(tok->tokAt(2), "false )");
|
|
Token::eraseTokens(tok, tok->linkAt(1)->next());
|
|
if (isFalse)
|
|
tok->deleteThis();
|
|
}
|
|
}
|
|
}
|
|
|
|
mTokenizer.calculateScopes();
|
|
|
|
unsigned int passCount = 0;
|
|
const unsigned int passCountMax = 10;
|
|
for (; passCount < passCountMax; ++passCount) {
|
|
if (passCount) {
|
|
// it may take more than one pass to simplify type aliases
|
|
bool usingChanged = false;
|
|
while (mTokenizer.simplifyUsing())
|
|
usingChanged = true;
|
|
|
|
if (!usingChanged && !mChanged)
|
|
break;
|
|
|
|
mChanged = usingChanged;
|
|
mTemplateDeclarations.clear();
|
|
mTemplateForwardDeclarations.clear();
|
|
mTemplateForwardDeclarationsMap.clear();
|
|
mTemplateSpecializationMap.clear();
|
|
mTemplatePartialSpecializationMap.clear();
|
|
mTemplateInstantiations.clear();
|
|
mInstantiatedTemplates.clear();
|
|
mExplicitInstantiationsToDelete.clear();
|
|
mTemplateNamePos.clear();
|
|
}
|
|
|
|
const bool hasTemplates = getTemplateDeclarations();
|
|
|
|
if (passCount == 0) {
|
|
codeWithTemplates = hasTemplates;
|
|
mDump.clear();
|
|
for (const TokenAndName& t: mTemplateDeclarations)
|
|
mDump += t.dump(mTokenizer.list.getFiles());
|
|
for (const TokenAndName& t: mTemplateForwardDeclarations)
|
|
mDump += t.dump(mTokenizer.list.getFiles());
|
|
if (!mDump.empty())
|
|
mDump = " <TemplateSimplifier>\n" + mDump + " </TemplateSimplifier>\n";
|
|
}
|
|
|
|
// Make sure there is something to simplify.
|
|
if (mTemplateDeclarations.empty() && mTemplateForwardDeclarations.empty())
|
|
return;
|
|
|
|
if (mSettings.debugtemplate && mSettings.debugnormal) {
|
|
std::string title("Template Simplifier pass " + std::to_string(passCount + 1));
|
|
mTokenList.front()->printOut(title.c_str(), mTokenList.getFiles());
|
|
}
|
|
|
|
// 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(passCount + 1) + " ###");
|
|
|
|
std::set<std::string> expandedtemplates;
|
|
|
|
for (std::list<TokenAndName>::const_reverse_iterator iter1 = mTemplateDeclarations.crbegin(); iter1 != mTemplateDeclarations.crend(); ++iter1) {
|
|
if (iter1->isAlias() || iter1->isFriend())
|
|
continue;
|
|
|
|
// get specializations..
|
|
std::list<const Token *> specializations;
|
|
for (std::list<TokenAndName>::const_iterator iter2 = mTemplateDeclarations.cbegin(); iter2 != mTemplateDeclarations.cend(); ++iter2) {
|
|
if (iter2->isAlias() || iter2->isFriend())
|
|
continue;
|
|
|
|
if (iter1->fullName() == iter2->fullName())
|
|
specializations.push_back(iter2->nameToken());
|
|
}
|
|
|
|
const bool instantiated = simplifyTemplateInstantiations(
|
|
*iter1,
|
|
specializations,
|
|
maxtime,
|
|
expandedtemplates);
|
|
if (instantiated) {
|
|
mInstantiatedTemplates.push_back(*iter1);
|
|
mTemplateNamePos.clear(); // positions might be invalid after instantiations
|
|
}
|
|
}
|
|
|
|
for (std::list<TokenAndName>::const_iterator it = mInstantiatedTemplates.cbegin(); it != mInstantiatedTemplates.cend(); ++it) {
|
|
auto decl = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), [&it](const TokenAndName& decl) {
|
|
return decl.token() == it->token();
|
|
});
|
|
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.cbegin()->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);
|
|
} else {
|
|
const std::list<TokenAndName>::iterator it1 = std::find_if(mTemplateForwardDeclarations.begin(),
|
|
mTemplateForwardDeclarations.end(),
|
|
FindToken(mMemberFunctionsToDelete.cbegin()->token()));
|
|
// multiple functions can share the same declaration so make sure it hasn't already been deleted
|
|
if (it1 != mTemplateForwardDeclarations.end()) {
|
|
removeTemplate(it1->token());
|
|
mTemplateForwardDeclarations.erase(it1);
|
|
}
|
|
}
|
|
mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin());
|
|
}
|
|
|
|
// remove explicit instantiations
|
|
for (const TokenAndName& j : mExplicitInstantiationsToDelete) {
|
|
Token * start = 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (passCount == passCountMax) {
|
|
if (mSettings.debugwarnings) {
|
|
const std::list<const Token*> locationList(1, mTokenList.front());
|
|
const ErrorMessage errmsg(locationList, &mTokenizer.list,
|
|
Severity::debug,
|
|
"debug",
|
|
"TemplateSimplifier: pass count limit hit before simplifications were finished.",
|
|
Certainty::normal);
|
|
if (mErrorLogger)
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
}
|
|
|
|
// Tweak uninstantiated C++17 fold expressions (... && args)
|
|
if (mSettings.standards.cpp >= Standards::CPP17) {
|
|
bool simplify = false;
|
|
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
if (tok->str() == "template")
|
|
simplify = false;
|
|
if (tok->str() == "{")
|
|
simplify = true;
|
|
if (!simplify || tok->str() != "(")
|
|
continue;
|
|
const Token *op = nullptr;
|
|
const Token *args = nullptr;
|
|
if (Token::Match(tok, "( ... %op%")) {
|
|
op = tok->tokAt(2);
|
|
args = tok->link()->previous();
|
|
} else if (Token::Match(tok, "( %name% %op% ...")) {
|
|
op = tok->tokAt(2);
|
|
args = tok->link()->previous()->isName() ? nullptr : tok->next();
|
|
} else if (Token::Match(tok->link()->tokAt(-3), "%op% ... )")) {
|
|
op = tok->link()->tokAt(-2);
|
|
args = tok->next();
|
|
} else if (Token::Match(tok->link()->tokAt(-3), "... %op% %name% )")) {
|
|
op = tok->link()->tokAt(-2);
|
|
args = tok->next()->isName() ? nullptr : tok->link()->previous();
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
const std::string strop = op->str();
|
|
const std::string strargs = (args && args->isName()) ? args->str() : "";
|
|
|
|
Token::eraseTokens(tok, tok->link());
|
|
tok->insertToken(")");
|
|
if (!strargs.empty()) {
|
|
tok->insertToken("...");
|
|
tok->insertToken(strargs);
|
|
}
|
|
tok->insertToken("(");
|
|
Token::createMutualLinks(tok->next(), tok->link()->previous());
|
|
tok->insertToken("__cppcheck_fold_" + strop + "__");
|
|
}
|
|
}
|
|
}
|
|
|
|
void TemplateSimplifier::syntaxError(const Token *tok)
|
|
{
|
|
throw InternalError(tok, "syntax error", InternalError::SYNTAX);
|
|
}
|