ucn 3.7.31 → 3.7.33
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/core/project.js +2 -2
- package/core/verify.js +8 -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/core/project.js
CHANGED
|
@@ -2420,7 +2420,7 @@ class ProjectIndex {
|
|
|
2420
2420
|
const symParts = symName.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase().split('_');
|
|
2421
2421
|
|
|
2422
2422
|
// Check for shared parts (require ≥50% of the longer name to match)
|
|
2423
|
-
const sharedParts = nameParts.filter(p => symParts.includes(p) && p.length >
|
|
2423
|
+
const sharedParts = nameParts.filter(p => symParts.includes(p) && p.length > 3);
|
|
2424
2424
|
const maxParts = Math.max(nameParts.length, symParts.length);
|
|
2425
2425
|
if (sharedParts.length > 0 && sharedParts.length / maxParts >= 0.5) {
|
|
2426
2426
|
const sym = symbols[0];
|
|
@@ -2786,7 +2786,7 @@ class ProjectIndex {
|
|
|
2786
2786
|
try {
|
|
2787
2787
|
const maxCallers = options.all ? Infinity : (options.maxCallers || 10);
|
|
2788
2788
|
const maxCallees = options.all ? Infinity : (options.maxCallees || 10);
|
|
2789
|
-
const includeMethods = options.includeMethods ??
|
|
2789
|
+
const includeMethods = options.includeMethods ?? false;
|
|
2790
2790
|
|
|
2791
2791
|
// Find symbol definition(s) — skip counts since about() computes its own via usages()
|
|
2792
2792
|
const definitions = this.find(name, { exact: true, file: options.file, className: options.className, skipCounts: true });
|
package/core/verify.js
CHANGED
|
@@ -327,6 +327,14 @@ function plan(index, name, options = {}) {
|
|
|
327
327
|
let changes = [];
|
|
328
328
|
|
|
329
329
|
if (options.addParam) {
|
|
330
|
+
// Check if parameter already exists
|
|
331
|
+
if (currentParams.some(p => p.name === options.addParam)) {
|
|
332
|
+
return {
|
|
333
|
+
found: true,
|
|
334
|
+
error: `Parameter "${options.addParam}" already exists in ${name}`,
|
|
335
|
+
currentParams: currentParams.map(p => p.name)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
330
338
|
operation = 'add-param';
|
|
331
339
|
const newParam = {
|
|
332
340
|
name: options.addParam,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.33",
|
|
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",
|