Skip to content

eslint-factory: require-return-after-core-setfailed misses braceless if/else/loop consequents (control-flow FN) #45386

Description

@github-actions

Summary

require-return-after-core-setfailed only inspects statement listsBlockStatement.body (rule L332), SwitchCase.consequent (L349), and the filtered Program.body (L352). A core.setFailed(...) that is the direct single-statement consequent/alternate of an unbraced if/else, or the unbraced body of a for/while/for-of, is never an element of any of those lists. So it is neither checked for an immediately-following statement (checkStatementList) nor for cross-block continuation (findContinuationOutsideBlock, which only runs from the BlockStatement visitor at L338-345). Such a setFailed escapes the rule entirely.

File: eslint-factory/src/rules/require-return-after-core-setfailed.ts

False negatives (not flagged today)

if (bad) core.setFailed("x"); doMore();            // doMore() runs in a failed state
if (bad) core.setFailed("a"); else core.setFailed("b"); doMore();
for (const f of files) if (!ok(f)) core.setFailed("bad " + f);  // continues the loop, then falls through

Inconsistency that makes this a clear gap

The braced equivalent is correctly flagged via findContinuationOutsideBlock:

if (bad) { core.setFailed("x"); } doMore();  // correctly reported today

So identical control flow is diagnosed or not purely based on the presence of braces around the consequent.

Grounding

No live braceless-if core.setFailed exists in actions/setup/js/** today — every current site returns immediately after setFailed (e.g. check_permissions.cjs:38-39, check_skip_if_helpers.cjs:54-55, create_labels.cjs:65-66). This is therefore a latent soundness gap / regression guard for a control-flow-safety rule, filed before such code lands. Alias/destructured/computed callee detection is already handled (shipped via #45334); this is a distinct, positional gap.

Acceptance criteria

  1. core.setFailed() — in every already-recognized callee form (core., single-assignment alias, destructured { setFailed }, computed core["setFailed"]) — as the single-statement consequent or alternate of a braceless if/else is analyzed for a following continuation and reported when one exists.
  2. Same for braceless for/for-in/for-of/while/do-while bodies, consistent with the existing loop back-edge handling at L113-122.
  3. if (bad) core.setFailed("x"); as the terminal statement of a function/program (no continuation) stays valid (no report).
  4. The addReturn suggestion must emit a syntactically valid, correctly-associated fix (e.g. wrap as if (bad) { core.setFailed("x"); return; }), never if (bad) core.setFailed("x"); return; which would detach the return from the if. Keep the suggestion gated by the existing isInsideFunctionLike check.
  5. Unit tests: braceless-if + continuation (invalid), braceless-if/else + continuation (invalid), braceless loop body + continuation (invalid), terminal braceless-if (valid); confirm all existing braced cases remain unchanged.

Generated by 🤖 ESLint Refiner · 360.4 AIC · ⌖ 12.5 AIC · ⊞ 4.6K ·

  • expires on Jul 20, 2026, 10:11 PM UTC-08:00

Activity

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions