Coverage for metrics / npath / typescript.py: 90%
167 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 TypeScript."""
3from __future__ import annotations
5from common import _count_bool_ops
8def compute_npath_ts(func_node) -> int:
9 """Compute NPath complexity for a TypeScript function node."""
10 for child in func_node.children:
11 if child.type == "statement_block":
12 return _npath_block_ts(child, func_node)
13 if func_node.type == "arrow_function":
14 children = [c for c in func_node.children if c.type not in ("=>", "formal_parameters", "identifier")]
15 for child in children:
16 return _npath_expr_ts(child, func_node)
17 return 1
20def _npath_block_ts(block, top_func) -> int:
21 """Multiply NPath of each child statement in a statement_block."""
22 result = 1
23 for child in block.children:
24 if child.type in ("{", "}"):
25 continue
26 result *= _npath_stmt_ts(child, top_func)
27 return result
30def _npath_stmt_ts(node, top_func) -> int:
31 """Dispatch a statement node to its NPath handler."""
32 if node.type in ("function_declaration", "function_expression",
33 "method_definition", "generator_function_declaration") and node is not top_func:
34 return 1
35 if node.type == "class_declaration":
36 return 1
37 if node.type == "arrow_function" and node is not top_func:
38 if node.parent and node.parent.type == "variable_declarator":
39 return 1
40 return compute_npath_ts(node)
42 if node.type == "if_statement":
43 return _npath_if_ts(node, top_func)
44 if node.type in ("for_statement", "for_in_statement", "while_statement"):
45 return _npath_loop_ts(node, top_func)
46 if node.type == "do_statement":
47 return _npath_do_while_ts(node, top_func)
48 if node.type == "try_statement":
49 return _npath_try_ts(node, top_func)
50 if node.type == "switch_statement":
51 return _npath_switch_ts(node, top_func)
52 if node.type == "ternary_expression":
53 return _npath_ternary_ts(node, top_func)
54 if node.type == "statement_block":
55 return _npath_block_ts(node, top_func)
56 if node.type in ("expression_statement", "return_statement",
57 "variable_declaration", "lexical_declaration"):
58 return _npath_expr_ts(node, top_func)
59 return 1
62def _npath_if_condition_ops(node) -> int:
63 """Count boolean operators in the if condition (before the body)."""
64 ops = 0
65 for child in node.children:
66 if child.type in ("statement_block", "else_clause"):
67 break
68 if child.type not in ("if", "(", ")"):
69 ops += _count_bool_ops(child)
70 return ops
73def _npath_if_body(node, top_func) -> int:
74 """Compute NPath for the if-body (the first statement_block)."""
75 for child in node.children:
76 if child.type == "statement_block":
77 return _npath_block_ts(child, top_func)
78 return 0
81def _npath_else_clause(else_node, top_func):
82 """Compute NPath for an else clause (may contain else-if or plain block)."""
83 for sub in else_node.children:
84 if sub.type == "if_statement":
85 return _npath_if_ts(sub, top_func)
86 if sub.type == "statement_block":
87 return _npath_block_ts(sub, top_func)
88 return 0
91def _npath_if_ts(node, top_func) -> int:
92 """NPath for if_statement with else-if/else chains."""
93 total = _npath_if_body(node, top_func)
94 has_else = False
96 for child in node.children:
97 if child.type == "else_clause":
98 has_else = True
99 total += _npath_else_clause(child, top_func)
101 if not has_else:
102 total += 1
104 return total + _npath_if_condition_ops(node)
107def _npath_loop_ts(node, top_func) -> int:
108 """NPath for for/while loops."""
109 body_npath = 1
110 bool_ops = 0
112 for child in node.children:
113 if child.type == "statement_block":
114 body_npath = _npath_block_ts(child, top_func)
116 if node.type == "while_statement":
117 for child in node.children:
118 if child.type == "statement_block":
119 break
120 if child.type not in ("while", "(", ")"):
121 bool_ops += _count_bool_ops(child)
123 return body_npath + 1 + bool_ops
126def _npath_do_while_ts(node, top_func) -> int:
127 """NPath for do-while: NP(body) + 1 + bool_ops(condition)."""
128 body_npath = 1
129 bool_ops = 0
131 for child in node.children:
132 if child.type == "statement_block":
133 body_npath = _npath_block_ts(child, top_func)
135 seen_while = False
136 for child in node.children:
137 if child.type == "while":
138 seen_while = True
139 continue
140 if seen_while and child.type not in ("(", ")", ";"):
141 bool_ops += _count_bool_ops(child)
143 return body_npath + 1 + bool_ops
146def _npath_try_ts(node, top_func) -> int:
147 """NPath for try_statement: sum of try/catch/finally bodies."""
148 total = 0
149 for child in node.children:
150 if child.type == "statement_block":
151 total += _npath_block_ts(child, top_func)
152 elif child.type == "catch_clause":
153 for sub in child.children:
154 if sub.type == "statement_block":
155 total += _npath_block_ts(sub, top_func)
156 elif child.type == "finally_clause":
157 for sub in child.children:
158 if sub.type == "statement_block":
159 total += _npath_block_ts(sub, top_func)
160 return max(total, 1)
163def _npath_switch_ts(node, top_func) -> int:
164 """NPath for switch_statement: sum of case bodies (+1 if no default)."""
165 total = 0
166 has_default = False
167 for child in node.children:
168 if child.type == "switch_body":
169 for case_node in child.children:
170 if case_node.type == "switch_case":
171 case_npath = 1
172 for sub in case_node.children:
173 if sub.type not in ("case", ":", "{", "}") and sub.type != "switch_case":
174 sub_np = _npath_stmt_ts(sub, top_func)
175 if sub_np > 1:
176 case_npath *= sub_np
177 total += case_npath
178 elif case_node.type == "switch_default":
179 has_default = True
180 case_npath = 1
181 for sub in case_node.children:
182 if sub.type not in ("default", ":", "{", "}"):
183 sub_np = _npath_stmt_ts(sub, top_func)
184 if sub_np > 1:
185 case_npath *= sub_np
186 total += case_npath
187 if not has_default:
188 total += 1
189 return max(total, 1)
192def _npath_ternary_ts(node, top_func) -> int:
193 """NPath for ternary_expression: NP(true) + NP(false) + bool_ops(cond)."""
194 non_punct = [c for c in node.children if c.type not in ("?", ":")]
195 if len(non_punct) >= 3:
196 condition = non_punct[0]
197 true_expr = non_punct[1]
198 false_expr = non_punct[2]
199 np_true = _npath_expr_ts(true_expr, top_func)
200 np_false = _npath_expr_ts(false_expr, top_func)
201 bool_ops = _count_bool_ops(condition)
202 return np_true + np_false + bool_ops
203 return 2
206def _npath_expr_ts(node, top_func) -> int:
207 """Scan expression for ternaries/inline arrows, multiplying results."""
208 if node.type == "ternary_expression":
209 return _npath_ternary_ts(node, top_func)
210 if node.type == "arrow_function" and node is not top_func:
211 if node.parent and node.parent.type == "variable_declarator":
212 return 1
213 return compute_npath_ts(node)
214 if node.type in ("function_expression", "function_declaration",
215 "class_declaration", "class_expression"):
216 return 1
218 result = 1
219 for child in node.children:
220 child_np = _npath_expr_ts(child, top_func)
221 if child_np > 1:
222 result *= child_np
223 return result