James Allardice
James Allardice

Reputation: 165971

Cyclomatic complexity of logically similar code

Consider the following three functions, which all behave in an identical manner, using different code to achieve the same thing (examples are written in JavaScript, and I'm particularly interested in answers as applied to JavaScript, but this question could really apply to any language with similar constructs):

// Random number from 0-9
var x = Math.floor(Math.random() * 10);

// JSHint reports a cyclomatic complexity of 3
function a() {
    if (x === 0) {
        return "First";
    } else if (x === 1 || x === 2) {
        return "Second"; 
    }
    return "Third";
}

// JSHint reports a cyclomatic complexity of 4
function b() {
    switch (x) {
    case 0:
        return "First";
    case 1:
    case 2:
        return "Second";
    default:
        return "Third";
    }
}

// JSHint reports a cyclomatic complexity of 1
function c() {
    return x === 0 ? "First" : x === 1 || x === 2 ? "Second" : "Third";
}

// All three functions return the same value
console.log(a(), b(), c());

The JSComplexity tool reports all three functions to have a complexity of 4, which means the || operators are treated as independant branches, as are fall-through case statements. JSHint doesn't seem to care about the || operator, but it does handle fall-through case statements in the same way. It seems to get the conditional operator completely wrong.

When calculating cyclomatic complexity, should fall-through case statements and logical "or" operators be treated as independent branches? What about ternary conditionals (I believe this is simpler, and JSHint is clearly wrong in this case)? Should all three functions above have the same cyclomatic complexity?

Upvotes: 3

Views: 998

Answers (1)

Xophmeister
Xophmeister

Reputation: 9211

Cyclomatic complexity is the number of linearly independent paths through the code. While fall-through case is vacuous, I believe it is unambiguously a different path. So the question is whether || introduces a new branch?

I'm just thinking out aloud, here, but I think that because JavaScript performs short-circuit evaluation on conditionals, we do indeed get two branches from the disjunction. For example, your a function is equivalent to:

function a() {
    if (x === 0) {
        return "First";
    } else if (x === 1) {
        return "Second"; 
    } else if (x === 2) {
        return "Second";
    } else {
       return "Third";
    }
}

...which has 4 branches, even though two of them perform the same function. (i.e., Linear independence on edges, rather than vertices.) However, if JS didn't do short-circuit evaluation, I would be inclined to consider x === 1 || x === 2 to just invoke one branch.

So, to answer your question, I think all three of your functions should have the same cyclomatic complexity: 4.

Upvotes: 7

Related Questions