-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathcall-resolver.test.ts
More file actions
182 lines (169 loc) · 6.38 KB
/
Copy pathcall-resolver.test.ts
File metadata and controls
182 lines (169 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* Unit tests for resolveByMethodOrGlobal in call-resolver.ts.
*
* Covers the qualified callerName fix (#1385): when callerName has more than
* one dot segment (e.g. 'Namespace.ClassName.method'), the same-class dispatch
* must use only the segment immediately before the method name ('ClassName'),
* not the full qualified prefix ('Namespace.ClassName').
*
* Also covers the static receiver confidence filter (#1398): the direct qualified
* method fallback must apply computeConfidence >= 0.5 to avoid false edges from
* distant files in a polyglot project.
*
* Also covers the bare-call JS/TS module-scope guard (#1407): bare `foo()` calls
* (no receiver) inside a JS/TS class method must NOT fall through to the same-class
* lookup, because bare calls in those languages are module-scoped, not class-scoped.
*/
import { describe, expect, it } from 'vitest';
import type { CallNodeLookup } from '../../src/domain/graph/builder/call-resolver.js';
import { resolveByMethodOrGlobal } from '../../src/domain/graph/builder/call-resolver.js';
function makeLookup(
methodMap: Record<string, Array<{ id: number; file: string; kind: string }>>,
): CallNodeLookup {
return {
byName(name) {
return methodMap[name] ?? [];
},
byNameAndFile() {
return [];
},
isBarrel() {
return false;
},
resolveBarrel() {
return null;
},
nodeId() {
return undefined;
},
};
}
describe('resolveByMethodOrGlobal — same-class this-dispatch with qualified callerName (#1385)', () => {
const method = { id: 42, file: 'shapes.js', kind: 'method' };
it('resolves this.area() inside ClassName.describe using bare ClassName', () => {
const lookup = makeLookup({ 'Shape.area': [method] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'area', receiver: 'this' },
'shapes.js',
new Map(),
'Shape.describe',
);
expect(result).toEqual([method]);
});
it('resolves this.area() inside Namespace.ClassName.describe using bare ClassName only', () => {
// Symbols are stored as 'Shape.area', not 'Namespace.Shape.area'.
// Before the fix, callerClass was 'Namespace.Shape' → lookup failed.
const lookup = makeLookup({ 'Shape.area': [method] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'area', receiver: 'this' },
'shapes.js',
new Map(),
'Namespace.Shape.describe',
);
expect(result).toEqual([method]);
});
it('does not resolve when callerName has no dot (bare function)', () => {
const lookup = makeLookup({ 'Shape.area': [method] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'area', receiver: 'this' },
'shapes.js',
new Map(),
'describe',
);
// No dot → no class prefix → falls through to exact bare-name lookup
expect(result).toEqual([]);
});
it('does not match namespace-qualified DB key when callerName has multiple dots', () => {
// Only a wrong key exists in the DB; the correct lookup should not find it.
const lookup = makeLookup({ 'Namespace.Shape.area': [method] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'area', receiver: 'this' },
'shapes.js',
new Map(),
'Namespace.Shape.describe',
);
// callerClass should be 'Shape', so 'Shape.area' is tried — which is absent.
expect(result).toEqual([]);
});
});
describe('resolveByMethodOrGlobal — static receiver confidence filter (#1398)', () => {
it('returns same-directory static target (confidence 0.7 >= 0.5)', () => {
const target = { id: 1, file: 'app/Validators.cs', kind: 'method' };
const lookup = makeLookup({ 'Validators.IsValidEmail': [target] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'IsValidEmail', receiver: 'Validators' },
'app/Program.cs',
new Map(),
);
expect(result).toEqual([target]);
});
it('filters out distant static target (confidence 0.3 < 0.5)', () => {
const target = { id: 2, file: 'lib/util/Validators.cs', kind: 'method' };
const lookup = makeLookup({ 'Validators.IsValidEmail': [target] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'IsValidEmail', receiver: 'Validators' },
'app/main/Program.cs',
new Map(),
);
expect(result).toEqual([]);
});
});
describe('resolveByMethodOrGlobal — bare-call JS/TS module-scope guard (#1407)', () => {
// `flush()` inside `Processor.run` — no receiver, JS/TS file.
// Must NOT resolve to `Processor.flush` (class-scoped lookup is incorrect for JS/TS).
const flushMethod = { id: 10, file: 'processor.ts', kind: 'method' };
it('does NOT resolve bare call to same-class method in a .ts file', () => {
const lookup = makeLookup({ 'Processor.flush': [flushMethod] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'flush', receiver: null },
'processor.ts',
new Map(),
'Processor.run',
);
// bare call + .ts → module-scoped language → same-class fallback skipped
expect(result).toEqual([]);
});
it('does NOT resolve bare call to same-class method in a .js file', () => {
const lookup = makeLookup({ 'Processor.flush': [flushMethod] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'flush', receiver: null },
'processor.js',
new Map(),
'Processor.run',
);
expect(result).toEqual([]);
});
it('DOES resolve this.flush() in a .ts file (receiver present — not a bare call)', () => {
const lookup = makeLookup({ 'Processor.flush': [flushMethod] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'flush', receiver: 'this' },
'processor.ts',
new Map(),
'Processor.run',
);
// this.flush() has a receiver → not a bare call → same-class fallback runs
expect(result).toEqual([flushMethod]);
});
it('DOES resolve bare call to same-class method in a .cs file (C# is not module-scoped)', () => {
const csMethod = { id: 20, file: 'Processor.cs', kind: 'method' };
const lookup = makeLookup({ 'Processor.Flush': [csMethod] });
const result = resolveByMethodOrGlobal(
lookup,
{ name: 'Flush', receiver: null },
'Processor.cs',
new Map(),
'Processor.Run',
);
// C# is not module-scoped → same-class fallback runs → Processor.Flush found
expect(result).toEqual([csMethod]);
});
});