ucn 3.1.3 → 3.1.4

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.

Potentially problematic release.


This version of ucn might be problematic. Click here for more details.

package/core/project.js CHANGED
@@ -1010,11 +1010,37 @@ class ProjectIndex {
1010
1010
  }
1011
1011
 
1012
1012
  // Look up each callee in the symbol table
1013
+ // For methods, prefer callees from: 1) same file, 2) same package, 3) same receiver type
1013
1014
  const result = [];
1015
+ const defDir = path.dirname(def.file);
1016
+ const defReceiver = def.receiver;
1017
+
1014
1018
  for (const [calleeName, count] of callees) {
1015
1019
  const symbols = this.symbols.get(calleeName);
1016
1020
  if (symbols && symbols.length > 0) {
1017
- const callee = symbols[0];
1021
+ let callee = symbols[0];
1022
+
1023
+ // If multiple definitions, try to find the best match
1024
+ if (symbols.length > 1) {
1025
+ // Priority 1: Same file
1026
+ const sameFile = symbols.find(s => s.file === def.file);
1027
+ if (sameFile) {
1028
+ callee = sameFile;
1029
+ } else {
1030
+ // Priority 2: Same directory (package)
1031
+ const sameDir = symbols.find(s => path.dirname(s.file) === defDir);
1032
+ if (sameDir) {
1033
+ callee = sameDir;
1034
+ } else if (defReceiver) {
1035
+ // Priority 3: Same receiver type (for methods)
1036
+ const sameReceiver = symbols.find(s => s.receiver === defReceiver);
1037
+ if (sameReceiver) {
1038
+ callee = sameReceiver;
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+
1018
1044
  result.push({
1019
1045
  ...callee,
1020
1046
  callCount: count,
@@ -2137,23 +2163,15 @@ class ProjectIndex {
2137
2163
 
2138
2164
  const def = definitions[0];
2139
2165
  const visited = new Set();
2166
+ const defDir = path.dirname(def.file);
2140
2167
 
2141
- const buildTree = (funcName, currentDepth, dir) => {
2142
- if (currentDepth > maxDepth || visited.has(funcName)) {
2168
+ const buildTree = (funcDef, currentDepth, dir) => {
2169
+ const funcName = funcDef.name;
2170
+ if (currentDepth > maxDepth || visited.has(`${funcDef.file}:${funcDef.startLine}`)) {
2143
2171
  return null;
2144
2172
  }
2145
- visited.add(funcName);
2146
-
2147
- const funcDefs = this.symbols.get(funcName);
2148
- if (!funcDefs || funcDefs.length === 0) {
2149
- return {
2150
- name: funcName,
2151
- external: true,
2152
- children: []
2153
- };
2154
- }
2173
+ visited.add(`${funcDef.file}:${funcDef.startLine}`);
2155
2174
 
2156
- const funcDef = funcDefs[0];
2157
2175
  const node = {
2158
2176
  name: funcName,
2159
2177
  file: funcDef.relativePath,
@@ -2165,7 +2183,8 @@ class ProjectIndex {
2165
2183
  if (dir === 'down' || dir === 'both') {
2166
2184
  const callees = this.findCallees(funcDef);
2167
2185
  for (const callee of callees.slice(0, 10)) { // Limit children
2168
- const childTree = buildTree(callee.name, currentDepth + 1, 'down');
2186
+ // callee already has the best-matched definition from findCallees
2187
+ const childTree = buildTree(callee, currentDepth + 1, 'down');
2169
2188
  if (childTree) {
2170
2189
  node.children.push({
2171
2190
  ...childTree,
@@ -2179,7 +2198,7 @@ class ProjectIndex {
2179
2198
  return node;
2180
2199
  };
2181
2200
 
2182
- const tree = buildTree(name, 0, direction);
2201
+ const tree = buildTree(def, 0, direction);
2183
2202
 
2184
2203
  // Also get callers if direction is 'up' or 'both'
2185
2204
  let callers = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "3.1.3",
3
+ "version": "3.1.4",
4
4
  "description": "Code navigation built by AI, for AI. Reduces context usage when working with large codebases.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -4497,6 +4497,56 @@ function main() {
4497
4497
  }
4498
4498
  });
4499
4499
 
4500
+ it('should prefer same-file callees for Go methods', () => {
4501
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ucn-go-callee-disambig-'));
4502
+ try {
4503
+ fs.writeFileSync(path.join(tmpDir, 'go.mod'), `module example.com/test
4504
+ go 1.21
4505
+ `);
4506
+
4507
+ // Two files with same method name 'helper'
4508
+ fs.writeFileSync(path.join(tmpDir, 'service_a.go'), `package main
4509
+
4510
+ type ServiceA struct{}
4511
+
4512
+ func (s *ServiceA) Process() {
4513
+ s.helper()
4514
+ }
4515
+
4516
+ func (s *ServiceA) helper() {}
4517
+ `);
4518
+
4519
+ fs.writeFileSync(path.join(tmpDir, 'service_b.go'), `package main
4520
+
4521
+ type ServiceB struct{}
4522
+
4523
+ func (s *ServiceB) Process() {
4524
+ s.helper()
4525
+ }
4526
+
4527
+ func (s *ServiceB) helper() {}
4528
+ `);
4529
+
4530
+ const index = new ProjectIndex(tmpDir);
4531
+ index.build('**/*.go', { quiet: true });
4532
+
4533
+ // Get callees for ServiceA.Process - should find ServiceA.helper, not ServiceB.helper
4534
+ const defs = index.symbols.get('Process') || [];
4535
+ const serviceAProcess = defs.find(d => d.relativePath.includes('service_a.go'));
4536
+ assert.ok(serviceAProcess, 'Should find ServiceA.Process');
4537
+
4538
+ const callees = index.findCallees(serviceAProcess);
4539
+ assert.ok(callees.length >= 1, 'Should find at least 1 callee');
4540
+
4541
+ const helperCallee = callees.find(c => c.name === 'helper');
4542
+ assert.ok(helperCallee, 'Should find helper callee');
4543
+ assert.ok(helperCallee.relativePath.includes('service_a.go'),
4544
+ 'helper callee should be from service_a.go, not service_b.go');
4545
+ } finally {
4546
+ fs.rmSync(tmpDir, { recursive: true, force: true });
4547
+ }
4548
+ });
4549
+
4500
4550
  it('should detect Rust method calls in usages', () => {
4501
4551
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ucn-rust-method-'));
4502
4552
  try {