ucn 3.7.32 → 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.
Files changed (2) hide show
  1. package/core/callers.js +41 -34
  2. 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) continue;
231
- const attrTypes = getInstanceAttributeTypes(index, filePath, callerSymbol.className);
232
- if (!attrTypes) continue;
233
- const targetClass = attrTypes.get(call.selfAttribute);
234
- if (!targetClass) continue;
235
- // Check if any definition of searched function belongs to targetClass
236
- const matchesDef = definitions.some(d => d.className === targetClass);
237
- if (!matchesDef) continue;
238
- resolvedBySameClass = true;
239
- // Falls through to add as caller
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) continue;
244
- // For super(), skip same-class — only check parent chain
245
- let matchesDef = call.receiver === 'super'
246
- ? false
247
- : definitions.some(d => d.className === callerSymbol.className);
248
- // Walk inheritance chain using BFS if not found in same class
249
- if (!matchesDef) {
250
- const visited = new Set([callerSymbol.className]);
251
- const callerFile = callerSymbol.file || filePath;
252
- const startParents = index._getInheritanceParents(callerSymbol.className, callerFile) || [];
253
- const queue = startParents.map(p => ({ name: p, contextFile: callerFile }));
254
- while (queue.length > 0 && !matchesDef) {
255
- const { name: current, contextFile } = queue.shift();
256
- if (visited.has(current)) continue;
257
- visited.add(current);
258
- matchesDef = definitions.some(d => d.className === current);
259
- if (!matchesDef) {
260
- const resolvedFile = index._resolveClassFile(current, contextFile);
261
- const grandparents = index._getInheritanceParents(current, resolvedFile) || [];
262
- for (const gp of grandparents) {
263
- if (!visited.has(gp)) queue.push({ name: gp, contextFile: resolvedFile });
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "3.7.32",
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",