Skip to content

fix(effect): nested function declarations are outside the catch handler - #49

Open
Yi-111-a wants to merge 1 commit into
dmmulroy:mainfrom
Yi-111-a:fix/nested-function-declaration-effect-tag-scope
Open

Yi-111-a wants to merge 1 commit into
dmmulroy:mainfrom
Yi-111-a:fix/nested-function-declaration-effect-tag-scope

Conversation

@Yi-111-a

Copy link
Copy Markdown

Closes #48.

Problem

isInsideBroadEffectHandler in src/effect/shared/tagged-values.ts walks up the parent chain and stops at the first ArrowFunctionExpression or FunctionExpression, then reports whether that function is the handler argument of Effect.catch / catchAll / catchIf.

A FunctionDeclaration was not a stopping point, so a helper declared inside a handler was treated as part of it:

Effect.catchAll((error) => {
  function label(value) {
    return value._tag === "Ready";
  }

  return recover(error, label(state));
});

That fires no-manual-effect-error-tag, which recommends Effect.catchTag / Effect.catchTags.

Cause

The same helper written as an arrow gives the opposite result:

Effect.catchAll((error) => {
  const label = (value) => value._tag === "Ready";

  return recover(error, label(state));
});

This fires no-manual-tag-comparison, which recommends Match / Predicate.isTagged.

So the spelling of an unrelated helper decided which of the two rules reported it, and the declaration spelling recommended catchTag — wrong advice, since label inspects state, not the caught error. catchTag would not help; the value being branched on is not the one catchAll hands the handler.

Change

Stop the walk at a FunctionDeclaration and report "not in a handler". A declaration is never the handler argument itself, so once the search reaches one the enclosing handler cannot own the comparison:

if (current.type === "FunctionDeclaration") {
  return false;
}

Both Effect tag rules now treat a nested helper the same way however it is spelled, and a plain tag check is reported by the rule about tag comparisons. Effect.catchAll(function (error) { … }) passed directly is a FunctionExpression, so that path is unchanged — the existing test for it still passes.

Coverage

  • no-manual-tag-comparison: a declaration helper and its arrow counterpart as invalid cases, so the two spellings now agree; a switch inside a declaration, to exercise the SwitchStatement visitor too.
  • no-manual-effect-error-tag: a declaration helper added to the valid set.

Verification

pnpm check (lint, all 24 RuleTester suites, tsc --noEmit, skill-asset sync check) on Node 22.23.0, oxlint 1.78.0, against c44ef22.

…tions

isInsideBroadEffectHandler walks parents until it reaches an arrow or a
function expression, then reports whether that function is the handler
argument of Effect.catch/catchAll/catchIf. A function declaration was not
a stopping point, so a helper declared inside a handler was treated as
part of it.

The same helper written as an arrow is outside the handler, so the two
spellings disagreed: a declaration was excused by no-manual-tag-comparison
and reported by no-manual-effect-error-tag, while an arrow or function
expression got the reverse. The advice also pointed the wrong way, since
catchTag only helps when the discriminated value is the caught error.

Stop the walk at a FunctionDeclaration and report "not in a handler". Both
Effect tag rules now treat a nested helper the same way however it is
spelled, and the plain tag check is reported by the rule about tag
comparisons.

Coverage: a declaration helper and its arrow counterpart in
no-manual-tag-comparison, a declaration helper in the valid set of
no-manual-effect-error-tag, plus a switch inside a declaration so both
visitors are exercised.

Verified with `pnpm check` on Node 22 (lint, 24 RuleTester suites,
tsc --noEmit, skill-asset sync check).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Effect tag rules give the wrong advice inside nested function declarations

1 participant