Java XOR operator (^) is not taken into account in cognitive complexity

How to repoduce

  • SonarLint plugin in IntelliJ (plugin version 4.10.0.19739)
  • Write a method with high enough cognitive complexity to trigger the issue
  • For me the following example does the trick with 20 congitive complexity
  • In the issue options, click Highlight all locations involved in this issue
  • The operators && and ||get highlighted with SonarLint: +1, but ^ does not.

Example

String foo(boolean a, boolean b) {
  String s = null;
  if (a ^ b) // '^' not highlighted for complexity
    s = "xor";
  if (a && b) // '&&' highlighted for complexity
    s = "and";
  if (a || b)// '||' highlighted for complexity
    s = "or";
  
  // increase cognitive complexity quickly with nested if statements
  if (true){
    if (true){
      if (true){
        if (true){
          if (true){
          }
        }
      }
    }
  }
  
  return s;
}

Hi Tobias,

I never saw the java xor operator used with boolean operands in real code, but only with numeric operands.
Do you really use if (a ^ b) instead of if (a != b) ? Do you have a real life use case?

IMO a ^ b is a mischievous way to compare a != b and we don’t increase the complexity for such comparison.

Furthermore, && and || operators create branches in the control flow of the program that increase the complexity, e.g.:

if(a() && b()) // when a() return false, b() is not called
if(a() || b()) // when a() return true, b() is not called

Contrary to != and ^ operators that always require evaluation of their operands:

if(a() != b()) // a() and b() are always called
if(a() ^ b()) // a() and b() are always called

Hi Alban,

you’re right, I could’ve just used != instead of ^, but in my use case I felt that the explicit xor was more intuitively readable. It was something like

if ((a != 0) ^ (b != null))

Using the != operator here creates nested equalities which feel more complex to me. I also don’t think of != as a logical operator.

Regarding cognitive complexity, I feel like any logical operation increases it slightly.

Best wishes
Tobias

Hi Tobias,

I agree that boolean logical operators &, ^, and | increase the cognitive complexity. But I would prefer having a rule to prevent the usage of 15.22.2. Boolean Logical Operators &, ^, and | instead of spending time to improving the complexity metric.
My proposal would be to improve RSPEC-2178 to not only support & → && and | → || but also ^ → !=.
Because I never saw such usage in real code:

  int foo(boolean a, boolean b, boolean c, boolean d) {
    if (a ^ b | c & d) { // Noncompliant \o/
      return 42;
    }
    return 0;
  }

Hi Alban :slight_smile:

I don’t think expanding RSPEC-2178 is the right call here. That rule is concerned with short circuiting which does not apply here.

There seems to be not technical advantage to using != or ^. The only difference seems to be personal preference and personal ease of understanding as seen in the comments to answers here.

I personally would change to cognitive complexity to include all three Boolean logical operators and possibly even != when comparing booleans as equivalent options should be treated the same.

Best wishes,
Tobias

Hi Tobias,

You are right, RSPEC-2178 is about short-circuiting so my proposal to include ^ → != was not a good idea. Too bad, it would have been a oneliner change.

About increasing the cognitive complexity, I will try to collect other opinions to be sure that it worth the effort to change it, I don’t want to increase the logic just for a corner case.

The change could be, given a binary operator having boolean operands:

  • +1 for & and | using the same logic than && and || (consecutive series of the same operator only counts as +1)
  • +1 for each ^ and !=

Hi Alban,

I think your proposed change is good. I will be on the lookout for other things that should increase cognitive complexity.

Best wishes
Tobi

I heard a remark about:

we should not treat != differently than ==

And I agree.

I think we could increase the complexity by +1 for ^ , != and == when at least one of the operand is also a binary expression, when one binary expression combine others:

if ((a < 2) ^ (b < 4))
if ((a < 2) != (b < 4))
if ((a < 2) == (b < 4))

But if no operand is a binary expression, even for boolean operands, I think we should not increase the complexity for == in this case, for example:

if (a == b)

And to be consistent, I think we should not increase the complexity for != and ^ in the same case.

My new proposal is, given a binary operator having boolean operands:

  • +1 for & and | using the same logic than && and || (consecutive series of the same operator only counts as +1)
  • +1 for ^, != and == when at least one operand is also a binary expression (binary expression combination)

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.