@optave/codegraph 3.0.4 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -52
- package/grammars/tree-sitter-go.wasm +0 -0
- package/package.json +9 -10
- package/src/ast-analysis/rules/csharp.js +201 -0
- package/src/ast-analysis/rules/go.js +182 -0
- package/src/ast-analysis/rules/index.js +82 -0
- package/src/ast-analysis/rules/java.js +175 -0
- package/src/ast-analysis/rules/javascript.js +246 -0
- package/src/ast-analysis/rules/php.js +219 -0
- package/src/ast-analysis/rules/python.js +196 -0
- package/src/ast-analysis/rules/ruby.js +204 -0
- package/src/ast-analysis/rules/rust.js +173 -0
- package/src/ast-analysis/shared.js +223 -0
- package/src/ast.js +15 -28
- package/src/audit.js +4 -5
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +84 -79
- package/src/builder.js +274 -159
- package/src/cfg.js +111 -341
- package/src/check.js +3 -3
- package/src/cli.js +122 -167
- package/src/cochange.js +1 -1
- package/src/communities.js +13 -16
- package/src/complexity.js +196 -1239
- package/src/cycles.js +1 -1
- package/src/dataflow.js +274 -697
- package/src/db/connection.js +88 -0
- package/src/db/migrations.js +312 -0
- package/src/db/query-builder.js +280 -0
- package/src/db/repository.js +134 -0
- package/src/db.js +19 -392
- package/src/embedder.js +145 -141
- package/src/export.js +1 -1
- package/src/flow.js +160 -228
- package/src/index.js +36 -2
- package/src/kinds.js +49 -0
- package/src/manifesto.js +3 -8
- package/src/mcp.js +97 -20
- package/src/owners.js +132 -132
- package/src/parser.js +58 -131
- package/src/queries-cli.js +866 -0
- package/src/queries.js +1356 -2261
- package/src/resolve.js +11 -2
- package/src/result-formatter.js +21 -0
- package/src/sequence.js +364 -0
- package/src/structure.js +200 -199
- package/src/test-filter.js +7 -0
- package/src/triage.js +120 -162
- package/src/viewer.js +1 -1
package/src/cfg.js
CHANGED
|
@@ -7,265 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { COMPLEXITY_RULES } from './
|
|
10
|
+
import { CFG_RULES, COMPLEXITY_RULES } from './ast-analysis/rules/index.js';
|
|
11
|
+
import {
|
|
12
|
+
makeCfgRules as _makeCfgRules,
|
|
13
|
+
buildExtensionSet,
|
|
14
|
+
buildExtToLangMap,
|
|
15
|
+
findFunctionNode,
|
|
16
|
+
} from './ast-analysis/shared.js';
|
|
11
17
|
import { openReadonlyOrFail } from './db.js';
|
|
12
18
|
import { info } from './logger.js';
|
|
13
|
-
import { paginateResult
|
|
14
|
-
import { LANGUAGE_REGISTRY } from './parser.js';
|
|
15
|
-
import { isTestFile } from './queries.js';
|
|
16
|
-
|
|
17
|
-
// ─── CFG Node Type Rules (extends COMPLEXITY_RULES) ──────────────────────
|
|
18
|
-
|
|
19
|
-
const CFG_DEFAULTS = {
|
|
20
|
-
ifNode: null,
|
|
21
|
-
ifNodes: null,
|
|
22
|
-
elifNode: null,
|
|
23
|
-
elseClause: null,
|
|
24
|
-
elseViaAlternative: false,
|
|
25
|
-
ifConsequentField: null,
|
|
26
|
-
forNodes: new Set(),
|
|
27
|
-
whileNode: null,
|
|
28
|
-
whileNodes: null,
|
|
29
|
-
doNode: null,
|
|
30
|
-
infiniteLoopNode: null,
|
|
31
|
-
unlessNode: null,
|
|
32
|
-
untilNode: null,
|
|
33
|
-
switchNode: null,
|
|
34
|
-
switchNodes: null,
|
|
35
|
-
caseNode: null,
|
|
36
|
-
caseNodes: null,
|
|
37
|
-
defaultNode: null,
|
|
38
|
-
tryNode: null,
|
|
39
|
-
catchNode: null,
|
|
40
|
-
finallyNode: null,
|
|
41
|
-
returnNode: null,
|
|
42
|
-
throwNode: null,
|
|
43
|
-
breakNode: null,
|
|
44
|
-
continueNode: null,
|
|
45
|
-
blockNode: null,
|
|
46
|
-
blockNodes: null,
|
|
47
|
-
labeledNode: null,
|
|
48
|
-
functionNodes: new Set(),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const CFG_RULE_KEYS = new Set(Object.keys(CFG_DEFAULTS));
|
|
52
|
-
|
|
53
|
-
export function makeCfgRules(overrides) {
|
|
54
|
-
for (const key of Object.keys(overrides)) {
|
|
55
|
-
if (!CFG_RULE_KEYS.has(key)) {
|
|
56
|
-
throw new Error(`CFG rules: unknown key "${key}"`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const rules = { ...CFG_DEFAULTS, ...overrides };
|
|
60
|
-
if (!(rules.functionNodes instanceof Set) || rules.functionNodes.size === 0) {
|
|
61
|
-
throw new Error('CFG rules: functionNodes must be a non-empty Set');
|
|
62
|
-
}
|
|
63
|
-
if (!(rules.forNodes instanceof Set)) {
|
|
64
|
-
throw new Error('CFG rules: forNodes must be a Set');
|
|
65
|
-
}
|
|
66
|
-
return rules;
|
|
67
|
-
}
|
|
19
|
+
import { paginateResult } from './paginate.js';
|
|
68
20
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
defaultNode: 'switch_default',
|
|
78
|
-
tryNode: 'try_statement',
|
|
79
|
-
catchNode: 'catch_clause',
|
|
80
|
-
finallyNode: 'finally_clause',
|
|
81
|
-
returnNode: 'return_statement',
|
|
82
|
-
throwNode: 'throw_statement',
|
|
83
|
-
breakNode: 'break_statement',
|
|
84
|
-
continueNode: 'continue_statement',
|
|
85
|
-
blockNode: 'statement_block',
|
|
86
|
-
labeledNode: 'labeled_statement',
|
|
87
|
-
functionNodes: new Set([
|
|
88
|
-
'function_declaration',
|
|
89
|
-
'function_expression',
|
|
90
|
-
'arrow_function',
|
|
91
|
-
'method_definition',
|
|
92
|
-
'generator_function',
|
|
93
|
-
'generator_function_declaration',
|
|
94
|
-
]),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const PYTHON_CFG = makeCfgRules({
|
|
98
|
-
ifNode: 'if_statement',
|
|
99
|
-
elifNode: 'elif_clause',
|
|
100
|
-
elseClause: 'else_clause',
|
|
101
|
-
forNodes: new Set(['for_statement']),
|
|
102
|
-
whileNode: 'while_statement',
|
|
103
|
-
switchNode: 'match_statement',
|
|
104
|
-
caseNode: 'case_clause',
|
|
105
|
-
tryNode: 'try_statement',
|
|
106
|
-
catchNode: 'except_clause',
|
|
107
|
-
finallyNode: 'finally_clause',
|
|
108
|
-
returnNode: 'return_statement',
|
|
109
|
-
throwNode: 'raise_statement',
|
|
110
|
-
breakNode: 'break_statement',
|
|
111
|
-
continueNode: 'continue_statement',
|
|
112
|
-
blockNode: 'block',
|
|
113
|
-
functionNodes: new Set(['function_definition']),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const GO_CFG = makeCfgRules({
|
|
117
|
-
ifNode: 'if_statement',
|
|
118
|
-
elseViaAlternative: true,
|
|
119
|
-
forNodes: new Set(['for_statement']),
|
|
120
|
-
switchNodes: new Set([
|
|
121
|
-
'expression_switch_statement',
|
|
122
|
-
'type_switch_statement',
|
|
123
|
-
'select_statement',
|
|
124
|
-
]),
|
|
125
|
-
caseNode: 'expression_case',
|
|
126
|
-
caseNodes: new Set(['type_case', 'communication_case']),
|
|
127
|
-
defaultNode: 'default_case',
|
|
128
|
-
returnNode: 'return_statement',
|
|
129
|
-
breakNode: 'break_statement',
|
|
130
|
-
continueNode: 'continue_statement',
|
|
131
|
-
blockNode: 'block',
|
|
132
|
-
labeledNode: 'labeled_statement',
|
|
133
|
-
functionNodes: new Set(['function_declaration', 'method_declaration', 'func_literal']),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const RUST_CFG = makeCfgRules({
|
|
137
|
-
ifNode: 'if_expression',
|
|
138
|
-
ifNodes: new Set(['if_let_expression']),
|
|
139
|
-
elseClause: 'else_clause',
|
|
140
|
-
forNodes: new Set(['for_expression']),
|
|
141
|
-
whileNode: 'while_expression',
|
|
142
|
-
whileNodes: new Set(['while_let_expression']),
|
|
143
|
-
infiniteLoopNode: 'loop_expression',
|
|
144
|
-
switchNode: 'match_expression',
|
|
145
|
-
caseNode: 'match_arm',
|
|
146
|
-
returnNode: 'return_expression',
|
|
147
|
-
breakNode: 'break_expression',
|
|
148
|
-
continueNode: 'continue_expression',
|
|
149
|
-
blockNode: 'block',
|
|
150
|
-
functionNodes: new Set(['function_item', 'closure_expression']),
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const JAVA_CFG = makeCfgRules({
|
|
154
|
-
ifNode: 'if_statement',
|
|
155
|
-
elseViaAlternative: true,
|
|
156
|
-
forNodes: new Set(['for_statement', 'enhanced_for_statement']),
|
|
157
|
-
whileNode: 'while_statement',
|
|
158
|
-
doNode: 'do_statement',
|
|
159
|
-
switchNode: 'switch_expression',
|
|
160
|
-
caseNode: 'switch_block_statement_group',
|
|
161
|
-
caseNodes: new Set(['switch_rule']),
|
|
162
|
-
tryNode: 'try_statement',
|
|
163
|
-
catchNode: 'catch_clause',
|
|
164
|
-
finallyNode: 'finally_clause',
|
|
165
|
-
returnNode: 'return_statement',
|
|
166
|
-
throwNode: 'throw_statement',
|
|
167
|
-
breakNode: 'break_statement',
|
|
168
|
-
continueNode: 'continue_statement',
|
|
169
|
-
blockNode: 'block',
|
|
170
|
-
labeledNode: 'labeled_statement',
|
|
171
|
-
functionNodes: new Set(['method_declaration', 'constructor_declaration', 'lambda_expression']),
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const CSHARP_CFG = makeCfgRules({
|
|
175
|
-
ifNode: 'if_statement',
|
|
176
|
-
elseViaAlternative: true,
|
|
177
|
-
forNodes: new Set(['for_statement', 'foreach_statement']),
|
|
178
|
-
whileNode: 'while_statement',
|
|
179
|
-
doNode: 'do_statement',
|
|
180
|
-
switchNode: 'switch_statement',
|
|
181
|
-
caseNode: 'switch_section',
|
|
182
|
-
tryNode: 'try_statement',
|
|
183
|
-
catchNode: 'catch_clause',
|
|
184
|
-
finallyNode: 'finally_clause',
|
|
185
|
-
returnNode: 'return_statement',
|
|
186
|
-
throwNode: 'throw_statement',
|
|
187
|
-
breakNode: 'break_statement',
|
|
188
|
-
continueNode: 'continue_statement',
|
|
189
|
-
blockNode: 'block',
|
|
190
|
-
labeledNode: 'labeled_statement',
|
|
191
|
-
functionNodes: new Set([
|
|
192
|
-
'method_declaration',
|
|
193
|
-
'constructor_declaration',
|
|
194
|
-
'lambda_expression',
|
|
195
|
-
'local_function_statement',
|
|
196
|
-
]),
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const RUBY_CFG = makeCfgRules({
|
|
200
|
-
ifNode: 'if',
|
|
201
|
-
elifNode: 'elsif',
|
|
202
|
-
elseClause: 'else',
|
|
203
|
-
forNodes: new Set(['for']),
|
|
204
|
-
whileNode: 'while',
|
|
205
|
-
unlessNode: 'unless',
|
|
206
|
-
untilNode: 'until',
|
|
207
|
-
switchNode: 'case',
|
|
208
|
-
caseNode: 'when',
|
|
209
|
-
defaultNode: 'else',
|
|
210
|
-
tryNode: 'begin',
|
|
211
|
-
catchNode: 'rescue',
|
|
212
|
-
finallyNode: 'ensure',
|
|
213
|
-
returnNode: 'return',
|
|
214
|
-
breakNode: 'break',
|
|
215
|
-
continueNode: 'next',
|
|
216
|
-
blockNodes: new Set(['then', 'do', 'body_statement']),
|
|
217
|
-
functionNodes: new Set(['method', 'singleton_method']),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const PHP_CFG = makeCfgRules({
|
|
221
|
-
ifNode: 'if_statement',
|
|
222
|
-
elifNode: 'else_if_clause',
|
|
223
|
-
elseClause: 'else_clause',
|
|
224
|
-
ifConsequentField: 'body',
|
|
225
|
-
forNodes: new Set(['for_statement', 'foreach_statement']),
|
|
226
|
-
whileNode: 'while_statement',
|
|
227
|
-
doNode: 'do_statement',
|
|
228
|
-
switchNode: 'switch_statement',
|
|
229
|
-
caseNode: 'case_statement',
|
|
230
|
-
defaultNode: 'default_statement',
|
|
231
|
-
tryNode: 'try_statement',
|
|
232
|
-
catchNode: 'catch_clause',
|
|
233
|
-
finallyNode: 'finally_clause',
|
|
234
|
-
returnNode: 'return_statement',
|
|
235
|
-
throwNode: 'throw_expression',
|
|
236
|
-
breakNode: 'break_statement',
|
|
237
|
-
continueNode: 'continue_statement',
|
|
238
|
-
blockNode: 'compound_statement',
|
|
239
|
-
functionNodes: new Set([
|
|
240
|
-
'function_definition',
|
|
241
|
-
'method_declaration',
|
|
242
|
-
'anonymous_function_creation_expression',
|
|
243
|
-
'arrow_function',
|
|
244
|
-
]),
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
export const CFG_RULES = new Map([
|
|
248
|
-
['javascript', JS_TS_CFG],
|
|
249
|
-
['typescript', JS_TS_CFG],
|
|
250
|
-
['tsx', JS_TS_CFG],
|
|
251
|
-
['python', PYTHON_CFG],
|
|
252
|
-
['go', GO_CFG],
|
|
253
|
-
['rust', RUST_CFG],
|
|
254
|
-
['java', JAVA_CFG],
|
|
255
|
-
['csharp', CSHARP_CFG],
|
|
256
|
-
['ruby', RUBY_CFG],
|
|
257
|
-
['php', PHP_CFG],
|
|
258
|
-
]);
|
|
259
|
-
|
|
260
|
-
const CFG_LANG_IDS = new Set(CFG_RULES.keys());
|
|
261
|
-
|
|
262
|
-
// JS/TS extensions
|
|
263
|
-
const CFG_EXTENSIONS = new Set();
|
|
264
|
-
for (const entry of LANGUAGE_REGISTRY) {
|
|
265
|
-
if (CFG_LANG_IDS.has(entry.id)) {
|
|
266
|
-
for (const ext of entry.extensions) CFG_EXTENSIONS.add(ext);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
21
|
+
import { outputResult } from './result-formatter.js';
|
|
22
|
+
import { isTestFile } from './test-filter.js';
|
|
23
|
+
|
|
24
|
+
// Re-export for backward compatibility
|
|
25
|
+
export { CFG_RULES };
|
|
26
|
+
export { _makeCfgRules as makeCfgRules };
|
|
27
|
+
|
|
28
|
+
const CFG_EXTENSIONS = buildExtensionSet(CFG_RULES);
|
|
269
29
|
|
|
270
30
|
// ─── Core Algorithm: AST → CFG ──────────────────────────────────────────
|
|
271
31
|
|
|
@@ -327,11 +87,23 @@ export function buildFunctionCFG(functionNode, langId) {
|
|
|
327
87
|
*/
|
|
328
88
|
function getStatements(node) {
|
|
329
89
|
if (!node) return [];
|
|
330
|
-
// Block-like nodes
|
|
331
|
-
if (
|
|
90
|
+
// Block-like nodes (including statement_list wrappers from tree-sitter-go 0.25+)
|
|
91
|
+
if (
|
|
92
|
+
node.type === 'statement_list' ||
|
|
93
|
+
node.type === rules.blockNode ||
|
|
94
|
+
rules.blockNodes?.has(node.type)
|
|
95
|
+
) {
|
|
332
96
|
const stmts = [];
|
|
333
97
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
334
|
-
|
|
98
|
+
const child = node.namedChild(i);
|
|
99
|
+
if (child.type === 'statement_list') {
|
|
100
|
+
// Unwrap nested statement_list (block → statement_list → stmts)
|
|
101
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
102
|
+
stmts.push(child.namedChild(j));
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
stmts.push(child);
|
|
106
|
+
}
|
|
335
107
|
}
|
|
336
108
|
return stmts;
|
|
337
109
|
}
|
|
@@ -888,7 +660,14 @@ export function buildFunctionCFG(functionNode, langId) {
|
|
|
888
660
|
for (let j = 0; j < caseClause.namedChildCount; j++) {
|
|
889
661
|
const child = caseClause.namedChild(j);
|
|
890
662
|
if (child !== valueNode && child !== patternNode && child.type !== 'switch_label') {
|
|
891
|
-
|
|
663
|
+
if (child.type === 'statement_list') {
|
|
664
|
+
// Unwrap statement_list (tree-sitter-go 0.25+)
|
|
665
|
+
for (let k = 0; k < child.namedChildCount; k++) {
|
|
666
|
+
caseStmts.push(child.namedChild(k));
|
|
667
|
+
}
|
|
668
|
+
} else {
|
|
669
|
+
caseStmts.push(child);
|
|
670
|
+
}
|
|
892
671
|
}
|
|
893
672
|
}
|
|
894
673
|
}
|
|
@@ -1046,9 +825,12 @@ export function buildFunctionCFG(functionNode, langId) {
|
|
|
1046
825
|
export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
1047
826
|
// Lazily init WASM parsers if needed
|
|
1048
827
|
let parsers = null;
|
|
1049
|
-
let extToLang = null;
|
|
1050
828
|
let needsFallback = false;
|
|
1051
829
|
|
|
830
|
+
// Always build ext→langId map so native-only builds (where _langId is unset)
|
|
831
|
+
// can still derive the language from the file extension.
|
|
832
|
+
const extToLang = buildExtToLangMap();
|
|
833
|
+
|
|
1052
834
|
for (const [relPath, symbols] of fileSymbols) {
|
|
1053
835
|
if (!symbols._tree) {
|
|
1054
836
|
const ext = path.extname(relPath).toLowerCase();
|
|
@@ -1068,12 +850,6 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
|
1068
850
|
if (needsFallback) {
|
|
1069
851
|
const { createParsers } = await import('./parser.js');
|
|
1070
852
|
parsers = await createParsers();
|
|
1071
|
-
extToLang = new Map();
|
|
1072
|
-
for (const entry of LANGUAGE_REGISTRY) {
|
|
1073
|
-
for (const ext of entry.extensions) {
|
|
1074
|
-
extToLang.set(ext, entry.id);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
853
|
}
|
|
1078
854
|
|
|
1079
855
|
let getParserFn = null;
|
|
@@ -1082,7 +858,7 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
|
1082
858
|
getParserFn = mod.getParser;
|
|
1083
859
|
}
|
|
1084
860
|
|
|
1085
|
-
|
|
861
|
+
// findFunctionNode imported from ./ast-analysis/shared.js at module level
|
|
1086
862
|
|
|
1087
863
|
const insertBlock = db.prepare(
|
|
1088
864
|
`INSERT INTO cfg_blocks (function_node_id, block_index, block_type, start_line, end_line, label)
|
|
@@ -1115,9 +891,9 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
|
1115
891
|
|
|
1116
892
|
// WASM fallback if no cached tree and not all native
|
|
1117
893
|
if (!tree && !allNative) {
|
|
1118
|
-
if (!
|
|
894
|
+
if (!getParserFn) continue;
|
|
1119
895
|
langId = extToLang.get(ext);
|
|
1120
|
-
if (!langId || !
|
|
896
|
+
if (!langId || !CFG_RULES.has(langId)) continue;
|
|
1121
897
|
|
|
1122
898
|
const absPath = path.join(rootDir, relPath);
|
|
1123
899
|
let code;
|
|
@@ -1138,7 +914,7 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
|
1138
914
|
}
|
|
1139
915
|
|
|
1140
916
|
if (!langId) {
|
|
1141
|
-
langId = extToLang
|
|
917
|
+
langId = extToLang.get(ext);
|
|
1142
918
|
if (!langId) continue;
|
|
1143
919
|
}
|
|
1144
920
|
|
|
@@ -1253,72 +1029,73 @@ function findNodes(db, name, opts = {}) {
|
|
|
1253
1029
|
*/
|
|
1254
1030
|
export function cfgData(name, customDbPath, opts = {}) {
|
|
1255
1031
|
const db = openReadonlyOrFail(customDbPath);
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
db
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1032
|
+
try {
|
|
1033
|
+
const noTests = opts.noTests || false;
|
|
1034
|
+
|
|
1035
|
+
if (!hasCfgTables(db)) {
|
|
1036
|
+
return {
|
|
1037
|
+
name,
|
|
1038
|
+
results: [],
|
|
1039
|
+
warning:
|
|
1040
|
+
'No CFG data found. Rebuild with `codegraph build` (CFG is now included by default).',
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1267
1043
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
}
|
|
1044
|
+
const nodes = findNodes(db, name, { noTests, file: opts.file, kind: opts.kind });
|
|
1045
|
+
if (nodes.length === 0) {
|
|
1046
|
+
return { name, results: [] };
|
|
1047
|
+
}
|
|
1273
1048
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1049
|
+
const blockStmt = db.prepare(
|
|
1050
|
+
`SELECT id, block_index, block_type, start_line, end_line, label
|
|
1051
|
+
FROM cfg_blocks WHERE function_node_id = ?
|
|
1052
|
+
ORDER BY block_index`,
|
|
1053
|
+
);
|
|
1054
|
+
const edgeStmt = db.prepare(
|
|
1055
|
+
`SELECT e.kind,
|
|
1056
|
+
sb.block_index AS source_index, sb.block_type AS source_type,
|
|
1057
|
+
tb.block_index AS target_index, tb.block_type AS target_type
|
|
1058
|
+
FROM cfg_edges e
|
|
1059
|
+
JOIN cfg_blocks sb ON e.source_block_id = sb.id
|
|
1060
|
+
JOIN cfg_blocks tb ON e.target_block_id = tb.id
|
|
1061
|
+
WHERE e.function_node_id = ?
|
|
1062
|
+
ORDER BY sb.block_index, tb.block_index`,
|
|
1063
|
+
);
|
|
1289
1064
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1065
|
+
const results = nodes.map((node) => {
|
|
1066
|
+
const cfgBlocks = blockStmt.all(node.id);
|
|
1067
|
+
const cfgEdges = edgeStmt.all(node.id);
|
|
1068
|
+
|
|
1069
|
+
return {
|
|
1070
|
+
name: node.name,
|
|
1071
|
+
kind: node.kind,
|
|
1072
|
+
file: node.file,
|
|
1073
|
+
line: node.line,
|
|
1074
|
+
blocks: cfgBlocks.map((b) => ({
|
|
1075
|
+
index: b.block_index,
|
|
1076
|
+
type: b.block_type,
|
|
1077
|
+
startLine: b.start_line,
|
|
1078
|
+
endLine: b.end_line,
|
|
1079
|
+
label: b.label,
|
|
1080
|
+
})),
|
|
1081
|
+
edges: cfgEdges.map((e) => ({
|
|
1082
|
+
source: e.source_index,
|
|
1083
|
+
sourceType: e.source_type,
|
|
1084
|
+
target: e.target_index,
|
|
1085
|
+
targetType: e.target_type,
|
|
1086
|
+
kind: e.kind,
|
|
1087
|
+
})),
|
|
1088
|
+
summary: {
|
|
1089
|
+
blockCount: cfgBlocks.length,
|
|
1090
|
+
edgeCount: cfgEdges.length,
|
|
1091
|
+
},
|
|
1092
|
+
};
|
|
1093
|
+
});
|
|
1319
1094
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1095
|
+
return paginateResult({ name, results }, 'results', opts);
|
|
1096
|
+
} finally {
|
|
1097
|
+
db.close();
|
|
1098
|
+
}
|
|
1322
1099
|
}
|
|
1323
1100
|
|
|
1324
1101
|
// ─── Export Formats ─────────────────────────────────────────────────────
|
|
@@ -1416,14 +1193,7 @@ function edgeStyle(kind) {
|
|
|
1416
1193
|
export function cfg(name, customDbPath, opts = {}) {
|
|
1417
1194
|
const data = cfgData(name, customDbPath, opts);
|
|
1418
1195
|
|
|
1419
|
-
if (opts
|
|
1420
|
-
console.log(JSON.stringify(data, null, 2));
|
|
1421
|
-
return;
|
|
1422
|
-
}
|
|
1423
|
-
if (opts.ndjson) {
|
|
1424
|
-
printNdjson(data.results);
|
|
1425
|
-
return;
|
|
1426
|
-
}
|
|
1196
|
+
if (outputResult(data, 'results', opts)) return;
|
|
1427
1197
|
|
|
1428
1198
|
if (data.warning) {
|
|
1429
1199
|
console.log(`\u26A0 ${data.warning}`);
|
package/src/check.js
CHANGED
|
@@ -5,7 +5,8 @@ import { loadConfig } from './config.js';
|
|
|
5
5
|
import { findCycles } from './cycles.js';
|
|
6
6
|
import { findDbPath, openReadonlyOrFail } from './db.js';
|
|
7
7
|
import { matchOwners, parseCodeowners } from './owners.js';
|
|
8
|
-
import {
|
|
8
|
+
import { outputResult } from './result-formatter.js';
|
|
9
|
+
import { isTestFile } from './test-filter.js';
|
|
9
10
|
|
|
10
11
|
// ─── Diff Parser ──────────────────────────────────────────────────────
|
|
11
12
|
|
|
@@ -361,8 +362,7 @@ export function check(customDbPath, opts = {}) {
|
|
|
361
362
|
process.exit(1);
|
|
362
363
|
}
|
|
363
364
|
|
|
364
|
-
if (opts
|
|
365
|
-
console.log(JSON.stringify(data, null, 2));
|
|
365
|
+
if (outputResult(data, null, opts)) {
|
|
366
366
|
if (!data.passed) process.exit(1);
|
|
367
367
|
return;
|
|
368
368
|
}
|