cppcheck/lib/templatesimplifier.cpp

4034 lines
169 KiB
C++
Raw Permalink Normal View History

/*
* Cppcheck - A tool for static C/C++ code analysis
2023-01-28 10:16:34 +01:00
* 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"
2017-05-27 04:33:47 +02:00
#include "errorlogger.h"
#include "errortypes.h"
#include "mathlib.h"
2017-05-27 04:33:47 +02:00
#include "settings.h"
#include "standards.h"
#include "token.h"
#include "tokenize.h"
2017-05-27 04:33:47 +02:00
#include "tokenlist.h"
#include "utils.h"
2017-05-27 04:33:47 +02:00
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
#include <memory>
2017-05-27 04:33:47 +02:00
#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;
}
2021-04-20 16:46:05 +02:00
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:
2018-06-17 19:24:40 +02:00
explicit FindToken(const Token *token) : mToken(token) {}
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
return tokenAndName.token() == mToken;
}
private:
2018-06-17 19:24:40 +02:00
const Token * const mToken;
};
class FindName {
public:
explicit FindName(std::string name) : mName(std::move(name)) {}
2018-06-17 19:24:40 +02:00
bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const {
return tokenAndName.name() == mName;
}
private:
2018-06-17 19:24:40 +02:00
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% ::") ||
2020-05-22 08:48:28 +02:00
(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);
Several fairly significant optimisations (#1518) * Code changes for Token::mImpl optimisation * Added new TokenImpl optimisation Moving members to the TokenImpl struct reduces the size of the Token class, which is a fairly significant optimisation. In my testing on Windows with 32-bit Release-PCRE, this change reduced the size of the Token class from 108 bits to 52 bits and reduced run-time of my test case by around 20%. * Several optimisations Deleted some code that ran very slowly and did nothing, as there is no need to change a Token's string to null if you are about to delete it. Added a frontToken to simplifyCalculations to reduce the amount of work it has to do on already-simplified calculations. Moved template removal to the end of the list as this reduces redundant iteration and saves time. * Added tok argument to simplifyCalculations This means callers can avoid unnecessary work if they know which tokens have already been simplified. Passing nullptr indicates the original behaviour (starting from the front of the list). * Removed mention of member from another change * Re-added and optimised some code deleted in error Changing mTemplateInstantiations to a vector avoids the high cost of doing repeated linear searches. Changing how the code iterates through the array was necessary because the vector can be resized at several points during the loop, which breaks existing references and iterators. * Changed mTemplateInstantiations to a vector This is an optimisation that makes repeated linear searches of this collection significantly faster. Also added a copy constructor to TokenAndName so code can make copies of these objects to keep a reference if a vector gets resized. * A cleaner optimisation to removing template tokens This reverts the previous change to made mInstantiatedTemplates a vector and the iterator changes to support this, and makes mTypesUsedInTemplateInstantiation so the eraseTokens logic can be unified. * Reverted vector to list Also made mTypesUsedInTemplateInstantiation a vector of TokenAndName objects so it can share the same logic as the other members. * Added member for template simplifier pointer This can be used more efficiently than marking Tokens with a flag and then searching through all templates to find the one that matches. * Turned loop inside out This means we only have to iterate through the std::list once. std::list is very expensive to iterate through. * Latest code from danmar and fixed optimisations In particular I have optimised simplifying template instantiation names as this was incredibly slow because of the number of times it had to iterate through the template instantiation list. Previous optimisations to this weren't very effective and broke some edge cases. * Added changes from danmar Made mExplicitInstantiationsToDelete a vector of TokenAndName to be consistent with the rest of the members, which are cleaned up very efficiently. * Tokens can have many templateSimplifierPointers * templateSimplifierPointers must be kept in sync
2018-12-21 13:51:45 +01:00
}
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)
Several fairly significant optimisations (#1518) * Code changes for Token::mImpl optimisation * Added new TokenImpl optimisation Moving members to the TokenImpl struct reduces the size of the Token class, which is a fairly significant optimisation. In my testing on Windows with 32-bit Release-PCRE, this change reduced the size of the Token class from 108 bits to 52 bits and reduced run-time of my test case by around 20%. * Several optimisations Deleted some code that ran very slowly and did nothing, as there is no need to change a Token's string to null if you are about to delete it. Added a frontToken to simplifyCalculations to reduce the amount of work it has to do on already-simplified calculations. Moved template removal to the end of the list as this reduces redundant iteration and saves time. * Added tok argument to simplifyCalculations This means callers can avoid unnecessary work if they know which tokens have already been simplified. Passing nullptr indicates the original behaviour (starting from the front of the list). * Removed mention of member from another change * Re-added and optimised some code deleted in error Changing mTemplateInstantiations to a vector avoids the high cost of doing repeated linear searches. Changing how the code iterates through the array was necessary because the vector can be resized at several points during the loop, which breaks existing references and iterators. * Changed mTemplateInstantiations to a vector This is an optimisation that makes repeated linear searches of this collection significantly faster. Also added a copy constructor to TokenAndName so code can make copies of these objects to keep a reference if a vector gets resized. * A cleaner optimisation to removing template tokens This reverts the previous change to made mInstantiatedTemplates a vector and the iterator changes to support this, and makes mTypesUsedInTemplateInstantiation so the eraseTokens logic can be unified. * Reverted vector to list Also made mTypesUsedInTemplateInstantiation a vector of TokenAndName objects so it can share the same logic as the other members. * Added member for template simplifier pointer This can be used more efficiently than marking Tokens with a flag and then searching through all templates to find the one that matches. * Turned loop inside out This means we only have to iterate through the std::list once. std::list is very expensive to iterate through. * Latest code from danmar and fixed optimisations In particular I have optimised simplifying template instantiation names as this was incredibly slow because of the number of times it had to iterate through the template instantiation list. Previous optimisations to this weren't very effective and broke some edge cases. * Added changes from danmar Made mExplicitInstantiationsToDelete a vector of TokenAndName to be consistent with the rest of the members, which are cleaned up very efficiently. * Tokens can have many templateSimplifierPointers * templateSimplifierPointers must be kept in sync
2018-12-21 13:51:45 +01:00
{
if (mToken)
mToken->templateSimplifierPointer(this);
}
Several fairly significant optimisations (#1518) * Code changes for Token::mImpl optimisation * Added new TokenImpl optimisation Moving members to the TokenImpl struct reduces the size of the Token class, which is a fairly significant optimisation. In my testing on Windows with 32-bit Release-PCRE, this change reduced the size of the Token class from 108 bits to 52 bits and reduced run-time of my test case by around 20%. * Several optimisations Deleted some code that ran very slowly and did nothing, as there is no need to change a Token's string to null if you are about to delete it. Added a frontToken to simplifyCalculations to reduce the amount of work it has to do on already-simplified calculations. Moved template removal to the end of the list as this reduces redundant iteration and saves time. * Added tok argument to simplifyCalculations This means callers can avoid unnecessary work if they know which tokens have already been simplified. Passing nullptr indicates the original behaviour (starting from the front of the list). * Removed mention of member from another change * Re-added and optimised some code deleted in error Changing mTemplateInstantiations to a vector avoids the high cost of doing repeated linear searches. Changing how the code iterates through the array was necessary because the vector can be resized at several points during the loop, which breaks existing references and iterators. * Changed mTemplateInstantiations to a vector This is an optimisation that makes repeated linear searches of this collection significantly faster. Also added a copy constructor to TokenAndName so code can make copies of these objects to keep a reference if a vector gets resized. * A cleaner optimisation to removing template tokens This reverts the previous change to made mInstantiatedTemplates a vector and the iterator changes to support this, and makes mTypesUsedInTemplateInstantiation so the eraseTokens logic can be unified. * Reverted vector to list Also made mTypesUsedInTemplateInstantiation a vector of TokenAndName objects so it can share the same logic as the other members. * Added member for template simplifier pointer This can be used more efficiently than marking Tokens with a flag and then searching through all templates to find the one that matches. * Turned loop inside out This means we only have to iterate through the std::list once. std::list is very expensive to iterate through. * Latest code from danmar and fixed optimisations In particular I have optimised simplifying template instantiation names as this was incredibly slow because of the number of times it had to iterate through the template instantiation list. Previous optimisations to this weren't very effective and broke some edge cases. * Added changes from danmar Made mExplicitInstantiationsToDelete a vector of TokenAndName to be consistent with the rest of the members, which are cleaned up very efficiently. * Tokens can have many templateSimplifierPointers * templateSimplifierPointers must be kept in sync
2018-12-21 13:51:45 +01:00
TemplateSimplifier::TokenAndName::~TokenAndName()
{
if (mToken && mToken->templateSimplifierPointers())
mToken->templateSimplifierPointers()->erase(this);
}
std::string TemplateSimplifier::TokenAndName::dump(const std::vector<std::string>& fileNames) const {
2023-04-16 18:17:05 +02:00
std::string ret = " <TokenAndName name=\"" + ErrorLogger::toxml(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 += " <template-token str=\"" + 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)
2021-08-07 20:51:18 +02:00
{}
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;
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() == ",") {
if (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;
if ((tok->str() == ">>" || tok->str() == ">>=") && level == 1)
return numberOfParameters;
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->tokType() != Token::eString && !tok->isName() && !tok->isOp())
return 0;
tok = tok->next();
2012-04-26 10:58:35 +02:00
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;
}
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
static T *findTemplateDeclarationEndImpl(T *tok)
{
if (Token::simpleMatch(tok, "template <")) {
tok = tok->next()->findClosingBracket();
if (tok)
tok = tok->next();
}
if (!tok)
return nullptr;
T * 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;
}
Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok)
{
return findTemplateDeclarationEndImpl(tok);
}
const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok)
{
return findTemplateDeclarationEndImpl(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();
}
static void invalidateForwardDecls(const Token* beg, const Token* end, std::map<Token*, Token*>* forwardDecls) {
if (!forwardDecls)
return;
for (auto& fwd : *forwardDecls) {
for (const Token* tok = beg; tok != end; tok = tok->next())
if (fwd.second == tok) {
fwd.second = nullptr;
break;
}
}
}
bool TemplateSimplifier::removeTemplate(Token *tok, std::map<Token*, Token*>* forwardDecls)
{
if (!Token::simpleMatch(tok, "template <"))
return false;
Token *end = findTemplateDeclarationEnd(tok);
if (end && end->next()) {
invalidateForwardDecls(tok, end->next(), forwardDecls);
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()) {
2017-12-27 22:29:45 +01:00
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);
2017-12-27 22:29:45 +01:00
if (tok->strAt(2)=="typename" &&
!Token::Match(tok->tokAt(3), "%name%|...|,|=|>"))
2017-12-27 22:29:45 +01:00
syntaxError(tok->next());
codeWithTemplates = true;
const Token * const parmEnd = tok1->next()->findClosingBracket();
2017-12-27 22:29:45 +01:00
for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) {
if (tok2->str() == "(" && tok2->link())
2017-12-27 22:29:45 +01:00
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 const Token* getFunctionToken(const Token* nameToken)
{
if (Token::Match(nameToken, "%name% ("))
return nameToken->next();
if (Token::Match(nameToken, "%name% <")) {
const Token* end = nameToken->next()->findClosingBracket();
if (Token::simpleMatch(end, "> ("))
return end->next();
}
return nullptr;
}
static void getFunctionArguments(const Token* nameToken, std::vector<const Token*>& args)
{
const Token* functionToken = getFunctionToken(nameToken);
if (!functionToken)
return;
const Token* argToken = functionToken->next();
if (argToken->str() == ")")
return;
args.push_back(argToken);
while ((argToken = argToken->nextArgumentBeforeCreateLinks2()))
args.push_back(argToken);
}
static bool isConstMethod(const Token* nameToken)
{
const Token* functionToken = getFunctionToken(nameToken);
if (!functionToken)
return false;
const Token* endToken = functionToken->link();
return Token::simpleMatch(endToken, ") const");
}
static bool areAllParamsTypes(const std::vector<const Token *> &params)
{
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 <")) {
2013-07-31 10:30:20 +02:00
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) {
2023-10-17 18:32:07 +02:00
// fixme: only type deduction 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;
}
// 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;
2017-12-30 09:00:19 +01:00
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);
2017-12-30 09:00:19 +01:00
}
}
}
}
}
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..
2021-08-01 10:31:36 +02:00
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;
}
--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 && mErrorLogger && mSettings.severity.isEnabled(Severity::debug)) {
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);
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();
2020-02-09 11:19:36 +01:00
else if (tok2->str() == "<") {
tok2 = tok2->findClosingBracket();
2020-02-09 11:19:36 +01:00
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();
const 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(...)
if (Token::simpleMatch(tok->next(), "decltype (")) {
const Token * end = tok->linkAt(2)->previous();
2020-06-29 22:13:06 +02:00
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(), ";|{|:"))
return true;
2020-06-29 22:13:06 +02:00
while (tok->next() && tok->next() != closing) {
tok = tok->next();
namepos++;
}
}
} else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:")) {
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(...)
if (Token::simpleMatch(tok->next(), "decltype (")) {
const Token * end = tok->linkAt(2);
2020-06-29 22:13:06 +02:00
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;
2020-06-29 22:13:06 +02:00
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)
2019-08-17 16:02:58 +02:00
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
2019-08-17 16:02:58 +02:00
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());
}
struct newInstantiation {
newInstantiation(Token* t, std::string s) : token(t), scope(std::move(s)) {}
Token* token;
std::string scope;
};
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();
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, "]|}|)")) {
2021-08-01 10:31:36 +02:00
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);
inTemplateDefinition = localTypeParametersInDeclaration.size() == typeParametersInDeclaration.size(); // Partial specialization
} 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% (")) {
2018-01-06 15:16:03 +01:00
// 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()) {
2018-01-06 15:16:03 +01:00
if (prev->str() == "template") {
istemplate = true;
tok5 = prev;
2018-01-06 15:16:03 +01:00
break;
}
}
if (!istemplate)
continue;
const Token *tok4 = tok3->next()->findClosingBracket();
while (tok4 && tok4->str() != "(")
tok4 = tok4->next();
if (!Tokenizer::isFunctionHead(tok4, ":{"))
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 && mTypesUsedInTemplateInstantiation.size() > 1 && !Token::simpleMatch(tok3->next(), "..."))
continue;
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;
}
}
2017-12-23 15:41:32 +01:00
// replace name..
2018-01-15 22:03:23 +01:00
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;
}
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
2019-08-17 16:02:58 +02:00
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());
brackets.pop();
if (brackets.empty() && !Token::Match(tok3, "} >|,|{")) {
inTemplateDefinition = false;
if (isClass && tok3->strAt(1) == ";") {
const Token* tokSemicolon = tok3->next();
mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->column(), tokSemicolon->fileIndex());
}
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) {
if (!inst.token)
continue;
simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket(), &newInstantiations);
// 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)
{
2015-11-28 09:26:44 +01:00
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);
2018-05-29 22:41:37 +02:00
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
2018-05-29 22:41:37 +02:00
} 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);
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;
2018-05-29 22:41:37 +02:00
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%|&&")) {
2018-05-29 22:41:37 +02:00
const bool op1 = !MathLib::isNullValue(num1);
const bool op2 = !MathLib::isNullValue(num2);
2018-04-04 21:51:31 +02:00
const bool result = (op->str() == "||") ? (op1 || op2) : (op1 && op2);
tok->str(result ? "1" : "0");
}
else if (Token::Match(tok->previous(), "- %num% - %num%"))
2018-05-29 22:41:37 +02:00
tok->str(MathLib::add(num1, num2));
else if (Token::Match(tok->previous(), "- %num% + %num%"))
2018-05-29 22:41:37 +02:00
tok->str(MathLib::subtract(num1, num2));
else {
try {
2018-05-29 22:41:37 +02:00
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;
}
static void invalidateInst(const Token* beg, const Token* end, std::vector<newInstantiation>* newInst) {
if (!newInst)
return;
for (auto& inst : *newInst) {
for (const Token* tok = beg; tok != end; tok = tok->next())
if (inst.token == tok) {
inst.token = nullptr;
break;
}
}
}
void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end, std::vector<newInstantiation>* newInst)
{
// 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(std::to_string(mTokenizer.sizeOfType(tok->tokAt(3))));
tok->deleteNext(4);
again = true;
} else if (Token::simpleMatch(tok->next(), "( * )")) {
tok->str(std::to_string(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(std::to_string(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::toBigNumber(tok->str()));
const std::string &cmp(tok->next()->str());
const MathLib::bigint op2(MathLib::toBigNumber(tok->strAt(2)));
std::string result;
if (cmp == "==")
result = bool_to_string(op1 == op2);
else if (cmp == "!=")
result = bool_to_string(op1 != op2);
else if (cmp == "<=")
result = bool_to_string(op1 <= op2);
else if (cmp == ">=")
result = bool_to_string(op1 >= op2);
else if (cmp == "<")
result = bool_to_string(op1 < op2);
else
result = bool_to_string(op1 > op2);
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")) {
invalidateInst(tok->next(), colon, newInst);
// 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 {
invalidateInst(colon->tokAt(-1), endTok, newInst);
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, const Token *backToken, bool isTemplate)
{
bool ret = false;
const bool bounded = frontToken || backToken;
2018-12-21 13:54:59 +01:00
if (!frontToken) {
Several fairly significant optimisations (#1518) * Code changes for Token::mImpl optimisation * Added new TokenImpl optimisation Moving members to the TokenImpl struct reduces the size of the Token class, which is a fairly significant optimisation. In my testing on Windows with 32-bit Release-PCRE, this change reduced the size of the Token class from 108 bits to 52 bits and reduced run-time of my test case by around 20%. * Several optimisations Deleted some code that ran very slowly and did nothing, as there is no need to change a Token's string to null if you are about to delete it. Added a frontToken to simplifyCalculations to reduce the amount of work it has to do on already-simplified calculations. Moved template removal to the end of the list as this reduces redundant iteration and saves time. * Added tok argument to simplifyCalculations This means callers can avoid unnecessary work if they know which tokens have already been simplified. Passing nullptr indicates the original behaviour (starting from the front of the list). * Removed mention of member from another change * Re-added and optimised some code deleted in error Changing mTemplateInstantiations to a vector avoids the high cost of doing repeated linear searches. Changing how the code iterates through the array was necessary because the vector can be resized at several points during the loop, which breaks existing references and iterators. * Changed mTemplateInstantiations to a vector This is an optimisation that makes repeated linear searches of this collection significantly faster. Also added a copy constructor to TokenAndName so code can make copies of these objects to keep a reference if a vector gets resized. * A cleaner optimisation to removing template tokens This reverts the previous change to made mInstantiatedTemplates a vector and the iterator changes to support this, and makes mTypesUsedInTemplateInstantiation so the eraseTokens logic can be unified. * Reverted vector to list Also made mTypesUsedInTemplateInstantiation a vector of TokenAndName objects so it can share the same logic as the other members. * Added member for template simplifier pointer This can be used more efficiently than marking Tokens with a flag and then searching through all templates to find the one that matches. * Turned loop inside out This means we only have to iterate through the std::list once. std::list is very expensive to iterate through. * Latest code from danmar and fixed optimisations In particular I have optimised simplifying template instantiation names as this was incredibly slow because of the number of times it had to iterate through the template instantiation list. Previous optimisations to this weren't very effective and broke some edge cases. * Added changes from danmar Made mExplicitInstantiationsToDelete a vector of TokenAndName to be consistent with the rest of the members, which are cleaned up very efficiently. * Tokens can have many templateSimplifierPointers * templateSimplifierPointers must be kept in sync
2018-12-21 13:51:45 +01:00
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(std::to_string(MathLib::toBigNumber(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;
2018-04-04 21:51:31 +02:00
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::toBigNumber(tok->str()));
const std::string &cmp(tok->next()->str());
const MathLib::bigint op2(MathLib::toBigNumber(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";
2015-08-15 19:46:31 +02:00
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;
// cppcheck-suppress shadowFunction - TODO: fix this
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, "(|["))
2019-07-31 09:18:43 +02:00
++indentlevel;
else if (Token::Match(tok3, ")|]"))
2019-07-31 09:18:43 +02:00
--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,
2016-02-12 12:05:32 +01:00
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) {
if (mErrorLogger && mSettings.severity.isEnabled(Severity::information)) {
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 ("
+ std::to_string(mSettings.maxTemplateRecursion)
+ ") reached for template '"+typeForNewName+"'. You might want to limit Cppcheck recursion.",
Certainty::normal);
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);
}
2016-02-12 12:05:32 +01:00
return false;
}
assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021
const Token *startToken = tok2;
2020-02-04 16:35:30 +01:00
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, "templateInstantiation",
"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;
2020-02-04 16:35:30 +01:00
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, "templateInstantiation",
"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;
++it;
if (it == strings.cend())
return false;
} else if (tok->isSigned()) {
if (*it != "signed")
return false;
++it;
if (it == strings.cend())
return false;
}
if (tok->isLong()) {
if (*it != "long")
return false;
++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)
{
2021-08-07 20:51:18 +02:00
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->empty()) {
// 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) {
2022-10-02 07:13:31 +02:00
ti = mTemplateInstantiations.erase(ti);
break;
}
++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)
{
// 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;
const 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;
constexpr 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();
}
getTemplateDeclarations();
if (passCount == 0) {
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) + " ###");
// Keep track of the order the names appear so sort can preserve that order
std::unordered_map<std::string, int> nameOrdinal;
int ordinal = 0;
for (const auto& decl : mTemplateDeclarations) {
nameOrdinal.emplace(decl.fullName(), ordinal++);
}
auto score = [&](const Token* arg) {
int i = 0;
for (const Token* tok = arg; tok; tok = tok->next()) {
if (tok->str() == ",")
return i;
if (tok->link() && Token::Match(tok, "(|{|["))
tok = tok->link();
else if (tok->str() == "<") {
const Token* temp = tok->findClosingBracket();
if (temp)
tok = temp;
} else if (Token::Match(tok, ")|;"))
return i;
else if (Token::simpleMatch(tok, "const"))
i--;
}
return 0;
};
// Sort so const parameters come first in the list
mTemplateDeclarations.sort([&](const TokenAndName& x, const TokenAndName& y) {
if (x.fullName() != y.fullName())
return nameOrdinal.at(x.fullName()) < nameOrdinal.at(y.fullName());
if (x.isFunction() && y.isFunction()) {
std::vector<const Token*> xargs;
getFunctionArguments(x.nameToken(), xargs);
std::vector<const Token*> yargs;
getFunctionArguments(y.nameToken(), yargs);
if (xargs.size() != yargs.size())
return xargs.size() < yargs.size();
if (isConstMethod(x.nameToken()) != isConstMethod(y.nameToken()))
return isConstMethod(x.nameToken());
return std::lexicographical_compare(xargs.begin(),
xargs.end(),
yargs.begin(),
yargs.end(),
[&](const Token* xarg, const Token* yarg) {
if (xarg != yarg)
return score(xarg) < score(yarg);
return false;
});
}
return false;
});
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(
2021-08-07 20:51:18 +02:00
*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, &mTemplateForwardDeclarationsMap);
removeTemplate(it->token(), &mTemplateForwardDeclarationsMap);
}
mTemplateDeclarations.erase(decl);
}
}
// remove out of line member functions
while (!mMemberFunctionsToDelete.empty()) {
const std::list<TokenAndName>::iterator it = std::find_if(mTemplateDeclarations.begin(),
2021-08-07 20:51:18 +02:00
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(),
2021-08-07 20:51:18 +02:00
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 && mErrorLogger) {
const std::list<const Token*> locationList(1, mTokenList.front());
const ErrorMessage errmsg(locationList, &mTokenizer.list,
2020-05-23 07:16:49 +02:00
Severity::debug,
"debug",
"TemplateSimplifier: pass count limit hit before simplifications were finished.",
Certainty::normal);
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);
}