Skip to content

eslint-factory: shared try/catch suggestion builder silently skips VariableDeclaration call sites #57868

Description

@github-actions

Summary

try-catch-rule-utils.ts's shared suggestion builder (findEnclosingStatement + buildTryCatchSuggestion) only offers an autofix suggestion when the flagged call sits in an ExpressionStatement or ReturnStatement. For the single most common real-world call shape — const x = fs.someSync(y); (a VariableDeclaration) — findEnclosingStatement returns null, so the rule reports the violation but ships suggest: []. Every rule built on this shared util inherits the gap: require-realpathsync-try-catch, require-mkdirsync-try-catch, require-mkdtempsync-try-catch, require-rmsync-try-catch, require-fs-sync-try-catch, require-json-parse-try-catch, require-execsync-try-catch, require-execfilesync-try-catch, require-fetch-try-catch, require-fetch-response-body-try-catch, require-new-url-try-catch, require-decodeuricomponent-try-catch, require-fs-io-try-catch (13 rules total).

Grounded live occurrences (require-realpathsync-try-catch, new rule shipped 2026-08-28, #56612)

  • actions/setup/js/create_prompt.cjs:50const root = fs.realpathSync(promptsDir);
  • actions/setup/js/create_prompt.cjs:56const resolved = fs.realpathSync(unresolved);
  • actions/setup/js/create_files.cjs:104const resolvedRoot = fs.realpathSync(root);
  • actions/setup/js/create_files.cjs:128const resolvedParent = fs.realpathSync(parentPath);

All four are outside any try block and get a real warn diagnostic today (the rule is registered "warn" in eslint.config.cjs), but the developer gets no actionable fix — just the message. Contrast with create_files.cjs:112 (assertPathWithin(resolvedRoot, fs.realpathSync(directoryPath));, an ExpressionStatement) a few lines away, which does get a suggestion. The inconsistency is confusing: two sibling call sites in the same function, one gets an autofix and the other silently doesn't.

Why this matters

hasSuggestions: true is declared in every affected rule's meta, so tooling/editors advertise "has a fix" — but for the dominant real-world pattern there is none. This isn't a correctness bug (no bad autofix is emitted), but it's a broad, high-impact weak diagnostic: the most common call shape across ~13 rules gets zero autofix coverage.

Suggested fix

Extend findEnclosingStatement/buildTryCatchSuggestion in eslint-factory/src/rules/try-catch-rule-utils.ts to also handle a single-declarator VariableDeclaration whose init is (or contains) the flagged call, via a declare-then-assign transform, e.g.:

let resolvedRoot;
try {
  resolvedRoot = fs.realpathSync(root);
} catch (err) {
  // TODO: handle filesystem failure for this fs.realpathSync call.
  throw new Error("fs.realpathSync failed: " + (err instanceof Error ? err.message : String(err)), { cause: err });
}

Guard the transform to only fire for const/let with a single Identifier declarator (bail out on destructuring patterns or multi-declarator statements, same conservative posture already used elsewhere in this util).

Acceptance criteria

  • findEnclosingStatement (or a new helper) recognizes single-declarator, identifier-target VariableDeclaration statements as suggestible.
  • buildTryCatchSuggestion (or a variant) emits the declare-then-assign form shown above, preserving indentation the way the existing builder does.
  • Add regression tests to at least require-realpathsync-try-catch.test.ts and require-fs-sync-try-catch.test.ts covering the const x = fs.xSync(y); shape, plus one negative test confirming multi-declarator/destructuring statements still correctly yield suggestions: [].
  • Spot-check 2-3 of the other 11 consumer rules to confirm the shared fix doesn't regress their existing suggestion tests.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.anthropic.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.anthropic.com"

See Network Configuration for more information.

Generated by 🤖 ESLint Refiner · claude · agent · 407.2 AIC · ⌖ 8.5 AIC · ⊞ 5.8K ·

  • expires on Sep 8, 2026, 9:40 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

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions