Context
Phase 8.3 (#1289) seeds the pts map from variable declarators (const fn = handler) and member-expression declarators (const fn = obj.method). It does not handle object property writes:
const handlers = {};
handlers.auth = authMiddleware; // property write — not currently tracked
handlers.log = logRequest;
router.use(handlers.auth); // call to handlers.auth — pts map has no entry for it
The handlers.auth abstract location is never seeded, so resolveViaPointsTo('auth', ...) finds nothing and the edge to authMiddleware is dropped.
What needs to happen
Extractor changes
Walk assignment expressions (assignment_expression) in extractTypeMapWalk. When the LHS is a member_expression and the RHS is an identifier:
handlers.auth = authMiddleware
→ FnRefBinding { lhs: 'handlers.auth', rhs: 'authMiddleware' }
The field-based key handlers.auth is already the format the pts solver uses for member-expression constraints, so no solver changes are needed.
Solver seeding
The pts map already handles composite keys like handlers.auth — if a binding { lhs: 'handlers.auth', rhs: 'authMiddleware' } is recorded, propagation produces pts('handlers.auth') = { 'authMiddleware' }.
When a call like router.use(handlers.auth) is seen, extractCallbackReferenceCalls emits { name: 'auth', receiver: 'handlers', dynamic: true }. The pts fallback currently only handles !call.receiver — this would need to be extended to also look up handlers.auth composite keys in the pts map when call.receiver is set.
Scope
- Only simple
obj.prop = identifier writes (not chained: a.b.c = x)
- Same file only for the first iteration
- Gate with
BUILTIN_GLOBALS check on the object name (skip console.x = y etc.)
Acceptance criteria
const handlers = {};
handlers.auth = authMiddleware;
router.use(handlers.auth); // edge: (caller) → authMiddleware
Context
Phase 8.3 (#1289) seeds the pts map from variable declarators (
const fn = handler) and member-expression declarators (const fn = obj.method). It does not handle object property writes:The
handlers.authabstract location is never seeded, soresolveViaPointsTo('auth', ...)finds nothing and the edge toauthMiddlewareis dropped.What needs to happen
Extractor changes
Walk assignment expressions (
assignment_expression) inextractTypeMapWalk. When the LHS is amember_expressionand the RHS is an identifier:The field-based key
handlers.authis already the format the pts solver uses for member-expression constraints, so no solver changes are needed.Solver seeding
The pts map already handles composite keys like
handlers.auth— if a binding{ lhs: 'handlers.auth', rhs: 'authMiddleware' }is recorded, propagation producespts('handlers.auth') = { 'authMiddleware' }.When a call like
router.use(handlers.auth)is seen,extractCallbackReferenceCallsemits{ name: 'auth', receiver: 'handlers', dynamic: true }. The pts fallback currently only handles!call.receiver— this would need to be extended to also look uphandlers.authcomposite keys in the pts map whencall.receiveris set.Scope
obj.prop = identifierwrites (not chained:a.b.c = x)BUILTIN_GLOBALScheck on the object name (skipconsole.x = yetc.)Acceptance criteria