cyclomatic-complexity

Same as cyclomatic_complexity in Pyramid Lint

Path
lint/complexity/cyclomatic-complexity
Default
Off, opt in
Origin
Pyramid Lint
Rule
213 of 512

Flags a function whose cyclomatic complexity exceeds the configured threshold.

Cyclomatic complexity counts the independent paths through a function; a high value means many branches to understand and test. The rule computes complexity as one plus each decision point — if, ternary, &&, ||, ??, loops (for/while/do), catch clauses, non-default cases, and pattern when guards — counted across the whole body tree, so decision points inside nested closures and local functions count toward the enclosing function. It reports at the function name when the total exceeds the threshold. Operator bodies are included. Collection if/for elements are not decision points themselves, but decisions in their conditions and children are counted. Parameter defaults are constant expressions, not runtime body decisions, and are excluded for both the declaration and its nested functions.

Options

max-complexity (integer, default: 20) — flag when the computed complexity exceeds this.

Invalid

example.dartdart
// Each function below exceeds the configured max_complexity of 3.
// Complexity = 1 + decision points (if / && / || / ?? / for / while / case / catch / ternary).

int classifyNumber(int a, int b) {
  if (a > 0) return 1;
  if (b > 0) return 2;
  if (a > b && b > 0) return 3;
  return 0;
}

int loopScan(List<int> xs, int limit) {
  var total = 0;
  for (final x in xs) total += x;
  while (total > limit) total -= 1;
  final capped = total > 0 || limit < 0;
  final safe = total ?? limit;
  return capped ? safe : 0;
}

int nestedTernary(int a, int b, int c) {
  final first = a > 0 ? 1 : (b > 0 ? 2 : 3);
  final second = c > 0 && a > 0 ? 4 : 5;
  return first + second;
}

String describe(int code) {
  switch (code) {
    case 1:
      return 'one';
    case 2:
      return 'two';
    case 3:
      return 'three';
    default:
      return 'other';
  }
}

int guarded(Object value) {
  try {
    if (value == null) return -1;
  } on FormatException {
    return -2;
  } on StateError {
    return -3;
  }
  return value.hashCode > 0 && value.hashCode < 100 ? 1 : 0;
}

class Calculator {
  int compute(int a, int b) {
    if (a > b) return a - b;
    if (a < b) return b - a;
    if (a == 0 || b == 0) return 0;
    return a + b;
  }
}

// Decision points inside a labeled loop must still count.
int labeledBranches(List<int> xs) {
  outer:
  while (xs.isNotEmpty) {
    if (xs.first > 0) return 1;
    if (xs.last < 0) return 2;
    if (xs.length > 5 && xs.first == 0) break outer;
    xs.removeAt(0);
  }
  return 0;
}
Function has a cyclomatic complexity of 5 (max 3).
3
4int classifyNumber(int a, int b) {
Function has a cyclomatic complexity of 6 (max 3).
10
11int loopScan(List<int> xs, int limit) {
Function has a cyclomatic complexity of 5 (max 3).
19
20int nestedTernary(int a, int b, int c) {
Function has a cyclomatic complexity of 4 (max 3).
25
26String describe(int code) {
Function has a cyclomatic complexity of 6 (max 3).
38
39int guarded(Object value) {
Function has a cyclomatic complexity of 5 (max 3).
50class Calculator {
51 int compute(int a, int b) {
Function has a cyclomatic complexity of 6 (max 3).
59// Decision points inside a labeled loop must still count.
60int labeledBranches(List<int> xs) {

Valid

example.dartdart
// Each function stays at or below a cyclomatic complexity of 3
// (at most two decision points), so none trigger the rule.

int addTwo(int a, int b) {
  return a + b;
}

int atMostOne(int a) {
  if (a > 0) return a;
  return 0;
}

int clamp(int a, int max) {
  final over = a > max;
  return over ? max : a;
}

int fallback(int? a, int b) {
  return a ?? b;
}

int sumList(List<int> xs) {
  var total = 0;
  for (final x in xs) total += x;
  return total;
}

class Counter {
  int value = 0;

  int increment() {
    value += 1;
    return value;
  }
}

// Labeled loop whose complexity stays within the limit.
int labeledLow(List<int> xs) {
  loop:
  while (xs.isNotEmpty) {
    xs.removeAt(0);
    continue loop;
  }
  return 0;
}

final outsideDeclaredBody = (bool value) {
  if (value) {
    if (value) {
      if (value) {}
    }
  }
};

class OutsideDeclaredBodies {
  void previous() {}

  final field = (bool value) {
    if (value) {
      if (value) {
        if (value) {}
      }
    }
  };

  final dynamic initialized;

  OutsideDeclaredBodies()
      : initialized = ((bool value) {
          if (value) {
            if (value) {
              if (value) {}
            }
          }
        }) {}
}

Configure cyclomatic-complexity

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "complexity": {
        "cyclomatic-complexity": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/complexity/cyclomatic-complexity: <reason>
  • join-return-with-assignmentjoin_return_with_assignment

    Flags an assignment immediately followed by return of the same variable.

  • max-lines-for-filemax_lines_for_file

    Flags a file longer than the configured line limit.

  • max-lines-for-functionmax_lines_for_function

    Flags a function or method longer than the configured line limit.

  • max-parameters-for-functionmax_parameters_for_function

    Flags callable declarations and function types with more than the configured number of parameters.

  • max-statements

    Reports function bodies with too many statements.

  • max-switch-casesmax_switch_cases

    Flags a switch statement with more than the configured number of cases.

  • maximum-nesting-levelmaximum_nesting_level

    Flags a function whose control-flow nesting runs deeper than the configured level.

  • no-boolean-literal-compareno-boolean-literal-comparerecommended

    Don't compare boolean values to boolean literals.