ucn 3.7.40 → 3.7.42
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/cli/index.js +2 -2
- package/core/callers.js +28 -5
- package/core/project.js +28 -1
- package/languages/go.js +6 -0
- package/languages/java.js +12 -0
- package/languages/javascript.js +5 -0
- package/languages/python.js +6 -0
- package/languages/rust.js +6 -0
- package/package.json +1 -1
package/cli/index.js
CHANGED
|
@@ -153,7 +153,7 @@ if (unknownFlags.length > 0) {
|
|
|
153
153
|
const VALUE_FLAGS = new Set([
|
|
154
154
|
'--file', '--depth', '--top', '--context', '--direction',
|
|
155
155
|
'--add-param', '--remove-param', '--rename-to', '--default',
|
|
156
|
-
'--base', '--exclude', '--not', '--in', '--max-lines'
|
|
156
|
+
'--base', '--exclude', '--not', '--in', '--max-lines', '--class-name'
|
|
157
157
|
]);
|
|
158
158
|
|
|
159
159
|
// Remove flags from args, then add args after -- (which are all positional)
|
|
@@ -555,7 +555,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
case 'verify': {
|
|
558
|
-
const { ok, result, error } = execute(index, 'verify', { name: arg,
|
|
558
|
+
const { ok, result, error } = execute(index, 'verify', { name: arg, ...flags });
|
|
559
559
|
if (!ok) fail(error);
|
|
560
560
|
printOutput(result, output.formatVerifyJson, output.formatVerify);
|
|
561
561
|
break;
|
package/core/callers.js
CHANGED
|
@@ -524,11 +524,18 @@ function findCallees(index, def, options = {}) {
|
|
|
524
524
|
if (bindings.length === 0 && call.isMethod) {
|
|
525
525
|
if (language !== 'go' && language !== 'java' && language !== 'rust') {
|
|
526
526
|
// JS/TS/Python: mark uncertain unless receiver has import/binding
|
|
527
|
-
// evidence in file scope
|
|
528
|
-
// repository.get() when m is
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
527
|
+
// evidence in file scope AND that binding can plausibly have this method.
|
|
528
|
+
// Prevents false positives like m.get() → repository.get() when m is
|
|
529
|
+
// just a parameter, AND dict.get() → api.get() when dict is a state object.
|
|
530
|
+
const receiverBinding = call.receiver &&
|
|
531
|
+
fileEntry?.bindings?.find(b => b.name === call.receiver);
|
|
532
|
+
if (!receiverBinding) {
|
|
533
|
+
isUncertain = true;
|
|
534
|
+
} else if (receiverBinding.type === 'state') {
|
|
535
|
+
// State objects (module-level dicts/lists) don't have user-defined methods
|
|
536
|
+
isUncertain = true;
|
|
537
|
+
} else if (receiverBinding.type === 'function') {
|
|
538
|
+
// Functions don't have user-defined methods (return value is unknown)
|
|
532
539
|
isUncertain = true;
|
|
533
540
|
}
|
|
534
541
|
} else {
|
|
@@ -542,6 +549,22 @@ function findCallees(index, def, options = {}) {
|
|
|
542
549
|
}
|
|
543
550
|
}
|
|
544
551
|
if (bindings.length === 1) {
|
|
552
|
+
// For method calls with a receiver, verify the receiver plausibly
|
|
553
|
+
// matches the binding's class. Prevents plt.close() → ReportGenerator.close()
|
|
554
|
+
// when close is defined in the same file as a class method.
|
|
555
|
+
if (call.isMethod && call.receiver && bindings[0].type === 'method' &&
|
|
556
|
+
language !== 'go' && language !== 'java' && language !== 'rust') {
|
|
557
|
+
// The binding is a class method — check if the receiver could be an instance
|
|
558
|
+
const bindingSym = index.symbols.get(call.name)?.find(
|
|
559
|
+
s => s.bindingId === bindings[0].id);
|
|
560
|
+
if (bindingSym?.className) {
|
|
561
|
+
// Receiver is not a known instance of this class → uncertain
|
|
562
|
+
const receiverType = localTypes?.get(call.receiver);
|
|
563
|
+
if (receiverType !== bindingSym.className) {
|
|
564
|
+
isUncertain = true;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
545
568
|
bindingResolved = bindings[0].id;
|
|
546
569
|
calleeKey = bindingResolved;
|
|
547
570
|
} else if (bindings.length > 1) {
|
package/core/project.js
CHANGED
|
@@ -1018,12 +1018,38 @@ class ProjectIndex {
|
|
|
1018
1018
|
// Try AST-based detection first (with per-operation cache)
|
|
1019
1019
|
const astUsages = this._getCachedUsages(filePath, name);
|
|
1020
1020
|
if (astUsages !== null) {
|
|
1021
|
+
// Pre-compute: does any imported project file define this name?
|
|
1022
|
+
// Used to filter namespace member expressions (e.g., DropdownMenuPrimitive.Separator)
|
|
1023
|
+
// while keeping module access patterns (e.g., output.formatExample())
|
|
1024
|
+
let _importedHasDef = null;
|
|
1025
|
+
const importedFileHasDef = () => {
|
|
1026
|
+
if (_importedHasDef !== null) return _importedHasDef;
|
|
1027
|
+
const importedFiles = this.importGraph.get(filePath) || [];
|
|
1028
|
+
_importedHasDef = importedFiles.some(imp => {
|
|
1029
|
+
const impEntry = this.files.get(imp);
|
|
1030
|
+
return impEntry?.symbols?.some(s => s.name === name);
|
|
1031
|
+
});
|
|
1032
|
+
return _importedHasDef;
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1021
1035
|
for (const u of astUsages) {
|
|
1022
1036
|
// Skip if this is a definition line (already added above)
|
|
1023
1037
|
if (definitions.some(d => d.file === filePath && d.startLine === u.line)) {
|
|
1024
1038
|
continue;
|
|
1025
1039
|
}
|
|
1026
1040
|
|
|
1041
|
+
// Filter member expressions with unrelated receivers in JS/TS/Python.
|
|
1042
|
+
// Keeps: standalone usages, self/this/cls/super, method calls on known types,
|
|
1043
|
+
// and module access (output.fn()) when the imported file defines the name.
|
|
1044
|
+
// Filters: namespace access to external packages (DropdownMenuPrimitive.Separator).
|
|
1045
|
+
if (u.receiver && !['self', 'this', 'cls', 'super'].includes(u.receiver) &&
|
|
1046
|
+
fileEntry.language !== 'go' && fileEntry.language !== 'java' && fileEntry.language !== 'rust') {
|
|
1047
|
+
const hasMethodDef = allDefinitions.some(d => d.className);
|
|
1048
|
+
if (!hasMethodDef && !importedFileHasDef()) {
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1027
1053
|
const lineContent = lines[u.line - 1] || '';
|
|
1028
1054
|
|
|
1029
1055
|
const usage = {
|
|
@@ -1032,7 +1058,8 @@ class ProjectIndex {
|
|
|
1032
1058
|
line: u.line,
|
|
1033
1059
|
content: lineContent,
|
|
1034
1060
|
usageType: u.usageType,
|
|
1035
|
-
isDefinition: false
|
|
1061
|
+
isDefinition: false,
|
|
1062
|
+
...(u.receiver && { receiver: u.receiver })
|
|
1036
1063
|
};
|
|
1037
1064
|
|
|
1038
1065
|
// Add context lines if requested
|
package/languages/go.js
CHANGED
|
@@ -804,6 +804,12 @@ function findUsagesInCode(code, name, parser) {
|
|
|
804
804
|
} else {
|
|
805
805
|
usageType = 'reference';
|
|
806
806
|
}
|
|
807
|
+
// Track receiver for selector expressions (obj.name → receiver = 'obj')
|
|
808
|
+
const operand = parent.childForFieldName('operand');
|
|
809
|
+
if (operand && operand.type === 'identifier') {
|
|
810
|
+
usages.push({ line, column, usageType, receiver: operand.text });
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
807
813
|
}
|
|
808
814
|
}
|
|
809
815
|
|
package/languages/java.js
CHANGED
|
@@ -887,6 +887,12 @@ function findUsagesInCode(code, name, parser) {
|
|
|
887
887
|
else if (parent.type === 'method_invocation' &&
|
|
888
888
|
parent.childForFieldName('name') === node) {
|
|
889
889
|
usageType = 'call';
|
|
890
|
+
// Track receiver for method invocations (obj.name() → receiver = 'obj')
|
|
891
|
+
const object = parent.childForFieldName('object');
|
|
892
|
+
if (object && object.type === 'identifier') {
|
|
893
|
+
usages.push({ line, column, usageType, receiver: object.text });
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
890
896
|
}
|
|
891
897
|
// Definition: method name
|
|
892
898
|
else if (parent.type === 'method_declaration' &&
|
|
@@ -946,6 +952,12 @@ function findUsagesInCode(code, name, parser) {
|
|
|
946
952
|
else if (parent.type === 'field_access' &&
|
|
947
953
|
parent.childForFieldName('field') === node) {
|
|
948
954
|
usageType = 'reference';
|
|
955
|
+
// Track receiver for field access (obj.field → receiver = 'obj')
|
|
956
|
+
const object = parent.childForFieldName('object');
|
|
957
|
+
if (object && object.type === 'identifier') {
|
|
958
|
+
usages.push({ line, column, usageType, receiver: object.text });
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
949
961
|
}
|
|
950
962
|
}
|
|
951
963
|
|
package/languages/javascript.js
CHANGED
|
@@ -1824,6 +1824,11 @@ function findUsagesInCode(code, name, parser) {
|
|
|
1824
1824
|
} else {
|
|
1825
1825
|
usageType = 'reference';
|
|
1826
1826
|
}
|
|
1827
|
+
// Track receiver for member expressions (obj.name → receiver = 'obj')
|
|
1828
|
+
if (object && object.type === 'identifier') {
|
|
1829
|
+
usages.push({ line, column, usageType, receiver: object.text });
|
|
1830
|
+
return true;
|
|
1831
|
+
}
|
|
1827
1832
|
}
|
|
1828
1833
|
// JSX component usage: <Component /> or <Component>...</Component>
|
|
1829
1834
|
else if (parent.type === 'jsx_self_closing_element' || parent.type === 'jsx_opening_element') {
|
package/languages/python.js
CHANGED
|
@@ -887,6 +887,12 @@ function findUsagesInCode(code, name, parser) {
|
|
|
887
887
|
} else {
|
|
888
888
|
usageType = 'reference';
|
|
889
889
|
}
|
|
890
|
+
// Track receiver for member expressions (obj.name → receiver = 'obj')
|
|
891
|
+
const object = parent.childForFieldName('object');
|
|
892
|
+
if (object && object.type === 'identifier') {
|
|
893
|
+
usages.push({ line, column, usageType, receiver: object.text });
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
890
896
|
}
|
|
891
897
|
}
|
|
892
898
|
|
package/languages/rust.js
CHANGED
|
@@ -1135,6 +1135,12 @@ function findUsagesInCode(code, name, parser) {
|
|
|
1135
1135
|
} else {
|
|
1136
1136
|
usageType = 'reference';
|
|
1137
1137
|
}
|
|
1138
|
+
// Track receiver for field expressions (obj.name → receiver = 'obj')
|
|
1139
|
+
const value = parent.childForFieldName('value');
|
|
1140
|
+
if (value && value.type === 'identifier') {
|
|
1141
|
+
usages.push({ line, column, usageType, receiver: value.text });
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1138
1144
|
}
|
|
1139
1145
|
}
|
|
1140
1146
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.42",
|
|
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",
|