@codeflow-map/core 0.1.0
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 +158 -0
- package/dist/analyzer/extractorUtils.d.ts +50 -0
- package/dist/analyzer/extractorUtils.js +247 -0
- package/dist/analyzer/index.d.ts +9 -0
- package/dist/analyzer/index.js +149 -0
- package/dist/analyzer/languages/go.d.ts +2 -0
- package/dist/analyzer/languages/go.js +232 -0
- package/dist/analyzer/languages/javascript.d.ts +2 -0
- package/dist/analyzer/languages/javascript.js +265 -0
- package/dist/analyzer/languages/jsx.d.ts +2 -0
- package/dist/analyzer/languages/jsx.js +275 -0
- package/dist/analyzer/languages/python.d.ts +2 -0
- package/dist/analyzer/languages/python.js +236 -0
- package/dist/analyzer/languages/tsx.d.ts +2 -0
- package/dist/analyzer/languages/tsx.js +358 -0
- package/dist/analyzer/languages/typescript.d.ts +2 -0
- package/dist/analyzer/languages/typescript.js +351 -0
- package/dist/analyzer/treeSitter.d.ts +4 -0
- package/dist/analyzer/treeSitter.js +62 -0
- package/dist/graph/callGraphBuilder.d.ts +2 -0
- package/dist/graph/callGraphBuilder.js +58 -0
- package/dist/graph/entryPointDetector.d.ts +2 -0
- package/dist/graph/entryPointDetector.js +11 -0
- package/dist/graph/flowPartitioner.d.ts +5 -0
- package/dist/graph/flowPartitioner.js +101 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +13 -0
- package/package.json +48 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsxAnalyzer = void 0;
|
|
4
|
+
const extractorUtils_1 = require("../extractorUtils");
|
|
5
|
+
exports.jsxAnalyzer = {
|
|
6
|
+
functionQuery: `
|
|
7
|
+
(function_declaration
|
|
8
|
+
name: (identifier) @fn.name
|
|
9
|
+
parameters: (formal_parameters) @fn.params) @fn.decl
|
|
10
|
+
|
|
11
|
+
(lexical_declaration
|
|
12
|
+
(variable_declarator
|
|
13
|
+
name: (identifier) @fn.name
|
|
14
|
+
value: [(arrow_function) (function_expression)] @fn.decl))
|
|
15
|
+
|
|
16
|
+
(export_statement
|
|
17
|
+
declaration: (lexical_declaration
|
|
18
|
+
(variable_declarator
|
|
19
|
+
name: (identifier) @fn.name
|
|
20
|
+
value: [(arrow_function) (function_expression)] @fn.decl)))
|
|
21
|
+
|
|
22
|
+
(method_definition
|
|
23
|
+
name: (property_identifier) @fn.name
|
|
24
|
+
parameters: (formal_parameters) @fn.params) @fn.decl
|
|
25
|
+
|
|
26
|
+
(call_expression
|
|
27
|
+
function: (identifier) @hook.name
|
|
28
|
+
arguments: (arguments
|
|
29
|
+
[(arrow_function) (function_expression)] @fn.decl)
|
|
30
|
+
(#match? @hook.name "^use"))
|
|
31
|
+
|
|
32
|
+
(generator_function_declaration
|
|
33
|
+
name: (identifier) @fn.name
|
|
34
|
+
parameters: (formal_parameters) @fn.params) @fn.decl
|
|
35
|
+
|
|
36
|
+
(lexical_declaration
|
|
37
|
+
(variable_declarator
|
|
38
|
+
name: (identifier) @fn.name
|
|
39
|
+
value: (generator_function) @fn.decl))
|
|
40
|
+
|
|
41
|
+
(export_statement
|
|
42
|
+
declaration: (lexical_declaration
|
|
43
|
+
(variable_declarator
|
|
44
|
+
name: (identifier) @fn.name
|
|
45
|
+
value: (generator_function) @fn.decl)))
|
|
46
|
+
|
|
47
|
+
(function_expression
|
|
48
|
+
name: (identifier) @fn.name
|
|
49
|
+
parameters: (formal_parameters) @fn.params) @fn.decl
|
|
50
|
+
|
|
51
|
+
(lexical_declaration
|
|
52
|
+
(variable_declarator
|
|
53
|
+
name: (identifier) @fn.name
|
|
54
|
+
value: (call_expression
|
|
55
|
+
function: [(parenthesized_expression
|
|
56
|
+
[(function_expression)(arrow_function)] @fn.decl)
|
|
57
|
+
(arrow_function) @fn.decl
|
|
58
|
+
(function_expression) @fn.decl])))
|
|
59
|
+
|
|
60
|
+
(expression_statement
|
|
61
|
+
(call_expression
|
|
62
|
+
function: (parenthesized_expression
|
|
63
|
+
[(function_expression)(arrow_function)] @fn.decl)))
|
|
64
|
+
|
|
65
|
+
(assignment_expression
|
|
66
|
+
left: (member_expression
|
|
67
|
+
object: (identifier) @worker.obj
|
|
68
|
+
property: (property_identifier) @worker.prop)
|
|
69
|
+
right: [(arrow_function) (function_expression)] @worker.fn
|
|
70
|
+
(#match? @worker.obj "^self$")
|
|
71
|
+
(#match? @worker.prop "^onmessage$"))
|
|
72
|
+
|
|
73
|
+
(call_expression
|
|
74
|
+
function: (member_expression
|
|
75
|
+
object: (identifier) @worker.obj
|
|
76
|
+
property: (property_identifier) @worker.method)
|
|
77
|
+
arguments: (arguments
|
|
78
|
+
(string) @worker.event
|
|
79
|
+
[(arrow_function) (function_expression)] @worker.fn)
|
|
80
|
+
(#match? @worker.obj "^self$")
|
|
81
|
+
(#match? @worker.method "^addEventListener$")
|
|
82
|
+
(#match? @worker.event "message"))
|
|
83
|
+
`,
|
|
84
|
+
callQuery: `
|
|
85
|
+
(call_expression
|
|
86
|
+
function: [(identifier)(member_expression)] @call.name) @call.expr
|
|
87
|
+
|
|
88
|
+
(jsx_element
|
|
89
|
+
open_tag: (jsx_opening_element
|
|
90
|
+
name: (identifier) @call.name
|
|
91
|
+
(#match? @call.name "^[A-Z]"))) @call.expr
|
|
92
|
+
|
|
93
|
+
(jsx_self_closing_element
|
|
94
|
+
name: (identifier) @call.name
|
|
95
|
+
(#match? @call.name "^[A-Z]")) @call.expr
|
|
96
|
+
|
|
97
|
+
(new_expression
|
|
98
|
+
constructor: (identifier) @call.name) @call.expr
|
|
99
|
+
|
|
100
|
+
(call_expression
|
|
101
|
+
function: (member_expression
|
|
102
|
+
property: (property_identifier) @call.name
|
|
103
|
+
(#match? @call.name "^(then|catch|finally)$"))) @call.expr
|
|
104
|
+
`,
|
|
105
|
+
extractFunction(match, filePath, languageId) {
|
|
106
|
+
const declCapture = match.captures.find((c) => c.name === 'fn.decl');
|
|
107
|
+
if (!declCapture) {
|
|
108
|
+
const workerFn = match.captures.find((c) => c.name === 'worker.fn');
|
|
109
|
+
if (workerFn) {
|
|
110
|
+
const workerProp = match.captures.find((c) => c.name === 'worker.prop');
|
|
111
|
+
const node = workerFn.node;
|
|
112
|
+
const rawName = workerProp ? 'onmessage_handler' : `message_handler_${node.startPosition.row}`;
|
|
113
|
+
const paramsList = (0, extractorUtils_1.getParamsFromFunctionNode)(node, false);
|
|
114
|
+
return {
|
|
115
|
+
id: `${filePath}::${rawName}::${node.startPosition.row}`,
|
|
116
|
+
name: rawName,
|
|
117
|
+
filePath,
|
|
118
|
+
startLine: node.startPosition.row,
|
|
119
|
+
endLine: node.endPosition.row,
|
|
120
|
+
params: paramsList,
|
|
121
|
+
returnType: null,
|
|
122
|
+
isAsync: (0, extractorUtils_1.checkIsAsync)(node),
|
|
123
|
+
isExported: false,
|
|
124
|
+
isEntryPoint: true,
|
|
125
|
+
language: languageId,
|
|
126
|
+
kind: 'function'
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const nameCapture = match.captures.find((c) => c.name === 'fn.name');
|
|
132
|
+
const hookNameCapture = match.captures.find((c) => c.name === 'hook.name');
|
|
133
|
+
let rawName = "anonymous";
|
|
134
|
+
let kind = 'function';
|
|
135
|
+
let parentFunctionId;
|
|
136
|
+
if (nameCapture) {
|
|
137
|
+
rawName = nameCapture.node.text;
|
|
138
|
+
if (declCapture.node.type === 'function_expression' && nameCapture.node.parent?.id === declCapture.node.id) {
|
|
139
|
+
if (declCapture.node.parent?.type === 'variable_declarator') {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
parentFunctionId = (0, extractorUtils_1.getEnclosingFunctionId)(declCapture.node, filePath) ?? undefined;
|
|
143
|
+
}
|
|
144
|
+
if (declCapture.node.type === 'method_definition') {
|
|
145
|
+
kind = 'method';
|
|
146
|
+
}
|
|
147
|
+
else if (rawName.length > 0 && rawName[0] === rawName[0].toUpperCase())
|
|
148
|
+
kind = 'component';
|
|
149
|
+
}
|
|
150
|
+
else if (hookNameCapture) {
|
|
151
|
+
const startLine = declCapture.node.startPosition.row;
|
|
152
|
+
rawName = `${hookNameCapture.node.text}_callback_${startLine}`;
|
|
153
|
+
kind = 'hook';
|
|
154
|
+
}
|
|
155
|
+
// Detect IIFE: fn.decl is inside a call_expression (the IIFE invocation)
|
|
156
|
+
const declParent = declCapture.node.parent;
|
|
157
|
+
const isParenWrapped = declParent?.type === 'parenthesized_expression';
|
|
158
|
+
const callParent = isParenWrapped ? declParent.parent : declParent;
|
|
159
|
+
if (callParent?.type === 'call_expression') {
|
|
160
|
+
if (rawName === 'anonymous') {
|
|
161
|
+
rawName = `iife_${declCapture.node.startPosition.row}`;
|
|
162
|
+
}
|
|
163
|
+
kind = 'iife';
|
|
164
|
+
}
|
|
165
|
+
else if (rawName === "anonymous" && !hookNameCapture) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
// Prefix with enclosing scope chain (parent functions, classes, and object literals)
|
|
169
|
+
const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, extractorUtils_1.JS_FN_SCOPE_TYPES, extractorUtils_1.JS_CLASS_SCOPE_TYPES);
|
|
170
|
+
if (scopeChain.length > 0) {
|
|
171
|
+
rawName = `${scopeChain.map(s => s.name).join('.')}.${rawName}`;
|
|
172
|
+
}
|
|
173
|
+
const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
|
|
174
|
+
const isExported = (0, extractorUtils_1.checkExportedByParentWalk)(declCapture.node);
|
|
175
|
+
const paramsList = paramsCapture
|
|
176
|
+
? (0, extractorUtils_1.buildJsParams)(paramsCapture.node, false)
|
|
177
|
+
: (0, extractorUtils_1.getParamsFromFunctionNode)(declCapture.node, false);
|
|
178
|
+
let finalReturnType = null;
|
|
179
|
+
const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.JS_NESTED_FN_TYPES);
|
|
180
|
+
if (returnValues.length > 0) {
|
|
181
|
+
finalReturnType = returnValues.join(' | ');
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
id: `${filePath}::${rawName}::${declCapture.node.startPosition.row}`,
|
|
185
|
+
name: rawName,
|
|
186
|
+
filePath,
|
|
187
|
+
startLine: declCapture.node.startPosition.row,
|
|
188
|
+
endLine: declCapture.node.endPosition.row,
|
|
189
|
+
params: paramsList,
|
|
190
|
+
returnType: finalReturnType,
|
|
191
|
+
isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
|
|
192
|
+
isExported,
|
|
193
|
+
isEntryPoint: false,
|
|
194
|
+
language: languageId,
|
|
195
|
+
kind,
|
|
196
|
+
...(parentFunctionId ? { parentFunctionId } : {})
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
extractCall(match, filePath) {
|
|
200
|
+
const nameCapture = match.captures.find((c) => c.name === 'call.name');
|
|
201
|
+
const exprCapture = match.captures.find((c) => c.name === 'call.expr');
|
|
202
|
+
if (nameCapture && exprCapture) {
|
|
203
|
+
let calleeName = nameCapture.node.text;
|
|
204
|
+
if (nameCapture.node.type === 'member_expression') {
|
|
205
|
+
const prop = nameCapture.node.childForFieldName('property');
|
|
206
|
+
if (prop)
|
|
207
|
+
calleeName = prop.text;
|
|
208
|
+
}
|
|
209
|
+
// Unwrap .bind(): handleWorkerMessage.bind(this) → ref to handleWorkerMessage
|
|
210
|
+
if (calleeName === 'bind' && nameCapture.node.type === 'member_expression') {
|
|
211
|
+
const obj = nameCapture.node.childForFieldName('object');
|
|
212
|
+
if (obj?.type === 'identifier') {
|
|
213
|
+
return {
|
|
214
|
+
callerFilePath: filePath,
|
|
215
|
+
calleeName: obj.text,
|
|
216
|
+
line: exprCapture.node.startPosition.row,
|
|
217
|
+
callType: (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : 'ref',
|
|
218
|
+
isRef: true
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const callType = (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : undefined;
|
|
223
|
+
const directCall = {
|
|
224
|
+
callerFilePath: filePath,
|
|
225
|
+
calleeName,
|
|
226
|
+
line: exprCapture.node.startPosition.row,
|
|
227
|
+
...(callType ? { callType } : {})
|
|
228
|
+
};
|
|
229
|
+
// Extract function reference arguments from any call
|
|
230
|
+
const refCalls = [];
|
|
231
|
+
const callNode = exprCapture.node;
|
|
232
|
+
const argsNode = callNode.childForFieldName('arguments');
|
|
233
|
+
if (argsNode) {
|
|
234
|
+
for (const arg of argsNode.namedChildren) {
|
|
235
|
+
let refName = null;
|
|
236
|
+
if (arg.type === 'identifier') {
|
|
237
|
+
refName = arg.text;
|
|
238
|
+
}
|
|
239
|
+
else if (arg.type === 'member_expression') {
|
|
240
|
+
const obj = arg.childForFieldName('object');
|
|
241
|
+
const prop = arg.childForFieldName('property');
|
|
242
|
+
if (obj?.type === 'this' && prop) {
|
|
243
|
+
refName = prop.text;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (arg.type === 'call_expression') {
|
|
247
|
+
const fn = arg.childForFieldName('function');
|
|
248
|
+
if (fn?.type === 'member_expression') {
|
|
249
|
+
const prop = fn.childForFieldName('property');
|
|
250
|
+
const obj = fn.childForFieldName('object');
|
|
251
|
+
if (prop?.text === 'bind' && obj?.type === 'identifier') {
|
|
252
|
+
refName = obj.text;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (refName) {
|
|
257
|
+
const refCallType = (0, extractorUtils_1.isInsidePromiseCombinator)(callNode) ? 'concurrent' : 'ref';
|
|
258
|
+
refCalls.push({
|
|
259
|
+
callerFilePath: filePath,
|
|
260
|
+
calleeName: refName,
|
|
261
|
+
line: callNode.startPosition.row,
|
|
262
|
+
callType: refCallType,
|
|
263
|
+
isRef: true
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (refCalls.length > 0) {
|
|
269
|
+
return [directCall, ...refCalls];
|
|
270
|
+
}
|
|
271
|
+
return directCall;
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pythonAnalyzer = void 0;
|
|
4
|
+
const extractorUtils_1 = require("../extractorUtils");
|
|
5
|
+
/** Extract a function-reference name from an AST node (identifier or attribute). */
|
|
6
|
+
function extractFunctionRef(node) {
|
|
7
|
+
if (!node)
|
|
8
|
+
return null;
|
|
9
|
+
if (node.type === 'identifier')
|
|
10
|
+
return node.text;
|
|
11
|
+
if (node.type === 'attribute') {
|
|
12
|
+
const attr = node.childForFieldName('attribute');
|
|
13
|
+
return attr ? attr.text : null;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/** Get the function reference from a keyword argument by keyword name. */
|
|
18
|
+
function getKeywordArgRef(argsNode, keyword) {
|
|
19
|
+
if (!argsNode)
|
|
20
|
+
return null;
|
|
21
|
+
for (const child of argsNode.namedChildren) {
|
|
22
|
+
if (child.type === 'keyword_argument') {
|
|
23
|
+
const nameNode = child.childForFieldName('name');
|
|
24
|
+
if (nameNode?.text === keyword) {
|
|
25
|
+
return extractFunctionRef(child.childForFieldName('value'));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/** Get the function reference from the nth positional argument (skipping keyword args). */
|
|
32
|
+
function getPositionalArgRef(argsNode, index) {
|
|
33
|
+
if (!argsNode)
|
|
34
|
+
return null;
|
|
35
|
+
let pos = 0;
|
|
36
|
+
for (const child of argsNode.namedChildren) {
|
|
37
|
+
if (child.type === 'keyword_argument')
|
|
38
|
+
continue;
|
|
39
|
+
if (pos === index)
|
|
40
|
+
return extractFunctionRef(child);
|
|
41
|
+
pos++;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Check if a call node is a direct argument to asyncio.gather(). */
|
|
46
|
+
function isInsideAsyncioGather(node) {
|
|
47
|
+
const parent = node.parent;
|
|
48
|
+
if (!parent || parent.type !== 'argument_list')
|
|
49
|
+
return false;
|
|
50
|
+
const callNode = parent.parent;
|
|
51
|
+
if (!callNode || callNode.type !== 'call')
|
|
52
|
+
return false;
|
|
53
|
+
const fn = callNode.childForFieldName('function');
|
|
54
|
+
if (fn?.type === 'attribute') {
|
|
55
|
+
const obj = fn.childForFieldName('object');
|
|
56
|
+
const attr = fn.childForFieldName('attribute');
|
|
57
|
+
return obj?.text === 'asyncio' && attr?.text === 'gather';
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/** Check if a call node is the argument to asyncio.create_task(). */
|
|
62
|
+
function isArgumentToCreateTask(node) {
|
|
63
|
+
const parent = node.parent;
|
|
64
|
+
if (!parent || parent.type !== 'argument_list')
|
|
65
|
+
return false;
|
|
66
|
+
const callNode = parent.parent;
|
|
67
|
+
if (!callNode || callNode.type !== 'call')
|
|
68
|
+
return false;
|
|
69
|
+
const fn = callNode.childForFieldName('function');
|
|
70
|
+
if (fn?.type === 'attribute') {
|
|
71
|
+
const obj = fn.childForFieldName('object');
|
|
72
|
+
const attr = fn.childForFieldName('attribute');
|
|
73
|
+
return obj?.text === 'asyncio' && attr?.text === 'create_task';
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
exports.pythonAnalyzer = {
|
|
78
|
+
functionQuery: `
|
|
79
|
+
(function_definition
|
|
80
|
+
name: (identifier) @fn.name
|
|
81
|
+
parameters: (parameters) @fn.params
|
|
82
|
+
return_type: (type)? @fn.return_type) @fn.decl
|
|
83
|
+
|
|
84
|
+
`,
|
|
85
|
+
callQuery: `
|
|
86
|
+
(call
|
|
87
|
+
function: [(identifier) (attribute attribute: (identifier))] @call.name) @call.expr
|
|
88
|
+
`,
|
|
89
|
+
extractFunction(match, filePath, languageId) {
|
|
90
|
+
const declCapture = match.captures.find((c) => c.name === 'fn.decl');
|
|
91
|
+
if (!declCapture)
|
|
92
|
+
return null;
|
|
93
|
+
const nameCapture = match.captures.find((c) => c.name === 'fn.name');
|
|
94
|
+
let fnName = "anonymous";
|
|
95
|
+
let kind = 'function';
|
|
96
|
+
if (nameCapture) {
|
|
97
|
+
fnName = nameCapture.node.text;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// Check for enclosing scopes (classes and parent functions)
|
|
103
|
+
const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, ['function_definition'], ['class_definition']);
|
|
104
|
+
if (scopeChain.length > 0) {
|
|
105
|
+
fnName = `${scopeChain.map(s => s.name).join('.')}.${fnName}`;
|
|
106
|
+
if (scopeChain[scopeChain.length - 1].type === 'class') {
|
|
107
|
+
kind = 'method';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
|
|
111
|
+
const returnCapture = match.captures.find((c) => c.name === 'fn.return_type');
|
|
112
|
+
// Python doesn't have explicit export keywords, but names not starting with _ are generally public API
|
|
113
|
+
const originalName = nameCapture.node.text;
|
|
114
|
+
const isExported = !originalName.startsWith('_');
|
|
115
|
+
// Parse parameters properly into name/type objects
|
|
116
|
+
const paramsList = [];
|
|
117
|
+
if (paramsCapture && paramsCapture.node) {
|
|
118
|
+
for (const child of paramsCapture.node.children) {
|
|
119
|
+
if (child.type === 'identifier') {
|
|
120
|
+
paramsList.push({ name: child.text, type: null });
|
|
121
|
+
}
|
|
122
|
+
else if (child.type === 'typed_parameter') {
|
|
123
|
+
const pName = child.childForFieldName('name') || child.children[0];
|
|
124
|
+
const pType = child.childForFieldName('type') || child.children[2];
|
|
125
|
+
paramsList.push({
|
|
126
|
+
name: pName ? pName.text : child.text,
|
|
127
|
+
type: pType ? pType.text : null
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (child.type === 'default_parameter') {
|
|
131
|
+
const pName = child.childForFieldName('name') || child.children[0];
|
|
132
|
+
paramsList.push({
|
|
133
|
+
name: pName ? pName.text : child.text,
|
|
134
|
+
type: null
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else if (child.type === 'typed_default_parameter') {
|
|
138
|
+
const pName = child.childForFieldName('name') || child.children[0];
|
|
139
|
+
const pType = child.childForFieldName('type') || child.children[2];
|
|
140
|
+
paramsList.push({
|
|
141
|
+
name: pName ? pName.text : child.text,
|
|
142
|
+
type: pType ? pType.text : null
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else if (child.type === 'list_splat_pattern') {
|
|
146
|
+
const nameNode = child.namedChildren.find((c) => c.type === 'identifier');
|
|
147
|
+
if (nameNode) {
|
|
148
|
+
paramsList.push({ name: `*${nameNode.text}`, type: null });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (child.type === 'dictionary_splat_pattern') {
|
|
152
|
+
const nameNode = child.namedChildren.find((c) => c.type === 'identifier');
|
|
153
|
+
if (nameNode) {
|
|
154
|
+
paramsList.push({ name: `**${nameNode.text}`, type: null });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Strip Python implementation-detail parameters
|
|
160
|
+
const filteredParams = paramsList.filter(p => p.name !== 'self' && p.name !== 'cls');
|
|
161
|
+
let finalReturnType = returnCapture ? returnCapture.node.text : null;
|
|
162
|
+
if (!finalReturnType) {
|
|
163
|
+
const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.PYTHON_NESTED_FN_TYPES);
|
|
164
|
+
if (returnValues.length > 0) {
|
|
165
|
+
finalReturnType = returnValues.join(' | ');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
id: `${filePath}::${fnName}::${declCapture.node.startPosition.row}`,
|
|
170
|
+
name: fnName,
|
|
171
|
+
filePath,
|
|
172
|
+
startLine: declCapture.node.startPosition.row,
|
|
173
|
+
endLine: declCapture.node.endPosition.row,
|
|
174
|
+
params: filteredParams,
|
|
175
|
+
returnType: finalReturnType,
|
|
176
|
+
isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
|
|
177
|
+
isExported,
|
|
178
|
+
isEntryPoint: false,
|
|
179
|
+
language: languageId,
|
|
180
|
+
kind
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
extractCall(match, filePath) {
|
|
184
|
+
const nameCapture = match.captures.find((c) => c.name === 'call.name');
|
|
185
|
+
const exprCapture = match.captures.find((c) => c.name === 'call.expr');
|
|
186
|
+
if (!nameCapture || !exprCapture)
|
|
187
|
+
return null;
|
|
188
|
+
const nameNode = nameCapture.node;
|
|
189
|
+
const callNode = exprCapture.node;
|
|
190
|
+
const isMethodCall = nameNode.type === 'attribute';
|
|
191
|
+
let calleeName = nameNode.text;
|
|
192
|
+
if (isMethodCall) {
|
|
193
|
+
const attr = nameNode.childForFieldName('attribute');
|
|
194
|
+
if (attr)
|
|
195
|
+
calleeName = attr.text;
|
|
196
|
+
}
|
|
197
|
+
const argsNode = callNode.childForFieldName('arguments');
|
|
198
|
+
// threading.Thread(target=fn) or Thread(target=fn)
|
|
199
|
+
if (calleeName === 'Thread') {
|
|
200
|
+
const ref = getKeywordArgRef(argsNode, 'target');
|
|
201
|
+
if (ref) {
|
|
202
|
+
return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// executor.submit(fn, ...)
|
|
206
|
+
if (calleeName === 'submit' && isMethodCall) {
|
|
207
|
+
const ref = getPositionalArgRef(argsNode, 0);
|
|
208
|
+
if (ref) {
|
|
209
|
+
return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// executor.map(fn, iterable)
|
|
213
|
+
if (calleeName === 'map' && isMethodCall) {
|
|
214
|
+
const ref = getPositionalArgRef(argsNode, 0);
|
|
215
|
+
if (ref) {
|
|
216
|
+
return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// loop.run_in_executor(executor, fn, ...)
|
|
220
|
+
if (calleeName === 'run_in_executor' && isMethodCall) {
|
|
221
|
+
const ref = getPositionalArgRef(argsNode, 1);
|
|
222
|
+
if (ref) {
|
|
223
|
+
return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Calls inside asyncio.gather() or asyncio.create_task() are concurrent
|
|
227
|
+
let callType;
|
|
228
|
+
if (isInsideAsyncioGather(callNode) || isArgumentToCreateTask(callNode)) {
|
|
229
|
+
callType = 'concurrent';
|
|
230
|
+
}
|
|
231
|
+
const result = { callerFilePath: filePath, calleeName, line: callNode.startPosition.row };
|
|
232
|
+
if (callType)
|
|
233
|
+
result.callType = callType;
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
};
|