diff --git a/lib/token.cpp b/lib/token.cpp index 769cba2e3..a62390545 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1985,6 +1985,8 @@ static bool removeContradiction(std::list& values) continue; if (x.isImpossible() == y.isImpossible()) continue; + if (x.isSymbolicValue() && !ValueFlow::Value::sameToken(x.tokvalue, y.tokvalue)) + continue; if (!x.equalValue(y)) { auto compare = [](const ValueFlow::Value& x, const ValueFlow::Value& y) { return x.compareValue(y, ValueFlow::less{}); @@ -2065,6 +2067,8 @@ static void mergeAdjacent(std::list& values) continue; if (x->valueKind != y->valueKind) continue; + if (x->isSymbolicValue() && !ValueFlow::Value::sameToken(x->tokvalue, y->tokvalue)) + continue; if (x->bound != y->bound) { if (y->bound != ValueFlow::Value::Bound::Point && isAdjacent(*x, *y)) { adjValues.clear(); @@ -2135,20 +2139,30 @@ static void removeContradictions(std::list& values) } } +bool sameValueType(const ValueFlow::Value& x, const ValueFlow::Value& y) +{ + if (x.valueType != y.valueType) + return false; + // Symbolic are the same type if they share the same tokvalue + if (x.isSymbolicValue()) + return x.tokvalue->exprId() == 0 || x.tokvalue->exprId() == y.tokvalue->exprId(); + return true; +} + bool Token::addValue(const ValueFlow::Value &value) { if (value.isKnown() && mImpl->mValues) { // Clear all other values of the same type since value is known mImpl->mValues->remove_if([&](const ValueFlow::Value& x) { - if (x.valueType != value.valueType) - return false; - // Allow multiple known symbolic values - if (x.isSymbolicValue()) - return !x.isKnown(); - return true; + return sameValueType(x, value); }); } + // assert(!value.isPossible() || !mImpl->mValues || std::none_of(mImpl->mValues->begin(), mImpl->mValues->end(), + // [&](const ValueFlow::Value& x) { + // return x.isKnown() && sameValueType(x, value); + // })); + if (mImpl->mValues) { // Don't handle more than 10 values for performance reasons // TODO: add setting? @@ -2389,6 +2403,16 @@ bool Token::hasKnownValue(ValueFlow::Value::ValueType t) const }); } +bool Token::hasKnownSymbolicValue(const Token* tok) const +{ + if (tok->exprId() == 0) + return false; + return mImpl->mValues && + std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { + return value.isSymbolicValue() && value.tokvalue && value.tokvalue->exprId() == tok->exprId(); + }); +} + const ValueFlow::Value* Token::getKnownValue(ValueFlow::Value::ValueType t) const { if (!mImpl->mValues) diff --git a/lib/token.h b/lib/token.h index ddd9db755..687784f04 100644 --- a/lib/token.h +++ b/lib/token.h @@ -1141,6 +1141,7 @@ public: bool hasKnownIntValue() const; bool hasKnownValue() const; bool hasKnownValue(ValueFlow::Value::ValueType t) const; + bool hasKnownSymbolicValue(const Token* tok) const; const ValueFlow::Value* getKnownValue(ValueFlow::Value::ValueType t) const; MathLib::bigint getKnownIntValue() const { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index bc12c06ed..1db761089 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -192,17 +192,17 @@ static void setValueBound(ValueFlow::Value& value, const Token* tok, bool invert } } -static void setConditionalValues(const Token *tok, - bool invert, +static void setConditionalValues(const Token* tok, + bool lhs, MathLib::bigint value, - ValueFlow::Value &true_value, - ValueFlow::Value &false_value) + ValueFlow::Value& true_value, + ValueFlow::Value& false_value) { if (Token::Match(tok, "==|!=|>=|<=")) { true_value = ValueFlow::Value{tok, value}; const char* greaterThan = ">="; const char* lessThan = "<="; - if (invert) + if (lhs) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { false_value = ValueFlow::Value{tok, value - 1}; @@ -214,7 +214,7 @@ static void setConditionalValues(const Token *tok, } else { const char* greaterThan = ">"; const char* lessThan = "<"; - if (invert) + if (lhs) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { true_value = ValueFlow::Value{tok, value + 1}; @@ -224,8 +224,8 @@ static void setConditionalValues(const Token *tok, false_value = ValueFlow::Value{tok, value}; } } - setValueBound(true_value, tok, invert); - setValueBound(false_value, tok, !invert); + setValueBound(true_value, tok, lhs); + setValueBound(false_value, tok, !lhs); } static bool isSaturated(MathLib::bigint value) @@ -4091,12 +4091,17 @@ static bool isTruncated(const ValueType* src, const ValueType* dst, const Settin return false; } +static void setSymbolic(ValueFlow::Value& value, const Token* tok) +{ + value.valueType = ValueFlow::Value::ValueType::SYMBOLIC; + value.tokvalue = tok; +} + static ValueFlow::Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0) { ValueFlow::Value value; value.setKnown(); - value.valueType = ValueFlow::Value::ValueType::SYMBOLIC; - value.tokvalue = tok; + setSymbolic(value, tok); value.intvalue = delta; return value; } @@ -4177,6 +4182,341 @@ static void valueFlowSymbolicAbs(TokenList* tokenlist, SymbolDatabase* symboldat } } +template +static const ValueFlow::Value* getCompareValue(const std::list& values, + Predicate pred, + Compare compare) +{ + const ValueFlow::Value* result = nullptr; + for (const ValueFlow::Value& value : values) { + if (!pred(value)) + continue; + if (result) + result = &std::min(value, *result, [compare](const ValueFlow::Value& x, const ValueFlow::Value& y) { + return compare(x.intvalue, y.intvalue); + }); + else + result = &value; + } + return result; +} + +struct Interval { + std::vector minvalue = {}; + std::vector maxvalue = {}; + std::vector minRef = {}; + std::vector maxRef = {}; + + void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + minvalue = {x}; + if (ref) + minRef = {ref}; + } + + void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + maxvalue = {x}; + if (ref) + maxRef = {ref}; + } + + bool isLessThan(MathLib::bigint x, std::vector* ref = nullptr) const + { + if (!this->maxvalue.empty() && this->maxvalue.front() < x) { + if (ref) + *ref = maxRef; + return true; + } + return false; + } + + bool isGreaterThan(MathLib::bigint x, std::vector* ref = nullptr) const + { + if (!this->minvalue.empty() && this->minvalue.front() > x) { + if (ref) + *ref = minRef; + return true; + } + return false; + } + + bool isScalar() const { + return minvalue.size() == 1 && minvalue == maxvalue; + } + + bool empty() const { + return minvalue.empty() && maxvalue.empty(); + } + + bool isScalarOrEmpty() const { + return empty() || isScalar(); + } + + MathLib::bigint getScalar() const + { + assert(isScalar()); + return minvalue.front(); + } + + std::vector getScalarRef() const + { + assert(isScalar()); + if (!minRef.empty()) + return minRef; + if (!maxRef.empty()) + return maxRef; + return {}; + } + + static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + Interval result; + result.setMinValue(x, ref); + result.setMaxValue(x, ref); + return result; + } + + template + static Interval fromValues(const std::list& values, Predicate predicate) + { + Interval result; + const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less{}); + if (minValue) { + if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper) + result.setMinValue(minValue->intvalue + 1, minValue); + if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower) + result.setMinValue(minValue->intvalue, minValue); + if (minValue->isKnown()) + return Interval::fromInt(minValue->intvalue, minValue); + } + const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater{}); + if (maxValue) { + if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower) + result.setMaxValue(maxValue->intvalue - 1, maxValue); + if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper) + result.setMaxValue(maxValue->intvalue, maxValue); + assert(!maxValue->isKnown()); + } + return result; + } + + static Interval fromValues(const std::list& values) + { + return Interval::fromValues(values, [](const ValueFlow::Value&) { + return true; + }); + } + + template + static std::vector apply(const std::vector& x, + const std::vector& y, + F f) + { + if (x.empty()) + return {}; + if (y.empty()) + return {}; + return {f(x.front(), y.front())}; + } + + static std::vector merge(std::vector x, + const std::vector& y) + { + x.insert(x.end(), y.begin(), y.end()); + return x; + } + + friend Interval operator-(const Interval& lhs, const Interval& rhs) + { + Interval result; + result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus{}); + result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus{}); + if (!result.minvalue.empty()) + result.minRef = merge(lhs.minRef, rhs.maxRef); + if (!result.maxvalue.empty()) + result.maxRef = merge(lhs.maxRef, rhs.minRef); + return result; + } + + static std::vector equal(const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + if (!lhs.isScalar()) + return {}; + if (!rhs.isScalar()) + return {}; + if (ref) + *ref = merge(lhs.minRef, rhs.minRef); + return {lhs.minvalue == rhs.minvalue}; + } + + static std::vector compare(const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + Interval diff = lhs - rhs; + if (diff.isGreaterThan(0, ref)) + return {1}; + if (diff.isLessThan(0, ref)) + return {-1}; + std::vector eq = Interval::equal(lhs, rhs, ref); + if (!eq.empty() && eq.front() != 0) + return {0}; + return {}; + } +}; + +void addToErrorPath(ValueFlow::Value& value, const std::vector& refs) +{ + for (const ValueFlow::Value* ref : refs) { + value.errorPath.insert(value.errorPath.end(), ref->errorPath.begin(), ref->errorPath.end()); + } +} + +void setValueKind(ValueFlow::Value& value, const std::vector& refs) +{ + bool isPossible = false; + bool isInconclusive = false; + for (const ValueFlow::Value* ref : refs) { + if (ref->isPossible()) + isPossible = true; + if (ref->isInconclusive()) + isInconclusive = true; + } + if (isInconclusive) + value.setInconclusive(); + else if (isPossible) + value.setPossible(); + else + value.setKnown(); +} + +struct InferModel { + virtual bool match(const ValueFlow::Value& value) const = 0; + virtual ValueFlow::Value yield(MathLib::bigint value) const = 0; + virtual ~InferModel() {} +}; + +static bool inferNotEqual(const std::list& values, MathLib::bigint x) +{ + return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& value) { + return value.isImpossible() && value.intvalue == x; + }); +} + +static std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + std::list rhsValues) +{ + std::vector result; + auto notMatch = [&](const ValueFlow::Value& value) { + return !model->match(value); + }; + lhsValues.remove_if(notMatch); + rhsValues.remove_if(notMatch); + if (lhsValues.empty() || rhsValues.empty()) + return result; + + Interval lhs = Interval::fromValues(lhsValues); + Interval rhs = Interval::fromValues(rhsValues); + + if (op == "-") { + Interval diff = lhs - rhs; + if (diff.isScalar()) { + std::vector refs = diff.getScalarRef(); + ValueFlow::Value value(diff.getScalar()); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(value); + } else { + if (!diff.minvalue.empty()) { + ValueFlow::Value value(diff.minvalue.front() - 1); + value.setImpossible(); + value.bound = ValueFlow::Value::Bound::Lower; + addToErrorPath(value, diff.minRef); + result.push_back(value); + } + if (!diff.maxvalue.empty()) { + ValueFlow::Value value(diff.maxvalue.front() + 1); + value.setImpossible(); + value.bound = ValueFlow::Value::Bound::Upper; + addToErrorPath(value, diff.maxRef); + result.push_back(value); + } + } + } else if ((op == "!=" || op == "==") && lhs.isScalarOrEmpty() && rhs.isScalarOrEmpty()) { + if (lhs.isScalar() && rhs.isScalar()) { + std::vector refs = Interval::merge(lhs.getScalarRef(), rhs.getScalarRef()); + ValueFlow::Value value(calculate(op, lhs.getScalar(), rhs.getScalar())); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(value); + } else { + std::vector refs; + if (lhs.isScalar() && inferNotEqual(rhsValues, lhs.getScalar())) + refs = lhs.getScalarRef(); + else if (rhs.isScalar() && inferNotEqual(lhsValues, rhs.getScalar())) + refs = rhs.getScalarRef(); + if (!refs.empty()) { + ValueFlow::Value value(op == "!="); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(value); + } + } + } else { + std::vector refs; + std::vector r = Interval::compare(lhs, rhs, &refs); + if (!r.empty()) { + int x = r.front(); + ValueFlow::Value value(calculate(op, x, 0)); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(value); + } + } + + return result; +} + +static std::vector infer(const ValuePtr& model, + const std::string& op, + MathLib::bigint lhs, + std::list rhsValues) +{ + return infer(model, op, {model->yield(lhs)}, std::move(rhsValues)); +} + +static std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + MathLib::bigint rhs) +{ + return infer(model, op, std::move(lhsValues), {model->yield(rhs)}); +} + +struct SymbolicInferModel : InferModel { + const Token* expr; + explicit SymbolicInferModel(const Token* tok) : expr(tok) { + assert(expr->exprId() != 0); + } + virtual bool match(const ValueFlow::Value& value) const OVERRIDE + { + return value.isSymbolicValue() && value.tokvalue && value.tokvalue->exprId() == expr->exprId(); + } + virtual ValueFlow::Value yield(MathLib::bigint value) const OVERRIDE + { + ValueFlow::Value result(value); + result.valueType = ValueFlow::Value::ValueType::SYMBOLIC; + result.tokvalue = expr; + result.setKnown(); + return result; + } +}; + static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { @@ -4189,6 +4529,10 @@ static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symbold continue; if (!tok->astOperand2()) continue; + if (tok->astOperand1()->exprId() == 0) + continue; + if (tok->astOperand2()->exprId() == 0) + continue; if (tok->astOperand1()->hasKnownIntValue()) continue; if (tok->astOperand2()->hasKnownIntValue()) @@ -4198,40 +4542,15 @@ static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symbold if (astIsFloat(tok->astOperand2(), false)) continue; - MathLib::bigint rhsvalue = 0; - const ValueFlow::Value* rhs = - ValueFlow::findValue(tok->astOperand2()->values(), nullptr, [&](const ValueFlow::Value& v) { - return v.isSymbolicValue() && v.tokvalue && v.tokvalue->exprId() == tok->astOperand1()->exprId(); - }); - if (rhs) - rhsvalue = rhs->intvalue; - MathLib::bigint lhsvalue = 0; - const ValueFlow::Value* lhs = - ValueFlow::findValue(tok->astOperand1()->values(), nullptr, [&](const ValueFlow::Value& v) { - return v.isSymbolicValue() && v.tokvalue && v.tokvalue->exprId() == tok->astOperand2()->exprId(); - }); - if (lhs) - lhsvalue = lhs->intvalue; - if (!lhs && !rhs) - continue; - - ValueFlow::Value value{calculate(tok->str(), lhsvalue, rhsvalue)}; - if (lhs && rhs) { - if (lhs->isImpossible() != rhs->isImpossible()) - continue; - combineValueProperties(*lhs, *rhs, &value); - } else if (lhs) { - value.valueKind = lhs->valueKind; - value.errorPath = lhs->errorPath; - } else if (rhs) { - value.valueKind = rhs->valueKind; - value.errorPath = rhs->errorPath; + SymbolicInferModel leftModel{tok->astOperand1()}; + std::vector values = infer(leftModel, tok->str(), 0, tok->astOperand2()->values()); + if (values.empty()) { + SymbolicInferModel rightModel{tok->astOperand2()}; + values = infer(rightModel, tok->str(), tok->astOperand1()->values(), 0); } - if (Token::Match(tok, "%comp%") && value.isImpossible() && value.intvalue != 0) { - value.intvalue = 0; - value.setKnown(); + for (const ValueFlow::Value& value : values) { + setTokenValue(tok, value, tokenlist->getSettings()); } - setTokenValue(tok, value, tokenlist->getSettings()); } } } @@ -5123,6 +5442,41 @@ static void valueFlowInferCondition(TokenList* tokenlist, } } +struct SymbolicConditionHandler : SimpleConditionHandler { + virtual std::vector parse(const Token* tok, const Settings*) const OVERRIDE + { + if (!Token::Match(tok, "%comp%")) + return {}; + if (tok->hasKnownIntValue()) + return {}; + if (!tok->astOperand1() || tok->astOperand1()->hasKnownIntValue() || tok->astOperand1()->isLiteral()) + return {}; + if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral()) + return {}; + + std::vector result; + for (int i = 0; i < 2; i++) { + const bool lhs = i == 0; + const Token* vartok = lhs ? tok->astOperand1() : tok->astOperand2(); + const Token* valuetok = lhs ? tok->astOperand2() : tok->astOperand1(); + if (valuetok->hasKnownSymbolicValue(vartok)) + continue; + ValueFlow::Value true_value; + ValueFlow::Value false_value; + setConditionalValues(tok, !lhs, 0, true_value, false_value); + setSymbolic(true_value, valuetok); + setSymbolic(false_value, valuetok); + + Condition cond; + cond.true_values = {true_value}; + cond.false_values = {false_value}; + cond.vartok = vartok; + result.push_back(cond); + } + return result; + } +}; + static bool valueFlowForLoop2(const Token *tok, ProgramMemory *memory1, ProgramMemory *memory2, @@ -7092,6 +7446,16 @@ const char* ValueFlow::Value::toString(LifetimeKind lifetimeKind) return ""; } +bool ValueFlow::Value::sameToken(const Token* tok1, const Token* tok2) +{ + if (tok1 == tok2) + return true; + if (!tok1) + return false; + if (tok1->exprId() == 0 || tok2->exprId() == 0) + return false; + return tok1->exprId() == tok2->exprId(); +} const char* ValueFlow::Value::toString(LifetimeScope lifetimeScope) { switch (lifetimeScope) { @@ -7162,6 +7526,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, values = getTotalValues(tokenlist); valueFlowImpossibleValues(tokenlist, settings); valueFlowSymbolicAbs(tokenlist, symboldatabase); + valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); valueFlowSymbolicInfer(tokenlist, symboldatabase); valueFlowArrayBool(tokenlist); valueFlowRightShift(tokenlist, settings); diff --git a/lib/valueflow.h b/lib/valueflow.h index 257c2efc0..ebc272c1d 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -138,7 +138,7 @@ namespace ValueFlow { return false; break; case ValueType::SYMBOLIC: - if (tokvalue != rhs.tokvalue) + if (!sameToken(tokvalue, rhs.tokvalue)) return false; if (intvalue != rhs.intvalue) return false; @@ -188,7 +188,7 @@ namespace ValueFlow { template bool compareValue(const Value& rhs, Compare compare) const { assert((!this->isSymbolicValue() && !rhs.isSymbolicValue()) || - (this->valueType == rhs.valueType && this->tokvalue == rhs.tokvalue)); + (this->valueType == rhs.valueType && sameToken(this->tokvalue, rhs.tokvalue))); bool result = false; visitValue( *this, @@ -425,6 +425,8 @@ namespace ValueFlow { bool errorSeverity() const { return !condition && !defaultArg; } + + static bool sameToken(const Token* tok1, const Token* tok2); }; /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). diff --git a/test/testcondition.cpp b/test/testcondition.cpp index f425dd2b1..417aab5b8 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -951,7 +951,7 @@ private: " }\n" " return false;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'c!=a' is always false\n", errout.str()); } void incorrectLogicOperator2() { @@ -3773,6 +3773,20 @@ private: "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'y[0]<42' is always true\n", errout.str()); + check("void f(int x, int y) {\n" + " if(x < y && x < 42) {\n" + " --x;\n" + " if(x == y) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'x==y' is always false\n", errout.str()); + + check("void f(bool a, bool b) { if (a == b && a && !b){} }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!b' is always false\n", errout.str()); + + check("bool f(bool a, bool b) { if(a && b && (!a)){} }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!a' is always false\n", errout.str()); + check("struct a {\n" " a *b() const;\n" "} c;\n" @@ -3793,6 +3807,17 @@ private: " return N;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("void f(int i, int j) {\n" + " if (i < j) {\n" + " i++;\n" + " if (i >= j)\n" + " return;\n" + " i++;\n" + " if (i >= j) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void alwaysTrueInfer() { diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index b4c027dea..20b6af4b5 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -2398,7 +2398,8 @@ private: "std :: vector < CharacterConversion > ( ) . swap ( c2c ) ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS( + "[test.cpp:4]: (debug) valueflow.cpp:4635:(valueFlow) bailout: variable 'it' used in loop\n", errout.str()); } void simplifyTypedef117() { // #6507 diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 9c1de322a..3ff7584a7 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -233,6 +233,27 @@ private: return false; } + bool testValueOfXImpossible(const char code[], unsigned int linenr, const std::string& expr, int value) + { + // Tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) { + if (tok->str() == "x" && tok->linenr() == linenr) { + for (const ValueFlow::Value& val : tok->values()) { + if (!val.isSymbolicValue()) + continue; + if (val.isImpossible() && val.intvalue == value && val.tokvalue->expressionString() == expr) + return true; + } + } + } + + return false; + } + bool testValueOfXInconclusive(const char code[], unsigned int linenr, int value) { // Tokenize.. Tokenizer tokenizer(&settings, this); @@ -271,6 +292,26 @@ private: return false; } + bool testValueOfX(const char code[], unsigned int linenr, const std::string& expr, int value) + { + // Tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) { + if (tok->str() == "x" && tok->linenr() == linenr) { + for (const ValueFlow::Value& v : tok->values()) { + if (v.isSymbolicValue() && !v.isImpossible() && v.intvalue == value && + v.tokvalue->expressionString() == expr) + return true; + } + } + } + + return false; + } + bool testValueOfX(const char code[], unsigned int linenr, float value, float diff) { // Tokenize.. Tokenizer tokenizer(&settings, this); @@ -2583,7 +2624,7 @@ private: " while (11 != (x = dostuff()) && y) {}\n" " a = x;\n" "}"; - TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 11)); + ASSERT_EQUALS(true, testValueOfX(code, 3U, 11)); code = "void f(int x) {\n" " while (x = dostuff()) {}\n" @@ -6087,6 +6128,61 @@ private: " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, "y", 0)); + + code = "int f(int i, int j) {\n" + " if (i == j) {\n" + " int x = i - j;\n" + " return x;\n" + " }\n" + " return 0;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); + + code = "void f(int x, int y) {\n" + " if (x == y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, "y", 0)); + + code = "void f(int x, int y) {\n" + " if (x != y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "y", 0)); + + code = "void f(int x, int y) {\n" + " if (x < y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, "y", -1)); + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "y", 0)); + + code = "void f(int x, int y) {\n" + " if (x <= y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, "y", 0)); + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "y", 1)); + + code = "void f(int x, int y) {\n" + " if (x > y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, "y", 1)); + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "y", 0)); + + code = "void f(int x, int y) {\n" + " if (x >= y) {\n" + " int a = x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, "y", 0)); + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "y", -1)); } };