Skip to content
Open
2 changes: 1 addition & 1 deletion gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ void MainWindow::loadSettings()
}

const ReportType reportType = static_cast<ReportType>(mSettings->value(SETTINGS_REPORT_TYPE, static_cast<int>(ReportType::normal)).toInt());
mUI->mActionReportNormal->setChecked(reportType <= ReportType::normal);
mUI->mActionReportNormal->setChecked(reportType == ReportType::normal);
mUI->mActionReportAutosar->setChecked(reportType == ReportType::autosar);
mUI->mActionReportCertC->setChecked(reportType == ReportType::certC);
mUI->mActionReportCertCpp->setChecked(reportType == ReportType::certCpp);
Expand Down
2 changes: 1 addition & 1 deletion lib/checkclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2032,7 +2032,7 @@ void CheckClassImpl::virtualDestructor()
}

// Check if destructor is empty and non-empty ..
if (mSettings.standards.cpp <= Standards::CPP03) {
if (mSettings.standards.cpp == Standards::CPP03) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI review

This edit and the one in gui/mainwindow.cpp look like workarounds for the new unsignedLessThanZero that the PR produces on x <= Enum::FirstValue. User code with the same pattern will get the same warning (reproduced, see the comment in symboldatabase.cpp).

The original <= Standards::CPP03 was reasonable code. I would treat the need to rewrite it as a sign that the warning should be suppressed for enumerators, not as something to fix at each use.

// Find the destructor
const Function *destructor = scope->getDestructor();

Expand Down
17 changes: 12 additions & 5 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7554,8 +7554,13 @@ static const Token* parsedecl(const Token* type,
valuetype->sign = ValueType::Sign::SIGNED;
else if (enum_type->isUnsigned())
valuetype->sign = ValueType::Sign::UNSIGNED;
else
valuetype->sign = defaultSignedness; // TODO: this is implementation-dependent might be separate from char
else if (!enum_type->isStandardType()) {
ValueType evt;
if (parsedecl(enum_type, &evt, defaultSignedness, settings))
*valuetype = evt;
else
valuetype->sign = defaultSignedness; // TODO: this is implementation-dependent might be separate from char
}
const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong());
if (t != ValueType::Type::UNKNOWN_TYPE)
valuetype->type = t;
Expand Down Expand Up @@ -7728,13 +7733,15 @@ static const Token* parsedecl(const Token* type,
valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD;
else if (!valuetype->typeScope && type->type() && type->type()->classScope && valuetype->type != ValueType::Type::SMART_POINTER) {
if (type->type()->classScope->type == ScopeType::eEnum) {
valuetype->sign = ValueType::Sign::SIGNED;
valuetype->type = getEnumType(type->type()->classScope, settings.platform);
if (valuetype->type == ValueType::Type::UNKNOWN_TYPE) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI review

Keeping the unsigned underlying type here makes checkSignOfUnsignedVariable() fire on enum comparisons against the first enumerator. Base reports none of these:

enum class Color : std::uint8_t { Red, Green, Blue };
bool isFirst(Color c) { return c <= Color::Red; }   // unsignedLessThanZero: Checking if unsigned expression 'c' is less than zero.
enum class U : unsigned { Red, Green };
bool isValid3(U c) { return c >= U::Red; }           // unsignedPositive: Unsigned expression 'c' can't be negative ...
for (Color c = Color::Blue; c >= Color::Red; ...)    // unsignedPositive

It also affects enum class X : unsigned char, which base did not warn about either. Comparing against an alias of the same value (First = Red, c >= Color::First) is not reported, so the result depends on the enumerator name.

I think these are false positives. The enumerator is zero only because it is declared first, the comparison is written in terms of enumerators and not 0, and range checks like c >= E::First are common. The message about "less than zero" / "negative" does not fit an enum.

Please consider skipping these checks when the zero operand is an enumerator, or when the other operand has an enum typeScope. Either way, add TestOther tests that pin down the intended behaviour.

valuetype->sign = ValueType::Sign::SIGNED;
valuetype->type = getEnumType(type->type()->classScope, settings.platform);
}
} else {
valuetype->type = ValueType::Type::RECORD;
}
valuetype->typeScope = type->type()->classScope;
} else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U)
} else if (type->isNameOnly() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U)
return nullptr;
else if (Token::Match(type->previous(), "!!:: %name% !!::")) {
if (!type->isKeyword())
Expand Down
7 changes: 7 additions & 0 deletions test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6650,6 +6650,13 @@ class TestCondition : public TestFixture {
"[test.cpp:2:28]: (style) Comparing expression of type 'signed int' against value 2147483647. Condition is always false. [compareValueOutOfTypeRangeError]\n"
"[test.cpp:7:17]: (style) Comparing expression of type 'signed int' against value 2147483647. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());

check("enum E : std::uint8_t {};\n" // #11243
"void f(E e) {\n"
" if (e == 256) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:14]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false. [compareValueOutOfTypeRangeError]\n",
errout_str());
}

void knownConditionCast() {
Expand Down
5 changes: 2 additions & 3 deletions test/testio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3497,9 +3497,8 @@ class TestIO : public TestFixture {
"void f(E e) {\n"
" printf(\"%lu\", e);\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'uint8_t'.\n",
"",
errout_str());
ASSERT_EQUALS("[test.cpp:3]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned char'.\n",
errout_str());

check("void f() {\n"
" printf(\"%lu\", sizeof(char));\n"
Expand Down
2 changes: 2 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9910,6 +9910,8 @@ class TestSymbolDatabase : public TestFixture {
ASSERT_EQUALS("unsigned long", typeOf("enum E : unsigned long { }; void foo() { E e[3]; bar(e[0]); }\n", "[ 0"));
ASSERT_EQUALS("signed long long", typeOf("enum E : long long { }; void foo() { E e[3]; bar(e[0]); }\n", "[ 0"));
ASSERT_EQUALS("unsigned long long", typeOf("enum E : unsigned long long { }; void foo() { E e[3]; bar(e[0]); }\n", "[ 0"));
ASSERT_EQUALS("unsigned char", typeOf("enum E : std::uint8_t {}; E e;\n", "e", /*cpp*/ true, &settings1));
ASSERT_EQUALS("bool", typeOf("enum E : bool {}; E e;\n", "e"));

#define CHECK_LIBRARY_FUNCTION_RETURN_TYPE(type) do { \
const char xmldata[] = "<?xml version=\"1.0\"?>\n" \
Expand Down
Loading