rspec/rules/S2183/csharp/rule.adoc
2023-07-04 15:35:17 +02:00

78 lines
3.1 KiB
Plaintext

== Why is this an issue?
The https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#left-shift-operator-[shifting] operators are used to do an https://en.wikipedia.org/wiki/Arithmetic_shift[arithmetic shift] to the bits of an https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types[integral numeric] value, either to the left or the right.
[source,csharp]
----
var number = 14; // ...01110 (14)
var left = number << 1; // ...11100 (28)
var right = number >> 1; // ...00111 (7)
----
Therefore, shifting an integral number by 0 is equivalent to doing nothing, since the bits do not move any positions to the left or the right.
On the other hand, shifting an integral number by a value greater than their count of bits minus one (`n_bits-1`) is equivalent to shifting by the value https://en.wikipedia.org/wiki/Modulo[modulo] the bit count of the number (`value % n_bits`).
In the case of `int` and `uint`, which take 32 bits in the memory, the shift count is given by the five low-order bits of the second operand, which can represent numbers from 0 to 31. This means that numbers having the same five low-order bits are treated the same by the shift operators.
[source,csharp]
----
var one = 0b0_00001;
var thirtyThree = 0b1_00001; // Same five low-order bits, 33 % 32 = 1
var shifted1 = 42 << one; // Results in 84
var shifted2 = 42 << thirtyThree; // Results in 84
----
Note that integral number with a less than 32-bit quantity (e.g. `short`, `ushort`) are implicitly converted to `int` before the shifting operation and so the rule for `int`/`uint` applies.
If the first operand is a `long` or `ulong` (64-bit quantity), the shift count is given by the six low-order bits of the second operand. That is, the actual shift count is 0 to 63 bits.
include::../exceptions.adoc[]
== How to fix it
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=1,diff-type=noncompliant]
----
short s = 1;
short shortShift1 = (short)(s << 0); // Noncompliant: the value does not change
short shortShift2 = (short)(s << 33); // Noncompliant: this is equivalent to shifting by 1
int i = 1;
int intShift = i << 33; // Noncompliant: this is equivalent to shifting by 1
long lg = 1;
long longShift1 = lg << 0; // Noncompliant: the value does not change
long longShift2 = lg << 65; // Noncompliant: this is equivalent to shifting by 1
----
==== Compliant solution
[source,csharp,diff-id=1,diff-type=compliant]
----
short s = 1;
short shortShift1 = s;
short shortShift2 = (short)(s << 1);
int i = 1;
var intShift = i << 1;
long lg = 1;
var longShift1 = lg;
var longShift2 = lg << 1;
----
== Resources
=== Documentation
* Microsoft Learn - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#left-shift-operator-[Bitwise and shift operators (C# reference)]
* Wikipedia - https://en.wikipedia.org/wiki/Arithmetic_shift[Arithmetic shift]
* Wikipedia - https://en.wikipedia.org/wiki/Modulo[Modulo]
include::../rspecator.adoc[]