diff --git a/Makefile b/Makefile
index 8fc112a73..51631663a 100644
--- a/Makefile
+++ b/Makefile
@@ -542,7 +542,7 @@ $(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml2/tinyxml2.h lib/conf
$(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/preprocessor.o $(libcppdir)/preprocessor.cpp
-$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h
+$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp
$(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
@@ -578,7 +578,7 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/
$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/utils.o $(libcppdir)/utils.cpp
-$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
+$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
diff --git a/lib/calculate.h b/lib/calculate.h
new file mode 100644
index 000000000..599662661
--- /dev/null
+++ b/lib/calculate.h
@@ -0,0 +1,120 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2021 Cppcheck team.
+ *
+ * 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
+ * along with this program. If not, see .
+ */
+
+#ifndef calculateH
+#define calculateH
+
+#include "mathlib.h"
+#include
+
+template
+bool isEqual(T x, T y)
+{
+ return x == y;
+}
+
+inline bool isEqual(double x, double y)
+{
+ const double diff = (x > y) ? x - y : y - x;
+ return !((diff / 2) < diff);
+}
+inline bool isEqual(float x, float y)
+{
+ return isEqual(double{x}, double{y});
+}
+
+template
+bool isZero(T x)
+{
+ return isEqual(x, T(0));
+}
+
+template
+R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
+{
+ auto wrap = [](T z) {
+ return R{z};
+ };
+ switch (MathLib::encodeMultiChar(s)) {
+ case '+':
+ return wrap(x + y);
+ case '-':
+ return wrap(x - y);
+ case '*':
+ return wrap(x * y);
+ case '/':
+ if (isZero(y)) {
+ if (error)
+ *error = true;
+ return R{};
+ }
+ return wrap(x / y);
+ case '%':
+ if (isZero(y)) {
+ if (error)
+ *error = true;
+ return R{};
+ }
+ return wrap(MathLib::bigint(x) % MathLib::bigint(y));
+ case '&':
+ return wrap(MathLib::bigint(x) & MathLib::bigint(y));
+ case '|':
+ return wrap(MathLib::bigint(x) | MathLib::bigint(y));
+ case '^':
+ return wrap(MathLib::bigint(x) ^ MathLib::bigint(y));
+ case '>':
+ return wrap(x > y);
+ case '<':
+ return wrap(x < y);
+ case '<<':
+ if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
+ if (error)
+ *error = true;
+ return R{};
+ }
+ return wrap(MathLib::bigint(x) << MathLib::bigint(y));
+ case '>>':
+ if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
+ if (error)
+ *error = true;
+ return R{};
+ }
+ return wrap(MathLib::bigint(x) >> MathLib::bigint(y));
+ case '&&':
+ return wrap(!isZero(x) && !isZero(y));
+ case '||':
+ return wrap(!isZero(x) || !isZero(y));
+ case '==':
+ return wrap(isEqual(x, y));
+ case '!=':
+ return wrap(!isEqual(x, y));
+ case '>=':
+ return wrap(x >= y);
+ case '<=':
+ return wrap(x <= y);
+ }
+ throw InternalError(nullptr, "Unknown operator: " + s);
+}
+
+template
+T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
+{
+ return calculate(s, x, y, error);
+}
+
+#endif
diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp
index 766141cfc..333f0ab54 100644
--- a/lib/programmemory.cpp
+++ b/lib/programmemory.cpp
@@ -1,13 +1,16 @@
#include "programmemory.h"
#include "astutils.h"
+#include "calculate.h"
+#include "errortypes.h"
#include "mathlib.h"
-#include "symboldatabase.h"
#include "settings.h"
+#include "symboldatabase.h"
#include "token.h"
#include "valueflow.h"
#include
#include
+#include
#include
#include
#include
@@ -36,9 +39,12 @@ bool ProgramMemory::getIntValue(nonneg int exprid, MathLib::bigint* result) cons
return false;
}
-void ProgramMemory::setIntValue(nonneg int exprid, MathLib::bigint value)
+void ProgramMemory::setIntValue(nonneg int exprid, MathLib::bigint value, bool impossible)
{
- values[exprid] = ValueFlow::Value(value);
+ ValueFlow::Value v(value);
+ if (impossible)
+ v.setImpossible();
+ values[exprid] = v;
}
bool ProgramMemory::getTokValue(nonneg int exprid, const Token** result) const
@@ -175,10 +181,6 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
return std::vector{};
};
if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
- if (then && !Token::Match(tok, "==|>=|<=|<|>"))
- return;
- if (!then && !Token::Match(tok, "<|>|!="))
- return;
ValueFlow::Value truevalue;
ValueFlow::Value falsevalue;
const Token* vartok = parseCompareInt(tok, truevalue, falsevalue, eval);
@@ -191,8 +193,7 @@ void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Toke
if (endTok && isExpressionChanged(vartok, tok->next(), endTok, settings, true))
return;
bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then);
- if (!impossible)
- pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue);
+ pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, impossible);
const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE);
if (containerTok)
pm.setContainerSizeValue(containerTok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, !impossible);
@@ -456,294 +457,234 @@ ProgramMemory getProgramMemory(const Token* tok, nonneg int exprid, const ValueF
return programMemory;
}
-static PMEvaluateFunction evaluateAsInt(PMEvaluateFunction f, ValueFlow::Value::ValueType t)
-{
- return [=](const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result) -> bool {
- const ValueFlow::Value* value = expr->getKnownValue(t);
- if (!value)
- value = programMemory->getValue(expr->exprId());
- if (value && value->valueType == t)
- {
- *result = value->intvalue;
- return true;
- }
- return f && f(expr, programMemory, result);
- };
+static bool isNumericValue(const ValueFlow::Value& value) {
+ return value.isIntValue() || value.isFloatValue();
}
-static std::set findIteratorTypes(const ProgramMemory& pm)
+static double asFloat(const ValueFlow::Value& value)
{
- std::set result;
- for (auto&& p : pm.values) {
- if (p.second.isIteratorValue())
- result.insert(p.second.valueType);
- if (result.size() == 2)
- break;
+ return value.isFloatValue() ? value.floatValue : value.intvalue;
+}
+
+static std::string removeAssign(const std::string& assign) {
+ return std::string{assign.begin(), assign.end() - 1};
+}
+
+struct assign {
+ template
+ void operator()(T& x, const U& y) const
+ {
+ x = y;
+ }
+};
+
+static ValueFlow::Value evaluate(const std::string& op, const ValueFlow::Value& lhs, const ValueFlow::Value& rhs)
+{
+ ValueFlow::Value result;
+ if (lhs.isImpossible() && rhs.isImpossible())
+ return ValueFlow::Value::unknown();
+ if (lhs.isImpossible() || rhs.isImpossible()) {
+ // noninvertible
+ if (contains({"%", "/", "&", "|"}, op))
+ return ValueFlow::Value::unknown();
+ result.setImpossible();
+ }
+ if (isNumericValue(lhs) && isNumericValue(rhs)) {
+ if (lhs.isFloatValue() || rhs.isFloatValue()) {
+ result.valueType = ValueFlow::Value::ValueType::FLOAT;
+ bool error = false;
+ result.floatValue = calculate(op, asFloat(lhs), asFloat(rhs), &error);
+ if (error)
+ return ValueFlow::Value::unknown();
+ return result;
+ }
+ }
+ result.valueType = ValueFlow::Value::ValueType::INT;
+ if (op == "+") {
+ if (lhs.isIteratorValue())
+ result.valueType = lhs.valueType;
+ else if (rhs.isIteratorValue())
+ result.valueType = rhs.valueType;
+ } else if (lhs.valueType != rhs.valueType) {
+ return ValueFlow::Value::unknown();
+ }
+ bool error = false;
+ result.intvalue = calculate(op, lhs.intvalue, rhs.intvalue, &error);
+ if (error)
+ return ValueFlow::Value::unknown();
+ if (result.isImpossible()) {
+ if ((result.intvalue == 0 && op == "!=") || (result.intvalue != 0 && op == "==")) {
+ result.setPossible();
+ result.intvalue = !result.intvalue;
+ }
}
return result;
}
-static bool isIterator(const Token* expr)
+static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm)
{
+ ValueFlow::Value unknown = ValueFlow::Value::unknown();
+ const ValueFlow::Value* value = nullptr;
if (!expr)
- return false;
- if (astIsIterator(expr))
- return true;
- return std::any_of(expr->values().begin(), expr->values().end(), std::mem_fn(&ValueFlow::Value::isIteratorValue));
-}
-
-void execute(const Token* expr,
- ProgramMemory* const programMemory,
- MathLib::bigint* result,
- bool* error,
- const PMEvaluateFunction& f)
-{
- if (!expr)
- *error = true;
-
- else if (f && f(expr, programMemory, result))
- *error = false;
-
+ return unknown;
else if (expr->hasKnownIntValue() && !expr->isAssignmentOp()) {
- *result = expr->values().front().intvalue;
- }
-
- else if (expr->isNumber()) {
- *result = MathLib::toLongNumber(expr->str());
+ return expr->values().front();
+ } else if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
+ (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) ||
+ (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) ||
+ (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) {
+ return *value;
+ } else if (expr->isNumber()) {
if (MathLib::isFloat(expr->str()))
- *error = true;
- }
-
- else if (expr->varId() > 0) {
- if (!programMemory->getIntValue(expr->varId(), result))
- *error = true;
- }
-
- else if (Token::Match(expr->tokAt(-3), "%var% . %name% (") && astIsContainer(expr->tokAt(-3))) {
- const Token* containerTok = expr->tokAt(-3);
+ return unknown;
+ return ValueFlow::Value{MathLib::toLongNumber(expr->str())};
+ } else if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
+ const Token* containerTok = expr->tokAt(-2)->astOperand1();
Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
if (yield == Library::Container::Yield::SIZE) {
- if (!programMemory->getContainerSizeValue(containerTok->exprId(), result))
- *error = true;
+ ValueFlow::Value v = execute(containerTok, pm);
+ if (!v.isContainerSizeValue())
+ return unknown;
+ v.valueType = ValueFlow::Value::ValueType::INT;
+ return v;
} else if (yield == Library::Container::Yield::EMPTY) {
- if (!programMemory->getContainerEmptyValue(containerTok->exprId(), result))
- *error = true;
- } else {
- *error = true;
+ ValueFlow::Value v = execute(containerTok, pm);
+ if (!v.isContainerSizeValue())
+ return unknown;
+ if (v.isImpossible() && v.intvalue == 0)
+ return ValueFlow::Value{0};
+ else if (!v.isImpossible())
+ return ValueFlow::Value{v.intvalue == 0};
}
- }
-
- else if (expr->exprId() > 0 && programMemory->hasValue(expr->exprId())) {
- if (!programMemory->getIntValue(expr->exprId(), result))
- *error = true;
- }
-
- else if (expr->isComparisonOp()) {
- MathLib::bigint result1(0), result2(0);
- bool error1 = false;
- bool error2 = false;
- execute(expr->astOperand1(), programMemory, &result1, &error1, f);
- execute(expr->astOperand2(), programMemory, &result2, &error2, f);
- if (error1 && error2 && (isIterator(expr->astOperand1()) || isIterator(expr->astOperand2()))) {
- for (ValueFlow::Value::ValueType t : findIteratorTypes(*programMemory)) {
- execute(expr->astOperand1(), programMemory, &result1, &error1, evaluateAsInt(f, t));
- execute(expr->astOperand2(), programMemory, &result2, &error2, evaluateAsInt(f, t));
- if (!error1 && !error2)
- break;
- }
- }
- if (error1 && error2) {
- *error = true;
- } else if (error1 && !error2) {
- ValueFlow::Value v = inferCondition(expr->str(), expr->astOperand1(), result2);
- *error = !v.isKnown();
- *result = v.intvalue;
- } else if (!error1 && error2) {
- ValueFlow::Value v = inferCondition(expr->str(), result1, expr->astOperand2());
- *error = !v.isKnown();
- *result = v.intvalue;
- } else {
- if (expr->str() == "<")
- *result = result1 < result2;
- else if (expr->str() == "<=")
- *result = result1 <= result2;
- else if (expr->str() == ">")
- *result = result1 > result2;
- else if (expr->str() == ">=")
- *result = result1 >= result2;
- else if (expr->str() == "==")
- *result = result1 == result2;
- else if (expr->str() == "!=")
- *result = result1 != result2;
- }
- }
-
- else if (expr->isAssignmentOp()) {
- execute(expr->astOperand2(), programMemory, result, error, f);
- if (!expr->astOperand1() || !expr->astOperand1()->exprId())
- *error = true;
- if (*error)
- return;
-
- if (expr->str() == "=") {
- programMemory->setIntValue(expr->astOperand1()->exprId(), *result);
- return;
- }
-
- long long intValue;
- if (!programMemory->getIntValue(expr->astOperand1()->exprId(), &intValue)) {
- *error = true;
- return;
- }
- if (expr->str() == "+=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue + *result);
- else if (expr->str() == "-=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue - *result);
- else if (expr->str() == "*=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue * *result);
- else if (expr->str() == "/=" && *result != 0)
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue / *result);
- else if (expr->str() == "%=" && *result != 0)
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue % *result);
- else if (expr->str() == "&=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue & *result);
- else if (expr->str() == "|=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue | *result);
- else if (expr->str() == "^=")
- programMemory->setIntValue(expr->astOperand1()->exprId(), intValue ^ *result);
- }
-
- else if (Token::Match(expr, "++|--")) {
- if (!expr->astOperand1() || expr->astOperand1()->exprId() == 0)
- *error = true;
- else {
- long long intValue;
- if (!programMemory->getIntValue(expr->astOperand1()->exprId(), &intValue))
- *error = true;
- else {
- if (intValue == 0 &&
- expr->str() == "--" &&
- expr->astOperand1()->variable() &&
- expr->astOperand1()->variable()->isUnsigned())
- *error = true; // overflow
- *result = intValue + (expr->str() == "++" ? 1 : -1);
- programMemory->setIntValue(expr->astOperand1()->exprId(), *result);
- }
- }
- }
-
- else if (expr->str() == "&&") {
- bool error1 = false;
- execute(expr->astOperand1(), programMemory, result, &error1, f);
- if (!error1 && *result == 0)
- *result = 0;
- else {
- bool error2 = false;
- execute(expr->astOperand2(), programMemory, result, &error2, f);
- if (error1 || error2)
- *error = true;
- }
- }
-
- else if (expr->str() == "||") {
- execute(expr->astOperand1(), programMemory, result, error, f);
- if (*result == 1 && *error == false)
- *result = 1;
- else if (*result == 0 && *error == false)
- execute(expr->astOperand2(), programMemory, result, error, f);
- } else if (expr->isConstOp() && expr->astOperand1() && expr->astOperand2()) {
- MathLib::bigint result1(0), result2(0);
- execute(expr->astOperand1(), programMemory, &result1, error, f);
- execute(expr->astOperand2(), programMemory, &result2, error, f);
- if (expr->str() == "+")
- *result = result1 + result2;
- else if (expr->str() == "-")
- *result = result1 - result2;
- else if (expr->str() == "*") {
- if (result2 && (result1 > std::numeric_limits::max()/result2))
- *error = true;
+ } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && expr->astOperand1()->exprId() > 0) {
+ ValueFlow::Value rhs = execute(expr->astOperand2(), pm);
+ if (rhs.isUninitValue())
+ return unknown;
+ if (expr->str() != "=") {
+ if (!pm.hasValue(expr->astOperand1()->exprId()))
+ return unknown;
+ ValueFlow::Value& lhs = pm.values.at(expr->astOperand1()->exprId());
+ rhs = evaluate(removeAssign(expr->str()), lhs, rhs);
+ if (lhs.isIntValue())
+ ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1));
+ else if (lhs.isFloatValue())
+ ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
else
- *result = result1 * result2;
- } else if (result2 == 0)
- *error = true;
- else if (expr->str() == "/")
- *result = result1 / result2;
- else if (expr->str() == "%")
- *result = result1 % result2;
- else if (expr->str() == "<<") {
- if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
- *error= true;
- } else {
- *result = result1 << result2;
- }
- } else if (expr->str() == ">>") {
- if (result2 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
- *error=true;
- } else {
- *result = result1 >> result2;
- }
- } else if (expr->str() == "&") {
- *result = result1 & result2;
- } else if (expr->str() == "|") {
- *result = result1 | result2;
+ return unknown;
+ return lhs;
} else {
- *error = true;
+ pm.values[expr->astOperand1()->exprId()] = rhs;
+ return rhs;
}
- }
-
- else if (expr->str() == "!") {
- execute(expr->astOperand1(), programMemory, result, error, f);
- *result = !(*result);
- } else if (expr->isUnaryOp("-")) {
- execute(expr->astOperand1(), programMemory, result, error, f);
- *result = -(*result);
- } else if (expr->isUnaryOp("+")) {
- execute(expr->astOperand1(), programMemory, result, error, f);
+ } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) {
+ ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
+ if (!lhs.isIntValue())
+ return unknown;
+ if (lhs.intvalue == 0)
+ return lhs;
+ return execute(expr->astOperand2(), pm);
+ } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) {
+ ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
+ if (!lhs.isIntValue())
+ return unknown;
+ if (lhs.intvalue != 0)
+ return lhs;
+ return execute(expr->astOperand2(), pm);
} else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
- execute(expr->astOperand1(), programMemory, result, error, f);
- execute(expr->astOperand2(), programMemory, result, error, f);
- }
+ execute(expr->astOperand1(), pm);
+ return execute(expr->astOperand2(), pm);
+ } else if (Token::Match(expr, "++|--") && expr->astOperand1() && expr->astOperand1()->exprId() != 0) {
+ if (!pm.hasValue(expr->astOperand1()->exprId()))
+ return unknown;
+ ValueFlow::Value& lhs = pm.values.at(expr->astOperand1()->exprId());
+ if (!lhs.isIntValue())
+ return unknown;
+ // overflow
+ if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1()))
+ return unknown;
- else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
+ if (expr->str() == "++")
+ lhs.intvalue++;
+ else
+ lhs.intvalue--;
+ return lhs;
+ } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
const Token *tokvalue = nullptr;
- if (!programMemory->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
+ if (!pm.getTokValue(expr->astOperand1()->exprId(), &tokvalue)) {
auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
expr->astOperand1()->values().end(),
std::mem_fn(&ValueFlow::Value::isTokValue));
if (tokvalue_it == expr->astOperand1()->values().end()) {
- *error = true;
- return;
+ return unknown;
}
tokvalue = tokvalue_it->tokvalue;
}
if (!tokvalue || !tokvalue->isLiteral()) {
- *error = true;
- return;
+ return unknown;
}
const std::string strValue = tokvalue->strValue();
- MathLib::bigint index = 0;
- execute(expr->astOperand2(), programMemory, &index, error, f);
+ ValueFlow::Value rhs = execute(expr->astOperand2(), pm);
+ if (!rhs.isIntValue())
+ return unknown;
+ MathLib::bigint index = rhs.intvalue;
if (index >= 0 && index < strValue.size())
- *result = strValue[index];
+ return ValueFlow::Value{strValue[index]};
else if (index == strValue.size())
- *result = 0;
- else
- *error = true;
+ return ValueFlow::Value{};
+ } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
+ ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
+ ValueFlow::Value rhs = execute(expr->astOperand2(), pm);
+ if (!lhs.isUninitValue() && !rhs.isUninitValue())
+ return evaluate(expr->str(), lhs, rhs);
+ if (expr->isComparisonOp()) {
+ if (rhs.isIntValue() && !rhs.isImpossible()) {
+ ValueFlow::Value v = inferCondition(expr->str(), expr->astOperand1(), rhs.intvalue);
+ if (v.isKnown())
+ return v;
+ } else if (lhs.isIntValue() && !lhs.isImpossible()) {
+ ValueFlow::Value v = inferCondition(expr->str(), lhs.intvalue, expr->astOperand2());
+ if (v.isKnown())
+ return v;
+ }
+ }
+ }
+ // Unary ops
+ else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) {
+ ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
+ if (!lhs.isIntValue())
+ return unknown;
+ if (expr->str() == "!")
+ lhs.intvalue = !lhs.intvalue;
+ if (expr->str() == "-")
+ lhs.intvalue = -lhs.intvalue;
+ return lhs;
} else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) {
- execute(expr->astOperand1(), programMemory, result, error, f);
- if (*error)
- return;
- const Token* childTok = expr->astOperand2();
- if (*result == 0)
- execute(childTok->astOperand2(), programMemory, result, error, f);
+ ValueFlow::Value cond = execute(expr->astOperand1(), pm);
+ if (!cond.isIntValue())
+ return unknown;
+ const Token* child = expr->astOperand2();
+ if (cond.intvalue == 0)
+ return execute(child->astOperand2(), pm);
else
- execute(childTok->astOperand1(), programMemory, result, error, f);
-
+ return execute(child->astOperand1(), pm);
} else if (expr->str() == "(" && expr->isCast()) {
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
- execute(expr->astOperand2(), programMemory, result, error);
+ return execute(expr->astOperand2(), pm);
else
- execute(expr->astOperand1(), programMemory, result, error);
- } else
- *error = true;
+ return execute(expr->astOperand1(), pm);
+ }
+ if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) {
+ return pm.values.at(expr->exprId());
+ }
+
+ return unknown;
+}
+
+void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error)
+{
+ ValueFlow::Value v = execute(expr, *programMemory);
+ if (!v.isIntValue() || v.isImpossible())
+ *error = true;
+ else
+ *result = v.intvalue;
}
diff --git a/lib/programmemory.h b/lib/programmemory.h
index f7ba8d423..d64291a2c 100644
--- a/lib/programmemory.h
+++ b/lib/programmemory.h
@@ -19,7 +19,7 @@ struct ProgramMemory {
const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const;
bool getIntValue(nonneg int exprid, MathLib::bigint* result) const;
- void setIntValue(nonneg int exprid, MathLib::bigint value);
+ void setIntValue(nonneg int exprid, MathLib::bigint value, bool impossible = false);
bool getContainerSizeValue(nonneg int exprid, MathLib::bigint* result) const;
bool getContainerEmptyValue(nonneg int exprid, MathLib::bigint* result) const;
@@ -62,14 +62,7 @@ struct ProgramMemoryState {
ProgramMemory get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const;
};
-using PMEvaluateFunction =
- std::function;
-
-void execute(const Token* expr,
- ProgramMemory* const programMemory,
- MathLib::bigint* result,
- bool* error,
- const PMEvaluateFunction& f = nullptr);
+void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error);
/**
* Is condition always false when variable has given value?
diff --git a/lib/utils.h b/lib/utils.h
index 19d752e44..41edc3a51 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -23,7 +23,9 @@
#include "config.h"
+#include
#include
+#include
#include
#include
@@ -41,6 +43,24 @@ struct SelectMapValues {
}
};
+template
+bool contains(const Range& r, const T& x)
+{
+ return std::find(r.begin(), r.end(), x) != r.end();
+}
+
+template
+bool contains(const std::initializer_list& r, const T& x)
+{
+ return std::find(r.begin(), r.end(), x) != r.end();
+}
+
+template
+bool contains(const std::initializer_list& r, const U& x)
+{
+ return std::find(r.begin(), r.end(), x) != r.end();
+}
+
// Enum hash for C++11. This is not needed in C++14
struct EnumClassHash {
template
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index 90668cd09..640598300 100644
--- a/lib/valueflow.cpp
+++ b/lib/valueflow.cpp
@@ -79,6 +79,7 @@
#include "analyzer.h"
#include "astutils.h"
+#include "calculate.h"
#include "checkuninitvar.h"
#include "config.h"
#include "errorlogger.h"
@@ -376,98 +377,6 @@ static bool isComputableValue(const Token* parent, const ValueFlow::Value& value
return true;
}
-template
-static bool isEqual(T x, T y)
-{
- return x == y;
-}
-
-template<>
-bool isEqual(double x, double y)
-{
- const double diff = (x > y) ? x - y : y - x;
- return !((diff / 2) < diff);
-}
-
-template
-static bool isZero(T x)
-{
- return isEqual(x, T(0));
-}
-
-template
-static R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
-{
- auto wrap = [](T z) {
- return R{z};
- };
- switch (MathLib::encodeMultiChar(s)) {
- case '+':
- return wrap(x + y);
- case '-':
- return wrap(x - y);
- case '*':
- return wrap(x * y);
- case '/':
- if (isZero(y)) {
- if (error)
- *error = true;
- return R{};
- }
- return wrap(x / y);
- case '%':
- if (isZero(y)) {
- if (error)
- *error = true;
- return R{};
- }
- return wrap(MathLib::bigint(x) % MathLib::bigint(y));
- case '&':
- return wrap(MathLib::bigint(x) & MathLib::bigint(y));
- case '|':
- return wrap(MathLib::bigint(x) | MathLib::bigint(y));
- case '^':
- return wrap(MathLib::bigint(x) ^ MathLib::bigint(y));
- case '>':
- return wrap(x > y);
- case '<':
- return wrap(x < y);
- case '<<':
- if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
- if (error)
- *error = true;
- return R{};
- }
- return wrap(MathLib::bigint(x) << MathLib::bigint(y));
- case '>>':
- if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
- if (error)
- *error = true;
- return R{};
- }
- return wrap(MathLib::bigint(x) >> MathLib::bigint(y));
- case '&&':
- return wrap(!isZero(x) && !isZero(y));
- case '||':
- return wrap(!isZero(x) || !isZero(y));
- case '==':
- return wrap(isEqual(x, y));
- case '!=':
- return wrap(!isEqual(x, y));
- case '>=':
- return wrap(x >= y);
- case '<=':
- return wrap(x <= y);
- }
- throw InternalError(nullptr, "Unknown operator: " + s);
-}
-
-template
-static T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
-{
- return calculate(s, x, y, error);
-}
-
/** Set token value for cast */
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings);