Add debug_valueflow instrinsic to show valueflow values and its error path (#4159)
* Add debug_valueflow instrinsic to show valueflow values and its error path * Format
This commit is contained in:
parent
703396e549
commit
e430a11b49
|
@ -1782,56 +1782,7 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
else {
|
else {
|
||||||
if (&value != &tok->mImpl->mValues->front())
|
if (&value != &tok->mImpl->mValues->front())
|
||||||
out << ",";
|
out << ",";
|
||||||
if (value.isImpossible())
|
out << value.toString();
|
||||||
out << "!";
|
|
||||||
if (value.bound == ValueFlow::Value::Bound::Lower)
|
|
||||||
out << ">=";
|
|
||||||
if (value.bound == ValueFlow::Value::Bound::Upper)
|
|
||||||
out << "<=";
|
|
||||||
switch (value.valueType) {
|
|
||||||
case ValueFlow::Value::ValueType::INT:
|
|
||||||
out << value.intvalue;
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::TOK:
|
|
||||||
out << value.tokvalue->str();
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::FLOAT:
|
|
||||||
out << value.floatValue;
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::MOVED:
|
|
||||||
out << ValueFlow::Value::toString(value.moveKind);
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::UNINIT:
|
|
||||||
out << "Uninit";
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::BUFFER_SIZE:
|
|
||||||
case ValueFlow::Value::ValueType::CONTAINER_SIZE:
|
|
||||||
out << "size=" << value.intvalue;
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::ITERATOR_START:
|
|
||||||
out << "start=" << value.intvalue;
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::ITERATOR_END:
|
|
||||||
out << "end=" << value.intvalue;
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::LIFETIME:
|
|
||||||
out << "lifetime[" << ValueFlow::Value::toString(value.lifetimeKind) << "]=("
|
|
||||||
<< value.tokvalue->expressionString() << ")";
|
|
||||||
break;
|
|
||||||
case ValueFlow::Value::ValueType::SYMBOLIC:
|
|
||||||
out << "symbolic=(" << value.tokvalue->expressionString();
|
|
||||||
if (value.intvalue > 0)
|
|
||||||
out << "+" << value.intvalue;
|
|
||||||
else if (value.intvalue < 0)
|
|
||||||
out << "-" << -value.intvalue;
|
|
||||||
out << ")";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (value.indirect > 0)
|
|
||||||
for (int i=0; i<value.indirect; i++)
|
|
||||||
out << "*";
|
|
||||||
if (value.path > 0)
|
|
||||||
out << "@" << value.path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xml)
|
if (xml)
|
||||||
|
|
53
lib/token.h
53
lib/token.h
|
@ -65,6 +65,8 @@ struct ScopeInfo2 {
|
||||||
std::set<std::string> usingNamespaces;
|
std::set<std::string> usingNamespaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class TokenDebug { None, ValueFlow };
|
||||||
|
|
||||||
struct TokenImpl {
|
struct TokenImpl {
|
||||||
nonneg int mVarId;
|
nonneg int mVarId;
|
||||||
nonneg int mFileIndex;
|
nonneg int mFileIndex;
|
||||||
|
@ -127,30 +129,34 @@ struct TokenImpl {
|
||||||
/** Bitfield bit count. */
|
/** Bitfield bit count. */
|
||||||
unsigned char mBits;
|
unsigned char mBits;
|
||||||
|
|
||||||
|
TokenDebug mDebug;
|
||||||
|
|
||||||
void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value);
|
void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value);
|
||||||
bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const;
|
bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const;
|
||||||
|
|
||||||
TokenImpl()
|
TokenImpl()
|
||||||
: mVarId(0)
|
: mVarId(0),
|
||||||
, mFileIndex(0)
|
mFileIndex(0),
|
||||||
, mLineNumber(0)
|
mLineNumber(0),
|
||||||
, mColumn(0)
|
mColumn(0),
|
||||||
, mExprId(0)
|
mExprId(0),
|
||||||
, mAstOperand1(nullptr)
|
mAstOperand1(nullptr),
|
||||||
, mAstOperand2(nullptr)
|
mAstOperand2(nullptr),
|
||||||
, mAstParent(nullptr)
|
mAstParent(nullptr),
|
||||||
, mScope(nullptr)
|
mScope(nullptr),
|
||||||
, mFunction(nullptr) // Initialize whole union
|
mFunction(nullptr) // Initialize whole union
|
||||||
, mProgressValue(0)
|
,
|
||||||
, mIndex(0)
|
mProgressValue(0),
|
||||||
, mOriginalName(nullptr)
|
mIndex(0),
|
||||||
, mValueType(nullptr)
|
mOriginalName(nullptr),
|
||||||
, mValues(nullptr)
|
mValueType(nullptr),
|
||||||
, mTemplateSimplifierPointers(nullptr)
|
mValues(nullptr),
|
||||||
, mScopeInfo(nullptr)
|
mTemplateSimplifierPointers(nullptr),
|
||||||
, mCppcheckAttributes(nullptr)
|
mScopeInfo(nullptr),
|
||||||
, mCpp11init(Cpp11init::UNKNOWN)
|
mCppcheckAttributes(nullptr),
|
||||||
, mBits(0)
|
mCpp11init(Cpp11init::UNKNOWN),
|
||||||
|
mBits(0),
|
||||||
|
mDebug(TokenDebug::None)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~TokenImpl();
|
~TokenImpl();
|
||||||
|
@ -1437,6 +1443,13 @@ public:
|
||||||
TokenImpl::Cpp11init isCpp11init() const {
|
TokenImpl::Cpp11init isCpp11init() const {
|
||||||
return mImpl->mCpp11init;
|
return mImpl->mCpp11init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenDebug getTokenDebug() const {
|
||||||
|
return mImpl->mDebug;
|
||||||
|
}
|
||||||
|
void setTokenDebug(TokenDebug td) {
|
||||||
|
mImpl->mDebug = td;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Token* findTypeEnd(Token* tok);
|
Token* findTypeEnd(Token* tok);
|
||||||
|
|
|
@ -4855,6 +4855,9 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
|
||||||
|
|
||||||
createLinks();
|
createLinks();
|
||||||
|
|
||||||
|
// Simplify debug intrinsics
|
||||||
|
simplifyDebug();
|
||||||
|
|
||||||
removePragma();
|
removePragma();
|
||||||
|
|
||||||
// Simplify the C alternative tokens (and, or, etc.)
|
// Simplify the C alternative tokens (and, or, etc.)
|
||||||
|
@ -11333,6 +11336,33 @@ void Tokenizer::simplifyKeyword()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Token* setTokenDebug(Token* start, TokenDebug td)
|
||||||
|
{
|
||||||
|
if (!start->link())
|
||||||
|
return nullptr;
|
||||||
|
Token* end = start->link();
|
||||||
|
start->deleteThis();
|
||||||
|
for (Token* tok = start; tok != end; tok = tok->next()) {
|
||||||
|
tok->setTokenDebug(td);
|
||||||
|
}
|
||||||
|
end->deleteThis();
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tokenizer::simplifyDebug()
|
||||||
|
{
|
||||||
|
if (!mSettings->debugnormal && !mSettings->debugwarnings)
|
||||||
|
return;
|
||||||
|
for (Token* tok = list.front(); tok; tok = tok->next()) {
|
||||||
|
if (!Token::Match(tok, "%name% ("))
|
||||||
|
continue;
|
||||||
|
if (Token::simpleMatch(tok, "debug_valueflow")) {
|
||||||
|
tok->deleteThis();
|
||||||
|
tok = setTokenDebug(tok, TokenDebug::ValueFlow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Tokenizer::simplifyAssignmentInFunctionCall()
|
void Tokenizer::simplifyAssignmentInFunctionCall()
|
||||||
{
|
{
|
||||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||||
|
|
|
@ -209,6 +209,7 @@ public:
|
||||||
*/
|
*/
|
||||||
nonneg int sizeOfType(const Token *type) const;
|
nonneg int sizeOfType(const Token *type) const;
|
||||||
|
|
||||||
|
void simplifyDebug();
|
||||||
/**
|
/**
|
||||||
* Try to determine if function parameter is passed by value by looking
|
* Try to determine if function parameter is passed by value by looking
|
||||||
* at the function declaration.
|
* at the function declaration.
|
||||||
|
|
|
@ -344,6 +344,7 @@ Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last,
|
||||||
tok2->tokType(tok->tokType());
|
tok2->tokType(tok->tokType());
|
||||||
tok2->flags(tok->flags());
|
tok2->flags(tok->flags());
|
||||||
tok2->varId(tok->varId());
|
tok2->varId(tok->varId());
|
||||||
|
tok2->setTokenDebug(tok->getTokenDebug());
|
||||||
|
|
||||||
// Check for links and fix them up
|
// Check for links and fix them up
|
||||||
if (Token::Match(tok2, "(|[|{"))
|
if (Token::Match(tok2, "(|[|{"))
|
||||||
|
|
|
@ -8299,6 +8299,36 @@ static void valueFlowUnknownFunctionReturn(TokenList *tokenlist, const Settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void valueFlowDebug(TokenList* tokenlist, ErrorLogger* errorLogger)
|
||||||
|
{
|
||||||
|
if (!tokenlist->getSettings()->debugnormal && !tokenlist->getSettings()->debugwarnings)
|
||||||
|
return;
|
||||||
|
for (Token* tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
if (tok->getTokenDebug() != TokenDebug::ValueFlow)
|
||||||
|
continue;
|
||||||
|
for (const ValueFlow::Value& v : tok->values()) {
|
||||||
|
std::string kind;
|
||||||
|
switch (v.valueKind) {
|
||||||
|
|
||||||
|
case ValueFlow::Value::ValueKind::Impossible:
|
||||||
|
case ValueFlow::Value::ValueKind::Known:
|
||||||
|
kind = "always";
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueKind::Inconclusive:
|
||||||
|
kind = "inconclusive";
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueKind::Possible:
|
||||||
|
kind = "possible";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::string msg = "The value is " + kind + " " + v.toString();
|
||||||
|
ErrorPath errorPath = v.errorPath;
|
||||||
|
errorPath.emplace_back(tok, "");
|
||||||
|
errorLogger->reportErr({errorPath, tokenlist, Severity::debug, "valueFlow", msg, CWE{0}, Certainty::normal});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ValueFlow::Value::Value(const Token* c, long long val, Bound b)
|
ValueFlow::Value::Value(const Token* c, long long val, Bound b)
|
||||||
: valueType(ValueType::INT),
|
: valueType(ValueType::INT),
|
||||||
bound(b),
|
bound(b),
|
||||||
|
@ -8331,6 +8361,62 @@ void ValueFlow::Value::assumeCondition(const Token* tok)
|
||||||
errorPath.emplace_back(tok, "Assuming that condition '" + tok->expressionString() + "' is not redundant");
|
errorPath.emplace_back(tok, "Assuming that condition '" + tok->expressionString() + "' is not redundant");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ValueFlow::Value::toString() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
if (this->isImpossible())
|
||||||
|
ss << "!";
|
||||||
|
if (this->bound == ValueFlow::Value::Bound::Lower)
|
||||||
|
ss << ">=";
|
||||||
|
if (this->bound == ValueFlow::Value::Bound::Upper)
|
||||||
|
ss << "<=";
|
||||||
|
switch (this->valueType) {
|
||||||
|
case ValueFlow::Value::ValueType::INT:
|
||||||
|
ss << this->intvalue;
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::TOK:
|
||||||
|
ss << this->tokvalue->str();
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::FLOAT:
|
||||||
|
ss << this->floatValue;
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::MOVED:
|
||||||
|
ss << ValueFlow::Value::toString(this->moveKind);
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::UNINIT:
|
||||||
|
ss << "Uninit";
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::BUFFER_SIZE:
|
||||||
|
case ValueFlow::Value::ValueType::CONTAINER_SIZE:
|
||||||
|
ss << "size=" << this->intvalue;
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::ITERATOR_START:
|
||||||
|
ss << "start=" << this->intvalue;
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::ITERATOR_END:
|
||||||
|
ss << "end=" << this->intvalue;
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::LIFETIME:
|
||||||
|
ss << "lifetime[" << ValueFlow::Value::toString(this->lifetimeKind) << "]=("
|
||||||
|
<< this->tokvalue->expressionString() << ")";
|
||||||
|
break;
|
||||||
|
case ValueFlow::Value::ValueType::SYMBOLIC:
|
||||||
|
ss << "symbolic=(" << this->tokvalue->expressionString();
|
||||||
|
if (this->intvalue > 0)
|
||||||
|
ss << "+" << this->intvalue;
|
||||||
|
else if (this->intvalue < 0)
|
||||||
|
ss << "-" << -this->intvalue;
|
||||||
|
ss << ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this->indirect > 0)
|
||||||
|
for (int i = 0; i < this->indirect; i++)
|
||||||
|
ss << "*";
|
||||||
|
if (this->path > 0)
|
||||||
|
ss << "@" << this->path;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string ValueFlow::Value::infoString() const
|
std::string ValueFlow::Value::infoString() const
|
||||||
{
|
{
|
||||||
switch (valueType) {
|
switch (valueType) {
|
||||||
|
@ -8509,6 +8595,8 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
}
|
}
|
||||||
|
|
||||||
valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings);
|
valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings);
|
||||||
|
|
||||||
|
valueFlowDebug(tokenlist, errorLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueFlow::Value ValueFlow::Value::unknown()
|
ValueFlow::Value ValueFlow::Value::unknown()
|
||||||
|
|
|
@ -251,6 +251,8 @@ namespace ValueFlow {
|
||||||
|
|
||||||
std::string infoString() const;
|
std::string infoString() const;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
enum class ValueType {
|
enum class ValueType {
|
||||||
INT,
|
INT,
|
||||||
TOK,
|
TOK,
|
||||||
|
|
Loading…
Reference in New Issue