@optave/codegraph 3.1.0 → 3.1.2
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 +5 -5
- package/grammars/tree-sitter-go.wasm +0 -0
- package/package.json +8 -9
- package/src/ast-analysis/engine.js +365 -0
- package/src/ast-analysis/metrics.js +118 -0
- 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-analysis/visitor-utils.js +176 -0
- package/src/ast-analysis/visitor.js +162 -0
- package/src/ast-analysis/visitors/ast-store-visitor.js +150 -0
- package/src/ast-analysis/visitors/cfg-visitor.js +792 -0
- package/src/ast-analysis/visitors/complexity-visitor.js +243 -0
- package/src/ast-analysis/visitors/dataflow-visitor.js +358 -0
- package/src/ast.js +26 -166
- package/src/audit.js +2 -88
- package/src/batch.js +0 -25
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +82 -172
- package/src/builder.js +48 -184
- package/src/cfg.js +148 -1174
- package/src/check.js +1 -84
- package/src/cli.js +118 -197
- package/src/cochange.js +1 -39
- package/src/commands/audit.js +88 -0
- package/src/commands/batch.js +26 -0
- package/src/commands/branch-compare.js +97 -0
- package/src/commands/cfg.js +55 -0
- package/src/commands/check.js +82 -0
- package/src/commands/cochange.js +37 -0
- package/src/commands/communities.js +69 -0
- package/src/commands/complexity.js +77 -0
- package/src/commands/dataflow.js +110 -0
- package/src/commands/flow.js +70 -0
- package/src/commands/manifesto.js +77 -0
- package/src/commands/owners.js +52 -0
- package/src/commands/query.js +21 -0
- package/src/commands/sequence.js +33 -0
- package/src/commands/structure.js +64 -0
- package/src/commands/triage.js +49 -0
- package/src/communities.js +22 -96
- package/src/complexity.js +234 -1591
- package/src/cycles.js +1 -1
- package/src/dataflow.js +274 -1352
- 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/build-stmts.js +104 -0
- package/src/db/repository/cfg.js +83 -0
- package/src/db/repository/cochange.js +41 -0
- package/src/db/repository/complexity.js +15 -0
- package/src/db/repository/dataflow.js +12 -0
- package/src/db/repository/edges.js +259 -0
- package/src/db/repository/embeddings.js +40 -0
- package/src/db/repository/graph-read.js +39 -0
- package/src/db/repository/index.js +42 -0
- package/src/db/repository/nodes.js +236 -0
- package/src/db.js +58 -399
- package/src/embedder.js +158 -174
- package/src/export.js +1 -1
- package/src/extractors/javascript.js +130 -5
- package/src/flow.js +153 -222
- package/src/index.js +53 -16
- package/src/infrastructure/result-formatter.js +21 -0
- package/src/infrastructure/test-filter.js +7 -0
- package/src/kinds.js +50 -0
- package/src/manifesto.js +1 -82
- package/src/mcp.js +37 -20
- package/src/owners.js +127 -182
- package/src/queries-cli.js +866 -0
- package/src/queries.js +1271 -2416
- package/src/sequence.js +179 -223
- package/src/structure.js +211 -269
- package/src/triage.js +117 -212
- package/src/viewer.js +1 -1
- package/src/watcher.js +7 -4
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruby — AST analysis rules.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { makeCfgRules, makeDataflowRules } from '../shared.js';
|
|
6
|
+
|
|
7
|
+
// ─── Complexity ───────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export const complexity = {
|
|
10
|
+
branchNodes: new Set([
|
|
11
|
+
'if',
|
|
12
|
+
'elsif',
|
|
13
|
+
'else',
|
|
14
|
+
'unless',
|
|
15
|
+
'case',
|
|
16
|
+
'for',
|
|
17
|
+
'while',
|
|
18
|
+
'until',
|
|
19
|
+
'rescue',
|
|
20
|
+
'conditional',
|
|
21
|
+
]),
|
|
22
|
+
caseNodes: new Set(['when']),
|
|
23
|
+
logicalOperators: new Set(['and', 'or', '&&', '||']),
|
|
24
|
+
logicalNodeType: 'binary',
|
|
25
|
+
optionalChainType: null,
|
|
26
|
+
nestingNodes: new Set(['if', 'unless', 'case', 'for', 'while', 'until', 'rescue', 'conditional']),
|
|
27
|
+
functionNodes: new Set(['method', 'singleton_method', 'lambda', 'do_block']),
|
|
28
|
+
ifNodeType: 'if',
|
|
29
|
+
elseNodeType: 'else',
|
|
30
|
+
elifNodeType: 'elsif',
|
|
31
|
+
elseViaAlternative: false,
|
|
32
|
+
switchLikeNodes: new Set(['case']),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ─── Halstead ─────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export const halstead = {
|
|
38
|
+
operatorLeafTypes: new Set([
|
|
39
|
+
'+',
|
|
40
|
+
'-',
|
|
41
|
+
'*',
|
|
42
|
+
'/',
|
|
43
|
+
'%',
|
|
44
|
+
'**',
|
|
45
|
+
'=',
|
|
46
|
+
'+=',
|
|
47
|
+
'-=',
|
|
48
|
+
'*=',
|
|
49
|
+
'/=',
|
|
50
|
+
'%=',
|
|
51
|
+
'**=',
|
|
52
|
+
'&=',
|
|
53
|
+
'|=',
|
|
54
|
+
'^=',
|
|
55
|
+
'<<=',
|
|
56
|
+
'>>=',
|
|
57
|
+
'==',
|
|
58
|
+
'!=',
|
|
59
|
+
'<',
|
|
60
|
+
'>',
|
|
61
|
+
'<=',
|
|
62
|
+
'>=',
|
|
63
|
+
'<=>',
|
|
64
|
+
'===',
|
|
65
|
+
'=~',
|
|
66
|
+
'!~',
|
|
67
|
+
'&&',
|
|
68
|
+
'||',
|
|
69
|
+
'!',
|
|
70
|
+
'and',
|
|
71
|
+
'or',
|
|
72
|
+
'not',
|
|
73
|
+
'&',
|
|
74
|
+
'|',
|
|
75
|
+
'^',
|
|
76
|
+
'~',
|
|
77
|
+
'<<',
|
|
78
|
+
'>>',
|
|
79
|
+
'if',
|
|
80
|
+
'else',
|
|
81
|
+
'elsif',
|
|
82
|
+
'unless',
|
|
83
|
+
'case',
|
|
84
|
+
'when',
|
|
85
|
+
'for',
|
|
86
|
+
'while',
|
|
87
|
+
'until',
|
|
88
|
+
'do',
|
|
89
|
+
'begin',
|
|
90
|
+
'end',
|
|
91
|
+
'return',
|
|
92
|
+
'raise',
|
|
93
|
+
'break',
|
|
94
|
+
'next',
|
|
95
|
+
'redo',
|
|
96
|
+
'retry',
|
|
97
|
+
'rescue',
|
|
98
|
+
'ensure',
|
|
99
|
+
'yield',
|
|
100
|
+
'def',
|
|
101
|
+
'class',
|
|
102
|
+
'module',
|
|
103
|
+
'.',
|
|
104
|
+
',',
|
|
105
|
+
':',
|
|
106
|
+
'::',
|
|
107
|
+
'=>',
|
|
108
|
+
'->',
|
|
109
|
+
]),
|
|
110
|
+
operandLeafTypes: new Set([
|
|
111
|
+
'identifier',
|
|
112
|
+
'constant',
|
|
113
|
+
'instance_variable',
|
|
114
|
+
'class_variable',
|
|
115
|
+
'global_variable',
|
|
116
|
+
'integer',
|
|
117
|
+
'float',
|
|
118
|
+
'string_content',
|
|
119
|
+
'symbol',
|
|
120
|
+
'true',
|
|
121
|
+
'false',
|
|
122
|
+
'nil',
|
|
123
|
+
'self',
|
|
124
|
+
]),
|
|
125
|
+
compoundOperators: new Set(['call', 'element_reference']),
|
|
126
|
+
skipTypes: new Set([]),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// ─── CFG ──────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
export const cfg = makeCfgRules({
|
|
132
|
+
ifNode: 'if',
|
|
133
|
+
elifNode: 'elsif',
|
|
134
|
+
elseClause: 'else',
|
|
135
|
+
forNodes: new Set(['for']),
|
|
136
|
+
whileNode: 'while',
|
|
137
|
+
unlessNode: 'unless',
|
|
138
|
+
untilNode: 'until',
|
|
139
|
+
switchNode: 'case',
|
|
140
|
+
caseNode: 'when',
|
|
141
|
+
defaultNode: 'else',
|
|
142
|
+
tryNode: 'begin',
|
|
143
|
+
catchNode: 'rescue',
|
|
144
|
+
finallyNode: 'ensure',
|
|
145
|
+
returnNode: 'return',
|
|
146
|
+
breakNode: 'break',
|
|
147
|
+
continueNode: 'next',
|
|
148
|
+
blockNodes: new Set(['then', 'do', 'body_statement']),
|
|
149
|
+
functionNodes: new Set(['method', 'singleton_method']),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ─── Dataflow ─────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
export const dataflow = makeDataflowRules({
|
|
155
|
+
functionNodes: new Set(['method', 'singleton_method', 'lambda']),
|
|
156
|
+
paramListField: 'parameters',
|
|
157
|
+
returnNode: 'return',
|
|
158
|
+
varDeclaratorNode: null,
|
|
159
|
+
assignmentNode: 'assignment',
|
|
160
|
+
assignLeftField: 'left',
|
|
161
|
+
assignRightField: 'right',
|
|
162
|
+
callNode: 'call',
|
|
163
|
+
callFunctionField: 'method',
|
|
164
|
+
callArgsField: 'arguments',
|
|
165
|
+
spreadType: 'splat_parameter',
|
|
166
|
+
memberNode: 'call',
|
|
167
|
+
memberObjectField: 'receiver',
|
|
168
|
+
memberPropertyField: 'method',
|
|
169
|
+
mutatingMethods: new Set([
|
|
170
|
+
'push',
|
|
171
|
+
'pop',
|
|
172
|
+
'shift',
|
|
173
|
+
'unshift',
|
|
174
|
+
'delete',
|
|
175
|
+
'clear',
|
|
176
|
+
'sort!',
|
|
177
|
+
'reverse!',
|
|
178
|
+
'map!',
|
|
179
|
+
'select!',
|
|
180
|
+
'reject!',
|
|
181
|
+
'compact!',
|
|
182
|
+
'flatten!',
|
|
183
|
+
'concat',
|
|
184
|
+
'replace',
|
|
185
|
+
'insert',
|
|
186
|
+
]),
|
|
187
|
+
extractParamName(node) {
|
|
188
|
+
if (node.type === 'identifier') return [node.text];
|
|
189
|
+
if (
|
|
190
|
+
node.type === 'optional_parameter' ||
|
|
191
|
+
node.type === 'keyword_parameter' ||
|
|
192
|
+
node.type === 'splat_parameter' ||
|
|
193
|
+
node.type === 'hash_splat_parameter'
|
|
194
|
+
) {
|
|
195
|
+
const nameNode = node.childForFieldName('name');
|
|
196
|
+
return nameNode ? [nameNode.text] : null;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ─── AST Node Types ───────────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
export const astTypes = null;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust — AST analysis rules.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { makeCfgRules, makeDataflowRules } from '../shared.js';
|
|
6
|
+
|
|
7
|
+
// ─── Complexity ───────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export const complexity = {
|
|
10
|
+
branchNodes: new Set([
|
|
11
|
+
'if_expression',
|
|
12
|
+
'else_clause',
|
|
13
|
+
'for_expression',
|
|
14
|
+
'while_expression',
|
|
15
|
+
'loop_expression',
|
|
16
|
+
'if_let_expression',
|
|
17
|
+
'while_let_expression',
|
|
18
|
+
'match_expression',
|
|
19
|
+
]),
|
|
20
|
+
caseNodes: new Set(['match_arm']),
|
|
21
|
+
logicalOperators: new Set(['&&', '||']),
|
|
22
|
+
logicalNodeType: 'binary_expression',
|
|
23
|
+
optionalChainType: null,
|
|
24
|
+
nestingNodes: new Set([
|
|
25
|
+
'if_expression',
|
|
26
|
+
'for_expression',
|
|
27
|
+
'while_expression',
|
|
28
|
+
'loop_expression',
|
|
29
|
+
'if_let_expression',
|
|
30
|
+
'while_let_expression',
|
|
31
|
+
'match_expression',
|
|
32
|
+
]),
|
|
33
|
+
functionNodes: new Set(['function_item', 'closure_expression']),
|
|
34
|
+
ifNodeType: 'if_expression',
|
|
35
|
+
elseNodeType: 'else_clause',
|
|
36
|
+
elifNodeType: null,
|
|
37
|
+
elseViaAlternative: false,
|
|
38
|
+
switchLikeNodes: new Set(['match_expression']),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ─── Halstead ─────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export const halstead = {
|
|
44
|
+
operatorLeafTypes: new Set([
|
|
45
|
+
'+',
|
|
46
|
+
'-',
|
|
47
|
+
'*',
|
|
48
|
+
'/',
|
|
49
|
+
'%',
|
|
50
|
+
'=',
|
|
51
|
+
'+=',
|
|
52
|
+
'-=',
|
|
53
|
+
'*=',
|
|
54
|
+
'/=',
|
|
55
|
+
'%=',
|
|
56
|
+
'&=',
|
|
57
|
+
'|=',
|
|
58
|
+
'^=',
|
|
59
|
+
'<<=',
|
|
60
|
+
'>>=',
|
|
61
|
+
'==',
|
|
62
|
+
'!=',
|
|
63
|
+
'<',
|
|
64
|
+
'>',
|
|
65
|
+
'<=',
|
|
66
|
+
'>=',
|
|
67
|
+
'&&',
|
|
68
|
+
'||',
|
|
69
|
+
'!',
|
|
70
|
+
'&',
|
|
71
|
+
'|',
|
|
72
|
+
'^',
|
|
73
|
+
'<<',
|
|
74
|
+
'>>',
|
|
75
|
+
'if',
|
|
76
|
+
'else',
|
|
77
|
+
'for',
|
|
78
|
+
'while',
|
|
79
|
+
'loop',
|
|
80
|
+
'match',
|
|
81
|
+
'return',
|
|
82
|
+
'break',
|
|
83
|
+
'continue',
|
|
84
|
+
'let',
|
|
85
|
+
'mut',
|
|
86
|
+
'ref',
|
|
87
|
+
'as',
|
|
88
|
+
'in',
|
|
89
|
+
'move',
|
|
90
|
+
'fn',
|
|
91
|
+
'struct',
|
|
92
|
+
'enum',
|
|
93
|
+
'trait',
|
|
94
|
+
'impl',
|
|
95
|
+
'pub',
|
|
96
|
+
'mod',
|
|
97
|
+
'use',
|
|
98
|
+
'.',
|
|
99
|
+
',',
|
|
100
|
+
';',
|
|
101
|
+
':',
|
|
102
|
+
'::',
|
|
103
|
+
'=>',
|
|
104
|
+
'->',
|
|
105
|
+
'?',
|
|
106
|
+
]),
|
|
107
|
+
operandLeafTypes: new Set([
|
|
108
|
+
'identifier',
|
|
109
|
+
'field_identifier',
|
|
110
|
+
'type_identifier',
|
|
111
|
+
'integer_literal',
|
|
112
|
+
'float_literal',
|
|
113
|
+
'string_content',
|
|
114
|
+
'char_literal',
|
|
115
|
+
'true',
|
|
116
|
+
'false',
|
|
117
|
+
'self',
|
|
118
|
+
'Self',
|
|
119
|
+
]),
|
|
120
|
+
compoundOperators: new Set(['call_expression', 'index_expression', 'field_expression']),
|
|
121
|
+
skipTypes: new Set([]),
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// ─── CFG ──────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
export const cfg = makeCfgRules({
|
|
127
|
+
ifNode: 'if_expression',
|
|
128
|
+
ifNodes: new Set(['if_let_expression']),
|
|
129
|
+
elseClause: 'else_clause',
|
|
130
|
+
forNodes: new Set(['for_expression']),
|
|
131
|
+
whileNode: 'while_expression',
|
|
132
|
+
whileNodes: new Set(['while_let_expression']),
|
|
133
|
+
infiniteLoopNode: 'loop_expression',
|
|
134
|
+
switchNode: 'match_expression',
|
|
135
|
+
caseNode: 'match_arm',
|
|
136
|
+
returnNode: 'return_expression',
|
|
137
|
+
breakNode: 'break_expression',
|
|
138
|
+
continueNode: 'continue_expression',
|
|
139
|
+
blockNode: 'block',
|
|
140
|
+
functionNodes: new Set(['function_item', 'closure_expression']),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ─── Dataflow ─────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
export const dataflow = makeDataflowRules({
|
|
146
|
+
functionNodes: new Set(['function_item', 'closure_expression']),
|
|
147
|
+
returnNode: 'return_expression',
|
|
148
|
+
varDeclaratorNode: 'let_declaration',
|
|
149
|
+
varNameField: 'pattern',
|
|
150
|
+
varValueField: 'value',
|
|
151
|
+
assignmentNode: 'assignment_expression',
|
|
152
|
+
callNode: 'call_expression',
|
|
153
|
+
callFunctionField: 'function',
|
|
154
|
+
callArgsField: 'arguments',
|
|
155
|
+
memberNode: 'field_expression',
|
|
156
|
+
memberObjectField: 'value',
|
|
157
|
+
memberPropertyField: 'field',
|
|
158
|
+
awaitNode: 'await_expression',
|
|
159
|
+
mutatingMethods: new Set(['push', 'pop', 'insert', 'remove', 'clear', 'sort', 'reverse']),
|
|
160
|
+
extractParamName(node) {
|
|
161
|
+
if (node.type === 'parameter') {
|
|
162
|
+
const pat = node.childForFieldName('pattern');
|
|
163
|
+
if (pat?.type === 'identifier') return [pat.text];
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
if (node.type === 'identifier') return [node.text];
|
|
167
|
+
return null;
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// ─── AST Node Types ───────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
export const astTypes = null;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for AST analysis modules (complexity, CFG, dataflow, AST nodes).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { LANGUAGE_REGISTRY } from '../parser.js';
|
|
6
|
+
|
|
7
|
+
// ─── Generic Rule Factory ─────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Merge defaults with overrides, validating that all keys are known.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} defaults - Default rule values (defines the valid key set)
|
|
13
|
+
* @param {object} overrides - Language-specific overrides
|
|
14
|
+
* @param {string} label - Label for error messages (e.g. "CFG", "Dataflow")
|
|
15
|
+
* @returns {object} Merged rules
|
|
16
|
+
*/
|
|
17
|
+
export function makeRules(defaults, overrides, label) {
|
|
18
|
+
const validKeys = new Set(Object.keys(defaults));
|
|
19
|
+
for (const key of Object.keys(overrides)) {
|
|
20
|
+
if (!validKeys.has(key)) {
|
|
21
|
+
throw new Error(`${label} rules: unknown key "${key}"`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { ...defaults, ...overrides };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── CFG Defaults + Factory ───────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export const CFG_DEFAULTS = {
|
|
30
|
+
ifNode: null,
|
|
31
|
+
ifNodes: null,
|
|
32
|
+
elifNode: null,
|
|
33
|
+
elseClause: null,
|
|
34
|
+
elseViaAlternative: false,
|
|
35
|
+
ifConsequentField: null,
|
|
36
|
+
forNodes: new Set(),
|
|
37
|
+
whileNode: null,
|
|
38
|
+
whileNodes: null,
|
|
39
|
+
doNode: null,
|
|
40
|
+
infiniteLoopNode: null,
|
|
41
|
+
unlessNode: null,
|
|
42
|
+
untilNode: null,
|
|
43
|
+
switchNode: null,
|
|
44
|
+
switchNodes: null,
|
|
45
|
+
caseNode: null,
|
|
46
|
+
caseNodes: null,
|
|
47
|
+
defaultNode: null,
|
|
48
|
+
tryNode: null,
|
|
49
|
+
catchNode: null,
|
|
50
|
+
finallyNode: null,
|
|
51
|
+
returnNode: null,
|
|
52
|
+
throwNode: null,
|
|
53
|
+
breakNode: null,
|
|
54
|
+
continueNode: null,
|
|
55
|
+
blockNode: null,
|
|
56
|
+
blockNodes: null,
|
|
57
|
+
labeledNode: null,
|
|
58
|
+
functionNodes: new Set(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function makeCfgRules(overrides) {
|
|
62
|
+
const rules = makeRules(CFG_DEFAULTS, overrides, 'CFG');
|
|
63
|
+
if (!(rules.functionNodes instanceof Set) || rules.functionNodes.size === 0) {
|
|
64
|
+
throw new Error('CFG rules: functionNodes must be a non-empty Set');
|
|
65
|
+
}
|
|
66
|
+
if (!(rules.forNodes instanceof Set)) {
|
|
67
|
+
throw new Error('CFG rules: forNodes must be a Set');
|
|
68
|
+
}
|
|
69
|
+
return rules;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Dataflow Defaults + Factory ──────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export const DATAFLOW_DEFAULTS = {
|
|
75
|
+
// Scope entry
|
|
76
|
+
functionNodes: new Set(), // REQUIRED: non-empty
|
|
77
|
+
|
|
78
|
+
// Function name extraction
|
|
79
|
+
nameField: 'name',
|
|
80
|
+
varAssignedFnParent: null, // parent type for `const fn = ...` (JS only)
|
|
81
|
+
assignmentFnParent: null, // parent type for `x = function...` (JS only)
|
|
82
|
+
pairFnParent: null, // parent type for `{ key: function }` (JS only)
|
|
83
|
+
|
|
84
|
+
// Parameters
|
|
85
|
+
paramListField: 'parameters',
|
|
86
|
+
paramIdentifier: 'identifier',
|
|
87
|
+
paramWrapperTypes: new Set(),
|
|
88
|
+
defaultParamType: null,
|
|
89
|
+
restParamType: null,
|
|
90
|
+
objectDestructType: null,
|
|
91
|
+
arrayDestructType: null,
|
|
92
|
+
shorthandPropPattern: null,
|
|
93
|
+
pairPatternType: null,
|
|
94
|
+
extractParamName: null, // override: (node) => string[]
|
|
95
|
+
|
|
96
|
+
// Return
|
|
97
|
+
returnNode: null,
|
|
98
|
+
|
|
99
|
+
// Variable declarations
|
|
100
|
+
varDeclaratorNode: null,
|
|
101
|
+
varDeclaratorNodes: null,
|
|
102
|
+
varNameField: 'name',
|
|
103
|
+
varValueField: 'value',
|
|
104
|
+
assignmentNode: null,
|
|
105
|
+
assignLeftField: 'left',
|
|
106
|
+
assignRightField: 'right',
|
|
107
|
+
|
|
108
|
+
// Calls
|
|
109
|
+
callNode: null,
|
|
110
|
+
callNodes: null,
|
|
111
|
+
callFunctionField: 'function',
|
|
112
|
+
callArgsField: 'arguments',
|
|
113
|
+
spreadType: null,
|
|
114
|
+
|
|
115
|
+
// Member access
|
|
116
|
+
memberNode: null,
|
|
117
|
+
memberObjectField: 'object',
|
|
118
|
+
memberPropertyField: 'property',
|
|
119
|
+
optionalChainNode: null,
|
|
120
|
+
|
|
121
|
+
// Await
|
|
122
|
+
awaitNode: null,
|
|
123
|
+
|
|
124
|
+
// Mutation
|
|
125
|
+
mutatingMethods: new Set(),
|
|
126
|
+
expressionStmtNode: 'expression_statement',
|
|
127
|
+
callObjectField: null, // Java: combined call+member has [object] field on call node
|
|
128
|
+
|
|
129
|
+
// Structural wrappers
|
|
130
|
+
expressionListType: null, // Go: expression_list wraps LHS/RHS of short_var_declaration
|
|
131
|
+
equalsClauseType: null, // C#: equals_value_clause wraps variable initializer
|
|
132
|
+
argumentWrapperType: null, // PHP: individual args wrapped in 'argument' nodes
|
|
133
|
+
extraIdentifierTypes: null, // Set of additional identifier-like types (PHP: variable_name, name)
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export function makeDataflowRules(overrides) {
|
|
137
|
+
const rules = makeRules(DATAFLOW_DEFAULTS, overrides, 'Dataflow');
|
|
138
|
+
if (!(rules.functionNodes instanceof Set) || rules.functionNodes.size === 0) {
|
|
139
|
+
throw new Error('Dataflow rules: functionNodes must be a non-empty Set');
|
|
140
|
+
}
|
|
141
|
+
return rules;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── AST Helpers ──────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Find the function body node in a parse tree that matches a given line range.
|
|
148
|
+
*/
|
|
149
|
+
export function findFunctionNode(rootNode, startLine, _endLine, rules) {
|
|
150
|
+
// tree-sitter lines are 0-indexed
|
|
151
|
+
const targetStart = startLine - 1;
|
|
152
|
+
|
|
153
|
+
let best = null;
|
|
154
|
+
|
|
155
|
+
function search(node) {
|
|
156
|
+
const nodeStart = node.startPosition.row;
|
|
157
|
+
const nodeEnd = node.endPosition.row;
|
|
158
|
+
|
|
159
|
+
// Prune branches outside range
|
|
160
|
+
if (nodeEnd < targetStart || nodeStart > targetStart + 1) return;
|
|
161
|
+
|
|
162
|
+
if (rules.functionNodes.has(node.type) && nodeStart === targetStart) {
|
|
163
|
+
// Found a function node at the right position — pick it
|
|
164
|
+
if (!best || nodeEnd - nodeStart < best.endPosition.row - best.startPosition.row) {
|
|
165
|
+
best = node;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
170
|
+
search(node.child(i));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
search(rootNode);
|
|
175
|
+
return best;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Truncate a string to a maximum length, appending an ellipsis if truncated.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} str - Input string
|
|
182
|
+
* @param {number} [max=200] - Maximum length
|
|
183
|
+
* @returns {string}
|
|
184
|
+
*/
|
|
185
|
+
export function truncate(str, max = 200) {
|
|
186
|
+
if (!str) return '';
|
|
187
|
+
return str.length > max ? `${str.slice(0, max)}…` : str;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── Extension / Language Mapping ─────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Build a Map from file extension → language ID using the parser registry.
|
|
194
|
+
*
|
|
195
|
+
* @param {Iterable} [registry=LANGUAGE_REGISTRY] - Language registry entries
|
|
196
|
+
* @returns {Map<string, string>}
|
|
197
|
+
*/
|
|
198
|
+
export function buildExtToLangMap(registry = LANGUAGE_REGISTRY) {
|
|
199
|
+
const map = new Map();
|
|
200
|
+
for (const entry of registry) {
|
|
201
|
+
for (const ext of entry.extensions) {
|
|
202
|
+
map.set(ext, entry.id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return map;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Build a Set of file extensions for languages that have entries in the given rules Map.
|
|
210
|
+
*
|
|
211
|
+
* @param {Map<string, any>} rulesMap - e.g. COMPLEXITY_RULES, CFG_RULES
|
|
212
|
+
* @param {Iterable} [registry=LANGUAGE_REGISTRY] - Language registry entries
|
|
213
|
+
* @returns {Set<string>}
|
|
214
|
+
*/
|
|
215
|
+
export function buildExtensionSet(rulesMap, registry = LANGUAGE_REGISTRY) {
|
|
216
|
+
const extensions = new Set();
|
|
217
|
+
for (const entry of registry) {
|
|
218
|
+
if (rulesMap.has(entry.id)) {
|
|
219
|
+
for (const ext of entry.extensions) extensions.add(ext);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return extensions;
|
|
223
|
+
}
|