Fix 10404: FP knownConditionTrueFalse after subtraction (#3390)

This commit is contained in:
Paul Fultz II 2021-08-09 00:45:41 -05:00 committed by GitHub
parent 820256d10f
commit a218ea3b23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 90 deletions

View File

@ -19,7 +19,9 @@
#ifndef analyzerH #ifndef analyzerH
#define analyzerH #define analyzerH
#include "config.h"
#include <string> #include <string>
#include <type_traits>
#include <vector> #include <vector>
class Token; class Token;
@ -32,7 +34,11 @@ struct Analyzer {
Action() : mFlag(0) {} Action() : mFlag(0) {}
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
Action(unsigned int f) : mFlag(f) {} template<class T,
REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
Action(T f) : mFlag(f)
{}
enum { enum {
None = 0, None = 0,

View File

@ -7,6 +7,7 @@
#include "valueptr.h" #include "valueptr.h"
#include <algorithm> #include <algorithm>
#include <cstdio>
#include <functional> #include <functional>
#include <tuple> #include <tuple>
#include <utility> #include <utility>

View File

@ -80,6 +80,7 @@
#include "analyzer.h" #include "analyzer.h"
#include "astutils.h" #include "astutils.h"
#include "checkuninitvar.h" #include "checkuninitvar.h"
#include "config.h"
#include "errorlogger.h" #include "errorlogger.h"
#include "errortypes.h" #include "errortypes.h"
#include "forwardanalyzer.h" #include "forwardanalyzer.h"
@ -112,6 +113,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits>
#include <vector> #include <vector>
static void bailoutInternal(const std::string& type, TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, std::string function) static void bailoutInternal(const std::string& type, TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, std::string function)
@ -380,7 +382,7 @@ static bool isZero(T x)
} }
template<class R, class T> template<class R, class T>
static R calculate(const std::string& s, const T& x, const T& y) static R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
{ {
auto wrap = [](T z) { auto wrap = [](T z) {
return R{z}; return R{z};
@ -393,11 +395,19 @@ static R calculate(const std::string& s, const T& x, const T& y)
case '*': case '*':
return wrap(x * y); return wrap(x * y);
case '/': case '/':
return isZero(y) ? R{} : if (isZero(y)) {
wrap(x / y); if (error)
*error = true;
return R{};
}
return wrap(x / y);
case '%': case '%':
return isZero(y) ? R{} : if (isZero(y)) {
wrap(MathLib::bigint(x) % MathLib::bigint(y)); if (error)
*error = true;
return R{};
}
return wrap(MathLib::bigint(x) % MathLib::bigint(y));
case '&': case '&':
return wrap(MathLib::bigint(x) & MathLib::bigint(y)); return wrap(MathLib::bigint(x) & MathLib::bigint(y));
case '|': case '|':
@ -409,11 +419,19 @@ static R calculate(const std::string& s, const T& x, const T& y)
case '<': case '<':
return wrap(x < y); return wrap(x < y);
case '<<': case '<<':
return (y >= sizeof(MathLib::bigint) * 8) ? R{} : if (y >= sizeof(MathLib::bigint) * 8) {
wrap(MathLib::bigint(x) << MathLib::bigint(y)); if (error)
*error = true;
return R{};
}
return wrap(MathLib::bigint(x) << MathLib::bigint(y));
case '>>': case '>>':
return (y >= sizeof(MathLib::bigint) * 8) ? R{} : if (y >= sizeof(MathLib::bigint) * 8) {
wrap(MathLib::bigint(x) >> MathLib::bigint(y)); if (error)
*error = true;
return R{};
}
return wrap(MathLib::bigint(x) >> MathLib::bigint(y));
case '&&': case '&&':
return wrap(!isZero(x) && !isZero(y)); return wrap(!isZero(x) && !isZero(y));
case '||': case '||':
@ -431,9 +449,9 @@ static R calculate(const std::string& s, const T& x, const T& y)
} }
template<class T> template<class T>
static T calculate(const std::string& s, const T& x, const T& y) static T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr)
{ {
return calculate<T, T>(s, x, y); return calculate<T, T>(s, x, y, error);
} }
/** Set token value for cast */ /** Set token value for cast */
@ -1711,9 +1729,14 @@ static bool isConditionKnown(const Token* tok, bool then)
static const std::string& invertAssign(const std::string& assign) static const std::string& invertAssign(const std::string& assign)
{ {
static std::unordered_map<std::string, std::string> lookup = { static std::unordered_map<std::string, std::string> lookup = {{"=", "="},
{"+=", "-="}, {"-=", "+="}, {"*=", "/="}, {"/=", "*="}, {"<<=", ">>="}, {">>=", "<<="}, {"^=", "^="} {"+=", "-="},
}; {"-=", "+="},
{"*=", "/="},
{"/=", "*="},
{"<<=", ">>="},
{">>=", "<<="},
{"^=", "^="}};
static std::string empty; static std::string empty;
auto it = lookup.find(assign); auto it = lookup.find(assign);
if (it == lookup.end()) if (it == lookup.end())
@ -1722,58 +1745,50 @@ static const std::string& invertAssign(const std::string& assign)
return it->second; return it->second;
} }
static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign, const ValueFlow::Value &rhsValue) static std::string removeAssign(const std::string& assign) {
return std::string{assign.begin(), assign.end() - 1};
}
template<class T, class U>
static T calculateAssign(const std::string& assign, const T& x, const U& y, bool* error = nullptr)
{ {
if (lhsValue.isIntValue()) { if (assign.empty() || assign.back() != '=') {
if (assign == "=") if (error)
lhsValue.intvalue = rhsValue.intvalue; *error = true;
else if (assign == "+=") return T{};
lhsValue.intvalue += rhsValue.intvalue; }
else if (assign == "-=") if (assign == "=")
lhsValue.intvalue -= rhsValue.intvalue; return y;
else if (assign == "*=") return calculate<T, T>(removeAssign(assign), x, y, error);
lhsValue.intvalue *= rhsValue.intvalue; }
else if (assign == "/=") {
if (rhsValue.intvalue == 0) template<class T, class U>
return false; static void assignValueIfMutable(T& x, const U& y)
lhsValue.intvalue /= rhsValue.intvalue; {
} else if (assign == "%=") { x = y;
if (rhsValue.intvalue == 0) }
return false;
lhsValue.intvalue %= rhsValue.intvalue; template<class T, class U>
} else if (assign == "&=") static void assignValueIfMutable(const T&, const U&)
lhsValue.intvalue &= rhsValue.intvalue; {}
else if (assign == "|=")
lhsValue.intvalue |= rhsValue.intvalue; template<class Value, REQUIRES("Value must ValueFlow::Value", std::is_convertible<Value&, const ValueFlow::Value&> )>
else if (assign == "^=") static bool evalAssignment(Value& lhsValue, const std::string& assign, const ValueFlow::Value& rhsValue)
lhsValue.intvalue ^= rhsValue.intvalue; {
else if (assign == "<<=") { bool error = false;
if (rhsValue.intvalue < 0) if (lhsValue.isSymbolicValue() && rhsValue.isIntValue()) {
return false; if (assign != "+=" && assign != "-=")
lhsValue.intvalue <<= rhsValue.intvalue;
} else if (assign == ">>=") {
if (rhsValue.intvalue < 0)
return false;
lhsValue.intvalue >>= rhsValue.intvalue;
} else
return false;
} else if (lhsValue.isFloatValue()) {
if (assign == "=")
lhsValue.intvalue = rhsValue.intvalue;
else if (assign == "+=")
lhsValue.floatValue += rhsValue.intvalue;
else if (assign == "-=")
lhsValue.floatValue -= rhsValue.intvalue;
else if (assign == "*=")
lhsValue.floatValue *= rhsValue.intvalue;
else if (assign == "/=")
lhsValue.floatValue /= rhsValue.intvalue;
else
return false; return false;
assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error));
} else if (lhsValue.isIntValue() && rhsValue.isIntValue()) {
assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error));
} else if (lhsValue.isFloatValue() && rhsValue.isIntValue()) {
assignValueIfMutable(lhsValue.floatValue,
calculateAssign(assign, lhsValue.floatValue, rhsValue.intvalue, &error));
} else { } else {
return false; return false;
} }
return true; return !error;
} }
template<class T> template<class T>
@ -1876,18 +1891,6 @@ static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid,
return false; return false;
} }
static const ValueFlow::Value* getKnownValue(const Token* tok, ValueFlow::Value::ValueType type)
{
if (!tok)
return nullptr;
auto it = std::find_if(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) {
return v.isKnown() && v.valueType == type;
});
if (it != tok->values().end())
return &*it;
return nullptr;
}
static bool bifurcate(const Token* tok, const std::set<nonneg int>& varids, const Settings* settings, int depth = 20); static bool bifurcate(const Token* tok, const std::set<nonneg int>& varids, const Settings* settings, int depth = 20);
static bool bifurcateVariableChanged(const Variable* var, static bool bifurcateVariableChanged(const Variable* var,
@ -2031,6 +2034,14 @@ struct ValueFlowAnalyzer : Analyzer {
return Action::None; return Action::None;
} }
static const std::string& getAssign(const Token* tok, Direction d)
{
if (d == Direction::Forward)
return tok->str();
else
return invertAssign(tok->str());
}
virtual Action isWritable(const Token* tok, Direction d) const { virtual Action isWritable(const Token* tok, Direction d) const {
const ValueFlow::Value* value = getValue(tok); const ValueFlow::Value* value = getValue(tok);
if (!value) if (!value)
@ -2041,13 +2052,10 @@ struct ValueFlowAnalyzer : Analyzer {
if (parent && parent->isAssignmentOp() && astIsLHS(tok) && if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
parent->astOperand2()->hasKnownValue()) { parent->astOperand2()->hasKnownValue()) {
// If the operator is invertible
if (d == Direction::Reverse && (parent->str() == "&=" || parent->str() == "|=" || parent->str() == "%="))
return Action::None;
const Token* rhs = parent->astOperand2(); const Token* rhs = parent->astOperand2();
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT); const ValueFlow::Value* rhsValue = rhs->getKnownValue(ValueFlow::Value::ValueType::INT);
Action a; Action a;
if (!rhsValue) if (!rhsValue || !evalAssignment(*value, getAssign(parent, d), *rhsValue))
a = Action::Invalid; a = Action::Invalid;
else else
a = Action::Write; a = Action::Write;
@ -2068,29 +2076,23 @@ struct ValueFlowAnalyzer : Analyzer {
return Action::None; return Action::None;
} }
static const std::string& getAssign(const Token* tok, Direction d) {
if (d == Direction::Forward)
return tok->str();
else
return invertAssign(tok->str());
}
virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const { virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const {
if (!value) if (!value)
return; return;
if (!tok->astParent()) if (!tok->astParent())
return; return;
if (tok->astParent()->isAssignmentOp()) { if (tok->astParent()->isAssignmentOp()) {
// TODO: Check result const ValueFlow::Value* rhsValue =
if (evalAssignment(*value, tok->astParent()->astOperand2()->getKnownValue(ValueFlow::Value::ValueType::INT);
getAssign(tok->astParent(), d), assert(rhsValue);
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) { if (evalAssignment(*value, getAssign(tok->astParent(), d), *rhsValue)) {
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " + const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
value->infoString()); value->infoString());
if (tok->astParent()->str() == "=") if (tok->astParent()->str() == "=")
value->errorPath.clear(); value->errorPath.clear();
value->errorPath.emplace_back(tok, info); value->errorPath.emplace_back(tok, info);
} else { } else {
assert(false && "Writable value cannot be evaluated");
// TODO: Don't set to zero // TODO: Don't set to zero
value->intvalue = 0; value->intvalue = 0;
} }

View File

@ -3782,6 +3782,17 @@ private:
" if (e) {}\n" " if (e) {}\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("int g(int i) {\n"
" if (i < 256)\n"
" return 1;\n"
" const int N = 2 * i;\n"
" i -= 256;\n"
" if (i == 0)\n"
" return 0;\n"
" return N;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void alwaysTrueInfer() { void alwaysTrueInfer() {