
Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
95 lines
2.7 KiB
Plaintext
95 lines
2.7 KiB
Plaintext
Compare integers of mixed signedness safely using ``++std::cmp_*++`` functions to avoid any unexpected results.
|
|
|
|
== Why is this an issue?
|
|
|
|
Comparison between `signed` and `unsigned` integers is dangerous because it produces counter-intuitive results due to implicit conversions.
|
|
When a signed integer is compared to an unsigned one, the former might be converted to unsigned.
|
|
Since {cpp}20, the conversion preserves the two's-complement bit pattern of the signed value that always corresponds to a large unsigned result.
|
|
For example, `2U < -1` is `true`.
|
|
|
|
This rule raises an issue when an unsigned integer is compared with a negative value.
|
|
|
|
=== What is the potential impact
|
|
|
|
Integer comparisons are often used in branch and loop conditions.
|
|
An unexpected result from one of these conditions can lead to hard-to-detect issues, such as unexpected infinite loops.
|
|
|
|
For example, using container size functions in a comparison can lead to such a problem since these return an unsigned integer value.
|
|
|
|
== How to fix it
|
|
|
|
{cpp}20 introduced a remedy to this common pitfall: a family of ``++std::cmp_*++`` functions defined in the `<utility>` header.
|
|
These functions correctly handle negative numbers and lossy integer conversion.
|
|
For example, `std::cmp_less(2U, -1)` is `false`.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,cpp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
bool less = 2U < -1; // Noncompliant
|
|
|
|
unsigned x = 1;
|
|
signed y = -1;
|
|
if (x < y) { // Noncompliant
|
|
// ...
|
|
}
|
|
|
|
bool fun(int x, std::vector<int> const& v) {
|
|
return x < v.size(); // Noncompliant: if x is negative, it is converted to unsigned, losing its value.
|
|
}
|
|
|
|
std::vector<int> v = foo();
|
|
if (-1 < v.size() && v.size() < 100) { // Noncompliant: -1 < v.size() is false for all sizes.
|
|
// -1 converted to unsigned is larger than any int value
|
|
}
|
|
----
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
[source,cpp,diff-id=1,diff-type=compliant]
|
|
----
|
|
bool less = std::cmp_less(2U, -1); // Compliant
|
|
|
|
unsigned x = 1;
|
|
signed y = -1;
|
|
if (std::cmp_less(x, y)) { // Compliant
|
|
// ...
|
|
}
|
|
|
|
bool fun(int x, std::vector<int> const& v) {
|
|
return std::cmp_less(x, v.size()); // Compliant
|
|
}
|
|
|
|
std::vector<int> v = foo();
|
|
if (0 <= v.size() && v.size() < 100) { // Compliant, even though v.size() returns an unsigned integer
|
|
}
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/intcmp[std::cmp_* functions]
|
|
|
|
=== Related rules
|
|
|
|
* S845 - a more generic rule about mixing signed and unsigned values.
|
|
* S6183 - a version of this rule that triggers when any signed and unsigned variables are compared.
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== relates to: S845
|
|
|
|
=== relates to: S6183
|
|
|
|
endif::env-github,rspecator-view[]
|
|
|