Refactor: Use ValueFlow::Value for ProgramMemory execute instead of integers (#3477)

This commit is contained in:
Paul Fultz II 2021-10-04 00:53:58 -05:00 committed by GitHub
parent 2ca2abdf0e
commit 7621fee3d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 347 additions and 364 deletions

View File

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

120
lib/calculate.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef calculateH
#define calculateH
#include "mathlib.h"
#include <string>
template<class T>
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<class T>
bool isZero(T x)
{
return isEqual<T>(x, T(0));
}
template<class R, class T>
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<class T>
T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
{
return calculate<T, T>(s, x, y, error);
}
#endif

View File

@ -1,13 +1,16 @@
#include "programmemory.h" #include "programmemory.h"
#include "astutils.h" #include "astutils.h"
#include "calculate.h"
#include "errortypes.h"
#include "mathlib.h" #include "mathlib.h"
#include "symboldatabase.h"
#include "settings.h" #include "settings.h"
#include "symboldatabase.h"
#include "token.h" #include "token.h"
#include "valueflow.h" #include "valueflow.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cmath>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <memory> #include <memory>
@ -36,9 +39,12 @@ bool ProgramMemory::getIntValue(nonneg int exprid, MathLib::bigint* result) cons
return false; 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 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<MathLib::bigint>{}; return std::vector<MathLib::bigint>{};
}; };
if (Token::Match(tok, "==|>=|<=|<|>|!=")) { if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
if (then && !Token::Match(tok, "==|>=|<=|<|>"))
return;
if (!then && !Token::Match(tok, "<|>|!="))
return;
ValueFlow::Value truevalue; ValueFlow::Value truevalue;
ValueFlow::Value falsevalue; ValueFlow::Value falsevalue;
const Token* vartok = parseCompareInt(tok, truevalue, falsevalue, eval); 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)) if (endTok && isExpressionChanged(vartok, tok->next(), endTok, settings, true))
return; return;
bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then); bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then);
if (!impossible) pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, impossible);
pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue);
const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE); const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE);
if (containerTok) if (containerTok)
pm.setContainerSizeValue(containerTok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, !impossible); 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; return programMemory;
} }
static PMEvaluateFunction evaluateAsInt(PMEvaluateFunction f, ValueFlow::Value::ValueType t) static bool isNumericValue(const ValueFlow::Value& value) {
{ return value.isIntValue() || value.isFloatValue();
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 std::set<ValueFlow::Value::ValueType> findIteratorTypes(const ProgramMemory& pm) static double asFloat(const ValueFlow::Value& value)
{ {
std::set<ValueFlow::Value::ValueType> result; return value.isFloatValue() ? value.floatValue : value.intvalue;
for (auto&& p : pm.values) { }
if (p.second.isIteratorValue())
result.insert(p.second.valueType); static std::string removeAssign(const std::string& assign) {
if (result.size() == 2) return std::string{assign.begin(), assign.end() - 1};
break; }
struct assign {
template<class T, class U>
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; 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) if (!expr)
return false; return unknown;
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;
else if (expr->hasKnownIntValue() && !expr->isAssignmentOp()) { else if (expr->hasKnownIntValue() && !expr->isAssignmentOp()) {
*result = expr->values().front().intvalue; return expr->values().front();
} } else if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) ||
(value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) ||
else if (expr->isNumber()) { (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) ||
*result = MathLib::toLongNumber(expr->str()); (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) {
return *value;
} else if (expr->isNumber()) {
if (MathLib::isFloat(expr->str())) if (MathLib::isFloat(expr->str()))
*error = true; return unknown;
} return ValueFlow::Value{MathLib::toLongNumber(expr->str())};
} else if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
else if (expr->varId() > 0) { const Token* containerTok = expr->tokAt(-2)->astOperand1();
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);
Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));
if (yield == Library::Container::Yield::SIZE) { if (yield == Library::Container::Yield::SIZE) {
if (!programMemory->getContainerSizeValue(containerTok->exprId(), result)) ValueFlow::Value v = execute(containerTok, pm);
*error = true; if (!v.isContainerSizeValue())
return unknown;
v.valueType = ValueFlow::Value::ValueType::INT;
return v;
} else if (yield == Library::Container::Yield::EMPTY) { } else if (yield == Library::Container::Yield::EMPTY) {
if (!programMemory->getContainerEmptyValue(containerTok->exprId(), result)) ValueFlow::Value v = execute(containerTok, pm);
*error = true; if (!v.isContainerSizeValue())
} else { return unknown;
*error = true; if (v.isImpossible() && v.intvalue == 0)
return ValueFlow::Value{0};
else if (!v.isImpossible())
return ValueFlow::Value{v.intvalue == 0};
} }
} } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && expr->astOperand1()->exprId() > 0) {
ValueFlow::Value rhs = execute(expr->astOperand2(), pm);
else if (expr->exprId() > 0 && programMemory->hasValue(expr->exprId())) { if (rhs.isUninitValue())
if (!programMemory->getIntValue(expr->exprId(), result)) return unknown;
*error = true; if (expr->str() != "=") {
} if (!pm.hasValue(expr->astOperand1()->exprId()))
return unknown;
else if (expr->isComparisonOp()) { ValueFlow::Value& lhs = pm.values.at(expr->astOperand1()->exprId());
MathLib::bigint result1(0), result2(0); rhs = evaluate(removeAssign(expr->str()), lhs, rhs);
bool error1 = false; if (lhs.isIntValue())
bool error2 = false; ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1));
execute(expr->astOperand1(), programMemory, &result1, &error1, f); else if (lhs.isFloatValue())
execute(expr->astOperand2(), programMemory, &result2, &error2, f); ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1));
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<MathLib::bigint>::max()/result2))
*error = true;
else else
*result = result1 * result2; return unknown;
} else if (result2 == 0) return lhs;
*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 { } else {
*result = result1 << result2; pm.values[expr->astOperand1()->exprId()] = rhs;
return rhs;
} }
} else if (expr->str() == ">>") { } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) {
if (result2 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
*error=true; if (!lhs.isIntValue())
} else { return unknown;
*result = result1 >> result2; if (lhs.intvalue == 0)
} return lhs;
} else if (expr->str() == "&") { return execute(expr->astOperand2(), pm);
*result = result1 & result2; } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) {
} else if (expr->str() == "|") { ValueFlow::Value lhs = execute(expr->astOperand1(), pm);
*result = result1 | result2; if (!lhs.isIntValue())
} else { return unknown;
*error = true; if (lhs.intvalue != 0)
} return lhs;
} return execute(expr->astOperand2(), pm);
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()) { } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
execute(expr->astOperand1(), programMemory, result, error, f); execute(expr->astOperand1(), pm);
execute(expr->astOperand2(), programMemory, result, error, f); 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; 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(), auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
expr->astOperand1()->values().end(), expr->astOperand1()->values().end(),
std::mem_fn(&ValueFlow::Value::isTokValue)); std::mem_fn(&ValueFlow::Value::isTokValue));
if (tokvalue_it == expr->astOperand1()->values().end()) { if (tokvalue_it == expr->astOperand1()->values().end()) {
*error = true; return unknown;
return;
} }
tokvalue = tokvalue_it->tokvalue; tokvalue = tokvalue_it->tokvalue;
} }
if (!tokvalue || !tokvalue->isLiteral()) { if (!tokvalue || !tokvalue->isLiteral()) {
*error = true; return unknown;
return;
} }
const std::string strValue = tokvalue->strValue(); const std::string strValue = tokvalue->strValue();
MathLib::bigint index = 0; ValueFlow::Value rhs = execute(expr->astOperand2(), pm);
execute(expr->astOperand2(), programMemory, &index, error, f); if (!rhs.isIntValue())
return unknown;
MathLib::bigint index = rhs.intvalue;
if (index >= 0 && index < strValue.size()) if (index >= 0 && index < strValue.size())
*result = strValue[index]; return ValueFlow::Value{strValue[index]};
else if (index == strValue.size()) else if (index == strValue.size())
*result = 0; return ValueFlow::Value{};
else } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) {
*error = true; 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()) { } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) {
execute(expr->astOperand1(), programMemory, result, error, f); ValueFlow::Value cond = execute(expr->astOperand1(), pm);
if (*error) if (!cond.isIntValue())
return; return unknown;
const Token* childTok = expr->astOperand2(); const Token* child = expr->astOperand2();
if (*result == 0) if (cond.intvalue == 0)
execute(childTok->astOperand2(), programMemory, result, error, f); return execute(child->astOperand2(), pm);
else else
execute(childTok->astOperand1(), programMemory, result, error, f); return execute(child->astOperand1(), pm);
} else if (expr->str() == "(" && expr->isCast()) { } else if (expr->str() == "(" && expr->isCast()) {
if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link())
execute(expr->astOperand2(), programMemory, result, error); return execute(expr->astOperand2(), pm);
else else
execute(expr->astOperand1(), programMemory, result, error); return execute(expr->astOperand1(), pm);
} else }
*error = true; 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;
} }

View File

@ -19,7 +19,7 @@ struct ProgramMemory {
const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const; const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const;
bool getIntValue(nonneg int exprid, MathLib::bigint* result) 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 getContainerSizeValue(nonneg int exprid, MathLib::bigint* result) const;
bool getContainerEmptyValue(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; ProgramMemory get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const;
}; };
using PMEvaluateFunction = void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error);
std::function<bool (const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result)>;
void execute(const Token* expr,
ProgramMemory* const programMemory,
MathLib::bigint* result,
bool* error,
const PMEvaluateFunction& f = nullptr);
/** /**
* Is condition always false when variable has given value? * Is condition always false when variable has given value?

View File

@ -23,7 +23,9 @@
#include "config.h" #include "config.h"
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <initializer_list>
#include <string> #include <string>
#include <vector> #include <vector>
@ -41,6 +43,24 @@ struct SelectMapValues {
} }
}; };
template<class Range, class T>
bool contains(const Range& r, const T& x)
{
return std::find(r.begin(), r.end(), x) != r.end();
}
template<class T>
bool contains(const std::initializer_list<T>& r, const T& x)
{
return std::find(r.begin(), r.end(), x) != r.end();
}
template<class T, class U>
bool contains(const std::initializer_list<T>& 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 // Enum hash for C++11. This is not needed in C++14
struct EnumClassHash { struct EnumClassHash {
template<typename T> template<typename T>

View File

@ -79,6 +79,7 @@
#include "analyzer.h" #include "analyzer.h"
#include "astutils.h" #include "astutils.h"
#include "calculate.h"
#include "checkuninitvar.h" #include "checkuninitvar.h"
#include "config.h" #include "config.h"
#include "errorlogger.h" #include "errorlogger.h"
@ -376,98 +377,6 @@ static bool isComputableValue(const Token* parent, const ValueFlow::Value& value
return true; return true;
} }
template<class T>
static bool isEqual(T x, T y)
{
return x == y;
}
template<>
bool isEqual<double>(double x, double y)
{
const double diff = (x > y) ? x - y : y - x;
return !((diff / 2) < diff);
}
template<class T>
static bool isZero(T x)
{
return isEqual<T>(x, T(0));
}
template<class R, class T>
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<class T>
static T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
{
return calculate<T, T>(s, x, y, error);
}
/** Set token value for cast */ /** Set token value for cast */
static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings); static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings);