Coverage for metrics/cyclomatic/shared.py: 100%

1"""Shared cyclomatic complexity logic for all languages."""

2

3 from __future__ import annotations

4

5

6 def create_cyclomatic_computer(skip_check_fn, decision_types, logical_ops=None):

7 """Factory to create language-specific cyclomatic complexity computer.

8

9 Args:

10 skip_check_fn: Function(node, top_func) -> bool that returns True to skip recursion into a node

11 decision_types: Set/SetList of node types that add 1 to complexity

12 logical_ops: Optional set of token types that count as logical operators (for &&/??)

13 """

14

15 def _count_decisions(node, top_func) -> int:

16 """Recursively count decision points in an AST subtree."""

17 count = 0

18

19 for child in node.children:

20 if skip_check_fn(child, top_func):

21 continue

22

23 if child.type in decision_types:

24 count += 1

25 elif logical_ops and child.type == "binary_expression":

26 count += sum(1 for sub in child.children if sub.type in logical_ops)

27

28 count += _count_decisions(child, top_func)

29

30 return count

31

32 return _count_decisions

33

34

35 def create_cyclomatic_wrapper(count_decisions_fn):

36 """Create compute_cyclomatic function wrapper."""

37

38 def compute_cyclomatic(func_node) -> int:

39 """Compute McCabe cyclomatic complexity for a function node.

40

41 CC = 1 + number of decision points.

42 """

43 return 1 + count_decisions_fn(func_node, top_func=func_node)

44

45 return compute_cyclomatic