2008-12-18 22:28:57 +01:00
|
|
|
/*
|
2009-01-21 21:04:20 +01:00
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2019-02-09 07:24:06 +01:00
|
|
|
* Copyright (C) 2007-2019 Cppcheck team.
|
2008-12-18 22:28:57 +01:00
|
|
|
*
|
|
|
|
* 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
|
2009-09-27 17:08:31 +02:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2008-12-18 22:28:57 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "token.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
|
2019-01-20 13:20:23 +01:00
|
|
|
#include "astutils.h"
|
2010-07-24 10:51:17 +02:00
|
|
|
#include "errorlogger.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "library.h"
|
2014-04-02 06:49:28 +02:00
|
|
|
#include "settings.h"
|
2014-08-05 06:24:23 +02:00
|
|
|
#include "symboldatabase.h"
|
2015-11-29 10:49:10 +01:00
|
|
|
#include "utils.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
|
2009-08-22 16:22:50 +02:00
|
|
|
#include <cassert>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <cctype>
|
2008-12-18 22:28:57 +01:00
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
2009-03-03 21:17:23 +01:00
|
|
|
#include <map>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <set>
|
2013-12-28 11:02:39 +01:00
|
|
|
#include <stack>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <utility>
|
2014-03-08 20:56:39 +01:00
|
|
|
|
2019-03-18 06:18:25 +01:00
|
|
|
static const std::string literal_prefix[4] = {"u8", "u", "U", "L"};
|
|
|
|
|
2019-03-10 10:38:50 +01:00
|
|
|
static bool isStringCharLiteral(const std::string &str, char q)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!endsWith(str, q))
|
|
|
|
return false;
|
2019-03-18 06:18:25 +01:00
|
|
|
if (str[0] == q && str.length() > 1)
|
|
|
|
return true;
|
2019-03-10 10:38:50 +01:00
|
|
|
|
2019-03-18 06:18:25 +01:00
|
|
|
for (const std::string & p: literal_prefix) {
|
2019-03-10 12:24:28 +01:00
|
|
|
if ((str.length() + 1) > p.length() && (str.compare(0, p.size() + 1, (p + q)) == 0))
|
2019-03-10 10:38:50 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
const std::list<ValueFlow::Value> TokenImpl::mEmptyValueList;
|
2017-08-13 14:15:24 +02:00
|
|
|
|
2018-05-25 07:15:05 +02:00
|
|
|
Token::Token(TokensFrontBack *tokensFrontBack) :
|
2018-06-16 16:22:35 +02:00
|
|
|
mTokensFrontBack(tokensFrontBack),
|
2018-06-16 16:16:55 +02:00
|
|
|
mNext(nullptr),
|
|
|
|
mPrevious(nullptr),
|
2018-06-16 20:30:09 +02:00
|
|
|
mLink(nullptr),
|
2018-06-16 16:40:02 +02:00
|
|
|
mTokType(eNone),
|
2018-12-21 13:51:45 +01:00
|
|
|
mFlags(0)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl = new TokenImpl();
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2009-01-03 21:29:20 +01:00
|
|
|
Token::~Token()
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
delete mImpl;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2018-04-08 22:54:10 +02:00
|
|
|
static const std::set<std::string> controlFlowKeywords = {
|
|
|
|
"goto",
|
|
|
|
"do",
|
|
|
|
"if",
|
|
|
|
"else",
|
|
|
|
"for",
|
|
|
|
"while",
|
|
|
|
"switch",
|
|
|
|
"case",
|
|
|
|
"break",
|
|
|
|
"continue",
|
|
|
|
"return"
|
|
|
|
};
|
2017-11-17 22:10:39 +01:00
|
|
|
|
2011-10-23 20:38:03 +02:00
|
|
|
void Token::update_property_info()
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2018-06-16 23:03:15 +02:00
|
|
|
setFlag(fIsControlFlowKeyword, controlFlowKeywords.find(mStr) != controlFlowKeywords.end());
|
2017-11-17 22:10:39 +01:00
|
|
|
|
2018-06-16 23:03:15 +02:00
|
|
|
if (!mStr.empty()) {
|
|
|
|
if (mStr == "true" || mStr == "false")
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eBoolean);
|
2019-03-10 10:38:50 +01:00
|
|
|
else if (isStringCharLiteral(mStr, '\"'))
|
|
|
|
tokType(eString);
|
|
|
|
else if (isStringCharLiteral(mStr, '\''))
|
|
|
|
tokType(eChar);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (std::isalpha((unsigned char)mStr[0]) || mStr[0] == '_' || mStr[0] == '$') { // Name
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mVarId)
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eVariable);
|
2018-06-16 16:40:02 +02:00
|
|
|
else if (mTokType != eVariable && mTokType != eFunction && mTokType != eType && mTokType != eKeyword)
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eName);
|
2018-06-16 23:03:15 +02:00
|
|
|
} else if (std::isdigit((unsigned char)mStr[0]) || (mStr.length() > 1 && mStr[0] == '-' && std::isdigit((unsigned char)mStr[1])))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eNumber);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr == "=" || mStr == "<<=" || mStr == ">>=" ||
|
|
|
|
(mStr.size() == 2U && mStr[1] == '=' && std::strchr("+-*/%&^|", mStr[0])))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eAssignmentOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() == 1 && mStr.find_first_of(",[]()?:") != std::string::npos)
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eExtendedOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr=="<<" || mStr==">>" || (mStr.size()==1 && mStr.find_first_of("+-*/%") != std::string::npos))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eArithmeticalOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() == 1 && mStr.find_first_of("&|^~") != std::string::npos)
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eBitOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() <= 2 &&
|
|
|
|
(mStr == "&&" ||
|
|
|
|
mStr == "||" ||
|
|
|
|
mStr == "!"))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eLogicalOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() <= 2 && !mLink &&
|
|
|
|
(mStr == "==" ||
|
|
|
|
mStr == "!=" ||
|
|
|
|
mStr == "<" ||
|
|
|
|
mStr == "<=" ||
|
|
|
|
mStr == ">" ||
|
|
|
|
mStr == ">="))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eComparisonOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() == 2 &&
|
|
|
|
(mStr == "++" ||
|
|
|
|
mStr == "--"))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eIncDecOp);
|
2018-06-16 23:03:15 +02:00
|
|
|
else if (mStr.size() == 1 && (mStr.find_first_of("{}") != std::string::npos || (mLink && mStr.find_first_of("<>") != std::string::npos)))
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eBracket);
|
2019-09-04 08:07:30 +02:00
|
|
|
else if (mStr == "...")
|
|
|
|
tokType(eEllipsis);
|
2011-04-18 06:56:39 +02:00
|
|
|
else
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eOther);
|
2011-10-23 20:38:03 +02:00
|
|
|
} else {
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eNone);
|
2011-04-18 06:56:39 +02:00
|
|
|
}
|
2011-11-09 21:45:59 +01:00
|
|
|
|
2019-03-18 06:18:25 +01:00
|
|
|
update_property_char_string_literal();
|
2011-11-09 21:45:59 +01:00
|
|
|
update_property_isStandardType();
|
|
|
|
}
|
|
|
|
|
2018-04-08 22:54:10 +02:00
|
|
|
static const std::set<std::string> stdTypes = { "bool"
|
|
|
|
, "_Bool"
|
|
|
|
, "char"
|
|
|
|
, "double"
|
|
|
|
, "float"
|
|
|
|
, "int"
|
|
|
|
, "long"
|
|
|
|
, "short"
|
|
|
|
, "size_t"
|
|
|
|
, "void"
|
|
|
|
, "wchar_t"
|
|
|
|
};
|
2015-06-10 21:14:17 +02:00
|
|
|
|
2011-11-09 21:45:59 +01:00
|
|
|
void Token::update_property_isStandardType()
|
|
|
|
{
|
2014-05-06 06:35:48 +02:00
|
|
|
isStandardType(false);
|
2011-11-09 21:45:59 +01:00
|
|
|
|
2018-06-16 23:03:15 +02:00
|
|
|
if (mStr.size() < 3)
|
2011-11-09 21:45:59 +01:00
|
|
|
return;
|
|
|
|
|
2018-06-16 23:03:15 +02:00
|
|
|
if (stdTypes.find(mStr)!=stdTypes.end()) {
|
2014-05-06 06:35:48 +02:00
|
|
|
isStandardType(true);
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eType);
|
2011-11-09 21:45:59 +01:00
|
|
|
}
|
2011-10-23 20:38:03 +02:00
|
|
|
}
|
2008-12-21 14:58:56 +01:00
|
|
|
|
2019-03-18 06:18:25 +01:00
|
|
|
void Token::update_property_char_string_literal()
|
|
|
|
{
|
|
|
|
if (!(mTokType == Token::eString || mTokType == Token::eChar)) // Token has already been updated
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (const std::string & p : literal_prefix) {
|
|
|
|
if (((mTokType == Token::eString) && mStr.compare(0, p.size() + 1, p + "\"") == 0) ||
|
|
|
|
((mTokType == Token::eChar) && (mStr.compare(0, p.size() + 1, p + "\'") == 0))) {
|
|
|
|
mStr = mStr.substr(p.size());
|
|
|
|
isLong(p != "u8");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-11-09 21:45:59 +01:00
|
|
|
|
2012-06-21 19:00:53 +02:00
|
|
|
bool Token::isUpperCaseName() const
|
|
|
|
{
|
|
|
|
if (!isName())
|
|
|
|
return false;
|
2019-09-19 20:29:33 +02:00
|
|
|
for (char i : mStr) {
|
|
|
|
if (std::islower(i))
|
2012-06-21 19:00:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-03-28 20:33:55 +01:00
|
|
|
void Token::concatStr(std::string const& b)
|
|
|
|
{
|
2018-06-16 23:03:15 +02:00
|
|
|
mStr.erase(mStr.length() - 1);
|
|
|
|
mStr.append(b.begin() + 1, b.end());
|
2011-10-23 20:38:03 +02:00
|
|
|
|
|
|
|
update_property_info();
|
2009-03-28 20:33:55 +01:00
|
|
|
}
|
|
|
|
|
2009-09-13 10:02:23 +02:00
|
|
|
std::string Token::strValue() const
|
2009-09-12 22:54:47 +02:00
|
|
|
{
|
2018-06-16 16:40:02 +02:00
|
|
|
assert(mTokType == eString);
|
2018-06-16 23:03:15 +02:00
|
|
|
std::string ret(mStr.substr(1, mStr.length() - 2));
|
2015-06-07 11:25:33 +02:00
|
|
|
std::string::size_type pos = 0U;
|
2015-08-14 20:46:13 +02:00
|
|
|
while ((pos = ret.find('\\', pos)) != std::string::npos) {
|
2015-06-07 11:25:33 +02:00
|
|
|
ret.erase(pos,1U);
|
|
|
|
if (ret[pos] >= 'a') {
|
|
|
|
if (ret[pos] == 'n')
|
|
|
|
ret[pos] = '\n';
|
|
|
|
else if (ret[pos] == 'r')
|
|
|
|
ret[pos] = '\r';
|
|
|
|
else if (ret[pos] == 't')
|
|
|
|
ret[pos] = '\t';
|
|
|
|
}
|
|
|
|
if (ret[pos] == '0')
|
|
|
|
return ret.substr(0,pos);
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return ret;
|
2009-09-12 22:54:47 +02:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
void Token::deleteNext(nonneg int count)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2019-07-13 15:47:53 +02:00
|
|
|
while (mNext && count > 0) {
|
2018-06-16 16:16:55 +02:00
|
|
|
Token *n = mNext;
|
2017-08-25 17:17:19 +02:00
|
|
|
|
|
|
|
// #8154 we are about to be unknown -> destroy the link to us
|
2018-06-16 20:30:09 +02:00
|
|
|
if (n->mLink && n->mLink->mLink == n)
|
|
|
|
n->mLink->link(nullptr);
|
2017-08-25 17:17:19 +02:00
|
|
|
|
2018-06-16 16:16:55 +02:00
|
|
|
mNext = n->next();
|
2011-12-07 23:36:11 +01:00
|
|
|
delete n;
|
2019-07-13 15:47:53 +02:00
|
|
|
--count;
|
2011-12-07 23:36:11 +01:00
|
|
|
}
|
|
|
|
|
2018-06-16 16:16:55 +02:00
|
|
|
if (mNext)
|
|
|
|
mNext->previous(this);
|
2018-06-16 16:22:35 +02:00
|
|
|
else if (mTokensFrontBack)
|
|
|
|
mTokensFrontBack->back = this;
|
2018-05-25 07:15:05 +02:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
void Token::deletePrevious(nonneg int count)
|
2018-05-25 07:15:05 +02:00
|
|
|
{
|
2019-07-13 15:47:53 +02:00
|
|
|
while (mPrevious && count > 0) {
|
2018-06-16 16:16:55 +02:00
|
|
|
Token *p = mPrevious;
|
2018-05-25 07:15:05 +02:00
|
|
|
|
|
|
|
// #8154 we are about to be unknown -> destroy the link to us
|
2018-06-16 20:30:09 +02:00
|
|
|
if (p->mLink && p->mLink->mLink == p)
|
|
|
|
p->mLink->link(nullptr);
|
2018-05-25 07:15:05 +02:00
|
|
|
|
2018-06-16 16:16:55 +02:00
|
|
|
mPrevious = p->previous();
|
2018-05-25 07:15:05 +02:00
|
|
|
delete p;
|
2019-07-13 15:47:53 +02:00
|
|
|
--count;
|
2018-05-25 07:15:05 +02:00
|
|
|
}
|
|
|
|
|
2018-06-16 16:16:55 +02:00
|
|
|
if (mPrevious)
|
|
|
|
mPrevious->next(this);
|
2018-06-16 16:22:35 +02:00
|
|
|
else if (mTokensFrontBack)
|
|
|
|
mTokensFrontBack->front = this;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2014-12-27 10:53:26 +01:00
|
|
|
void Token::swapWithNext()
|
|
|
|
{
|
2018-06-16 16:16:55 +02:00
|
|
|
if (mNext) {
|
2018-06-16 23:03:15 +02:00
|
|
|
std::swap(mStr, mNext->mStr);
|
2018-06-16 16:40:02 +02:00
|
|
|
std::swap(mTokType, mNext->mTokType);
|
2018-06-16 16:16:55 +02:00
|
|
|
std::swap(mFlags, mNext->mFlags);
|
2018-12-21 13:51:45 +01:00
|
|
|
std::swap(mImpl, mNext->mImpl);
|
2018-12-21 13:54:59 +01:00
|
|
|
for (auto templateSimplifierPointer : mImpl->mTemplateSimplifierPointers) {
|
2019-08-10 08:42:12 +02:00
|
|
|
templateSimplifierPointer->token(this);
|
2018-12-21 13:51:45 +01:00
|
|
|
}
|
|
|
|
|
2018-12-21 13:54:59 +01:00
|
|
|
for (auto templateSimplifierPointer : mNext->mImpl->mTemplateSimplifierPointers) {
|
2019-08-10 08:42:12 +02:00
|
|
|
templateSimplifierPointer->token(mNext);
|
2018-12-21 13:51:45 +01:00
|
|
|
}
|
2018-06-16 20:30:09 +02:00
|
|
|
if (mNext->mLink)
|
|
|
|
mNext->mLink->mLink = this;
|
|
|
|
if (this->mLink)
|
|
|
|
this->mLink->mLink = mNext;
|
|
|
|
std::swap(mLink, mNext->mLink);
|
2014-12-27 10:53:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-25 23:30:04 +02:00
|
|
|
void Token::takeData(Token *fromToken)
|
|
|
|
{
|
2018-06-16 23:03:15 +02:00
|
|
|
mStr = fromToken->mStr;
|
2018-06-16 16:40:02 +02:00
|
|
|
tokType(fromToken->mTokType);
|
2018-06-16 16:14:34 +02:00
|
|
|
mFlags = fromToken->mFlags;
|
2018-12-21 13:51:45 +01:00
|
|
|
delete mImpl;
|
|
|
|
mImpl = fromToken->mImpl;
|
|
|
|
fromToken->mImpl = nullptr;
|
2018-12-21 13:54:59 +01:00
|
|
|
for (auto templateSimplifierPointer : mImpl->mTemplateSimplifierPointers) {
|
2019-08-10 08:42:12 +02:00
|
|
|
templateSimplifierPointer->token(this);
|
2017-08-25 23:30:04 +02:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
mLink = fromToken->mLink;
|
2018-06-16 20:30:09 +02:00
|
|
|
if (mLink)
|
|
|
|
mLink->link(this);
|
2017-08-25 23:30:04 +02:00
|
|
|
}
|
|
|
|
|
2009-03-13 00:07:05 +01:00
|
|
|
void Token::deleteThis()
|
|
|
|
{
|
2018-06-16 16:16:55 +02:00
|
|
|
if (mNext) { // Copy next to this and delete next
|
|
|
|
takeData(mNext);
|
|
|
|
mNext->link(nullptr); // mark as unlinked
|
2009-03-13 00:07:05 +01:00
|
|
|
deleteNext();
|
2018-06-16 16:16:55 +02:00
|
|
|
} else if (mPrevious && mPrevious->mPrevious) { // Copy previous to this and delete previous
|
|
|
|
takeData(mPrevious);
|
2012-02-13 17:44:08 +01:00
|
|
|
|
2018-06-16 16:16:55 +02:00
|
|
|
Token* toDelete = mPrevious;
|
|
|
|
mPrevious = mPrevious->mPrevious;
|
|
|
|
mPrevious->mNext = this;
|
2012-02-13 17:44:08 +01:00
|
|
|
|
|
|
|
delete toDelete;
|
2011-10-13 20:53:06 +02:00
|
|
|
} else {
|
2009-03-13 00:07:05 +01:00
|
|
|
// We are the last token in the list, we can't delete
|
2012-02-13 17:44:08 +01:00
|
|
|
// ourselves, so just make us empty
|
|
|
|
str("");
|
2009-03-13 00:07:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-26 23:26:50 +01:00
|
|
|
void Token::replace(Token *replaceThis, Token *start, Token *end)
|
|
|
|
{
|
|
|
|
// Fix the whole in the old location of start and end
|
2010-04-02 07:30:58 +02:00
|
|
|
if (start->previous())
|
2009-05-26 22:22:00 +02:00
|
|
|
start->previous()->next(end->next());
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (end->next())
|
2009-05-26 22:22:00 +02:00
|
|
|
end->next()->previous(start->previous());
|
2009-01-26 23:26:50 +01:00
|
|
|
|
|
|
|
// Move start and end to their new location
|
2010-04-02 07:30:58 +02:00
|
|
|
if (replaceThis->previous())
|
2009-05-26 22:22:00 +02:00
|
|
|
replaceThis->previous()->next(start);
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (replaceThis->next())
|
2009-05-26 22:22:00 +02:00
|
|
|
replaceThis->next()->previous(end);
|
|
|
|
|
2009-01-26 23:26:50 +01:00
|
|
|
start->previous(replaceThis->previous());
|
|
|
|
end->next(replaceThis->next());
|
|
|
|
|
2018-06-16 16:22:35 +02:00
|
|
|
if (end->mTokensFrontBack && end->mTokensFrontBack->back == end) {
|
2010-04-02 07:30:58 +02:00
|
|
|
while (end->next())
|
2010-01-06 20:19:27 +01:00
|
|
|
end = end->next();
|
2018-06-16 16:22:35 +02:00
|
|
|
end->mTokensFrontBack->back = end;
|
2010-01-06 20:19:27 +01:00
|
|
|
}
|
|
|
|
|
2018-06-16 16:41:25 +02:00
|
|
|
// Update mProgressValue, fileIndex and linenr
|
2012-01-22 00:02:55 +01:00
|
|
|
for (Token *tok = start; tok != end->next(); tok = tok->next())
|
2018-12-21 13:51:45 +01:00
|
|
|
tok->mImpl->mProgressValue = replaceThis->mImpl->mProgressValue;
|
2012-01-21 21:05:41 +01:00
|
|
|
|
2009-01-26 23:26:50 +01:00
|
|
|
// Delete old token, which is replaced
|
|
|
|
delete replaceThis;
|
|
|
|
}
|
|
|
|
|
2009-01-03 21:29:20 +01:00
|
|
|
const Token *Token::tokAt(int index) const
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2009-01-03 21:29:20 +01:00
|
|
|
const Token *tok = this;
|
2015-12-14 22:04:26 +01:00
|
|
|
while (index > 0 && tok) {
|
|
|
|
tok = tok->next();
|
|
|
|
--index;
|
|
|
|
}
|
|
|
|
while (index < 0 && tok) {
|
|
|
|
tok = tok->previous();
|
|
|
|
++index;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
2011-11-11 21:55:37 +01:00
|
|
|
const Token *Token::linkAt(int index) const
|
|
|
|
{
|
|
|
|
const Token *tok = this->tokAt(index);
|
2011-11-19 13:34:36 +01:00
|
|
|
if (!tok) {
|
2012-07-25 06:43:54 +02:00
|
|
|
throw InternalError(this, "Internal error. Token::linkAt called with index outside the tokens range.");
|
2011-11-19 13:34:36 +01:00
|
|
|
}
|
2012-09-09 14:34:07 +02:00
|
|
|
return tok->link();
|
2011-11-11 21:55:37 +01:00
|
|
|
}
|
|
|
|
|
2011-11-14 09:16:47 +01:00
|
|
|
const std::string &Token::strAt(int index) const
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2009-01-03 21:29:20 +01:00
|
|
|
const Token *tok = this->tokAt(index);
|
2018-06-16 23:03:15 +02:00
|
|
|
return tok ? tok->mStr : emptyString;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
static int multiComparePercent(const Token *tok, const char*& haystack, nonneg int varid)
|
2011-11-05 19:24:21 +01:00
|
|
|
{
|
2014-06-26 18:17:05 +02:00
|
|
|
++haystack;
|
|
|
|
// Compare only the first character of the string for optimization reasons
|
|
|
|
switch (haystack[0]) {
|
|
|
|
case '\0':
|
|
|
|
case ' ':
|
|
|
|
case '|':
|
|
|
|
//simple '%' character
|
|
|
|
haystack += 1;
|
|
|
|
if (tok->isArithmeticalOp() && tok->str() == "%")
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
if (haystack[3] == '%') { // %var%
|
|
|
|
haystack += 4;
|
2015-01-31 10:50:39 +01:00
|
|
|
if (tok->varId() != 0)
|
2014-06-26 18:17:05 +02:00
|
|
|
return 1;
|
|
|
|
} else { // %varid%
|
|
|
|
if (varid == 0) {
|
|
|
|
throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers");
|
|
|
|
}
|
2011-11-05 19:24:21 +01:00
|
|
|
|
2014-06-26 18:17:05 +02:00
|
|
|
haystack += 6;
|
|
|
|
|
|
|
|
if (tok->varId() == varid)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
// Type (%type%)
|
|
|
|
{
|
|
|
|
haystack += 5;
|
|
|
|
if (tok->isName() && tok->varId() == 0 && !tok->isKeyword())
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'a':
|
2015-12-31 01:15:49 +01:00
|
|
|
// Accept any token (%any%) or assign (%assign%)
|
2014-06-26 18:17:05 +02:00
|
|
|
{
|
2015-12-31 01:15:49 +01:00
|
|
|
if (haystack[3] == '%') { // %any%
|
|
|
|
haystack += 4;
|
|
|
|
return 1;
|
|
|
|
} else { // %assign%
|
|
|
|
haystack += 7;
|
|
|
|
if (tok->isAssignmentOp())
|
|
|
|
return 1;
|
|
|
|
}
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
2015-12-31 01:15:49 +01:00
|
|
|
break;
|
2014-06-26 18:17:05 +02:00
|
|
|
case 'n':
|
2015-01-31 10:50:39 +01:00
|
|
|
// Number (%num%) or name (%name%)
|
2014-06-26 18:17:05 +02:00
|
|
|
{
|
2015-01-31 10:50:39 +01:00
|
|
|
if (haystack[4] == '%') { // %name%
|
|
|
|
haystack += 5;
|
|
|
|
if (tok->isName())
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
haystack += 4;
|
|
|
|
if (tok->isNumber())
|
|
|
|
return 1;
|
|
|
|
}
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'c': {
|
|
|
|
haystack += 1;
|
|
|
|
// Character (%char%)
|
|
|
|
if (haystack[0] == 'h') {
|
|
|
|
haystack += 4;
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->tokType() == Token::eChar)
|
2011-11-05 19:24:21 +01:00
|
|
|
return 1;
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
|
|
|
// Const operator (%cop%)
|
|
|
|
else if (haystack[1] == 'p') {
|
|
|
|
haystack += 3;
|
2013-03-01 11:43:59 +01:00
|
|
|
if (tok->isConstOp())
|
|
|
|
return 1;
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
|
|
|
// Comparison operator (%comp%)
|
|
|
|
else {
|
|
|
|
haystack += 4;
|
|
|
|
if (tok->isComparisonOp())
|
2011-11-05 19:24:21 +01:00
|
|
|
return 1;
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
// String (%str%)
|
|
|
|
{
|
|
|
|
haystack += 4;
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->tokType() == Token::eString)
|
2014-06-26 18:17:05 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
// Bool (%bool%)
|
|
|
|
{
|
|
|
|
haystack += 5;
|
|
|
|
if (tok->isBoolean())
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'o': {
|
|
|
|
++haystack;
|
|
|
|
if (haystack[1] == '%') {
|
|
|
|
// Op (%op%)
|
|
|
|
if (haystack[0] == 'p') {
|
|
|
|
haystack += 2;
|
|
|
|
if (tok->isOp())
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// Or (%or%)
|
|
|
|
else {
|
|
|
|
haystack += 2;
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->tokType() == Token::eBitOp && tok->str() == "|")
|
2014-06-26 18:17:05 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Oror (%oror%)
|
|
|
|
else {
|
|
|
|
haystack += 4;
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->tokType() == Token::eLogicalOp && tok->str() == "||")
|
2011-11-05 19:24:21 +01:00
|
|
|
return 1;
|
|
|
|
}
|
2014-06-26 18:17:05 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//unknown %cmd%, abort
|
2015-03-29 21:05:18 +02:00
|
|
|
throw InternalError(tok, "Unexpected command");
|
2011-11-05 19:24:21 +01:00
|
|
|
}
|
|
|
|
|
2014-06-26 18:17:05 +02:00
|
|
|
if (*haystack == '|')
|
|
|
|
haystack += 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
|
2011-11-05 19:24:21 +01:00
|
|
|
return 0xFFFF;
|
|
|
|
}
|
|
|
|
|
2019-07-14 17:31:26 +02:00
|
|
|
int Token::multiCompare(const Token *tok, const char *haystack, nonneg int varid)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2014-06-26 17:32:24 +02:00
|
|
|
const char *needle = tok->str().c_str();
|
2009-01-27 20:30:01 +01:00
|
|
|
const char *needlePointer = needle;
|
2012-03-25 11:51:59 +02:00
|
|
|
for (;;) {
|
2014-06-26 18:17:05 +02:00
|
|
|
if (needlePointer == needle && haystack[0] == '%' && haystack[1] != '|' && haystack[1] != '\0' && haystack[1] != ' ') {
|
2018-04-04 21:51:31 +02:00
|
|
|
const int ret = multiComparePercent(tok, haystack, varid);
|
2014-06-26 18:17:05 +02:00
|
|
|
if (ret < 2)
|
|
|
|
return ret;
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (*haystack == '|') {
|
|
|
|
if (*needlePointer == 0) {
|
2010-09-19 15:14:13 +02:00
|
|
|
// If needle is at the end, we have a match.
|
2008-12-18 22:28:57 +01:00
|
|
|
return 1;
|
2009-01-27 20:30:01 +01:00
|
|
|
}
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2009-01-27 20:30:01 +01:00
|
|
|
needlePointer = needle;
|
2010-09-19 15:14:13 +02:00
|
|
|
++haystack;
|
2014-06-26 18:17:05 +02:00
|
|
|
} else if (*needlePointer == *haystack) {
|
|
|
|
if (*needlePointer == '\0')
|
|
|
|
return 1;
|
|
|
|
++needlePointer;
|
|
|
|
++haystack;
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (*haystack == ' ' || *haystack == '\0') {
|
2010-09-19 15:14:13 +02:00
|
|
|
if (needlePointer == needle)
|
|
|
|
return 0;
|
|
|
|
break;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
2009-01-27 20:30:01 +01:00
|
|
|
// If haystack and needle don't share the same character,
|
|
|
|
// find next '|' character.
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2010-09-19 15:14:13 +02:00
|
|
|
needlePointer = needle;
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
do {
|
2010-09-19 15:14:13 +02:00
|
|
|
++haystack;
|
2011-10-13 20:53:06 +02:00
|
|
|
} while (*haystack != ' ' && *haystack != '|' && *haystack);
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (*haystack == ' ' || *haystack == '\0') {
|
2016-02-01 09:55:16 +01:00
|
|
|
return -1;
|
2010-09-19 15:14:13 +02:00
|
|
|
}
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2010-09-19 15:14:13 +02:00
|
|
|
++haystack;
|
2009-01-27 20:30:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-19 15:14:13 +02:00
|
|
|
if (*needlePointer == '\0')
|
|
|
|
return 1;
|
|
|
|
|
2008-12-18 22:28:57 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-01-03 21:29:20 +01:00
|
|
|
bool Token::simpleMatch(const Token *tok, const char pattern[])
|
2008-12-22 00:28:09 +01:00
|
|
|
{
|
2014-04-27 09:32:02 +02:00
|
|
|
if (!tok)
|
|
|
|
return false; // shortcut
|
2014-07-05 12:10:23 +02:00
|
|
|
const char *current = pattern;
|
|
|
|
const char *next = std::strchr(pattern, ' ');
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!next)
|
2012-12-27 11:51:12 +01:00
|
|
|
next = pattern + std::strlen(pattern);
|
2008-12-23 22:45:47 +01:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
while (*current) {
|
2018-04-04 21:51:31 +02:00
|
|
|
const std::size_t length = next - current;
|
2008-12-23 22:45:47 +01:00
|
|
|
|
2018-06-16 23:03:15 +02:00
|
|
|
if (!tok || length != tok->mStr.length() || std::strncmp(current, tok->mStr.c_str(), length))
|
2008-12-22 00:28:09 +01:00
|
|
|
return false;
|
2008-12-23 22:45:47 +01:00
|
|
|
|
|
|
|
current = next;
|
2011-10-13 20:53:06 +02:00
|
|
|
if (*next) {
|
2012-12-27 11:51:12 +01:00
|
|
|
next = std::strchr(++current, ' ');
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!next)
|
2012-12-27 11:51:12 +01:00
|
|
|
next = current + std::strlen(current);
|
2008-12-23 22:45:47 +01:00
|
|
|
}
|
|
|
|
tok = tok->next();
|
2008-12-22 00:28:09 +01:00
|
|
|
}
|
2008-12-23 22:45:47 +01:00
|
|
|
|
2008-12-22 00:28:09 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-16 15:28:38 +02:00
|
|
|
bool Token::firstWordEquals(const char *str, const char *word)
|
2009-08-16 22:28:17 +02:00
|
|
|
{
|
2011-10-13 20:53:06 +02:00
|
|
|
for (;;) {
|
|
|
|
if (*str != *word) {
|
2012-04-26 16:39:16 +02:00
|
|
|
return (*str == ' ' && *word == 0);
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (*str == 0)
|
2009-08-16 22:28:17 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
++str;
|
|
|
|
++word;
|
|
|
|
}
|
|
|
|
|
2012-04-26 16:39:16 +02:00
|
|
|
return true;
|
2009-08-16 22:28:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *Token::chrInFirstWord(const char *str, char c)
|
|
|
|
{
|
2011-10-13 20:53:06 +02:00
|
|
|
for (;;) {
|
2010-04-02 07:30:58 +02:00
|
|
|
if (*str == ' ' || *str == 0)
|
2017-08-09 20:00:26 +02:00
|
|
|
return nullptr;
|
2009-08-16 22:28:17 +02:00
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (*str == c)
|
2009-08-16 22:28:17 +02:00
|
|
|
return str;
|
|
|
|
|
|
|
|
++str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
bool Token::Match(const Token *tok, const char pattern[], nonneg int varid)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
|
|
|
const char *p = pattern;
|
2011-10-13 20:53:06 +02:00
|
|
|
while (*p) {
|
2008-12-18 22:28:57 +01:00
|
|
|
// Skip spaces in pattern..
|
2010-04-02 07:30:58 +02:00
|
|
|
while (*p == ' ')
|
2009-01-01 23:22:28 +01:00
|
|
|
++p;
|
2008-12-18 22:28:57 +01:00
|
|
|
|
|
|
|
// No token => Success!
|
2012-12-01 00:47:07 +01:00
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
2008-12-18 22:28:57 +01:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (!tok) {
|
2009-01-10 01:33:48 +01:00
|
|
|
// If we have no tokens, pattern "!!else" should return true
|
2012-11-25 15:13:41 +01:00
|
|
|
if (p[0] == '!' && p[1] == '!' && p[2] != '\0') {
|
2010-09-20 20:15:07 +02:00
|
|
|
while (*p && *p != ' ')
|
|
|
|
++p;
|
2009-01-10 01:33:48 +01:00
|
|
|
continue;
|
2011-10-13 20:53:06 +02:00
|
|
|
} else
|
2009-01-10 01:33:48 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-12-18 22:28:57 +01:00
|
|
|
// [.. => search for a one-character token..
|
2014-06-26 18:17:05 +02:00
|
|
|
if (p[0] == '[' && chrInFirstWord(p, ']')) {
|
2012-11-20 00:16:53 +01:00
|
|
|
if (tok->str().length() != 1)
|
2011-07-15 19:01:36 +02:00
|
|
|
return false;
|
2011-07-15 19:02:16 +02:00
|
|
|
|
2012-11-20 00:16:53 +01:00
|
|
|
const char *temp = p+1;
|
2009-08-16 22:28:17 +02:00
|
|
|
bool chrFound = false;
|
2019-07-13 16:06:24 +02:00
|
|
|
int count = 0;
|
2011-10-13 20:53:06 +02:00
|
|
|
while (*temp && *temp != ' ') {
|
|
|
|
if (*temp == ']') {
|
2009-08-16 22:28:17 +02:00
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
2012-11-20 00:16:53 +01:00
|
|
|
else if (*temp == tok->str()[0]) {
|
2009-08-16 22:28:17 +02:00
|
|
|
chrFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++temp;
|
|
|
|
}
|
|
|
|
|
2012-11-20 00:16:53 +01:00
|
|
|
if (count > 1 && tok->str()[0] == ']')
|
|
|
|
chrFound = true;
|
2009-08-16 22:28:17 +02:00
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!chrFound)
|
2008-12-18 22:28:57 +01:00
|
|
|
return false;
|
2012-11-20 00:16:53 +01:00
|
|
|
|
|
|
|
p = temp;
|
|
|
|
while (*p && *p != ' ')
|
|
|
|
++p;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2014-06-26 18:17:05 +02:00
|
|
|
// Parse "not" options. Token can be anything except the given one
|
|
|
|
else if (p[0] == '!' && p[1] == '!' && p[2] != '\0') {
|
|
|
|
p += 2;
|
|
|
|
if (firstWordEquals(p, tok->str().c_str()))
|
|
|
|
return false;
|
|
|
|
while (*p && *p != ' ')
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
2008-12-18 22:28:57 +01:00
|
|
|
// Parse multi options, such as void|int|char (accept token which is one of these 3)
|
2014-06-26 18:17:05 +02:00
|
|
|
else {
|
2018-04-04 21:51:31 +02:00
|
|
|
const int res = multiCompare(tok, p, varid);
|
2011-10-13 20:53:06 +02:00
|
|
|
if (res == 0) {
|
2008-12-18 22:28:57 +01:00
|
|
|
// Empty alternative matches, use the same token on next round
|
2010-09-20 20:15:07 +02:00
|
|
|
while (*p && *p != ' ')
|
|
|
|
++p;
|
2008-12-18 22:28:57 +01:00
|
|
|
continue;
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (res == -1) {
|
2008-12-18 22:28:57 +01:00
|
|
|
// No match
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-20 20:15:07 +02:00
|
|
|
while (*p && *p != ' ')
|
|
|
|
++p;
|
2008-12-18 22:28:57 +01:00
|
|
|
|
|
|
|
tok = tok->next();
|
|
|
|
}
|
|
|
|
|
|
|
|
// The end of the pattern has been reached and nothing wrong has been found
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
nonneg int Token::getStrLength(const Token *tok)
|
2009-08-30 13:07:10 +02:00
|
|
|
{
|
2014-02-15 08:05:54 +01:00
|
|
|
assert(tok != nullptr);
|
2018-06-16 16:40:02 +02:00
|
|
|
assert(tok->mTokType == eString);
|
2009-08-30 13:07:10 +02:00
|
|
|
|
2019-07-13 16:06:24 +02:00
|
|
|
int len = 0;
|
2015-06-07 11:25:33 +02:00
|
|
|
std::string::const_iterator it = tok->str().begin() + 1U;
|
|
|
|
const std::string::const_iterator end = tok->str().end() - 1U;
|
2009-08-30 13:07:10 +02:00
|
|
|
|
2015-06-07 11:25:33 +02:00
|
|
|
while (it != end) {
|
|
|
|
if (*it == '\\') {
|
|
|
|
++it;
|
2009-09-26 17:58:14 +02:00
|
|
|
|
|
|
|
// string ends at '\0'
|
2015-06-07 11:25:33 +02:00
|
|
|
if (*it == '0')
|
|
|
|
return len;
|
2009-09-26 17:58:14 +02:00
|
|
|
}
|
|
|
|
|
2015-06-07 11:25:33 +02:00
|
|
|
if (*it == '\0')
|
|
|
|
return len;
|
|
|
|
|
|
|
|
++it;
|
2009-08-30 13:07:10 +02:00
|
|
|
++len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
nonneg int Token::getStrSize(const Token *tok)
|
2014-08-01 13:12:18 +02:00
|
|
|
{
|
2019-05-07 10:28:31 +02:00
|
|
|
assert(tok != nullptr);
|
|
|
|
assert(tok->tokType() == eString);
|
2014-08-01 13:12:18 +02:00
|
|
|
const std::string &str = tok->str();
|
2019-07-13 16:06:24 +02:00
|
|
|
int sizeofstring = 1;
|
|
|
|
for (int i = 1; i < (int)str.size() - 1; i++) {
|
2014-08-01 13:12:18 +02:00
|
|
|
if (str[i] == '\\')
|
|
|
|
++i;
|
|
|
|
++sizeofstring;
|
|
|
|
}
|
|
|
|
return sizeofstring;
|
|
|
|
}
|
|
|
|
|
2019-07-13 16:06:24 +02:00
|
|
|
std::string Token::getCharAt(const Token *tok, MathLib::bigint index)
|
2013-01-13 20:52:38 +01:00
|
|
|
{
|
2014-02-15 08:05:54 +01:00
|
|
|
assert(tok != nullptr);
|
2013-01-13 20:52:38 +01:00
|
|
|
|
2015-10-26 11:51:05 +01:00
|
|
|
std::string::const_iterator it = tok->str().begin() + 1U;
|
|
|
|
const std::string::const_iterator end = tok->str().end() - 1U;
|
2013-01-13 20:52:38 +01:00
|
|
|
|
2015-10-26 11:51:05 +01:00
|
|
|
while (it != end) {
|
2013-01-13 20:52:38 +01:00
|
|
|
if (index == 0) {
|
2015-10-26 11:51:05 +01:00
|
|
|
if (*it == '\0')
|
|
|
|
return "\\0";
|
|
|
|
|
|
|
|
std::string ret(1, *it);
|
|
|
|
if (*it == '\\') {
|
|
|
|
++it;
|
|
|
|
ret += *it;
|
2013-01-13 20:52:38 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-10-26 11:51:05 +01:00
|
|
|
if (*it == '\\')
|
|
|
|
++it;
|
|
|
|
++it;
|
2013-01-13 20:52:38 +01:00
|
|
|
--index;
|
|
|
|
}
|
|
|
|
assert(index == 0);
|
|
|
|
|
|
|
|
return "\\0";
|
|
|
|
}
|
|
|
|
|
2009-11-27 22:21:13 +01:00
|
|
|
void Token::move(Token *srcStart, Token *srcEnd, Token *newLocation)
|
|
|
|
{
|
|
|
|
/**[newLocation] -> b -> c -> [srcStart] -> [srcEnd] -> f */
|
|
|
|
|
|
|
|
// Fix the gap, which tokens to be moved will leave
|
|
|
|
srcStart->previous()->next(srcEnd->next());
|
|
|
|
srcEnd->next()->previous(srcStart->previous());
|
|
|
|
|
|
|
|
// Fix the tokens to be moved
|
|
|
|
srcEnd->next(newLocation->next());
|
|
|
|
srcStart->previous(newLocation);
|
|
|
|
|
|
|
|
// Fix the tokens at newLocation
|
|
|
|
newLocation->next()->previous(srcEnd);
|
|
|
|
newLocation->next(srcStart);
|
2010-08-03 16:36:21 +02:00
|
|
|
|
|
|
|
// Update _progressValue
|
2012-01-21 21:05:41 +01:00
|
|
|
for (Token *tok = srcStart; tok != srcEnd->next(); tok = tok->next())
|
2018-12-21 13:51:45 +01:00
|
|
|
tok->mImpl->mProgressValue = newLocation->mImpl->mProgressValue;
|
2009-11-27 22:21:13 +01:00
|
|
|
}
|
|
|
|
|
2011-12-07 21:15:00 +01:00
|
|
|
Token* Token::nextArgument() const
|
2011-10-23 11:23:48 +02:00
|
|
|
{
|
|
|
|
for (const Token* tok = this; tok; tok = tok->next()) {
|
|
|
|
if (tok->str() == ",")
|
2011-10-28 18:57:10 +02:00
|
|
|
return tok->next();
|
2014-09-14 11:26:16 +02:00
|
|
|
else if (tok->link() && Token::Match(tok, "(|{|[|<"))
|
2012-04-16 15:28:38 +02:00
|
|
|
tok = tok->link();
|
2014-09-14 11:26:16 +02:00
|
|
|
else if (Token::Match(tok, ")|;"))
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2011-10-23 11:23:48 +02:00
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2011-10-23 11:23:48 +02:00
|
|
|
}
|
|
|
|
|
2014-08-20 14:57:00 +02:00
|
|
|
Token* Token::nextArgumentBeforeCreateLinks2() const
|
|
|
|
{
|
|
|
|
for (const Token* tok = this; tok; tok = tok->next()) {
|
|
|
|
if (tok->str() == ",")
|
|
|
|
return tok->next();
|
2014-09-14 11:26:16 +02:00
|
|
|
else if (tok->link() && Token::Match(tok, "(|{|["))
|
2014-08-20 14:57:00 +02:00
|
|
|
tok = tok->link();
|
|
|
|
else if (tok->str() == "<") {
|
|
|
|
const Token* temp = tok->findClosingBracket();
|
|
|
|
if (temp)
|
|
|
|
tok = temp;
|
2014-09-14 11:26:16 +02:00
|
|
|
} else if (Token::Match(tok, ")|;"))
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2014-08-20 14:57:00 +02:00
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2014-08-20 14:57:00 +02:00
|
|
|
}
|
|
|
|
|
2015-01-03 20:35:33 +01:00
|
|
|
Token* Token::nextTemplateArgument() const
|
|
|
|
{
|
|
|
|
for (const Token* tok = this; tok; tok = tok->next()) {
|
|
|
|
if (tok->str() == ",")
|
|
|
|
return tok->next();
|
|
|
|
else if (tok->link() && Token::Match(tok, "(|{|[|<"))
|
|
|
|
tok = tok->link();
|
|
|
|
else if (Token::Match(tok, ">|;"))
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2015-01-03 20:35:33 +01:00
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2015-01-03 20:35:33 +01:00
|
|
|
}
|
|
|
|
|
2013-07-31 10:30:20 +02:00
|
|
|
const Token * Token::findClosingBracket() const
|
2012-04-18 16:02:03 +02:00
|
|
|
{
|
2018-06-16 23:03:15 +02:00
|
|
|
if (mStr != "<")
|
2018-01-01 12:22:04 +01:00
|
|
|
return nullptr;
|
|
|
|
|
2014-02-16 11:47:52 +01:00
|
|
|
const Token *closing = nullptr;
|
2019-08-04 10:24:44 +02:00
|
|
|
const bool templateParameter(strAt(-1) == "template");
|
|
|
|
std::set<std::string> templateParameters;
|
2013-07-31 10:30:20 +02:00
|
|
|
|
2018-01-01 12:22:04 +01:00
|
|
|
unsigned int depth = 0;
|
|
|
|
for (closing = this; closing != nullptr; closing = closing->next()) {
|
|
|
|
if (Token::Match(closing, "{|[|(")) {
|
|
|
|
closing = closing->link();
|
|
|
|
if (!closing)
|
|
|
|
return nullptr; // #6803
|
|
|
|
} else if (Token::Match(closing, "}|]|)|;"))
|
|
|
|
return nullptr;
|
2019-08-04 10:24:44 +02:00
|
|
|
// we can make some guesses for template parameters
|
|
|
|
else if (closing->str() == "<" &&
|
|
|
|
(!templateParameter || (closing->previous() && closing->previous()->isName() &&
|
|
|
|
templateParameters.find(closing->strAt(-1)) == templateParameters.end())))
|
2018-01-01 12:22:04 +01:00
|
|
|
++depth;
|
|
|
|
else if (closing->str() == ">") {
|
|
|
|
if (--depth == 0)
|
|
|
|
return closing;
|
|
|
|
} else if (closing->str() == ">>") {
|
|
|
|
if (depth <= 2)
|
|
|
|
return closing;
|
|
|
|
depth -= 2;
|
2012-04-18 16:02:03 +02:00
|
|
|
}
|
2019-08-04 10:24:44 +02:00
|
|
|
// save named template parameter
|
|
|
|
else if (templateParameter && depth == 1 && closing->str() == "," &&
|
|
|
|
closing->previous()->isName() && !Match(closing->previous(), "class|typename|."))
|
|
|
|
templateParameters.insert(closing->strAt(-1));
|
2012-04-18 16:02:03 +02:00
|
|
|
}
|
|
|
|
|
2013-07-31 10:30:20 +02:00
|
|
|
return closing;
|
|
|
|
}
|
|
|
|
|
|
|
|
Token * Token::findClosingBracket()
|
|
|
|
{
|
|
|
|
// return value of const function
|
|
|
|
return const_cast<Token*>(const_cast<const Token*>(this)->findClosingBracket());
|
2012-04-18 16:02:03 +02:00
|
|
|
}
|
|
|
|
|
2018-10-14 16:57:07 +02:00
|
|
|
const Token * Token::findOpeningBracket() const
|
|
|
|
{
|
|
|
|
if (mStr != ">")
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
const Token *opening = nullptr;
|
|
|
|
|
|
|
|
unsigned int depth = 0;
|
|
|
|
for (opening = this; opening != nullptr; opening = opening->previous()) {
|
|
|
|
if (Token::Match(opening, "}|]|)")) {
|
|
|
|
opening = opening->link();
|
|
|
|
if (!opening)
|
|
|
|
return nullptr;
|
|
|
|
} else if (Token::Match(opening, "{|{|(|;"))
|
|
|
|
return nullptr;
|
|
|
|
else if (opening->str() == ">")
|
|
|
|
++depth;
|
|
|
|
else if (opening->str() == "<") {
|
|
|
|
if (--depth == 0)
|
|
|
|
return opening;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return opening;
|
|
|
|
}
|
|
|
|
|
|
|
|
Token * Token::findOpeningBracket()
|
|
|
|
{
|
|
|
|
// return value of const function
|
|
|
|
return const_cast<Token*>(const_cast<const Token*>(this)->findOpeningBracket());
|
|
|
|
}
|
|
|
|
|
2008-12-18 22:28:57 +01:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2016-10-29 21:37:45 +02:00
|
|
|
const Token *Token::findsimplematch(const Token * const startTok, const char pattern[])
|
2011-10-27 10:54:50 +02:00
|
|
|
{
|
2015-08-27 16:14:33 +02:00
|
|
|
for (const Token* tok = startTok; tok; tok = tok->next()) {
|
2011-10-27 10:54:50 +02:00
|
|
|
if (Token::simpleMatch(tok, pattern))
|
|
|
|
return tok;
|
|
|
|
}
|
2017-08-09 20:00:26 +02:00
|
|
|
return nullptr;
|
2011-10-27 10:54:50 +02:00
|
|
|
}
|
|
|
|
|
2016-10-29 21:37:45 +02:00
|
|
|
const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], const Token * const end)
|
2011-10-27 10:54:50 +02:00
|
|
|
{
|
2015-08-27 16:14:33 +02:00
|
|
|
for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) {
|
2011-10-27 10:54:50 +02:00
|
|
|
if (Token::simpleMatch(tok, pattern))
|
|
|
|
return tok;
|
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2011-10-27 10:54:50 +02:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
const Token *Token::findmatch(const Token * const startTok, const char pattern[], const nonneg int varId)
|
2009-01-04 20:55:12 +01:00
|
|
|
{
|
2015-08-27 16:14:33 +02:00
|
|
|
for (const Token* tok = startTok; tok; tok = tok->next()) {
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, pattern, varId))
|
2009-01-04 20:55:12 +01:00
|
|
|
return tok;
|
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2009-01-04 20:55:12 +01:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
const Token *Token::findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId)
|
2010-07-26 16:46:37 +02:00
|
|
|
{
|
2015-08-27 16:14:33 +02:00
|
|
|
for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) {
|
2010-07-26 16:46:37 +02:00
|
|
|
if (Token::Match(tok, pattern, varId))
|
|
|
|
return tok;
|
|
|
|
}
|
2015-12-06 12:50:05 +01:00
|
|
|
return nullptr;
|
2010-07-26 16:46:37 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 14:00:59 +02:00
|
|
|
void Token::function(const Function *f)
|
|
|
|
{
|
2019-07-05 12:30:42 +02:00
|
|
|
mImpl->mFunction = f;
|
|
|
|
if (f) {
|
|
|
|
if (f->isLambda())
|
|
|
|
tokType(eLambda);
|
|
|
|
else
|
|
|
|
tokType(eFunction);
|
|
|
|
} else if (mTokType == eFunction)
|
|
|
|
tokType(eName);
|
|
|
|
}
|
|
|
|
|
2013-09-24 06:43:03 +02:00
|
|
|
void Token::insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend)
|
|
|
|
{
|
2014-07-05 12:10:23 +02:00
|
|
|
Token *newToken;
|
2018-06-16 23:03:15 +02:00
|
|
|
if (mStr.empty())
|
2013-09-24 06:43:03 +02:00
|
|
|
newToken = this;
|
|
|
|
else
|
2018-06-16 16:22:35 +02:00
|
|
|
newToken = new Token(mTokensFrontBack);
|
2013-09-24 06:43:03 +02:00
|
|
|
newToken->str(tokenStr);
|
2014-06-26 10:57:39 +02:00
|
|
|
if (!originalNameStr.empty())
|
|
|
|
newToken->originalName(originalNameStr);
|
2013-09-24 06:43:03 +02:00
|
|
|
|
2012-02-13 17:44:08 +01:00
|
|
|
if (newToken != this) {
|
2018-12-21 13:51:45 +01:00
|
|
|
newToken->mImpl->mLineNumber = mImpl->mLineNumber;
|
|
|
|
newToken->mImpl->mFileIndex = mImpl->mFileIndex;
|
|
|
|
newToken->mImpl->mProgressValue = mImpl->mProgressValue;
|
2018-04-28 17:17:40 +02:00
|
|
|
|
2012-11-20 02:58:19 +01:00
|
|
|
if (prepend) {
|
2018-11-07 21:25:42 +01:00
|
|
|
if (this->previous()) {
|
2012-11-20 02:58:19 +01:00
|
|
|
newToken->previous(this->previous());
|
|
|
|
newToken->previous()->next(newToken);
|
2018-11-07 21:25:42 +01:00
|
|
|
} else if (mTokensFrontBack) {
|
|
|
|
mTokensFrontBack->front = newToken;
|
|
|
|
}
|
2012-11-20 02:58:19 +01:00
|
|
|
this->previous(newToken);
|
|
|
|
newToken->next(this);
|
|
|
|
} else {
|
|
|
|
if (this->next()) {
|
|
|
|
newToken->next(this->next());
|
|
|
|
newToken->next()->previous(newToken);
|
2018-06-16 16:22:35 +02:00
|
|
|
} else if (mTokensFrontBack) {
|
|
|
|
mTokensFrontBack->back = newToken;
|
2012-11-20 02:58:19 +01:00
|
|
|
}
|
|
|
|
this->next(newToken);
|
|
|
|
newToken->previous(this);
|
2012-02-13 17:44:08 +01:00
|
|
|
}
|
2019-07-31 09:19:27 +02:00
|
|
|
|
|
|
|
if (mImpl->mScopeInfo) {
|
|
|
|
// If the brace is immediately closed there is no point opening a new scope for it
|
|
|
|
if (tokenStr == "{") {
|
|
|
|
std::string nextScopeNameAddition = "";
|
|
|
|
// This might be the opening of a member function
|
|
|
|
Token *tok1 = newToken;
|
|
|
|
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
|
|
|
|
tok1 = tok1->previous();
|
|
|
|
if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") {
|
|
|
|
tok1 = tok1->linkAt(-1);
|
|
|
|
if (Token::Match(tok1->previous(), "throw|noexcept")) {
|
|
|
|
tok1 = tok1->previous();
|
|
|
|
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
|
|
|
|
tok1 = tok1->previous();
|
|
|
|
if (tok1->strAt(-1) != ")")
|
|
|
|
return;
|
|
|
|
} else if (Token::Match(newToken->tokAt(-2), ":|, %name%")) {
|
|
|
|
tok1 = tok1->tokAt(-2);
|
|
|
|
if (tok1->strAt(-1) != ")")
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (tok1->strAt(-1) == ">")
|
|
|
|
tok1 = tok1->previous()->findOpeningBracket();
|
|
|
|
if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) {
|
|
|
|
tok1 = tok1->tokAt(-2);
|
|
|
|
std::string scope = tok1->strAt(-1);
|
|
|
|
while (Token::Match(tok1->tokAt(-2), ":: %name%")) {
|
|
|
|
scope = tok1->strAt(-3) + " :: " + scope;
|
|
|
|
tok1 = tok1->tokAt(-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nextScopeNameAddition.empty() && !scope.empty()) nextScopeNameAddition += " :: ";
|
|
|
|
nextScopeNameAddition += scope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Or it might be a namespace/class/struct
|
|
|
|
if (Token::Match(newToken->previous(), "%name%|>")) {
|
|
|
|
Token* nameTok = newToken->previous();
|
|
|
|
while (nameTok && !Token::Match(nameTok, "namespace|class|struct|union %name% {|::|:|<")) {
|
|
|
|
nameTok = nameTok->previous();
|
|
|
|
}
|
|
|
|
if (nameTok) {
|
|
|
|
for (nameTok = nameTok->next(); nameTok && !Token::Match(nameTok, "{|:|<"); nameTok = nameTok->next()) {
|
|
|
|
nextScopeNameAddition.append(nameTok->str());
|
|
|
|
nextScopeNameAddition.append(" ");
|
|
|
|
}
|
|
|
|
if (nextScopeNameAddition.length() > 0) nextScopeNameAddition = nextScopeNameAddition.substr(0, nextScopeNameAddition.length() - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// New scope is opening, record it here
|
|
|
|
std::shared_ptr<ScopeInfo2> newScopeInfo = std::make_shared<ScopeInfo2>(mImpl->mScopeInfo->name, nullptr, mImpl->mScopeInfo->usingNamespaces);
|
|
|
|
|
|
|
|
if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) newScopeInfo->name.append(" :: ");
|
|
|
|
newScopeInfo->name.append(nextScopeNameAddition);
|
2019-08-02 19:55:08 +02:00
|
|
|
nextScopeNameAddition = "";
|
2019-07-31 09:19:27 +02:00
|
|
|
|
|
|
|
newToken->scopeInfo(newScopeInfo);
|
|
|
|
} else if (tokenStr == "}") {
|
|
|
|
Token* matchingTok = newToken->previous();
|
|
|
|
int depth = 0;
|
|
|
|
while (matchingTok && (depth != 0 || !Token::simpleMatch(matchingTok, "{"))) {
|
|
|
|
if (Token::simpleMatch(matchingTok, "}")) depth++;
|
|
|
|
if (Token::simpleMatch(matchingTok, "{")) depth--;
|
|
|
|
matchingTok = matchingTok->previous();
|
|
|
|
}
|
|
|
|
if (matchingTok && matchingTok->previous()) {
|
|
|
|
newToken->mImpl->mScopeInfo = matchingTok->previous()->scopeInfo();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (prepend && newToken->previous()) {
|
|
|
|
newToken->mImpl->mScopeInfo = newToken->previous()->scopeInfo();
|
2019-07-31 11:11:01 +02:00
|
|
|
} else {
|
2019-07-31 09:19:27 +02:00
|
|
|
newToken->mImpl->mScopeInfo = mImpl->mScopeInfo;
|
|
|
|
}
|
|
|
|
if (tokenStr == ";") {
|
|
|
|
const Token* statementStart;
|
|
|
|
for (statementStart = newToken; statementStart->previous() && !Token::Match(statementStart->previous(), ";|{"); statementStart = statementStart->previous());
|
|
|
|
if (Token::Match(statementStart, "using namespace %name% ::|;")) {
|
|
|
|
const Token * tok1 = statementStart->tokAt(2);
|
|
|
|
std::string nameSpace;
|
|
|
|
while (tok1 && tok1->str() != ";") {
|
|
|
|
if (!nameSpace.empty())
|
|
|
|
nameSpace += " ";
|
|
|
|
nameSpace += tok1->str();
|
|
|
|
tok1 = tok1->next();
|
|
|
|
}
|
|
|
|
mImpl->mScopeInfo->usingNamespaces.insert(nameSpace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-13 17:44:08 +01:00
|
|
|
}
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
|
|
|
|
2009-01-05 16:49:57 +01:00
|
|
|
void Token::eraseTokens(Token *begin, const Token *end)
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2012-02-13 17:44:08 +01:00
|
|
|
if (!begin || begin == end)
|
2008-12-18 22:28:57 +01:00
|
|
|
return;
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
while (begin->next() && begin->next() != end) {
|
2008-12-18 22:28:57 +01:00
|
|
|
begin->deleteNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-22 16:22:50 +02:00
|
|
|
void Token::createMutualLinks(Token *begin, Token *end)
|
|
|
|
{
|
2014-02-15 08:05:54 +01:00
|
|
|
assert(begin != nullptr);
|
|
|
|
assert(end != nullptr);
|
2009-08-22 16:22:50 +02:00
|
|
|
assert(begin != end);
|
|
|
|
begin->link(end);
|
|
|
|
end->link(begin);
|
|
|
|
}
|
|
|
|
|
2009-01-05 16:49:57 +01:00
|
|
|
void Token::printOut(const char *title) const
|
2008-12-18 22:28:57 +01:00
|
|
|
{
|
2013-08-18 09:01:03 +02:00
|
|
|
if (title && title[0])
|
2012-04-16 19:51:07 +02:00
|
|
|
std::cout << "\n### " << title << " ###\n";
|
2017-08-09 20:00:26 +02:00
|
|
|
std::cout << stringifyList(true, true, true, true, true, nullptr, nullptr) << std::endl;
|
2009-11-27 23:04:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Token::printOut(const char *title, const std::vector<std::string> &fileNames) const
|
|
|
|
{
|
2013-08-18 09:01:03 +02:00
|
|
|
if (title && title[0])
|
2012-04-16 19:51:07 +02:00
|
|
|
std::cout << "\n### " << title << " ###\n";
|
2017-08-09 20:00:26 +02:00
|
|
|
std::cout << stringifyList(true, true, true, true, true, &fileNames, nullptr) << std::endl;
|
2008-12-18 22:28:57 +01:00
|
|
|
}
|
2009-02-13 07:25:29 +01:00
|
|
|
|
2014-09-29 14:50:00 +02:00
|
|
|
void Token::stringify(std::ostream& os, bool varid, bool attributes, bool macro) const
|
2011-12-13 21:42:38 +01:00
|
|
|
{
|
2012-04-16 19:51:07 +02:00
|
|
|
if (attributes) {
|
|
|
|
if (isUnsigned())
|
|
|
|
os << "unsigned ";
|
|
|
|
else if (isSigned())
|
|
|
|
os << "signed ";
|
2015-08-27 14:34:00 +02:00
|
|
|
if (isComplex())
|
|
|
|
os << "_Complex ";
|
2013-08-31 06:26:39 +02:00
|
|
|
if (isLong()) {
|
2018-06-16 16:40:02 +02:00
|
|
|
if (mTokType == eString || mTokType == eChar)
|
2013-08-31 06:26:39 +02:00
|
|
|
os << "L";
|
|
|
|
else
|
|
|
|
os << "long ";
|
|
|
|
}
|
2011-12-13 21:42:38 +01:00
|
|
|
}
|
2014-09-29 14:50:00 +02:00
|
|
|
if (macro && isExpandedMacro())
|
2013-10-13 17:42:06 +02:00
|
|
|
os << "$";
|
2018-06-16 23:03:15 +02:00
|
|
|
if (isName() && mStr.find(' ') != std::string::npos) {
|
2019-09-19 20:29:33 +02:00
|
|
|
for (char i : mStr) {
|
|
|
|
if (i != ' ')
|
|
|
|
os << i;
|
2017-12-25 23:16:51 +01:00
|
|
|
}
|
2018-06-16 23:03:15 +02:00
|
|
|
} else if (mStr[0] != '\"' || mStr.find('\0') == std::string::npos)
|
|
|
|
os << mStr;
|
2012-12-25 08:56:12 +01:00
|
|
|
else {
|
2019-09-19 20:29:33 +02:00
|
|
|
for (char i : mStr) {
|
|
|
|
if (i == '\0')
|
2012-12-25 08:56:12 +01:00
|
|
|
os << "\\0";
|
|
|
|
else
|
2019-09-19 20:29:33 +02:00
|
|
|
os << i;
|
2012-12-25 08:56:12 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
if (varid && mImpl->mVarId != 0)
|
|
|
|
os << '@' << mImpl->mVarId;
|
2011-12-13 21:42:38 +01:00
|
|
|
}
|
|
|
|
|
2012-04-16 19:51:07 +02:00
|
|
|
std::string Token::stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector<std::string>* fileNames, const Token* end) const
|
2009-11-27 23:04:04 +01:00
|
|
|
{
|
2012-04-16 19:51:07 +02:00
|
|
|
if (this == end)
|
|
|
|
return "";
|
2009-11-27 23:04:04 +01:00
|
|
|
|
2009-02-13 07:25:29 +01:00
|
|
|
std::ostringstream ret;
|
2009-03-03 20:45:58 +01:00
|
|
|
|
2018-12-21 13:51:45 +01:00
|
|
|
unsigned int lineNumber = mImpl->mLineNumber - (linenumbers ? 1U : 0U);
|
2019-07-17 16:28:47 +02:00
|
|
|
unsigned int fileIndex = files ? ~0U : mImpl->mFileIndex;
|
2010-08-06 21:02:43 +02:00
|
|
|
std::map<int, unsigned int> lineNumbers;
|
2012-04-16 19:51:07 +02:00
|
|
|
for (const Token *tok = this; tok != end; tok = tok->next()) {
|
2009-03-03 21:17:23 +01:00
|
|
|
bool fileChange = false;
|
2019-07-17 16:28:47 +02:00
|
|
|
if (tok->mImpl->mFileIndex != fileIndex) {
|
|
|
|
if (fileIndex != ~0U) {
|
|
|
|
lineNumbers[fileIndex] = tok->mImpl->mFileIndex;
|
2009-03-03 21:17:23 +01:00
|
|
|
}
|
|
|
|
|
2019-07-17 16:28:47 +02:00
|
|
|
fileIndex = tok->mImpl->mFileIndex;
|
2012-04-16 19:51:07 +02:00
|
|
|
if (files) {
|
|
|
|
ret << "\n\n##file ";
|
2018-12-21 13:51:45 +01:00
|
|
|
if (fileNames && fileNames->size() > tok->mImpl->mFileIndex)
|
|
|
|
ret << fileNames->at(tok->mImpl->mFileIndex);
|
2012-04-16 19:51:07 +02:00
|
|
|
else
|
2019-07-17 16:28:47 +02:00
|
|
|
ret << fileIndex;
|
2016-07-18 10:42:03 +02:00
|
|
|
ret << '\n';
|
2012-04-16 19:51:07 +02:00
|
|
|
}
|
2009-03-03 21:17:23 +01:00
|
|
|
|
2019-07-17 16:28:47 +02:00
|
|
|
lineNumber = lineNumbers[fileIndex];
|
2009-03-03 21:17:23 +01:00
|
|
|
fileChange = true;
|
|
|
|
}
|
|
|
|
|
2012-04-16 19:51:07 +02:00
|
|
|
if (linebreaks && (lineNumber != tok->linenr() || fileChange)) {
|
2019-07-17 16:28:47 +02:00
|
|
|
if (lineNumber+4 < tok->linenr() && fileIndex == tok->mImpl->mFileIndex) {
|
2012-08-02 20:36:54 +02:00
|
|
|
ret << '\n' << lineNumber+1 << ":\n|\n";
|
|
|
|
ret << tok->linenr()-1 << ":\n";
|
|
|
|
ret << tok->linenr() << ": ";
|
2016-07-18 10:42:03 +02:00
|
|
|
} else if (this == tok && linenumbers) {
|
|
|
|
ret << tok->linenr() << ": ";
|
2012-08-02 20:36:54 +02:00
|
|
|
} else {
|
|
|
|
while (lineNumber < tok->linenr()) {
|
|
|
|
++lineNumber;
|
|
|
|
ret << '\n';
|
|
|
|
if (linenumbers) {
|
|
|
|
ret << lineNumber << ':';
|
|
|
|
if (lineNumber == tok->linenr())
|
|
|
|
ret << ' ';
|
|
|
|
}
|
2012-04-16 19:51:07 +02:00
|
|
|
}
|
2009-03-03 21:17:23 +01:00
|
|
|
}
|
2010-04-09 21:40:37 +02:00
|
|
|
lineNumber = tok->linenr();
|
2009-02-13 07:25:29 +01:00
|
|
|
}
|
2009-03-03 21:17:23 +01:00
|
|
|
|
2014-09-29 14:50:00 +02:00
|
|
|
tok->stringify(ret, varid, attributes, attributes); // print token
|
2012-04-16 19:51:07 +02:00
|
|
|
if (tok->next() != end && (!linebreaks || (tok->next()->linenr() <= tok->linenr() && tok->next()->fileIndex() == tok->fileIndex())))
|
|
|
|
ret << ' ';
|
2009-02-13 07:25:29 +01:00
|
|
|
}
|
2016-07-18 10:42:03 +02:00
|
|
|
if (linebreaks && (files || linenumbers))
|
2012-04-16 19:51:07 +02:00
|
|
|
ret << '\n';
|
2009-02-13 07:25:29 +01:00
|
|
|
return ret.str();
|
|
|
|
}
|
|
|
|
|
2012-04-16 19:51:07 +02:00
|
|
|
std::string Token::stringifyList(const Token* end, bool attributes) const
|
|
|
|
{
|
2017-08-09 20:00:26 +02:00
|
|
|
return stringifyList(false, attributes, false, false, false, nullptr, end);
|
2012-04-16 19:51:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string Token::stringifyList(bool varid) const
|
|
|
|
{
|
2017-08-09 20:00:26 +02:00
|
|
|
return stringifyList(varid, false, true, true, true, nullptr, nullptr);
|
2012-04-16 19:51:07 +02:00
|
|
|
}
|
2012-12-15 20:21:09 +01:00
|
|
|
|
|
|
|
void Token::astOperand1(Token *tok)
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mAstOperand1)
|
|
|
|
mImpl->mAstOperand1->mImpl->mAstParent = nullptr;
|
2012-12-16 11:48:19 +01:00
|
|
|
// goto parent operator
|
2014-05-19 21:54:59 +02:00
|
|
|
if (tok) {
|
2018-02-10 14:39:57 +01:00
|
|
|
std::set<Token*> visitedParents;
|
2018-12-21 13:51:45 +01:00
|
|
|
while (tok->mImpl->mAstParent) {
|
|
|
|
if (!visitedParents.insert(tok->mImpl->mAstParent).second) // #6838/#6726/#8352 avoid hang on garbage code
|
2015-07-15 15:18:22 +02:00
|
|
|
throw InternalError(this, "Internal error. Token::astOperand1() cyclic dependency.");
|
2018-12-21 13:51:45 +01:00
|
|
|
tok = tok->mImpl->mAstParent;
|
2015-07-14 21:07:10 +02:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
tok->mImpl->mAstParent = this;
|
2014-05-19 21:54:59 +02:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mAstOperand1 = tok;
|
2012-12-15 20:21:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Token::astOperand2(Token *tok)
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mAstOperand2)
|
|
|
|
mImpl->mAstOperand2->mImpl->mAstParent = nullptr;
|
2012-12-16 11:48:19 +01:00
|
|
|
// goto parent operator
|
2014-05-19 21:54:59 +02:00
|
|
|
if (tok) {
|
2018-02-10 14:39:57 +01:00
|
|
|
std::set<Token*> visitedParents;
|
2018-12-21 13:51:45 +01:00
|
|
|
while (tok->mImpl->mAstParent) {
|
2018-06-16 16:18:50 +02:00
|
|
|
//std::cout << tok << " -> " << tok->mAstParent ;
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!visitedParents.insert(tok->mImpl->mAstParent).second) // #6838/#6726 avoid hang on garbage code
|
2015-07-15 15:18:22 +02:00
|
|
|
throw InternalError(this, "Internal error. Token::astOperand2() cyclic dependency.");
|
2018-12-21 13:51:45 +01:00
|
|
|
tok = tok->mImpl->mAstParent;
|
2015-07-14 21:07:10 +02:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
tok->mImpl->mAstParent = this;
|
2014-05-19 21:54:59 +02:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mAstOperand2 = tok;
|
2012-12-15 20:21:09 +01:00
|
|
|
}
|
|
|
|
|
2018-10-18 12:09:55 +02:00
|
|
|
static const Token* goToLeftParenthesis(const Token* start, const Token* end)
|
|
|
|
{
|
|
|
|
// move start to lpar in such expression: '(*it).x'
|
|
|
|
int par = 0;
|
|
|
|
for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
|
|
|
|
if (tok->str() == "(")
|
|
|
|
++par;
|
|
|
|
else if (tok->str() == ")") {
|
|
|
|
if (par == 0)
|
|
|
|
start = tok->link();
|
|
|
|
else
|
|
|
|
--par;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const Token* goToRightParenthesis(const Token* start, const Token* end)
|
|
|
|
{
|
|
|
|
// move end to rpar in such expression: '2>(x+1)'
|
|
|
|
int par = 0;
|
|
|
|
for (const Token *tok = end; tok && tok != start; tok = tok->previous()) {
|
|
|
|
if (tok->str() == ")")
|
|
|
|
++par;
|
|
|
|
else if (tok->str() == "(") {
|
|
|
|
if (par == 0)
|
|
|
|
end = tok->link();
|
|
|
|
else
|
|
|
|
--par;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return end;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<const Token *, const Token *> Token::findExpressionStartEndTokens() const
|
|
|
|
{
|
|
|
|
const Token * const top = this;
|
2019-01-20 13:20:23 +01:00
|
|
|
|
|
|
|
// find start node in AST tree
|
2018-10-18 12:09:55 +02:00
|
|
|
const Token *start = top;
|
|
|
|
while (start->astOperand1() &&
|
|
|
|
(start->astOperand2() || !start->isUnaryPreOp() || Token::simpleMatch(start, "( )") || start->str() == "{"))
|
|
|
|
start = start->astOperand1();
|
2019-01-20 13:20:23 +01:00
|
|
|
|
|
|
|
// find end node in AST tree
|
2018-10-18 12:09:55 +02:00
|
|
|
const Token *end = top;
|
|
|
|
while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) {
|
2019-01-20 13:20:23 +01:00
|
|
|
// lambda..
|
|
|
|
if (end->str() == "[") {
|
|
|
|
const Token *lambdaEnd = findLambdaEndToken(end);
|
|
|
|
if (lambdaEnd) {
|
|
|
|
end = lambdaEnd;
|
|
|
|
break;
|
|
|
|
}
|
2019-01-20 13:23:19 +01:00
|
|
|
}
|
|
|
|
if (Token::Match(end,"(|[") &&
|
|
|
|
!(Token::Match(end, "( %type%") && !end->astOperand2())) {
|
2018-10-18 12:09:55 +02:00
|
|
|
end = end->link();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
end = end->astOperand2() ? end->astOperand2() : end->astOperand1();
|
|
|
|
}
|
|
|
|
|
2019-01-20 13:20:23 +01:00
|
|
|
// skip parentheses
|
2018-10-18 12:09:55 +02:00
|
|
|
start = goToLeftParenthesis(start, end);
|
|
|
|
end = goToRightParenthesis(start, end);
|
2018-12-14 18:56:09 +01:00
|
|
|
if (Token::simpleMatch(end, "{"))
|
|
|
|
end = end->link();
|
2018-10-18 12:09:55 +02:00
|
|
|
return std::pair<const Token *, const Token *>(start,end);
|
|
|
|
}
|
|
|
|
|
2015-09-09 14:46:47 +02:00
|
|
|
bool Token::isCalculation() const
|
2013-12-28 11:02:39 +01:00
|
|
|
{
|
|
|
|
if (!Token::Match(this, "%cop%|++|--"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Token::Match(this, "*|&")) {
|
|
|
|
// dereference or address-of?
|
|
|
|
if (!this->astOperand2())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (this->astOperand2()->str() == "[")
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// type specification?
|
|
|
|
std::stack<const Token *> operands;
|
|
|
|
operands.push(this);
|
|
|
|
while (!operands.empty()) {
|
|
|
|
const Token *op = operands.top();
|
|
|
|
operands.pop();
|
|
|
|
if (op->isNumber() || op->varId() > 0)
|
|
|
|
return true;
|
|
|
|
if (op->astOperand1())
|
|
|
|
operands.push(op->astOperand1());
|
|
|
|
if (op->astOperand2())
|
|
|
|
operands.push(op->astOperand2());
|
|
|
|
else if (Token::Match(op, "*|&"))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// type specification => return false
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-24 17:02:00 +02:00
|
|
|
bool Token::isUnaryPreOp() const
|
2014-07-28 14:27:35 +02:00
|
|
|
{
|
2015-05-24 17:02:00 +02:00
|
|
|
if (!astOperand1() || astOperand2())
|
2014-07-28 14:27:35 +02:00
|
|
|
return false;
|
2015-05-24 17:02:00 +02:00
|
|
|
if (!Token::Match(this, "++|--"))
|
2014-07-28 14:27:35 +02:00
|
|
|
return true;
|
2018-06-16 16:16:55 +02:00
|
|
|
const Token *tokbefore = mPrevious;
|
|
|
|
const Token *tokafter = mNext;
|
2015-10-26 13:29:47 +01:00
|
|
|
for (int distance = 1; distance < 10 && tokbefore; distance++) {
|
2018-12-21 13:51:45 +01:00
|
|
|
if (tokbefore == mImpl->mAstOperand1)
|
2014-07-28 14:27:35 +02:00
|
|
|
return false;
|
2018-12-21 13:51:45 +01:00
|
|
|
if (tokafter == mImpl->mAstOperand1)
|
2014-07-28 14:27:35 +02:00
|
|
|
return true;
|
2018-06-16 16:16:55 +02:00
|
|
|
tokbefore = tokbefore->mPrevious;
|
|
|
|
tokafter = tokafter->mPrevious;
|
2014-07-28 14:27:35 +02:00
|
|
|
}
|
|
|
|
return false; // <- guess
|
|
|
|
}
|
|
|
|
|
2016-09-19 06:13:09 +02:00
|
|
|
static std::string stringFromTokenRange(const Token* start, const Token* end)
|
|
|
|
{
|
2017-04-30 14:22:18 +02:00
|
|
|
std::ostringstream ret;
|
2017-07-21 09:16:42 +02:00
|
|
|
if (end)
|
2017-07-21 09:17:25 +02:00
|
|
|
end = end->next();
|
2014-01-17 18:37:49 +01:00
|
|
|
for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
|
2017-07-21 09:17:25 +02:00
|
|
|
if (tok->isUnsigned())
|
|
|
|
ret << "unsigned ";
|
|
|
|
if (tok->isLong())
|
|
|
|
ret << (tok->isLiteral() ? "L" : "long ");
|
2017-07-22 16:42:42 +02:00
|
|
|
if (tok->originalName().empty() || tok->isUnsigned() || tok->isLong()) {
|
2017-04-30 14:22:18 +02:00
|
|
|
ret << tok->str();
|
2017-07-21 09:16:42 +02:00
|
|
|
} else
|
|
|
|
ret << tok->originalName();
|
2015-01-31 10:50:39 +01:00
|
|
|
if (Token::Match(tok, "%name%|%num% %name%|%num%"))
|
2017-04-30 14:22:18 +02:00
|
|
|
ret << ' ';
|
2014-01-17 18:37:49 +01:00
|
|
|
}
|
2017-04-30 14:22:18 +02:00
|
|
|
return ret.str();
|
2014-01-17 18:37:49 +01:00
|
|
|
}
|
|
|
|
|
2016-09-19 06:13:09 +02:00
|
|
|
std::string Token::expressionString() const
|
|
|
|
{
|
2018-10-18 12:09:55 +02:00
|
|
|
const auto tokens = findExpressionStartEndTokens();
|
|
|
|
return stringFromTokenRange(tokens.first, tokens.second);
|
2016-09-19 06:13:09 +02:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
static void astStringXml(const Token *tok, nonneg int indent, std::ostream &out)
|
2014-07-13 17:21:45 +02:00
|
|
|
{
|
|
|
|
const std::string strindent(indent, ' ');
|
|
|
|
|
2014-07-14 15:51:45 +02:00
|
|
|
out << strindent << "<token str=\"" << tok->str() << '\"';
|
|
|
|
if (tok->varId() > 0U)
|
|
|
|
out << " varId=\"" << MathLib::toString(tok->varId()) << '\"';
|
|
|
|
if (tok->variable())
|
|
|
|
out << " variable=\"" << tok->variable() << '\"';
|
|
|
|
if (tok->function())
|
|
|
|
out << " function=\"" << tok->function() << '\"';
|
2017-03-27 18:48:34 +02:00
|
|
|
if (!tok->values().empty())
|
|
|
|
out << " values=\"" << &tok->values() << '\"';
|
2014-07-14 15:51:45 +02:00
|
|
|
|
2014-07-13 17:21:45 +02:00
|
|
|
if (!tok->astOperand1() && !tok->astOperand2()) {
|
2014-07-14 15:51:45 +02:00
|
|
|
out << "/>" << std::endl;
|
2014-07-13 17:21:45 +02:00
|
|
|
}
|
|
|
|
|
2014-07-14 15:51:45 +02:00
|
|
|
else {
|
|
|
|
out << '>' << std::endl;
|
|
|
|
if (tok->astOperand1())
|
|
|
|
astStringXml(tok->astOperand1(), indent+2U, out);
|
|
|
|
if (tok->astOperand2())
|
|
|
|
astStringXml(tok->astOperand2(), indent+2U, out);
|
|
|
|
out << strindent << "</token>" << std::endl;
|
|
|
|
}
|
2014-07-13 17:21:45 +02:00
|
|
|
}
|
|
|
|
|
2014-07-14 15:51:45 +02:00
|
|
|
void Token::printAst(bool verbose, bool xml, std::ostream &out) const
|
2013-11-02 18:37:35 +01:00
|
|
|
{
|
2015-07-21 11:54:11 +02:00
|
|
|
std::set<const Token *> printed;
|
2013-11-02 18:37:35 +01:00
|
|
|
for (const Token *tok = this; tok; tok = tok->next()) {
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!tok->mImpl->mAstParent && tok->mImpl->mAstOperand1) {
|
2015-07-21 11:54:11 +02:00
|
|
|
if (printed.empty() && !xml)
|
2014-07-14 15:51:45 +02:00
|
|
|
out << "\n\n##AST" << std::endl;
|
2015-07-21 11:54:11 +02:00
|
|
|
else if (printed.find(tok) != printed.end())
|
|
|
|
continue;
|
|
|
|
printed.insert(tok);
|
|
|
|
|
2014-07-13 17:21:45 +02:00
|
|
|
if (xml) {
|
2019-04-13 10:22:13 +02:00
|
|
|
out << "<ast scope=\"" << tok->scope() << "\" fileIndex=\"" << tok->fileIndex() << "\" linenr=\"" << tok->linenr()
|
2019-08-18 12:19:05 +02:00
|
|
|
<< "\" column=\"" << tok->column() << "\">" << std::endl;
|
2015-07-21 11:54:11 +02:00
|
|
|
astStringXml(tok, 2U, out);
|
2014-07-14 15:51:45 +02:00
|
|
|
out << "</ast>" << std::endl;
|
2014-07-13 17:21:45 +02:00
|
|
|
} else if (verbose)
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
out << tok->astStringVerbose() << std::endl;
|
2014-02-24 17:22:29 +01:00
|
|
|
else
|
2015-07-21 11:54:11 +02:00
|
|
|
out << tok->astString(" ") << std::endl;
|
2014-01-18 09:58:32 +01:00
|
|
|
if (tok->str() == "(")
|
|
|
|
tok = tok->link();
|
2013-11-02 18:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-18 09:58:32 +01:00
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2)
|
2014-02-24 17:22:29 +01:00
|
|
|
{
|
2019-07-13 16:06:24 +02:00
|
|
|
for (int i = 0; i < indent1; ++i)
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
str += ' ';
|
2019-07-13 16:06:24 +02:00
|
|
|
for (int i = indent1; i < indent2; i += 2)
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
str += "| ";
|
2014-02-24 17:22:29 +01:00
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
void Token::astStringVerboseRecursive(std::string& ret, const nonneg int indent1, const nonneg int indent2) const
|
2014-02-24 17:22:29 +01:00
|
|
|
{
|
2015-01-17 23:11:22 +01:00
|
|
|
if (isExpandedMacro())
|
2016-01-01 13:54:07 +01:00
|
|
|
ret += '$';
|
2018-06-16 23:03:15 +02:00
|
|
|
ret += mStr;
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mValueType)
|
|
|
|
ret += " \'" + mImpl->mValueType->str() + '\'';
|
2016-01-01 13:54:07 +01:00
|
|
|
ret += '\n';
|
2015-01-17 23:11:22 +01:00
|
|
|
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mAstOperand1) {
|
2019-07-13 16:06:24 +02:00
|
|
|
int i1 = indent1, i2 = indent2 + 2;
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
if (indent1 == indent2 && !mImpl->mAstOperand2)
|
2014-02-24 17:22:29 +01:00
|
|
|
i1 += 2;
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
indent(ret, indent1, indent2);
|
|
|
|
ret += mImpl->mAstOperand2 ? "|-" : "`-";
|
|
|
|
mImpl->mAstOperand1->astStringVerboseRecursive(ret, i1, i2);
|
2014-02-24 17:22:29 +01:00
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mAstOperand2) {
|
2019-07-13 16:06:24 +02:00
|
|
|
int i1 = indent1, i2 = indent2 + 2;
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
if (indent1 == indent2)
|
2014-02-24 17:22:29 +01:00
|
|
|
i1 += 2;
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
indent(ret, indent1, indent2);
|
|
|
|
ret += "`-";
|
|
|
|
mImpl->mAstOperand2->astStringVerboseRecursive(ret, i1, i2);
|
2014-02-24 17:22:29 +01:00
|
|
|
}
|
Optimize astStringVerbose() for large arrays (#1815)
Change the astStringVerbose() recursion to extend a string instead of
returning one. This has the benefit that for tokens where the recursion
runs deep (typically large arrays), the time savings can be substantial
(see comments on benchmarks further down).
The reason is that previously, for each token, the astString of its
operands was constructed, and then appended to this tokens astString.
This led to a lot of unnecessary string copying (and with that
allocations). Instead, by passing the string by reference, the number
of temporary strings is greatly reduced.
Another way of seeing it is that previously, the string was constructed
from end to beginning, but now it is constructed from the beginning to
end. There was no notable speedup by preallocating the entire string
using string::reserve() (at least not on Linux).
To benchmark, the changes and master were tested on Linux using the
commands:
make
time cppcheck --debug --verbose $file >/dev/null
i.e., the cppcheck binary was compiled with the settings in the
Makefile. Printing the output to screen or file will of course take
longer time.
In Trac ticket #8355 which triggered this change, an example file from the
Wine repository was attached. Running the above cppcheck on master took
24 minutes and with the changes in this commmit, took 22 seconds.
Another test made was on lib/tokenlist.cpp in the cppcheck repo, which is
more "normal" file. On that file there was no measurable time difference.
A synthetic benchmark was generated to illustrate the effects on dumping
the ast for arrays of different sizes. The generate code looked as
follows:
const int array[] = {...};
with different number of elements. The results are as follows (times are
in seconds):
N master optimized
10 0.1 0.1
100 0.1 0.1
1000 2.8 0.7
2000 19 1.8
3000 53 3.8
5000 350 10
10000 3215 38
As we can see, for small arrays, there is no time difference, but for
large arrays the time savings are substantial.
2019-04-30 13:35:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string Token::astStringVerbose() const
|
|
|
|
{
|
|
|
|
std::string ret;
|
|
|
|
astStringVerboseRecursive(ret);
|
2014-02-24 17:22:29 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-14 15:51:45 +02:00
|
|
|
void Token::printValueFlow(bool xml, std::ostream &out) const
|
2014-01-18 09:58:32 +01:00
|
|
|
{
|
2019-07-13 16:06:24 +02:00
|
|
|
int line = 0;
|
2014-07-14 15:51:45 +02:00
|
|
|
if (xml)
|
|
|
|
out << " <valueflow>" << std::endl;
|
|
|
|
else
|
|
|
|
out << "\n\n##Value flow" << std::endl;
|
2014-01-18 09:58:32 +01:00
|
|
|
for (const Token *tok = this; tok; tok = tok->next()) {
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!tok->mImpl->mValues)
|
2014-01-18 09:58:32 +01:00
|
|
|
continue;
|
2014-07-14 15:51:45 +02:00
|
|
|
if (xml)
|
2018-12-21 13:51:45 +01:00
|
|
|
out << " <values id=\"" << tok->mImpl->mValues << "\">" << std::endl;
|
2014-07-14 15:51:45 +02:00
|
|
|
else if (line != tok->linenr())
|
|
|
|
out << "Line " << tok->linenr() << std::endl;
|
2014-01-18 09:58:32 +01:00
|
|
|
line = tok->linenr();
|
2015-07-26 15:36:09 +02:00
|
|
|
if (!xml) {
|
2018-12-21 13:51:45 +01:00
|
|
|
out << " " << tok->str() << (tok->mImpl->mValues->front().isKnown() ? " always " : " possible ");
|
|
|
|
if (tok->mImpl->mValues->size() > 1U)
|
2015-07-26 15:36:09 +02:00
|
|
|
out << '{';
|
|
|
|
}
|
2018-12-21 13:51:45 +01:00
|
|
|
for (const ValueFlow::Value &value : *tok->mImpl->mValues) {
|
2014-07-14 15:51:45 +02:00
|
|
|
if (xml) {
|
2014-08-03 20:11:22 +02:00
|
|
|
out << " <value ";
|
2018-08-10 11:29:16 +02:00
|
|
|
switch (value.valueType) {
|
2016-11-13 22:59:56 +01:00
|
|
|
case ValueFlow::Value::INT:
|
2017-07-22 12:19:46 +02:00
|
|
|
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
2018-08-10 11:29:16 +02:00
|
|
|
out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"';
|
2017-07-22 12:19:46 +02:00
|
|
|
else
|
2018-08-10 11:29:16 +02:00
|
|
|
out << "intvalue=\"" << value.intvalue << '\"';
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
|
|
|
case ValueFlow::Value::TOK:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << "tokvalue=\"" << value.tokvalue << '\"';
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
|
|
|
case ValueFlow::Value::FLOAT:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << "floatvalue=\"" << value.floatValue << '\"';
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
2016-11-20 15:14:49 +01:00
|
|
|
case ValueFlow::Value::MOVED:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"';
|
2016-11-20 15:14:49 +01:00
|
|
|
break;
|
2017-04-23 18:05:14 +02:00
|
|
|
case ValueFlow::Value::UNINIT:
|
|
|
|
out << "uninit=\"1\"";
|
|
|
|
break;
|
2019-03-17 13:09:15 +01:00
|
|
|
case ValueFlow::Value::BUFFER_SIZE:
|
|
|
|
out << "buffer-size=\"" << value.intvalue << "\"";
|
|
|
|
break;
|
2018-08-10 11:29:16 +02:00
|
|
|
case ValueFlow::Value::CONTAINER_SIZE:
|
|
|
|
out << "container-size=\"" << value.intvalue << '\"';
|
|
|
|
break;
|
2018-11-14 06:14:04 +01:00
|
|
|
case ValueFlow::Value::LIFETIME:
|
|
|
|
out << "lifetime=\"" << value.tokvalue << '\"';
|
|
|
|
break;
|
2016-11-13 22:59:56 +01:00
|
|
|
}
|
2018-08-10 11:29:16 +02:00
|
|
|
if (value.condition)
|
|
|
|
out << " condition-line=\"" << value.condition->linenr() << '\"';
|
|
|
|
if (value.isKnown())
|
2015-07-26 15:36:09 +02:00
|
|
|
out << " known=\"true\"";
|
2018-08-10 11:29:16 +02:00
|
|
|
else if (value.isPossible())
|
2015-07-26 15:36:09 +02:00
|
|
|
out << " possible=\"true\"";
|
2018-08-10 11:29:16 +02:00
|
|
|
else if (value.isInconclusive())
|
2017-09-20 22:41:36 +02:00
|
|
|
out << " inconclusive=\"true\"";
|
2014-07-14 15:51:45 +02:00
|
|
|
out << "/>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
2018-12-21 13:51:45 +01:00
|
|
|
if (&value != &tok->mImpl->mValues->front())
|
2014-08-03 20:11:22 +02:00
|
|
|
out << ",";
|
2018-08-10 11:29:16 +02:00
|
|
|
switch (value.valueType) {
|
2016-11-13 22:59:56 +01:00
|
|
|
case ValueFlow::Value::INT:
|
2017-07-22 12:19:46 +02:00
|
|
|
if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
|
2018-08-10 11:29:16 +02:00
|
|
|
out << (MathLib::biguint)value.intvalue;
|
2017-07-22 12:19:46 +02:00
|
|
|
else
|
2018-08-10 11:29:16 +02:00
|
|
|
out << value.intvalue;
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
|
|
|
case ValueFlow::Value::TOK:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << value.tokvalue->str();
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
|
|
|
case ValueFlow::Value::FLOAT:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << value.floatValue;
|
2016-11-13 22:59:56 +01:00
|
|
|
break;
|
2016-11-20 15:14:49 +01:00
|
|
|
case ValueFlow::Value::MOVED:
|
2018-08-10 11:29:16 +02:00
|
|
|
out << ValueFlow::Value::toString(value.moveKind);
|
2016-11-20 15:14:49 +01:00
|
|
|
break;
|
2017-04-23 18:05:14 +02:00
|
|
|
case ValueFlow::Value::UNINIT:
|
|
|
|
out << "Uninit";
|
|
|
|
break;
|
2019-03-17 13:09:15 +01:00
|
|
|
case ValueFlow::Value::BUFFER_SIZE:
|
2018-08-10 11:29:16 +02:00
|
|
|
case ValueFlow::Value::CONTAINER_SIZE:
|
|
|
|
out << "size=" << value.intvalue;
|
|
|
|
break;
|
2018-11-14 06:14:04 +01:00
|
|
|
case ValueFlow::Value::LIFETIME:
|
|
|
|
out << "lifetime=" << value.tokvalue->str();
|
|
|
|
break;
|
2016-11-13 22:59:56 +01:00
|
|
|
}
|
2019-08-24 11:27:47 +02:00
|
|
|
if (value.indirect > 0)
|
2019-08-24 14:43:35 +02:00
|
|
|
for (int i=0; i<value.indirect; i++)
|
2019-08-24 11:27:47 +02:00
|
|
|
out << "*";
|
2014-07-14 15:51:45 +02:00
|
|
|
}
|
2014-01-18 09:58:32 +01:00
|
|
|
}
|
2014-07-14 15:51:45 +02:00
|
|
|
if (xml)
|
|
|
|
out << " </values>" << std::endl;
|
2018-12-21 13:51:45 +01:00
|
|
|
else if (tok->mImpl->mValues->size() > 1U)
|
2015-07-26 15:36:09 +02:00
|
|
|
out << '}' << std::endl;
|
2014-07-14 15:51:45 +02:00
|
|
|
else
|
2015-07-26 15:36:09 +02:00
|
|
|
out << std::endl;
|
2014-01-18 09:58:32 +01:00
|
|
|
}
|
2014-07-14 15:51:45 +02:00
|
|
|
if (xml)
|
|
|
|
out << " </valueflow>" << std::endl;
|
2014-01-18 09:58:32 +01:00
|
|
|
}
|
2014-04-02 06:49:28 +02:00
|
|
|
|
|
|
|
const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!mImpl->mValues)
|
2017-03-27 18:48:34 +02:00
|
|
|
return nullptr;
|
2014-04-02 06:49:28 +02:00
|
|
|
const ValueFlow::Value *ret = nullptr;
|
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2016-11-13 22:33:39 +01:00
|
|
|
if (it->isIntValue() && it->intvalue <= val) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive()))
|
2014-04-02 06:49:28 +02:00
|
|
|
ret = &(*it);
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret->isInconclusive() && !ret->condition)
|
2014-04-02 06:49:28 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (settings && ret) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (ret->isInconclusive() && !settings->inconclusive)
|
2014-04-02 06:49:28 +02:00
|
|
|
return nullptr;
|
2017-04-11 11:49:09 +02:00
|
|
|
if (ret->condition && !settings->isEnabled(Settings::WARNING))
|
2014-04-02 06:49:28 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!mImpl->mValues)
|
2017-03-27 18:48:34 +02:00
|
|
|
return nullptr;
|
2014-04-02 06:49:28 +02:00
|
|
|
const ValueFlow::Value *ret = nullptr;
|
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2016-11-13 22:33:39 +01:00
|
|
|
if (it->isIntValue() && it->intvalue >= val) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive()))
|
2014-04-02 06:49:28 +02:00
|
|
|
ret = &(*it);
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret->isInconclusive() && !ret->condition)
|
2014-04-02 06:49:28 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (settings && ret) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (ret->isInconclusive() && !settings->inconclusive)
|
2017-04-20 22:14:54 +02:00
|
|
|
return nullptr;
|
|
|
|
if (ret->condition && !settings->isEnabled(Settings::WARNING))
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-07-14 12:22:33 +02:00
|
|
|
const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const
|
2017-04-20 22:14:54 +02:00
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!mImpl->mValues || !settings)
|
2017-04-20 22:14:54 +02:00
|
|
|
return nullptr;
|
|
|
|
const ValueFlow::Value *ret = nullptr;
|
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2018-07-15 23:05:48 +02:00
|
|
|
if ((it->isIntValue() && !settings->library.isIntArgValid(ftok, argnr, it->intvalue)) ||
|
|
|
|
(it->isFloatValue() && !settings->library.isFloatArgValid(ftok, argnr, it->floatValue))) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive()))
|
2017-04-20 22:14:54 +02:00
|
|
|
ret = &(*it);
|
2017-09-20 22:41:36 +02:00
|
|
|
if (!ret->isInconclusive() && !ret->condition)
|
2017-04-20 22:14:54 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-01-28 14:27:01 +01:00
|
|
|
if (ret) {
|
2017-09-20 22:41:36 +02:00
|
|
|
if (ret->isInconclusive() && !settings->inconclusive)
|
2014-04-02 06:49:28 +02:00
|
|
|
return nullptr;
|
2017-04-11 11:49:09 +02:00
|
|
|
if (ret->condition && !settings->isEnabled(Settings::WARNING))
|
2014-04-02 06:49:28 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-08-04 08:25:10 +02:00
|
|
|
const Token *Token::getValueTokenMinStrSize() const
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!mImpl->mValues)
|
2017-03-27 18:48:34 +02:00
|
|
|
return nullptr;
|
2014-08-04 08:25:10 +02:00
|
|
|
const Token *ret = nullptr;
|
2019-07-13 16:06:24 +02:00
|
|
|
int minsize = INT_MAX;
|
2014-08-04 08:25:10 +02:00
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2016-11-13 22:33:39 +01:00
|
|
|
if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) {
|
2019-07-13 16:06:24 +02:00
|
|
|
const int size = getStrSize(it->tokvalue);
|
2014-08-04 08:25:10 +02:00
|
|
|
if (!ret || size < minsize) {
|
|
|
|
minsize = size;
|
|
|
|
ret = it->tokvalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Token *Token::getValueTokenMaxStrLength() const
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (!mImpl->mValues)
|
2017-03-27 18:48:34 +02:00
|
|
|
return nullptr;
|
2014-08-04 08:25:10 +02:00
|
|
|
const Token *ret = nullptr;
|
2019-07-13 16:06:24 +02:00
|
|
|
int maxlength = 0;
|
2014-08-04 08:25:10 +02:00
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2016-11-13 22:33:39 +01:00
|
|
|
if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) {
|
2019-07-13 16:06:24 +02:00
|
|
|
const int length = getStrLength(it->tokvalue);
|
2014-08-04 08:25:10 +02:00
|
|
|
if (!ret || length > maxlength) {
|
|
|
|
maxlength = length;
|
|
|
|
ret = it->tokvalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-08-14 06:47:19 +02:00
|
|
|
static const Scope *getfunctionscope(const Scope *s)
|
|
|
|
{
|
|
|
|
while (s && s->type != Scope::eFunction)
|
|
|
|
s = s->nestedIn;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2014-08-05 06:24:23 +02:00
|
|
|
const Token *Token::getValueTokenDeadPointer() const
|
|
|
|
{
|
2014-08-14 06:47:19 +02:00
|
|
|
const Scope * const functionscope = getfunctionscope(this->scope());
|
|
|
|
|
2014-08-05 06:24:23 +02:00
|
|
|
std::list<ValueFlow::Value>::const_iterator it;
|
2017-03-27 18:48:34 +02:00
|
|
|
for (it = values().begin(); it != values().end(); ++it) {
|
2014-08-05 06:24:23 +02:00
|
|
|
// Is this a pointer alias?
|
2016-11-13 22:33:39 +01:00
|
|
|
if (!it->isTokValue() || (it->tokvalue && it->tokvalue->str() != "&"))
|
2014-08-05 06:24:23 +02:00
|
|
|
continue;
|
|
|
|
// Get variable
|
|
|
|
const Token *vartok = it->tokvalue->astOperand1();
|
|
|
|
if (!vartok || !vartok->isName() || !vartok->variable())
|
|
|
|
continue;
|
|
|
|
const Variable * const var = vartok->variable();
|
2015-01-30 19:29:37 +01:00
|
|
|
if (var->isStatic() || var->isReference())
|
2014-08-05 06:24:23 +02:00
|
|
|
continue;
|
2015-07-01 07:50:13 +02:00
|
|
|
if (!var->scope())
|
|
|
|
return nullptr; // #6804
|
2015-03-11 20:25:27 +01:00
|
|
|
if (var->scope()->type == Scope::eUnion && var->scope()->nestedIn == this->scope())
|
|
|
|
continue;
|
2014-08-14 06:47:19 +02:00
|
|
|
// variable must be in same function (not in subfunction)
|
|
|
|
if (functionscope != getfunctionscope(var->scope()))
|
|
|
|
continue;
|
|
|
|
// Is variable defined in this scope or upper scope?
|
2014-08-05 06:24:23 +02:00
|
|
|
const Scope *s = this->scope();
|
|
|
|
while ((s != nullptr) && (s != var->scope()))
|
|
|
|
s = s->nestedIn;
|
|
|
|
if (!s)
|
|
|
|
return it->tokvalue;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-03-27 18:48:34 +02:00
|
|
|
bool Token::addValue(const ValueFlow::Value &value)
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (value.isKnown() && mImpl->mValues) {
|
2018-11-14 06:59:25 +01:00
|
|
|
// Clear all other values of the same type since value is known
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mValues->remove_if([&](const ValueFlow::Value & x) {
|
2018-11-14 06:59:25 +01:00
|
|
|
return x.valueType == value.valueType;
|
|
|
|
});
|
2017-03-27 18:48:34 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mValues) {
|
2017-03-27 18:48:34 +02:00
|
|
|
// Don't handle more than 10 values for performance reasons
|
|
|
|
// TODO: add setting?
|
2018-12-21 13:51:45 +01:00
|
|
|
if (mImpl->mValues->size() >= 10U)
|
2017-03-27 18:48:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// if value already exists, don't add it again
|
|
|
|
std::list<ValueFlow::Value>::iterator it;
|
2018-12-21 13:51:45 +01:00
|
|
|
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
|
2017-03-27 18:48:34 +02:00
|
|
|
// different types => continue
|
|
|
|
if (it->valueType != value.valueType)
|
|
|
|
continue;
|
2019-07-24 12:30:33 +02:00
|
|
|
|
|
|
|
// different value => continue
|
|
|
|
bool differentValue = true;
|
|
|
|
switch (it->valueType) {
|
|
|
|
case ValueFlow::Value::ValueType::INT:
|
|
|
|
case ValueFlow::Value::ValueType::CONTAINER_SIZE:
|
|
|
|
case ValueFlow::Value::ValueType::BUFFER_SIZE:
|
|
|
|
differentValue = (it->intvalue != value.intvalue);
|
|
|
|
break;
|
|
|
|
case ValueFlow::Value::ValueType::TOK:
|
|
|
|
case ValueFlow::Value::ValueType::LIFETIME:
|
|
|
|
differentValue = (it->tokvalue != value.tokvalue);
|
|
|
|
break;
|
|
|
|
case ValueFlow::Value::ValueType::FLOAT:
|
|
|
|
// TODO: Write some better comparison
|
|
|
|
differentValue = (it->floatValue > value.floatValue || it->floatValue < value.floatValue);
|
|
|
|
break;
|
|
|
|
case ValueFlow::Value::ValueType::MOVED:
|
|
|
|
differentValue = (it->moveKind != value.moveKind);
|
|
|
|
break;
|
|
|
|
case ValueFlow::Value::ValueType::UNINIT:
|
|
|
|
differentValue = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (differentValue)
|
|
|
|
continue;
|
|
|
|
|
2018-11-14 06:59:25 +01:00
|
|
|
if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
2017-03-27 18:48:34 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// same value, but old value is inconclusive so replace it
|
2017-09-20 22:41:36 +02:00
|
|
|
if (it->isInconclusive() && !value.isInconclusive()) {
|
2017-03-27 18:48:34 +02:00
|
|
|
*it = value;
|
|
|
|
if (it->varId == 0)
|
2018-12-21 13:51:45 +01:00
|
|
|
it->varId = mImpl->mVarId;
|
2017-03-27 18:48:34 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same value already exists, don't add new value
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add value
|
2018-12-21 13:51:45 +01:00
|
|
|
if (it == mImpl->mValues->end()) {
|
2017-03-27 18:48:34 +02:00
|
|
|
ValueFlow::Value v(value);
|
|
|
|
if (v.varId == 0)
|
2018-12-21 13:51:45 +01:00
|
|
|
v.varId = mImpl->mVarId;
|
2018-11-14 19:10:52 +01:00
|
|
|
if (v.isKnown() && v.isIntValue())
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mValues->push_front(v);
|
2018-11-14 06:59:25 +01:00
|
|
|
else
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mValues->push_back(v);
|
2017-03-27 18:48:34 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ValueFlow::Value v(value);
|
|
|
|
if (v.varId == 0)
|
2018-12-21 13:51:45 +01:00
|
|
|
v.varId = mImpl->mVarId;
|
|
|
|
mImpl->mValues = new std::list<ValueFlow::Value>(1, v);
|
2017-03-27 18:48:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-20 21:55:08 +02:00
|
|
|
void Token::assignProgressValues(Token *tok)
|
|
|
|
{
|
2019-07-13 16:06:24 +02:00
|
|
|
int total_count = 0;
|
2014-05-20 21:55:08 +02:00
|
|
|
for (Token *tok2 = tok; tok2; tok2 = tok2->next())
|
|
|
|
++total_count;
|
2019-07-13 16:06:24 +02:00
|
|
|
int count = 0;
|
2014-05-20 21:55:08 +02:00
|
|
|
for (Token *tok2 = tok; tok2; tok2 = tok2->next())
|
2018-12-21 13:51:45 +01:00
|
|
|
tok2->mImpl->mProgressValue = count++ * 100 / total_count;
|
2014-05-20 21:55:08 +02:00
|
|
|
}
|
2015-10-04 19:42:58 +02:00
|
|
|
|
2019-06-21 22:16:23 +02:00
|
|
|
void Token::assignIndexes()
|
|
|
|
{
|
2019-07-13 16:06:24 +02:00
|
|
|
int index = (mPrevious ? mPrevious->mImpl->mIndex : 0) + 1;
|
2019-06-21 22:16:23 +02:00
|
|
|
for (Token *tok = this; tok; tok = tok->next())
|
|
|
|
tok->mImpl->mIndex = index++;
|
|
|
|
}
|
|
|
|
|
2015-10-04 19:42:58 +02:00
|
|
|
void Token::setValueType(ValueType *vt)
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
if (vt != mImpl->mValueType) {
|
|
|
|
delete mImpl->mValueType;
|
|
|
|
mImpl->mValueType = vt;
|
2015-10-04 19:42:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-22 06:02:54 +02:00
|
|
|
void Token::type(const ::Type *t)
|
|
|
|
{
|
2018-12-21 13:51:45 +01:00
|
|
|
mImpl->mType = t;
|
2016-04-22 06:02:54 +02:00
|
|
|
if (t) {
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eType);
|
2018-12-21 13:51:45 +01:00
|
|
|
isEnumType(mImpl->mType->isEnumType());
|
2018-06-16 16:40:02 +02:00
|
|
|
} else if (mTokType == eType)
|
2017-10-15 01:27:47 +02:00
|
|
|
tokType(eName);
|
2016-04-22 06:02:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-02 11:04:23 +02:00
|
|
|
const ::Type *Token::typeOf(const Token *tok)
|
|
|
|
{
|
|
|
|
if (Token::simpleMatch(tok, "return")) {
|
|
|
|
const Scope *scope = tok->scope();
|
|
|
|
if (!scope)
|
|
|
|
return nullptr;
|
|
|
|
const Function *function = scope->function;
|
|
|
|
if (!function)
|
|
|
|
return nullptr;
|
|
|
|
return function->retType;
|
|
|
|
} else if (Token::Match(tok, "%type%")) {
|
|
|
|
return tok->type();
|
|
|
|
} else if (Token::Match(tok, "%var%")) {
|
|
|
|
const Variable *var = tok->variable();
|
|
|
|
if (!var)
|
|
|
|
return nullptr;
|
|
|
|
return var->type();
|
|
|
|
} else if (Token::Match(tok, "%name%")) {
|
|
|
|
const Function *function = tok->function();
|
|
|
|
if (!function)
|
|
|
|
return nullptr;
|
|
|
|
return function->retType;
|
|
|
|
} else if (Token::simpleMatch(tok, "=")) {
|
|
|
|
return Token::typeOf(tok->astOperand1());
|
|
|
|
} else if (Token::simpleMatch(tok, ".")) {
|
|
|
|
return Token::typeOf(tok->astOperand2());
|
2019-09-10 19:41:35 +02:00
|
|
|
} else if (Token::simpleMatch(tok, "[")) {
|
|
|
|
return Token::typeOf(tok->astOperand1());
|
2019-05-02 11:04:23 +02:00
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<const Token*, const Token*> Token::typeDecl(const Token * tok)
|
|
|
|
{
|
|
|
|
if (Token::simpleMatch(tok, "return")) {
|
|
|
|
const Scope *scope = tok->scope();
|
|
|
|
if (!scope)
|
|
|
|
return {};
|
|
|
|
const Function *function = scope->function;
|
|
|
|
if (!function)
|
|
|
|
return {};
|
|
|
|
return {function->retDef, function->returnDefEnd()};
|
|
|
|
} else if (Token::Match(tok, "%type%")) {
|
|
|
|
return {tok, tok->next()};
|
|
|
|
} else if (Token::Match(tok, "%var%")) {
|
|
|
|
const Variable *var = tok->variable();
|
|
|
|
if (!var)
|
|
|
|
return {};
|
|
|
|
if (!var->typeStartToken() || !var->typeEndToken())
|
|
|
|
return {};
|
|
|
|
return {var->typeStartToken(), var->typeEndToken()->next()};
|
|
|
|
} else if (Token::Match(tok, "%name%")) {
|
|
|
|
const Function *function = tok->function();
|
|
|
|
if (!function)
|
|
|
|
return {};
|
|
|
|
return {function->retDef, function->returnDefEnd()};
|
|
|
|
} else if (Token::simpleMatch(tok, "=")) {
|
|
|
|
return Token::typeDecl(tok->astOperand1());
|
|
|
|
} else if (Token::simpleMatch(tok, ".")) {
|
|
|
|
return Token::typeDecl(tok->astOperand2());
|
|
|
|
} else {
|
|
|
|
const ::Type * t = typeOf(tok);
|
|
|
|
if (!t || !t->classDef)
|
|
|
|
return {};
|
|
|
|
return {t->classDef->next(), t->classDef->tokAt(2)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::string Token::typeStr(const Token* tok)
|
|
|
|
{
|
2019-06-02 10:14:48 +02:00
|
|
|
if (tok->valueType()) {
|
|
|
|
const ValueType * vt = tok->valueType();
|
|
|
|
std::string ret = vt->str();
|
|
|
|
if (!ret.empty())
|
2019-06-02 13:29:20 +02:00
|
|
|
return ret;
|
2019-06-02 10:14:48 +02:00
|
|
|
}
|
2019-05-02 11:04:23 +02:00
|
|
|
std::pair<const Token*, const Token*> r = Token::typeDecl(tok);
|
|
|
|
if (!r.first || !r.second)
|
|
|
|
return "";
|
|
|
|
return r.first->stringifyList(r.second, false);
|
|
|
|
}
|
|
|
|
|
2019-07-31 11:11:01 +02:00
|
|
|
void Token::scopeInfo(std::shared_ptr<ScopeInfo2> newScopeInfo)
|
|
|
|
{
|
2019-07-31 09:19:27 +02:00
|
|
|
mImpl->mScopeInfo = newScopeInfo;
|
|
|
|
}
|
2019-07-31 11:11:01 +02:00
|
|
|
std::shared_ptr<ScopeInfo2> Token::scopeInfo() const
|
|
|
|
{
|
2019-07-31 09:19:27 +02:00
|
|
|
return mImpl->mScopeInfo;
|
|
|
|
}
|
|
|
|
|
2018-12-21 13:51:45 +01:00
|
|
|
TokenImpl::~TokenImpl()
|
|
|
|
{
|
|
|
|
delete mOriginalName;
|
|
|
|
delete mValueType;
|
|
|
|
delete mValues;
|
|
|
|
|
2018-12-21 13:54:59 +01:00
|
|
|
for (auto templateSimplifierPointer : mTemplateSimplifierPointers) {
|
2019-08-10 08:42:12 +02:00
|
|
|
templateSimplifierPointer->token(nullptr);
|
2018-12-21 13:51:45 +01:00
|
|
|
}
|
2019-07-12 11:09:24 +02:00
|
|
|
|
|
|
|
while (mCppcheckAttributes) {
|
|
|
|
struct CppcheckAttributes *c = mCppcheckAttributes;
|
|
|
|
mCppcheckAttributes = mCppcheckAttributes->next;
|
|
|
|
delete c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TokenImpl::setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value)
|
|
|
|
{
|
|
|
|
struct CppcheckAttributes *attr = mCppcheckAttributes;
|
|
|
|
while (attr && attr->type != type)
|
|
|
|
attr = attr->next;
|
|
|
|
if (attr)
|
|
|
|
attr->value = value;
|
|
|
|
else {
|
|
|
|
attr = new CppcheckAttributes;
|
|
|
|
attr->type = type;
|
|
|
|
attr->value = value;
|
|
|
|
attr->next = mCppcheckAttributes;
|
|
|
|
mCppcheckAttributes = attr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TokenImpl::getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const
|
|
|
|
{
|
|
|
|
struct CppcheckAttributes *attr = mCppcheckAttributes;
|
|
|
|
while (attr && attr->type != type)
|
|
|
|
attr = attr->next;
|
|
|
|
if (attr)
|
|
|
|
*value = attr->value;
|
|
|
|
return attr != nullptr;
|
2018-12-21 13:51:45 +01:00
|
|
|
}
|