## Why is this an issue?

In Java, numeric promotions happen when two operands of an arithmetic expression have different sizes. More specifically, narrower operands get promoted to the type of wider operands. For instance, an operation between a `byte` and an `int`, will trigger a promotion of the `byte` operand, converting it into an `int`.

When this happens, the sequence of 8 bits that represents the `byte` will need to be extended to match the 32-bit long sequence that represents the `int` operand. Since Java uses two’s complement notation for signed number types, the promotion will fill the missing leading bits with zeros or ones, depending on the sign of the value. For instance, the byte `0b1000_0000` (equal to `-128` in decimal notation), when promoted to `int`, will become `0b1111_1111_1111_1111_1111_1111_1000_0000`.

When performing shifting or bitwise operations without considering that bytes are signed, the bits added during the promotion may have unexpected effects on the final result of the operations.

## How to fix it

This rule raises an issue any time a `byte` value is used as an operand combined with shifts without being masked.

To prevent such accidental value conversions, you can mask promoted bytes to only consider the least significant 8 bits. Masking can be achieved with the bitwise AND operator `&` and the appropriate mask of `0xff` (255 in decimal and `0b1111_1111` in binary) or, since Java 8, with the more convenient `Byte.toUnsignedInt(byte b)` or `Byte.toUnsignedLong(byte b)`.

### Code examples

#### Noncompliant code example

      public static void main(String[] args) {
        byte[] bytes12 = BigInteger.valueOf(12).toByteArray(); // This byte array will be simply [12]
        System.out.println(intFromBuffer(bytes12)); // In this case, the bytes promotion will not cause any issues, and "12" will be printed.

        // Here the bytes will be [2, -128] since 640 in binary is represented as 0b0000_0010_1000_0000
        // which is equivalent to the concatenation of 2 bytes: 0b0000_0010 = 2, and 0b1000_0000 = -128
        byte[] bytes640 = BigInteger.valueOf(640).toByteArray();

        // In this case, the shifting operation combined with the bitwise OR, will produce the wrong binary string and "-128" will be printed.
        System.out.println(intFromBuffer(bytes640));
      }

      static int intFromBuffer(byte[] bytes) {
        int originalInt = 0;
        for (int i = 0; i < bytes.length; i++) {
          // Here the right operand of the bitwise OR, which is a byte, will be promoted to an `int`
          // and if its value was negative, the added ones in front of the binary string will alter the value of the `originalInt`
          originalInt = (originalInt << 8) | bytes[i]; // Noncompliant
        }
        return originalInt;
      }

#### Compliant solution

      public static void main(String[] args) {
        byte[] bytes12 = BigInteger.valueOf(12).toByteArray(); // This byte array will be simply [12]
        System.out.println(intFromBuffer(bytes12)); // In this case, the bytes promotion will not cause any issues, and "12" will be printed.

        // Here the bytes will be [2, -128] since 640 in binary is represented as 0b0000_0010_1000_0000
        // which is equivalent to the concatenation of 2 bytes: 0b0000_0010 = 2, and 0b1000_0000 = -128
        byte[] bytes640 = BigInteger.valueOf(640).toByteArray();

        // This will correctly print "640" now.
        System.out.println(intFromBuffer(bytes640));
      }

      static int intFromBuffer(byte[] bytes) {
        int originalInt = 0;
        for (int i = 0; i < bytes.length; i++) {
          originalInt = (originalInt << 8) | Byte.toUnsignedInt(bytes[i]); // Compliant, only the relevant 8 least significant bits will affect the bitwise OR
        }
        return originalInt;
      }

## References

- CERT, NUM52-J. - Be aware of numeric promotion behavior
- Wikipedia - Two’s complement