How does Sonar calculate the cyclomatic complexity?

19.9k views Asked by At

Sonar gives me the following cyclomatic complexity number : 22.

For the following program :

private static SomeDto checkSomething(AnotherDto anotherDto, String reference)
{
SomeDto someDto = new SomeDto();

// condition 1
if (!someDto.getA())
    return new SomeDto("bla1", "blabla");

// condition 2
if (someDto.getName2() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla2", "blabla");

// condition 3
if (someDto.getName3() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla3", "blabla");

// condition 4
if (someDto.getName4() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla4", "blabla");

// condition 5
if (someDto.getName5() == null || checkSurName(anotherDto.getName()))
    return new SomeDto("bla5", "blabla");

// condition 6
if (someDto.getName6() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla6", "blabla");

// condition 7
if (someDto.getName7() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla7", "blabla");

// condition 8
if (someDto.getName8() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla8", "blabla");

// condition 9
if (someDto.getName9() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla9", "blabla");

// condition 10
if (someDto.getName10() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla10", "blabla");

// condition 11
if (someDto.getName11() == null && checkSurName(anotherDto.getName()))
    return new SomeDto("bla11", "blabla");

return someDto;
}    

The issue i get is the following :

"The Cyclomatic Complexity of this method "checkSomething" is 22 which is greater than 12 authorized."

My question is : considering the McCabe formula V(G) = E - N + 2, how does Sonar reach the number of 22 ?

Where :

E = number of edges

N = number of nodes

How many edges and nodes are there in this method ? What is the control flow for this method ?

We're on SonarQube Version 6.3 (build 19869).

2

There are 2 answers

3
Celinio Fernandes On

Well, after further investigation and according to this link (checkstyle tool), I have concluded that the McCabe formula is not really applied to calculate the cyclomatic complexity in a Java program.

The complexity is equal to the number of decision points + 1 Decision points: if, while , do, for, ?:, catch , switch, case statements, and operators && and || in the body of target.

So if I apply this rule to the previous sample code :

private static SomeDto checkSomething(AnotherDto anotherDto, String reference)  // 1
{
SomeDto someDto = new SomeDto();

// condition 1
if (!someDto.getA())                                                             // 2
return new SomeDto("bla1", "blabla");

// condition 2
if (someDto.getName2() == null || checkSurName(anotherDto.getName()))             // 4
return new SomeDto("bla2", "blabla");

// condition 3
if (someDto.getName3() == null || checkSurName(anotherDto.getName()))             // 6
return new SomeDto("bla3", "blabla");

// condition 4
if (someDto.getName4() == null && checkSurName(anotherDto.getName()))             // 8
return new SomeDto("bla4", "blabla");

// condition 5
if (someDto.getName5() == null || checkSurName(anotherDto.getName()))              // 10
return new SomeDto("bla5", "blabla");

// condition 6
if (someDto.getName6() == null && checkSurName(anotherDto.getName()))              // 12
return new SomeDto("bla6", "blabla");

// condition 7
if (someDto.getName7() == null && checkSurName(anotherDto.getName()))              // 14
return new SomeDto("bla7", "blabla");

// condition 8
if (someDto.getName8() == null && checkSurName(anotherDto.getName()))              // 16
return new SomeDto("bla8", "blabla");

// condition 9
if (someDto.getName9() == null && checkSurName(anotherDto.getName()))              // 18
return new SomeDto("bla9", "blabla");

// condition 10
if (someDto.getName10() == null && checkSurName(anotherDto.getName()))             // 20
return new SomeDto("bla10", "blabla");

// condition 11
if (someDto.getName11() == null && checkSurName(anotherDto.getName()))             // 22
return new SomeDto("bla11", "blabla");

return someDto;
}    

Correct me if i am wrong. Shouldn't the return statements (except the one that is the last statement of the method) be taken into account at least ?

Anyways, the result number of 22 makes sense. This method has an awful number of successive "if" conditions and something should be done about it to improve its maintainability.

0
bluesman80 On

The SonarQube documentation for the latest version clearly states how it calculates the Cyclomatic Complexity:

Complexity (complexity) It is the Cyclomatic Complexity calculated based on the number of paths through the code. Whenever the control flow of a function splits, the complexity counter gets incremented by one. Each function has a minimum complexity of 1. This calculation varies slightly by language because keywords and functionalities do.

And if you open the "Language-specific details" underneath that paragraph, the Java line reads as follows.

Keywords incrementing the complexity: if, for, while, case, catch, throw, &&, ||, ?

Since the OP was using version 6.3 by the time of the question, I also checked the documentation for the oldest version that I could find, which is 6.7. By then, the calculation was slightly different.

Keywords incrementing the complexity: if, for, while, case, catch, throw, return (that is not the last statement of a method), &&, ||, ?

Notes:

else, default, and finally keywords do not increment the complexity.

a simple method with a switch statement and a huge block of case statements can have a surprisingly high complexity value (still it has the same value when converting a switch block to an equivalent sequence of if statements).

Example: the following method has a complexity of 5

public void process(Car myCar){          // +1
    if(myCar.isNotMine()){               // +1
         return;                         // +1
    }
    car.paint("red");
    car.changeWheel();
    while(car.hasGazol() && car.getDriver().isNotStressed()){   // +2
         car.drive();
    }
    return; }