Spaces:
Sleeping
Sleeping
| from .call_graph import analyze_local_call_graph | |
| from .node_types import ( | |
| BOOLEAN_OPERATOR, | |
| BREAK_STATEMENT, | |
| CLASS_DEFINITION, | |
| CONDITIONAL_EXPRESSION, | |
| CONTINUE_STATEMENT, | |
| ELIF_CLAUSE, | |
| ELSE_CLAUSE, | |
| EXCEPT_CLAUSE, | |
| FOR_STATEMENT, | |
| FUNCTION_DEFINITION, | |
| IF_STATEMENT, | |
| LAMBDA, | |
| TRY_STATEMENT, | |
| WHILE_STATEMENT, | |
| WITH_STATEMENT, | |
| ) | |
| PENALIZED_TYPES = { | |
| IF_STATEMENT, | |
| FOR_STATEMENT, | |
| WHILE_STATEMENT, | |
| EXCEPT_CLAUSE, | |
| } | |
| PLAIN_INCREMENT_TYPES = { | |
| ELIF_CLAUSE, | |
| ELSE_CLAUSE, | |
| WITH_STATEMENT, | |
| BREAK_STATEMENT, | |
| CONTINUE_STATEMENT, | |
| CONDITIONAL_EXPRESSION, | |
| } | |
| NESTING_PARENTS = { | |
| IF_STATEMENT, | |
| ELIF_CLAUSE, | |
| ELSE_CLAUSE, | |
| FOR_STATEMENT, | |
| WHILE_STATEMENT, | |
| TRY_STATEMENT, | |
| EXCEPT_CLAUSE, | |
| "finally_clause", | |
| WITH_STATEMENT, | |
| LAMBDA, | |
| } | |
| def _boolean_sequence_cost(node) -> int: | |
| operators = [] | |
| def collect(current): | |
| for child in current.children: | |
| if child.type in {"and", "or"}: | |
| operators.append(child.type) | |
| elif child.type == BOOLEAN_OPERATOR: | |
| collect(child) | |
| collect(node) | |
| if not operators: | |
| return 0 | |
| return 1 + sum(a != b for a, b in zip(operators, operators[1:])) | |
| def count_recursive_functions(root_node) -> int: | |
| """Count functions in direct or statically resolved indirect cycles.""" | |
| return analyze_local_call_graph(root_node)["recursive_function_count"] | |
| def compute_cognitive_complexity(root_node) -> int: | |
| # Sonar-style cognitive complexity adds one increment for each function | |
| # that participates in direct recursion, regardless of call count. | |
| total = count_recursive_functions(root_node) | |
| def visit(node, nesting): | |
| nonlocal total | |
| if node.type == "ERROR": | |
| return | |
| if node.type in PENALIZED_TYPES: | |
| total += 1 + nesting | |
| elif node.type in PLAIN_INCREMENT_TYPES: | |
| total += 1 | |
| elif node.type == BOOLEAN_OPERATOR and ( | |
| node.parent is None or node.parent.type != BOOLEAN_OPERATOR | |
| ): | |
| total += _boolean_sequence_cost(node) | |
| child_nesting = 0 if node.type in { | |
| FUNCTION_DEFINITION, | |
| CLASS_DEFINITION, | |
| } else nesting | |
| for child in node.children: | |
| next_nesting = child_nesting | |
| if child.type == "block" and node.type in NESTING_PARENTS: | |
| next_nesting += 1 | |
| visit(child, next_nesting) | |
| visit(root_node, 0) | |
| return total | |