ucn 3.7.32 → 3.7.34
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/core/callers.js +41 -34
- package/languages/python.js +27 -0
- package/package.json +1 -1
package/core/callers.js
CHANGED
|
@@ -227,47 +227,54 @@ function findCallers(index, name, options = {}) {
|
|
|
227
227
|
if (call.selfAttribute && fileEntry.language === 'python') {
|
|
228
228
|
// self.attr.method() — resolve via attribute type inference
|
|
229
229
|
const callerSymbol = index.findEnclosingFunction(filePath, call.line, true);
|
|
230
|
-
if (!callerSymbol?.className)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
230
|
+
if (!callerSymbol?.className) {
|
|
231
|
+
// Can't resolve — include only if includeMethods requested
|
|
232
|
+
if (!options.includeMethods) continue;
|
|
233
|
+
} else {
|
|
234
|
+
const attrTypes = getInstanceAttributeTypes(index, filePath, callerSymbol.className);
|
|
235
|
+
const targetClass = attrTypes?.get(call.selfAttribute);
|
|
236
|
+
if (targetClass && definitions.some(d => d.className === targetClass)) {
|
|
237
|
+
resolvedBySameClass = true;
|
|
238
|
+
} else if (!options.includeMethods) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
240
242
|
} else if (['self', 'cls', 'this', 'super'].includes(call.receiver)) {
|
|
241
243
|
// self/this/super.method() — resolve to same-class or parent method
|
|
242
244
|
const callerSymbol = index.findEnclosingFunction(filePath, call.line, true);
|
|
243
|
-
if (!callerSymbol?.className)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const { name:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
245
|
+
if (!callerSymbol?.className) {
|
|
246
|
+
if (!options.includeMethods) continue;
|
|
247
|
+
} else {
|
|
248
|
+
// For super(), skip same-class — only check parent chain
|
|
249
|
+
let matchesDef = call.receiver === 'super'
|
|
250
|
+
? false
|
|
251
|
+
: definitions.some(d => d.className === callerSymbol.className);
|
|
252
|
+
// Walk inheritance chain using BFS if not found in same class
|
|
253
|
+
if (!matchesDef) {
|
|
254
|
+
const visited = new Set([callerSymbol.className]);
|
|
255
|
+
const callerFile = callerSymbol.file || filePath;
|
|
256
|
+
const startParents = index._getInheritanceParents(callerSymbol.className, callerFile) || [];
|
|
257
|
+
const queue = startParents.map(p => ({ name: p, contextFile: callerFile }));
|
|
258
|
+
while (queue.length > 0 && !matchesDef) {
|
|
259
|
+
const { name: current, contextFile } = queue.shift();
|
|
260
|
+
if (visited.has(current)) continue;
|
|
261
|
+
visited.add(current);
|
|
262
|
+
matchesDef = definitions.some(d => d.className === current);
|
|
263
|
+
if (!matchesDef) {
|
|
264
|
+
const resolvedFile = index._resolveClassFile(current, contextFile);
|
|
265
|
+
const grandparents = index._getInheritanceParents(current, resolvedFile) || [];
|
|
266
|
+
for (const gp of grandparents) {
|
|
267
|
+
if (!visited.has(gp)) queue.push({ name: gp, contextFile: resolvedFile });
|
|
268
|
+
}
|
|
264
269
|
}
|
|
265
270
|
}
|
|
266
271
|
}
|
|
272
|
+
if (matchesDef) {
|
|
273
|
+
resolvedBySameClass = true;
|
|
274
|
+
} else if (!options.includeMethods) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
267
277
|
}
|
|
268
|
-
if (!matchesDef) continue;
|
|
269
|
-
resolvedBySameClass = true;
|
|
270
|
-
// Falls through to add as caller
|
|
271
278
|
} else {
|
|
272
279
|
// Go doesn't use this/self/cls - always include Go method calls
|
|
273
280
|
// Java method calls are always obj.method() - include by default
|
package/languages/python.js
CHANGED
|
@@ -982,6 +982,30 @@ function findInstanceAttributeTypes(code, parser) {
|
|
|
982
982
|
const initBody = child.childForFieldName('body');
|
|
983
983
|
if (!initBody) continue;
|
|
984
984
|
|
|
985
|
+
// Build parameter type map from __init__ annotations
|
|
986
|
+
// e.g. def __init__(self, market: MarketDataFetcher = None) → {market: MarketDataFetcher}
|
|
987
|
+
const paramTypes = new Map();
|
|
988
|
+
const params = child.childForFieldName('parameters');
|
|
989
|
+
if (params) {
|
|
990
|
+
for (let p = 0; p < params.childCount; p++) {
|
|
991
|
+
const param = params.child(p);
|
|
992
|
+
// typed_parameter or typed_default_parameter
|
|
993
|
+
if (param.type === 'typed_parameter' || param.type === 'typed_default_parameter') {
|
|
994
|
+
const pName = param.childForFieldName('name') || param.child(0);
|
|
995
|
+
const pType = param.childForFieldName('type');
|
|
996
|
+
if (pName && pType) {
|
|
997
|
+
const typeIdent = pType.type === 'type' ? pType.firstChild : pType;
|
|
998
|
+
if (typeIdent?.type === 'identifier') {
|
|
999
|
+
const tn = typeIdent.text;
|
|
1000
|
+
if (!PRIMITIVE_TYPES.has(tn) && tn[0] >= 'A' && tn[0] <= 'Z') {
|
|
1001
|
+
paramTypes.set(pName.text, tn);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
985
1009
|
traverseTree(initBody, (stmt) => {
|
|
986
1010
|
if (stmt.type !== 'expression_statement') return true;
|
|
987
1011
|
|
|
@@ -1004,6 +1028,9 @@ function findInstanceAttributeTypes(code, parser) {
|
|
|
1004
1028
|
const typeName = extractConstructorName(rhs);
|
|
1005
1029
|
if (typeName) {
|
|
1006
1030
|
attrTypes.set(attrName, typeName);
|
|
1031
|
+
} else if (rhs.type === 'identifier' && paramTypes.has(rhs.text)) {
|
|
1032
|
+
// self.X = param where param has type annotation
|
|
1033
|
+
attrTypes.set(attrName, paramTypes.get(rhs.text));
|
|
1007
1034
|
}
|
|
1008
1035
|
|
|
1009
1036
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.34",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
5
|
"description": "Universal Code Navigator — AST-based call graph analysis for AI agents. Find callers, trace impact, detect dead code across JS/TS, Python, Go, Rust, Java, and HTML. CLI, MCP server, and agent skill.",
|
|
6
6
|
"main": "index.js",
|