Skip to content

Commit 92ecd22

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/spread-inline-literal-1379
2 parents 1925d82 + 784951d commit 92ecd22

128 files changed

Lines changed: 4858 additions & 102 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 65 additions & 65 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/import-jelly-micro.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function buildNameMap(src, filename) {
203203

204204
// ── Jelly → expected-edges conversion ────────────────────────────────────────
205205

206-
const SCHEMA = '../../expected-edges.schema.json';
206+
const SCHEMA = '../../../expected-edges.schema.json';
207207

208208
/**
209209
* Convert a Jelly .json call graph + .js source to codegraph expected-edges format.

src/domain/graph/builder/call-resolver.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ export function resolveByMethodOrGlobal(
7272
const effectiveReceiver = call.receiver.startsWith('this.')
7373
? call.receiver.slice('this.'.length)
7474
: call.receiver;
75-
const typeEntry = typeMap.get(effectiveReceiver) ?? typeMap.get(call.receiver);
75+
const typeEntry =
76+
typeMap.get(effectiveReceiver) ??
77+
typeMap.get(call.receiver) ??
78+
(callerName ? typeMap.get(`${callerName}::${effectiveReceiver}`) : undefined);
7679
let typeName = typeEntry
7780
? typeof typeEntry === 'string'
7881
? typeEntry

src/domain/graph/builder/stages/build-edges.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -756,16 +756,30 @@ function buildObjectRestParamPostPass(
756756
symbols.typeMap instanceof Map ? symbols.typeMap : [],
757757
);
758758

759-
// Seed typeMap[restName] = { type: argName } for each matching pair.
760-
// Mirrors the seeding in buildCallEdgesJS Phase 8.3f.
759+
// Seed typeMap[callee::restName] = { type: argName } for each matching pair.
760+
// Mirrors the seeding in buildCallEdgesJS Phase 8.3f. Keys are scoped by
761+
// callee so two functions with the same rest-param name (e.g. `...rest`) in
762+
// the same file don't collide (#1358).
763+
// When only one callee uses a given rest name, also seed the unscoped key
764+
// as a null-callerName fallback so edges aren't silently dropped if
765+
// findCaller can't identify the enclosing function (#1358).
766+
const restNameCallees = new Map<string, Set<string>>();
767+
for (const orpb of symbols.objectRestParamBindings!) {
768+
if (!restNameCallees.has(orpb.restName)) restNameCallees.set(orpb.restName, new Set());
769+
restNameCallees.get(orpb.restName)!.add(orpb.callee);
770+
}
761771
const restNames = new Set<string>();
762772
for (const orpb of symbols.objectRestParamBindings!) {
763773
for (const pb of symbols.paramBindings!) {
764774
if (pb.callee === orpb.callee && pb.argIndex === orpb.argIndex) {
765-
if (!typeMap.has(orpb.restName)) {
766-
typeMap.set(orpb.restName, { type: pb.argName, confidence: 0.65 });
767-
restNames.add(orpb.restName);
775+
const scopedKey = `${orpb.callee}::${orpb.restName}`;
776+
if (!typeMap.has(scopedKey)) {
777+
typeMap.set(scopedKey, { type: pb.argName, confidence: 0.65 });
778+
if (restNameCallees.get(orpb.restName)!.size === 1 && !typeMap.has(orpb.restName)) {
779+
typeMap.set(orpb.restName, { type: pb.argName, confidence: 0.65 });
780+
}
768781
}
782+
restNames.add(orpb.restName);
769783
}
770784
}
771785
}
@@ -777,14 +791,16 @@ function buildObjectRestParamPostPass(
777791

778792
const caller = findCaller(lookup, call, symbols.definitions, relPath, fileNodeRow);
779793

780-
// Resolve with the enriched typeMap (typeMap[restName] = { type: argName } is seeded above).
794+
// Resolve with the enriched typeMap. callerName is passed so
795+
// resolveByMethodOrGlobal can look up the scoped key callee::restName (#1358).
781796
// seenByPair deduplicates edges the native engine already emitted.
782797
const { targets, importedFrom } = resolveCallTargets(
783798
lookup,
784799
call,
785800
relPath,
786801
importedNames,
787802
typeMap as Map<string, unknown>,
803+
caller.callerName,
788804
);
789805
for (const t of targets) {
790806
const edgeKey = `${caller.id}|${t.id}`;
@@ -1019,16 +1035,26 @@ function buildCallEdgesJS(
10191035
symbols.typeMap instanceof Map ? symbols.typeMap : [],
10201036
);
10211037

1022-
// Phase 8.3f: seed typeMap[restName] = { type: argName } for each object-destructuring
1023-
// rest parameter binding cross-referenced with call-site argument bindings.
1024-
// e.g. function f({ a, ...rest }) called as f(obj) → typeMap['rest'] = { type: 'obj' }
1025-
// so that `rest.method()` resolves via typeMap['obj.method'].
1038+
// Phase 8.3f: seed typeMap[callee::restName] = { type: argName } for each
1039+
// object-destructuring rest parameter binding × call-site argument binding.
1040+
// Keys are scoped so two functions with the same rest-param name in the same
1041+
// file don't collide (#1358). When only one callee uses a given rest name,
1042+
// also seed the unscoped key as a null-callerName fallback.
10261043
if (symbols.objectRestParamBindings?.length && symbols.paramBindings?.length) {
1044+
const restNameCallees = new Map<string, Set<string>>();
1045+
for (const orpb of symbols.objectRestParamBindings) {
1046+
if (!restNameCallees.has(orpb.restName)) restNameCallees.set(orpb.restName, new Set());
1047+
restNameCallees.get(orpb.restName)!.add(orpb.callee);
1048+
}
10271049
for (const orpb of symbols.objectRestParamBindings) {
10281050
for (const pb of symbols.paramBindings) {
10291051
if (pb.callee === orpb.callee && pb.argIndex === orpb.argIndex) {
1030-
if (!typeMap.has(orpb.restName)) {
1031-
typeMap.set(orpb.restName, { type: pb.argName, confidence: 0.65 });
1052+
const scopedKey = `${orpb.callee}::${orpb.restName}`;
1053+
if (!typeMap.has(scopedKey)) {
1054+
typeMap.set(scopedKey, { type: pb.argName, confidence: 0.65 });
1055+
if (restNameCallees.get(orpb.restName)!.size === 1 && !typeMap.has(orpb.restName)) {
1056+
typeMap.set(orpb.restName, { type: pb.argName, confidence: 0.65 });
1057+
}
10321058
}
10331059
}
10341060
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function f1() {}
2+
3+
const obj = {
4+
baz: undefined,
5+
get foo() {
6+
return this.baz;
7+
},
8+
set bar(x) {
9+
this.baz = x;
10+
},
11+
};
12+
13+
obj.bar = f1;
14+
15+
const t1 = obj.foo;
16+
t1();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "../../../expected-edges.schema.json",
3+
"language": "javascript",
4+
"description": "Jelly micro-test: accessors",
5+
"source": "https://github.com/cs-au-dk/jelly/blob/master/tests/micro/accessors.js",
6+
"edges": []
7+
}

0 commit comments

Comments
 (0)