Skip to content

Handle statically false loops and loop binding patterns in the outbox listener reachability walk #1071

Description

@dahlia

Why

outbox-listener-delivery-required and outbox-listener-delivery-not-awaited both decide which code in an outbox listener can run through collectReachableStatements() in packages/lint/src/lib/reachability.ts. The function already skips an if branch whose test is statically false, but two loop forms are handled inaccurately. CodeRabbit pointed both out while reviewing #1067, where the function was moved into the shared module unchanged; the behavior dates back to #1050.

First, a loop whose test is statically false still has its body and update treated as reachable:

while (false) { ctx.sendActivity(sender, inbox, activity); }
for (;false;) { ctx.sendActivity(sender, inbox, activity); }
for (let i = 0; false; ctx.sendActivity(sender, inbox, activity)) {}

None of these ever delivers, yet outbox-listener-delivery-required accepts all three listeners, and outbox-listener-delivery-not-awaited reports the unawaited call in the first two although it never runs. A do…while (false) body runs once, and both rules already handle it correctly.

Second, for…of and for…in visit only right and body, so an expression evaluated inside the loop variable's binding pattern is never seen:

for (const { id = ctx.sendActivity(sender, inbox, activity) } of [{}]) {}

This listener does deliver, but outbox-listener-delivery-required reports it as not delivering, and outbox-listener-delivery-not-awaited misses the dropped promise.

I checked each case against the current rules. They are rare in real code, which is why they were left out of #1067, but the fix is contained in one function.

Scope

In collectReachableStatements():

  • For while and for, skip the body when the test is statically false, and for for also skip the update, while keeping the initializer and the test. Keep the do…while body, which always runs once.
  • For for…of and for…in, include the expressions evaluated in left, such as default values and computed keys in a binding pattern, without treating the bound names as reads.

Use the existing isStaticallyFalsy() helper that the if handling already uses.

Suggested checks

Add tests for both rules covering each example above, plus a do…while (false) case that stays as it is. The shared behavior is easiest to cover through the tests of the existing rule, the same way #1067 covered the rest of lib/reachability.ts. This changes what outbox-listener-delivery-required reports, so it needs a changelog fragment for @fedify/lint.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions