Fix 10464: FP: knownConditionTrueFalse (#3452)

This commit is contained in:
Paul Fultz II 2021-09-09 00:49:56 -05:00 committed by GitHub
parent 47f5e5d145
commit b0b3f7ec2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 29 deletions

View File

@ -41,6 +41,15 @@ struct SelectMapValues {
} }
}; };
// Enum hash for C++11. This is not needed in C++14
struct EnumClassHash {
template<typename T>
std::size_t operator()(T t) const
{
return static_cast<std::size_t>(t);
}
};
inline bool endsWith(const std::string &str, char c) inline bool endsWith(const std::string &str, char c)
{ {
return str[str.size()-1U] == c; return str[str.size()-1U] == c;

View File

@ -115,6 +115,7 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -306,6 +307,10 @@ static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign
return value; return value;
} }
static bool isNumeric(const ValueFlow::Value& value) {
return value.isIntValue() || value.isFloatValue();
}
static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result) static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result)
{ {
if (value1.isKnown() && value2.isKnown()) if (value1.isKnown() && value2.isKnown())
@ -316,6 +321,14 @@ static void combineValueProperties(const ValueFlow::Value &value1, const ValueFl
result->setInconclusive(); result->setInconclusive();
else else
result->setPossible(); result->setPossible();
if (value1.isSymbolicValue()) {
result->valueType = value1.valueType;
result->tokvalue = value1.tokvalue;
}
if (value2.isSymbolicValue()) {
result->valueType = value2.valueType;
result->tokvalue = value2.tokvalue;
}
if (value1.isIteratorValue()) if (value1.isIteratorValue())
result->valueType = value1.valueType; result->valueType = value1.valueType;
if (value2.isIteratorValue()) if (value2.isIteratorValue())
@ -420,14 +433,14 @@ static R calculate(const std::string& s, const T& x, const T& y, bool* error = n
case '<': case '<':
return wrap(x < y); return wrap(x < y);
case '<<': case '<<':
if (y >= sizeof(MathLib::bigint) * 8) { if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
if (error) if (error)
*error = true; *error = true;
return R{}; return R{};
} }
return wrap(MathLib::bigint(x) << MathLib::bigint(y)); return wrap(MathLib::bigint(x) << MathLib::bigint(y));
case '>>': case '>>':
if (y >= sizeof(MathLib::bigint) * 8) { if (y >= sizeof(MathLib::bigint) * 8 || y < 0 || x < 0) {
if (error) if (error)
*error = true; *error = true;
return R{}; return R{};
@ -458,8 +471,35 @@ static T calculate(const std::string& s, const T& x, const T& y, bool* error = n
/** 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);
static bool isCompatibleValueTypes(ValueFlow::Value::ValueType x, ValueFlow::Value::ValueType y)
{
static const std::unordered_map<ValueFlow::Value::ValueType,
std::unordered_set<ValueFlow::Value::ValueType, EnumClassHash>,
EnumClassHash>
compatibleTypes = {
{ValueFlow::Value::ValueType::INT,
{ValueFlow::Value::ValueType::FLOAT,
ValueFlow::Value::ValueType::SYMBOLIC,
ValueFlow::Value::ValueType::TOK}},
{ValueFlow::Value::ValueType::FLOAT, {ValueFlow::Value::ValueType::INT}},
{ValueFlow::Value::ValueType::TOK, {ValueFlow::Value::ValueType::INT}},
{ValueFlow::Value::ValueType::ITERATOR_START, {ValueFlow::Value::ValueType::INT}},
{ValueFlow::Value::ValueType::ITERATOR_END, {ValueFlow::Value::ValueType::INT}},
};
if (x == y)
return true;
auto it = compatibleTypes.find(x);
if (it == compatibleTypes.end())
return false;
return it->second.count(y) > 0;
}
static bool isCompatibleValues(const ValueFlow::Value& value1, const ValueFlow::Value& value2) static bool isCompatibleValues(const ValueFlow::Value& value1, const ValueFlow::Value& value2)
{ {
if (value1.isSymbolicValue() && value2.isSymbolicValue() && value1.tokvalue->exprId() != value2.tokvalue->exprId())
return false;
if (!isCompatibleValueTypes(value1.valueType, value2.valueType))
return false;
if (value1.isKnown() || value2.isKnown()) if (value1.isKnown() || value2.isKnown())
return true; return true;
if (value1.isImpossible() || value2.isImpossible()) if (value1.isImpossible() || value2.isImpossible())
@ -744,15 +784,18 @@ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* se
continue; continue;
ValueFlow::Value result(0); ValueFlow::Value result(0);
combineValueProperties(value1, value2, &result); combineValueProperties(value1, value2, &result);
const double floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue; if (astIsFloat(parent, false)) {
const double floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue; if (!result.isIntValue() && !result.isFloatValue())
const bool isFloat = value1.isFloatValue() || value2.isFloatValue(); continue;
if (isFloat && Token::Match(parent, "&|^|%|<<|>>|%or%")) result.valueType = ValueFlow::Value::ValueType::FLOAT;
continue; }
if (Token::Match(parent, "<<|>>") && const double floatValue1 = value1.isFloatValue() ? value1.floatValue : value1.intvalue;
!(value1.intvalue >= 0 && value2.intvalue >= 0 && value2.intvalue < MathLib::bigint_bits)) const double floatValue2 = value2.isFloatValue() ? value2.floatValue : value2.intvalue;
continue; const MathLib::bigint intValue1 =
if (Token::Match(parent, "/|%") && isZero<double>(floatValue2)) value1.isFloatValue() ? static_cast<MathLib::bigint>(value1.floatValue) : value1.intvalue;
const MathLib::bigint intValue2 =
value2.isFloatValue() ? static_cast<MathLib::bigint>(value2.floatValue) : value2.intvalue;
if ((value1.isFloatValue() || value2.isFloatValue()) && Token::Match(parent, "&|^|%|<<|>>|==|!=|%or%"))
continue; continue;
if (Token::Match(parent, "==|!=")) { if (Token::Match(parent, "==|!=")) {
if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) { if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) {
@ -761,28 +804,30 @@ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* se
else if (parent->str() == "!=") else if (parent->str() == "!=")
result.intvalue = 1; result.intvalue = 1;
} else if (value1.isIntValue() && value2.isIntValue()) { } else if (value1.isIntValue() && value2.isIntValue()) {
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue); bool error = false;
result.intvalue = calculate(parent->str(), intValue1, intValue2, &error);
if (error)
continue;
} else { } else {
continue; continue;
} }
setTokenValue(parent, result, settings); setTokenValue(parent, result, settings);
} else if (Token::Match(parent, "%comp%")) {
if (!isFloat && !value1.isIntValue() && !value2.isIntValue())
continue;
if (isFloat)
result.intvalue = calculate(parent->str(), floatValue1, floatValue2);
else
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue);
setTokenValue(parent, result, settings);
} else if (Token::Match(parent, "%op%")) { } else if (Token::Match(parent, "%op%")) {
if (value1.isTokValue() || value2.isTokValue()) if (Token::Match(parent, "%comp%")) {
break; if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue())
if (isFloat) { continue;
result.valueType = ValueFlow::Value::ValueType::FLOAT;
result.floatValue = calculate(parent->str(), floatValue1, floatValue2);
} else { } else {
result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue); if (value1.isTokValue() || value2.isTokValue())
break;
} }
bool error = false;
if (result.isFloatValue()) {
result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error);
} else {
result.intvalue = calculate(parent->str(), intValue1, intValue2, &error);
}
if (error)
continue;
// If the bound comes from the second value then invert the bound when subtracting // If the bound comes from the second value then invert the bound when subtracting
if (Token::simpleMatch(parent, "-") && value2.bound == result.bound && if (Token::simpleMatch(parent, "-") && value2.bound == result.bound &&
value2.bound != ValueFlow::Value::Bound::Point) value2.bound != ValueFlow::Value::Bound::Point)
@ -945,14 +990,13 @@ static void setTokenValueCast(Token *parent, const ValueType &valueType, const V
setTokenValue(parent, castValue(value, valueType.sign, settings->long_bit), settings); setTokenValue(parent, castValue(value, valueType.sign, settings->long_bit), settings);
else if (valueType.type == ValueType::Type::LONGLONG) else if (valueType.type == ValueType::Type::LONGLONG)
setTokenValue(parent, castValue(value, valueType.sign, settings->long_long_bit), settings); setTokenValue(parent, castValue(value, valueType.sign, settings->long_long_bit), settings);
else if (valueType.isFloat()) { else if (valueType.isFloat() && isNumeric(value)) {
ValueFlow::Value floatValue = value; ValueFlow::Value floatValue = value;
floatValue.valueType = ValueFlow::Value::ValueType::FLOAT; floatValue.valueType = ValueFlow::Value::ValueType::FLOAT;
if (value.isIntValue()) if (value.isIntValue())
floatValue.floatValue = value.intvalue; floatValue.floatValue = value.intvalue;
setTokenValue(parent, floatValue, settings); setTokenValue(parent, floatValue, settings);
} } else if (value.isIntValue()) {
else if (value.isIntValue()) {
const long long charMax = settings->signedCharMax(); const long long charMax = settings->signedCharMax();
const long long charMin = settings->signedCharMin(); const long long charMin = settings->signedCharMin();
if (charMin <= value.intvalue && value.intvalue <= charMax) { if (charMin <= value.intvalue && value.intvalue <= charMax) {

View File

@ -826,6 +826,12 @@ private:
ASSERT(tokenValues(";10>>-1;",">>").empty()); ASSERT(tokenValues(";10>>-1;",">>").empty());
ASSERT(tokenValues(";10>>64;",">>").empty()); ASSERT(tokenValues(";10>>64;",">>").empty());
code = "float f(const uint16_t& value) {\n"
" const uint16_t uVal = value; \n"
" return static_cast<float>(uVal) / 2;\n"
"}\n";
ASSERT_EQUALS(true, tokenValues(code, "/").empty());
// calculation using 1,2 variables/values // calculation using 1,2 variables/values
code = "void f(int x) {\n" code = "void f(int x) {\n"
" a = x+456;\n" " a = x+456;\n"