From 7621fee3d492997e223d562eaf48c419ff85719c Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Mon, 4 Oct 2021 00:53:58 -0500 Subject: [PATCH] Refactor: Use ValueFlow::Value for ProgramMemory execute instead of integers (#3477) --- Makefile | 4 +- lib/calculate.h | 120 +++++++++++ lib/programmemory.cpp | 463 ++++++++++++++++++------------------------ lib/programmemory.h | 11 +- lib/utils.h | 20 ++ lib/valueflow.cpp | 93 +-------- 6 files changed, 347 insertions(+), 364 deletions(-) create mode 100644 lib/calculate.h 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);