CodeQL queries 1.25
Skip to end of metadata
Go to start of metadata

Name: Average cyclomatic complexity of files

Description: The average cyclomatic complexity of the functions in a file.

ID: cpp/average-cyclomatic-complexity-per-file

Kind: metric

Query: FCyclomaticComplexity.ql
/**
 * @name Average cyclomatic complexity of files
 * @description The average cyclomatic complexity of the functions in a file.
 * @kind treemap
 * @id cpp/average-cyclomatic-complexity-per-file
 * @treemap.warnOn highValues
 * @metricType file
 * @metricAggregate avg max
 * @tags testability
 *       complexity
 */

import cpp

from File f, float complexity, float loc
where
  f.fromSource() and
  loc = sum(FunctionDeclarationEntry fde | fde.getFile() = f | fde.getNumberOfLines()).(float) and
  if loc > 0
  then
    // Weighted average of complexity by function length
    complexity =
      sum(FunctionDeclarationEntry fde |
          fde.getFile() = f
        |
          fde.getNumberOfLines() * fde.getCyclomaticComplexity()
        ).(float) / loc
  else complexity = 0
select f, complexity

This metric measures the average cyclomatic complexity of the functions in a file.

The cyclomatic complexity of a function is the number of linearly independent execution paths through that function. A path is linearly independent path if it differs from all other paths by at least one node. Straight-line code therefore has a cyclomatic complexity of one, while branches, switches and loops increase cyclomatic complexity.

Functions with a high cyclomatic complexity are typically hard to understand and test. By extension, files whose functions have a high average cyclomatic complexity are problematic, and usually would benefit from refactoring.

As a concrete example, consider the following function:

int f(int i, int j) {
    // start
    int result;
    if(i % 2 == 0) {
        // iEven
        result = i + j;
    }
    else {
        // iOdd
        if(j % 2 == 0) {
            // jEven
            result = i * j;
        }
        else {
            // jOdd
            result = i - j;
        }
    }
    return result;
    // end
}

The control flow graph for this function is as follows:

The graph shows that the number of linearly independent execution paths through the function, and hence its cyclomatic complexity, is 3. The three paths are:

  • start -> iEven -> end
  • start -> iOdd -> jEven -> end
  • start -> iOdd -> jOdd -> end
Recommendation

Functions with a high cyclomatic complexity should be simplified, for instance by tidying up any complex logic within them or by splitting them into multiple methods using the Extract Method refactoring.

References