ucn 3.7.39 → 3.7.41
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 +4 -3
- package/core/callers.js +28 -5
- package/core/project.js +5 -4
- 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
|
@@ -101,6 +101,7 @@ function parseFlags(tokens) {
|
|
|
101
101
|
maxLines: getValueFlag('--max-lines') || null,
|
|
102
102
|
regex: tokens.includes('--no-regex') ? false : undefined,
|
|
103
103
|
functions: tokens.includes('--functions'),
|
|
104
|
+
className: getValueFlag('--class-name'),
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -125,7 +126,7 @@ const knownFlags = new Set([
|
|
|
125
126
|
'--default', '--top', '--no-follow-symlinks',
|
|
126
127
|
'--base', '--staged', '--stack',
|
|
127
128
|
'--regex', '--no-regex', '--functions',
|
|
128
|
-
'--max-lines'
|
|
129
|
+
'--max-lines', '--class-name'
|
|
129
130
|
]);
|
|
130
131
|
|
|
131
132
|
// Handle help flag
|
|
@@ -152,7 +153,7 @@ if (unknownFlags.length > 0) {
|
|
|
152
153
|
const VALUE_FLAGS = new Set([
|
|
153
154
|
'--file', '--depth', '--top', '--context', '--direction',
|
|
154
155
|
'--add-param', '--remove-param', '--rename-to', '--default',
|
|
155
|
-
'--base', '--exclude', '--not', '--in', '--max-lines'
|
|
156
|
+
'--base', '--exclude', '--not', '--in', '--max-lines', '--class-name'
|
|
156
157
|
]);
|
|
157
158
|
|
|
158
159
|
// Remove flags from args, then add args after -- (which are all positional)
|
|
@@ -554,7 +555,7 @@ function runProjectCommand(rootDir, command, arg) {
|
|
|
554
555
|
}
|
|
555
556
|
|
|
556
557
|
case 'verify': {
|
|
557
|
-
const { ok, result, error } = execute(index, 'verify', { name: arg,
|
|
558
|
+
const { ok, result, error } = execute(index, 'verify', { name: arg, ...flags });
|
|
558
559
|
if (!ok) fail(error);
|
|
559
560
|
printOutput(result, output.formatVerifyJson, output.formatVerify);
|
|
560
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
|
@@ -1032,7 +1032,8 @@ class ProjectIndex {
|
|
|
1032
1032
|
line: u.line,
|
|
1033
1033
|
content: lineContent,
|
|
1034
1034
|
usageType: u.usageType,
|
|
1035
|
-
isDefinition: false
|
|
1035
|
+
isDefinition: false,
|
|
1036
|
+
...(u.receiver && { receiver: u.receiver })
|
|
1036
1037
|
};
|
|
1037
1038
|
|
|
1038
1039
|
// Add context lines if requested
|
|
@@ -2745,9 +2746,9 @@ class ProjectIndex {
|
|
|
2745
2746
|
}
|
|
2746
2747
|
}
|
|
2747
2748
|
}
|
|
2748
|
-
//
|
|
2749
|
-
//
|
|
2750
|
-
return
|
|
2749
|
+
// className explicitly set but receiver type unknown — filter it out.
|
|
2750
|
+
// User asked for a specific class; unknown receivers are likely unrelated.
|
|
2751
|
+
return false;
|
|
2751
2752
|
});
|
|
2752
2753
|
}
|
|
2753
2754
|
|
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.41",
|
|
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",
|