Repository navigation
Description
Rule
require-return-after-core-setfailed (eslint-factory/src/rules/require-return-after-core-setfailed.ts)
This rule exists to catch the "execution continues after failure is declared" pattern. Two precision gaps were found by static review. Neither has live sites in actions/setup/js today, so both are regression-guard hardening (cheap, self-contained).
1. False negative — bare switch case fall-through (HIGH confidence, HIGH impact)
When core.setFailed(...) is the last statement of an un-braced case consequent and control falls through to the next case, the following code runs in a failed state but is not flagged:
switch (x) {
case 1:
core.setFailed("bad"); // falls through — NOT flagged
case 2:
doMore(); // runs after failure declared
}Root cause: the SwitchCase visitor (rule lines ~338-340) only calls checkStatementList, which reports solely on an in-list next sibling (next = stmts[i+1]). When setFailed is the last consequent statement, next is undefined, so nothing fires. The dedicated switch-fall-through logic already present in findContinuationOutsideBlock (rule lines ~129-140) is only reachable via checkNestedContinuation, which is never invoked for a setFailed sitting directly in a SwitchCase consequent — so that code path is effectively dead for the bare case.
Proof it's a gap, not a design choice — the sibling shapes ARE flagged:
case "a": core.setFailed("bad"); doMore(); break;→ flagged (test line 121, in-list sibling)case 1: if (bad) { core.setFailed("x"); } case 2: doMore();→ flagged (test line 155, BlockStatement visitor fires)
Only the bare, last-in-consequent shape slips through — an inconsistency.
Fix: in the SwitchCase visitor, also run checkNestedContinuation on the last consequent statement when it is a setFailed (reuses the existing findContinuationOutsideBlock machinery, which already handles switch fall-through).
2. False positive — trailing hoisted function declaration (HIGH confidence, LOW impact)
core.setFailed("bad");
function helper() {} // hoisted, never "runs" — but currently flaggedRoot cause: isExecutableStatement (rule lines ~57-65) excludes ImportDeclaration/ExportAllDeclaration/ExportDefaultDeclaration/ExportNamedDeclaration/TSModuleDeclaration but not FunctionDeclaration. So a hoisted function declaration becomes next, isControlTransfer returns false, and a spurious report fires. A hoisted declaration has no runtime continuation effect.
Fix: treat FunctionDeclaration (and likely TSInterfaceDeclaration/TSTypeAliasDeclaration) as non-continuation in isExecutableStatement.
Acceptance criteria
-
switch (x) { case 1: core.setFailed("bad"); case 2: doMore(); }is flagged withmissingReturnAfterSetFailed(new invalid test). - Braced/in-sibling/if-wrapped switch cases (existing tests, lines 121 & 155) remain unchanged.
-
core.setFailed("bad"); function helper() {}at module top level is not flagged (new valid test). - A terminating case —
case 1: core.setFailed("bad"); break; case 2: doMore();— remains valid (regression test). -
npm testpasses.
Grounding
No live sites in actions/setup/js (verified: no switch+setFailed fall-through, no top-level function decl after setFailed). Both are regression-guard hardening. Found via static soundness review; consistency with the rule's already-handled if/braced variants makes #1 high-confidence.
Generated by 🤖 ESLint Refiner · 455.7 AIC · ⌖ 10.8 AIC · ⊞ 4.6K · ◷
- expires on Jul 27, 2026, 10:19 PM UTC-08:00
Activity
github-actions commentedon Jul 21, 2026
🍪 Issue Monster selected this for Copilot
I have identified this issue as a good candidate for automated resolution and requested assignment to the Copilot coding agent.
If assignment succeeds, the Copilot coding agent will analyze the issue and create a pull request with the fix.
Om nom nom! 🍪
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
pi.devSee Network Configuration for more information.