fix(javascript): bind named class expressions in their class scope (#510)
* fix(javascript): bind named class expressions in their class scope Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com> * test(javascript): isolate class expression lexical names regression cases Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com> --------- Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>
This commit is contained in:
parent
488fd5f60b
commit
55f0267c40
|
|
@ -166,6 +166,50 @@ describe("JavaScript semantic analysis: structure 1", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("JavaScript semantic analysis: class expression lexical names", () => {
|
||||
it("resolves named class expressions inside the class without changing the outer binding", () => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
const Service = "outside";
|
||||
const Wrapper = class Service {
|
||||
create() { return new Service(); }
|
||||
};
|
||||
consume(Service);
|
||||
`);
|
||||
expect(bindingsNamed(ir, "Service")).toHaveLength(2);
|
||||
expect(topLevelBinding(ir, "Service").value).toEqual({
|
||||
status: "literal",
|
||||
value: "outside",
|
||||
});
|
||||
const references = ir.references.filter(
|
||||
({ name, role }) => name === "Service" && role === "read",
|
||||
);
|
||||
expect(references).toHaveLength(2);
|
||||
expect(references[0]?.bindingId).not.toBe(references[1]?.bindingId);
|
||||
expect(references[0]?.resolution).toBe("resolved");
|
||||
const classScope = ir.scopes.find(({ kind }) => kind === "class");
|
||||
expect(
|
||||
bindingsNamed(ir, "Service").some(
|
||||
({ scopeId }) => scopeId === classScope?.scopeId,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not expose a named class expression outside its class", () => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
const Wrapper = class Internal { create() { return new Internal(); } };
|
||||
consume(Internal);
|
||||
`);
|
||||
const references = ir.references.filter(
|
||||
({ name, role }) => name === "Internal" && role === "read",
|
||||
);
|
||||
expect(references.map(({ resolution }) => resolution)).toEqual([
|
||||
"resolved",
|
||||
"unbound",
|
||||
]);
|
||||
expect(topLevelBinding(ir, "Wrapper").provenance.status).toBe("local");
|
||||
});
|
||||
});
|
||||
|
||||
describe("JavaScript semantic analysis: structure 2", () => {
|
||||
it("retains ESM, re-export, require, and CommonJS export relationships", () => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
|
|
|
|||
|
|
@ -213,6 +213,16 @@ const bindInnerDeclaration = (
|
|||
? parent.kind !== "const"
|
||||
: true,
|
||||
});
|
||||
else if (t.isClassExpression(node) && t.isIdentifier(node.id))
|
||||
addBinding({
|
||||
state,
|
||||
scope,
|
||||
name: node.id.name,
|
||||
kind: "class",
|
||||
mutable: false,
|
||||
definitionNode: node.id,
|
||||
initializer: node,
|
||||
});
|
||||
else if (t.isFunction(node)) bindFunctionLocals(node, scope, state);
|
||||
else if (t.isCatchClause(node) && node.param != null)
|
||||
bindPattern({
|
||||
|
|
|
|||
Loading…
Reference in New Issue