Coverage for metrics / npath / go.py: 89%
132 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 15:04 -0800
1"""NPath complexity for Go."""
3from __future__ import annotations
5from common import _count_bool_ops
6from parsers.go import is_named_func_literal
9def compute_npath_go(func_node) -> int:
10 """Compute NPath complexity for a Go function node."""
11 body = func_node.child_by_field_name("body")
12 if body and body.type == "block":
13 return _npath_block_go(body, func_node)
14 for child in func_node.children:
15 if child.type == "block":
16 return _npath_block_go(child, func_node)
17 return 1
20def _npath_block_go(block, top_func) -> int:
21 """Multiply NPath of each child statement in a block."""
22 result = 1
23 for child in block.children:
24 if child.type == "statement_list":
25 for stmt in child.children:
26 result *= _npath_stmt_go(stmt, top_func)
27 elif child.type not in ("{", "}"):
28 result *= _npath_stmt_go(child, top_func)
29 return result
32def _npath_stmt_go(node, top_func) -> int:
33 """Dispatch a statement node to its NPath handler."""
34 if node.type in ("function_declaration", "method_declaration") and node is not top_func:
35 return 1
36 if node.type == "func_literal" and node is not top_func:
37 if is_named_func_literal(node):
38 return 1
39 return compute_npath_go(node)
41 if node.type == "if_statement":
42 return _npath_if_go(node, top_func)
43 if node.type == "for_statement":
44 return _npath_loop_go(node, top_func)
45 if node.type in ("expression_switch_statement", "type_switch_statement"):
46 return _npath_switch_go(node, top_func)
47 if node.type == "select_statement":
48 return _npath_select_go(node, top_func)
49 if node.type == "block":
50 return _npath_block_go(node, top_func)
51 if node.type in ("expression_statement", "return_statement",
52 "short_var_declaration", "assignment_statement",
53 "go_statement", "defer_statement", "send_statement"):
54 return _npath_expr_go(node, top_func)
55 return 1
58def _npath_if_go(node, top_func) -> int:
59 """NPath for Go if_statement using consequence/alternative fields."""
60 total = 0
61 has_else = False
63 condition = node.child_by_field_name("condition")
64 bool_ops = _count_bool_ops(condition) if condition else 0
66 consequence = node.child_by_field_name("consequence")
67 if consequence and consequence.type == "block":
68 total += _npath_block_go(consequence, top_func)
70 alternative = node.child_by_field_name("alternative")
71 if alternative is not None:
72 has_else = True
73 if alternative.type == "if_statement":
74 total += _npath_if_go(alternative, top_func)
75 elif alternative.type == "block":
76 total += _npath_block_go(alternative, top_func)
78 if not has_else:
79 total += 1
81 total += bool_ops
82 return total
85def _npath_loop_go(node, top_func) -> int:
86 """NPath for Go for_statement (covers for, for-range, infinite loop)."""
87 body_npath = 1
88 bool_ops = 0
90 body = node.child_by_field_name("body")
91 if body and body.type == "block":
92 body_npath = _npath_block_go(body, top_func)
94 condition = node.child_by_field_name("condition")
95 if condition:
96 bool_ops = _count_bool_ops(condition)
98 return body_npath + 1 + bool_ops
101def _npath_switch_go(node, top_func) -> int:
102 """NPath for expression_switch_statement / type_switch_statement."""
103 total = 0
104 has_default = False
105 for child in node.children:
106 if child.type in ("expression_case", "type_case"):
107 case_npath = 1
108 for sub in child.children:
109 if sub.type in ("expression_list", "type_list", "case", "default", ":"):
110 continue
111 sub_np = _npath_stmt_go(sub, top_func)
112 if sub_np > 1:
113 case_npath *= sub_np
114 total += case_npath
115 elif child.type == "default_case":
116 has_default = True
117 case_npath = 1
118 for sub in child.children:
119 if sub.type in ("default", ":"):
120 continue
121 sub_np = _npath_stmt_go(sub, top_func)
122 if sub_np > 1:
123 case_npath *= sub_np
124 total += case_npath
125 if not has_default:
126 total += 1
127 return max(total, 1)
130def _npath_select_go(node, top_func) -> int:
131 """NPath for select_statement with communication_case / default_case."""
132 total = 0
133 has_default = False
134 for child in node.children:
135 if child.type == "communication_case":
136 case_npath = 1
137 for sub in child.children:
138 if sub.type in ("case", "default", ":"):
139 continue
140 sub_np = _npath_stmt_go(sub, top_func)
141 if sub_np > 1:
142 case_npath *= sub_np
143 total += case_npath
144 elif child.type == "default_case":
145 has_default = True
146 case_npath = 1
147 for sub in child.children:
148 if sub.type in ("default", ":"):
149 continue
150 sub_np = _npath_stmt_go(sub, top_func)
151 if sub_np > 1:
152 case_npath *= sub_np
153 total += case_npath
154 if not has_default:
155 total += 1
156 return max(total, 1)
159def _npath_expr_go(node, top_func) -> int:
160 """Scan expression for inline func_literals, multiplying results."""
161 if node.type == "func_literal" and node is not top_func:
162 if is_named_func_literal(node):
163 return 1
164 return compute_npath_go(node)
166 result = 1
167 for child in node.children:
168 child_np = _npath_expr_go(child, top_func)
169 if child_np > 1:
170 result *= child_np
171 return result