From e430a11b4950e547825aaf7bd6fc6ad390c5ac53 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Tue, 31 May 2022 23:53:21 -0500 Subject: [PATCH] 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 --- lib/token.cpp | 51 +-------------------------- lib/token.h | 53 +++++++++++++++++----------- lib/tokenize.cpp | 30 ++++++++++++++++ lib/tokenize.h | 1 + lib/tokenlist.cpp | 1 + lib/valueflow.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++ lib/valueflow.h | 2 ++ 7 files changed, 156 insertions(+), 70 deletions(-) diff --git a/lib/token.cpp b/lib/token.cpp index 4a68fc7a4..020344261 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1782,56 +1782,7 @@ void Token::printValueFlow(bool xml, std::ostream &out) const else { if (&value != &tok->mImpl->mValues->front()) out << ","; - if (value.isImpossible()) - 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 0) - out << "@" << value.path; + out << value.toString(); } } if (xml) diff --git a/lib/token.h b/lib/token.h index 8edf52706..324eceaa5 100644 --- a/lib/token.h +++ b/lib/token.h @@ -65,6 +65,8 @@ struct ScopeInfo2 { std::set usingNamespaces; }; +enum class TokenDebug { None, ValueFlow }; + struct TokenImpl { nonneg int mVarId; nonneg int mFileIndex; @@ -127,30 +129,34 @@ struct TokenImpl { /** Bitfield bit count. */ unsigned char mBits; + TokenDebug mDebug; + void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const; TokenImpl() - : mVarId(0) - , mFileIndex(0) - , mLineNumber(0) - , mColumn(0) - , mExprId(0) - , mAstOperand1(nullptr) - , mAstOperand2(nullptr) - , mAstParent(nullptr) - , mScope(nullptr) - , mFunction(nullptr) // Initialize whole union - , mProgressValue(0) - , mIndex(0) - , mOriginalName(nullptr) - , mValueType(nullptr) - , mValues(nullptr) - , mTemplateSimplifierPointers(nullptr) - , mScopeInfo(nullptr) - , mCppcheckAttributes(nullptr) - , mCpp11init(Cpp11init::UNKNOWN) - , mBits(0) + : mVarId(0), + mFileIndex(0), + mLineNumber(0), + mColumn(0), + mExprId(0), + mAstOperand1(nullptr), + mAstOperand2(nullptr), + mAstParent(nullptr), + mScope(nullptr), + mFunction(nullptr) // Initialize whole union + , + mProgressValue(0), + mIndex(0), + mOriginalName(nullptr), + mValueType(nullptr), + mValues(nullptr), + mTemplateSimplifierPointers(nullptr), + mScopeInfo(nullptr), + mCppcheckAttributes(nullptr), + mCpp11init(Cpp11init::UNKNOWN), + mBits(0), + mDebug(TokenDebug::None) {} ~TokenImpl(); @@ -1437,6 +1443,13 @@ public: TokenImpl::Cpp11init isCpp11init() const { return mImpl->mCpp11init; } + + TokenDebug getTokenDebug() const { + return mImpl->mDebug; + } + void setTokenDebug(TokenDebug td) { + mImpl->mDebug = td; + } }; Token* findTypeEnd(Token* tok); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 13546a08c..0b8fd2c32 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4855,6 +4855,9 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) createLinks(); + // Simplify debug intrinsics + simplifyDebug(); + removePragma(); // 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() { for (Token *tok = list.front(); tok; tok = tok->next()) { diff --git a/lib/tokenize.h b/lib/tokenize.h index b1d9c50e8..7125a3081 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -209,6 +209,7 @@ public: */ nonneg int sizeOfType(const Token *type) const; + void simplifyDebug(); /** * Try to determine if function parameter is passed by value by looking * at the function declaration. diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 877cf7033..fa26e6dbe 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -344,6 +344,7 @@ Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last, tok2->tokType(tok->tokType()); tok2->flags(tok->flags()); tok2->varId(tok->varId()); + tok2->setTokenDebug(tok->getTokenDebug()); // Check for links and fix them up if (Token::Match(tok2, "(|[|{")) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 51a3daf3a..4c520e1bf 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -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) : valueType(ValueType::INT), 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"); } +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 { switch (valueType) { @@ -8509,6 +8595,8 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, } valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings); + + valueFlowDebug(tokenlist, errorLogger); } ValueFlow::Value ValueFlow::Value::unknown() diff --git a/lib/valueflow.h b/lib/valueflow.h index a89a6daf0..bca23a8cd 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -251,6 +251,8 @@ namespace ValueFlow { std::string infoString() const; + std::string toString() const; + enum class ValueType { INT, TOK,