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

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions