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:
Paul Fultz II 2022-05-31 23:53:21 -05:00 committed by GitHub
parent 703396e549
commit e430a11b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 156 additions and 70 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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()) {

View File

@ -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.

View File

@ -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, "(|[|{"))

View File

@ -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()

View File

@ -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,