diff --git a/src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts b/src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts index 479639fe1c..c0a0cbda07 100644 --- a/src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts +++ b/src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts @@ -233,6 +233,24 @@ describe("getter receiver mutation aliases (#1665, #2654)", () => { ).toMatchObject({ status: "literal", value: 1 }); }); + it.each([ + ["static block", "class { static { self.x = 2; } }"], + ["static field value", "class { static s = (self.x = 2); }"], + [ + "static block of a nested heritage class", + "class extends (class { static { self.x = 2; } }) {}", + ], + ])( + "invalidates the receiver after a %s writes through a captured alias", + (_label, result) => { + expect( + resultValue( + `const box = { x: 1, get trigger() { const self = this; return ${result}; } }; box.trigger; return box.x;`, + ), + ).toMatchObject({ status: "unknown" }); + }, + ); + it("terminates recursive receiver getters", () => { expect( resultValue( diff --git a/src/domain/javascript/javascriptSemanticMemberMutations.ts b/src/domain/javascript/javascriptSemanticMemberMutations.ts index cc6a2d65a2..a8172a74ef 100644 --- a/src/domain/javascript/javascriptSemanticMemberMutations.ts +++ b/src/domain/javascript/javascriptSemanticMemberMutations.ts @@ -405,12 +405,27 @@ const computedDefinitionMembers = (node: t.Function | t.Class): t.Node[] => { ); }; -/** Expressions a definition runs immediately; deferred bodies and class-receiver initializers stay out. */ +/** + * Expressions a definition runs immediately: heritage, computed keys, static + * blocks and static field values. Deferred bodies and instance initializers + * stay out; `this` inside static work is the class, not the enclosing receiver. + */ const definitionTimeExpressions = (node: t.Function | t.Class): t.Node[] => [ ...(t.isClass(node) && node.superClass != null ? [node.superClass] : []), ...computedDefinitionMembers(node).flatMap((member) => "key" in member ? [member.key] : [], ), + ...(t.isClass(node) + ? node.body.body.flatMap((member): t.Node[] => + t.isStaticBlock(member) + ? [member] + : (t.isClassProperty(member) || t.isClassPrivateProperty(member)) && + member.static && + member.value != null + ? [member.value] + : [], + ) + : []), ]; /** Collect invocation operands once; receiver interpretation belongs to the getter owner. */