Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions lib/checkcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,180 @@ void CheckConditionImpl::assignmentInCondition(const Token *eq)
Certainty::normal);
}

static bool getIntegerTypeRange(const Token* tok,
const Settings& settings,
MathLib::bigint& lower,
MathLib::bigint& upper)
{
if (!tok || !tok->valueType() || tok->valueType()->pointer)
return false;

std::uint8_t bits = 0;
switch (tok->valueType()->type) {
case ValueType::Type::BOOL:
bits = 1;
break;
case ValueType::Type::CHAR:
bits = settings.platform.char_bit;
break;
case ValueType::Type::SHORT:
bits = settings.platform.short_bit;
break;
case ValueType::Type::INT:
bits = settings.platform.int_bit;
break;
case ValueType::Type::LONG:
bits = settings.platform.long_bit;
break;
case ValueType::Type::LONGLONG:
bits = settings.platform.long_long_bit;
break;
default:
return false;
}
if (bits == 0 || bits > 64)
return false;

if (bits == 64 && tok->valueType()->sign == ValueType::Sign::UNSIGNED)
return false;
const MathLib::bigint max = bits == 64 ? std::numeric_limits<MathLib::bigint>::max() : (MathLib::bigint(1) << bits) - 1;
if (tok->valueType()->sign == ValueType::Sign::SIGNED) {
if (bits == 64) {
lower = std::numeric_limits<MathLib::bigint>::min();
upper = std::numeric_limits<MathLib::bigint>::max();
} else {
lower = -(MathLib::bigint(1) << (bits - 1));
upper = max / 2;
}
} else {
lower = 0;
upper = max;
}
return true;
}

static bool getIntegerExpressionRange(const Token* tok,
const Settings& settings,
MathLib::bigint& lower,
MathLib::bigint& upper)
{
if (!tok)
return false;
if (tok->hasKnownIntValue()) {
lower = upper = tok->getKnownIntValue();
return true;
}

if (tok->isCast() && tok->astOperand1()) {
MathLib::bigint typeLower;
MathLib::bigint typeUpper;
const bool hasSourceRange = getIntegerExpressionRange(tok->astOperand1(), settings, lower, upper);
const bool hasTypeRange = getIntegerTypeRange(tok, settings, typeLower, typeUpper);
if (!hasSourceRange && !hasTypeRange)
return false;
if (!hasSourceRange) {
lower = typeLower;
upper = typeUpper;
return true;
}
if (!hasTypeRange)
return true;
lower = std::max(lower, typeLower);
upper = std::min(upper, typeUpper);
return lower <= upper;
}

if (Token::simpleMatch(tok, "(") && tok->astOperand1())
return getIntegerExpressionRange(tok->astOperand1(), settings, lower, upper);

if (Token::Match(tok, "+|-")) {
MathLib::bigint operandLower;
MathLib::bigint operandUpper;
MathLib::bigint constantLower;
MathLib::bigint constantUpper;
if (!getIntegerExpressionRange(tok->astOperand1(), settings, operandLower, operandUpper) ||
!getIntegerExpressionRange(tok->astOperand2(), settings, constantLower, constantUpper) ||
constantLower != constantUpper)
return false;
const MathLib::bigint minimum = std::numeric_limits<MathLib::bigint>::min();
const MathLib::bigint maximum = std::numeric_limits<MathLib::bigint>::max();
if (tok->str() == "+" &&
((constantLower > 0 && operandUpper > maximum - constantLower) ||
(constantLower < 0 && operandLower < minimum - constantLower)))
return false;
if (tok->str() == "-" &&
((constantUpper > 0 && operandLower < minimum + constantUpper) ||
(constantUpper < 0 && operandUpper > maximum + constantUpper)))
return false;
if (tok->str() == "+") {
lower = operandLower + constantLower;
upper = operandUpper + constantUpper;
} else {
lower = operandLower - constantUpper;
upper = operandUpper - constantLower;
}
return true;
}

return getIntegerTypeRange(tok, settings, lower, upper);
}

static bool compareIntegerRange(const Token* comparison,
MathLib::bigint lower,
MathLib::bigint upper,
MathLib::bigint value,
bool& result)
{
if (!comparison || !comparison->isComparisonOp())
return false;
if (comparison->str() == "<") {
if (upper < value)
result = true;
else if (lower >= value)
result = false;
else
return false;
} else if (comparison->str() == "<=") {
if (upper <= value)
result = true;
else if (lower > value)
result = false;
else
return false;
} else if (comparison->str() == ">") {
if (lower > value)
result = true;
else if (upper <= value)
result = false;
else
return false;
} else if (comparison->str() == ">=") {
if (lower >= value)
result = true;
else if (upper < value)
result = false;
else
return false;
} else if (comparison->str() == "==") {
if (lower == upper)
result = (lower == value);
else if (value < lower || value > upper)
result = false;
else
return false;
} else if (comparison->str() == "!=") {
if (lower == upper)
result = (lower != value);
else if (value < lower || value > upper)
result = true;
else
return false;
} else {
return false;
}
return true;
}

void CheckConditionImpl::checkCompareValueOutOfTypeRange()
{
if (!mSettings.severity.isEnabled(Severity::style) && !mSettings.isPremiumEnabled("compareValueOutOfTypeRangeError"))
Expand All @@ -1988,6 +2162,26 @@ void CheckConditionImpl::checkCompareValueOutOfTypeRange()
if (!tok->isComparisonOp() || !tok->isBinaryOp())
continue;

for (int i = 0; i < 2; ++i) {
const Token* const expressionTok = (i == 0) ? tok->astOperand2() : tok->astOperand1();
const Token* const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2();
if (!expressionTok || !valueTok || !valueTok->hasKnownIntValue() || expressionTok->hasKnownIntValue())
continue;
if (expressionTok->str() != "(" && !expressionTok->isCast() && !expressionTok->isArithmeticalOp())
continue;
MathLib::bigint lower;
MathLib::bigint upper;
if (!getIntegerExpressionRange(expressionTok, mSettings, lower, upper))
continue;
bool result;
if (!compareIntegerRange(tok, lower, upper, valueTok->getKnownIntValue(), result) || diag(tok))
continue;
compareValueOutOfTypeRangeError(valueTok,
expressionTok->valueType() ? expressionTok->valueType()->str() : "",
valueTok->getKnownIntValue(),
result);
}

for (int i = 0; i < 2; ++i) {
const Token * const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2();
const Token * const typeTok = valueTok->astSibling();
Expand Down
39 changes: 39 additions & 0 deletions test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6546,6 +6546,45 @@ class TestCondition : public TestFixture {
"[test.cpp:4:13]: (style) Comparing expression of type 'const unsigned int &' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("typedef unsigned int uint32;\n"
"typedef unsigned long long uint64;\n"
"typedef long long sint64;\n"
"void f(uint32 x) {\n"
" uint64 tmp = ((uint64)x) + 1ULL;\n"
" if (tmp > 4294967295ULL)\n"
" tmp = 4294967295ULL;\n"
" if ((((sint64)((uint32)tmp)) - 1LL) < 0LL) {}\n"
" if ((((sint64)((uint32)tmp)) - 1LL) > 4294967295LL) {}\n"
"}\n", settingsUnix64);
ASSERT_EQUALS("[test.cpp:9:43]: (style) Comparing expression of type 'signed long long' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n",

@chrchr-github chrchr-github Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text/ID of the warning seems incorrect. 4294967295 is within range for signed long long.

errout_str());

// cast directly around variable: both the range-based and the declared-type
// analysis can prove the condition invariant; diag() must prevent duplicates
check("void f(unsigned char c) {\n"
" if ((unsigned char)c > 255) {}\n"
"}\n", settingsUnix64);
ASSERT_EQUALS("[test.cpp:2:28]: (style) Comparing expression of type 'unsigned char' against value 255. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("void f(unsigned char c) {\n"
" if ((unsigned char)c == 256) {}\n"
"}\n", settingsUnix64);
ASSERT_EQUALS("[test.cpp:2:29]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("void f(unsigned int u) {\n"
" if ((unsigned int)u > 4294967295ULL) {}\n"
"}\n", settingsUnix64);
ASSERT_EQUALS("[test.cpp:2:27]: (style) Comparing expression of type 'unsigned int' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("void f(unsigned short s) {\n"
" if ((unsigned int)s > 4294967295ULL) {}\n"
"}\n", settingsUnix64);
ASSERT_EQUALS("[test.cpp:2:27]: (style) Comparing expression of type 'unsigned int' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("void f() {\n"
" long long ll = 1024 * 1024 * 1024;\n"
" if (ll * 8 < INT_MAX) {}\n"
Expand Down
Loading