Skip to content

fix(eslint-factory): require-parseInt-radix false negative on radix 0/undefined#42581

Merged
pelikhan merged 4 commits into
mainfrom
copilot/eslint-factory-fix-parseint-radix
Jul 1, 2026
Merged

fix(eslint-factory): require-parseInt-radix false negative on radix 0/undefined#42581
pelikhan merged 4 commits into
mainfrom
copilot/eslint-factory-fix-parseint-radix

Conversation

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

require-parseInt-radix short-circuited on arguments.length >= 2, treating any second argument as a valid radix. This silently accepted parseInt(x, 0) and parseInt(x, undefined) — both spec-equivalent to omitting the radix, reintroducing the implicit hex-detection hazard the rule exists to prevent.

Changes

  • require-parseInt-radix.ts

    • Replaces the length >= 2 early return with isValidSecondArg(): rejects literal 0 and the identifier undefined; accepts all other values (including non-literal identifiers like base — static verification isn't possible, but they're not known-bad literals)
    • Suppresses the , 10 autofix suggestion when the first argument is a SpreadElementparseInt(...args, 10) is broken output
  • require-parseInt-radix.test.ts

    • Adds parseInt(x, base) to valid cases (non-literal identifier policy)
    • Adds invalid cases for radix 0 and undefined across all call forms (parseInt, Number.parseInt, globalThis.*, etc.)
    • Adds spread case: parseInt(...args) is flagged but offers no suggestion
parseInt(str, 0);        // ❌ now flagged — spec-equivalent to no radix
parseInt(str, undefined);// ❌ now flagged — spec-equivalent to no radix
parseInt(str, 16);       // ✅ accepted — explicit non-zero literal
parseInt(str, base);     // ✅ accepted — non-literal, not a known-bad value
parseInt(...args);       // ❌ flagged, no broken fix offered

Copilot AI and others added 2 commits June 30, 2026 22:47
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… and guards spread fix

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix eslint-factory require-parseInt-radix false negatives fix(eslint-factory): require-parseInt-radix false negative on radix 0/undefined Jun 30, 2026
Copilot AI requested a review from pelikhan June 30, 2026 22:51
@pelikhan pelikhan marked this pull request as ready for review July 1, 2026 00:20
Copilot AI review requested due to automatic review settings July 1, 2026 00:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the require-parseInt-radix ESLint rule in eslint-factory to close a false-negative gap where parseInt(x, 0) and parseInt(x, undefined) were being treated as “safe enough”, despite being spec-equivalent to omitting the radix.

Changes:

  • Tightens the rule’s “has radix” check by validating the second argument (rejecting 0 and undefined).
  • Avoids offering a broken , 10 suggestion when the first argument is a spread element.
  • Extends unit tests to cover the new invalid/valid cases and the spread-element no-suggestion behavior.
Show a summary per file
File Description
eslint-factory/src/rules/require-parseInt-radix.ts Implements second-argument validation and suppresses broken spread-argument suggestions.
eslint-factory/src/rules/require-parseInt-radix.test.ts Adds coverage for radix 0/undefined, identifier radix policy, and spread-argument suggestion suppression.
.github/workflows/daily-team-status.lock.yml Regenerated lockfile output for the Team Status workflow.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +70 to +74
// The identifier `undefined` is equivalent to no radix
if (arg.type === "Identifier" && arg.name === "undefined") {
return false;
}
return true;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5a6dde. isValidSecondArg() now only rejects the global undefined binding, so locally shadowed identifiers stay on the existing non-literal path.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: require-parseInt-radix false-negative fix

Good fix overall — the length >= 2 short-circuit was genuinely broken and the spread-element guard is correct. Three issues worth addressing before merge:

# Severity Location Issue
1 🔴 Correctness isValidSecondArg L67 parseInt(str, null) is a false negative — ToInt32(null) === 0 makes it spec-equivalent to no radix
2 🟡 Usability suggest L87 No autofix suggestion when radix is 0 or undefined; the error fires but the user gets no addRadix10 suggestion to replace the bad argument
3 🟡 Test coverage test L207 undefined radix test is missing window["parseInt"] and global.parseInt variants (5 call forms tested for radix 0, only 3 for undefined)
Why the null case matters

parseInt(str, null) is the same hazard this rule exists to prevent: a string like "0xff" will be silently parsed as 255. null is not excluded from spec hex-detection the same way a valid non-zero radix is. The fix is a one-liner alongside the existing 0 check.

Suggestion for the autofix gap

When arguments.length >= 2 but isValidSecondArg returns false, the current code emits undefined for suggest. Offering fixer.replaceText(node.arguments[1], "10") would give the same developer experience as the insert-radix path.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 43 AIC · ⌖ 7.09 AIC · ⊞ 4.9K

*/
function isValidSecondArg(arg: TSESTree.CallExpressionArgument): boolean {
// Literal 0 is spec-equivalent to no radix
if (arg.type === "Literal" && arg.value === 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null radix is also spec-equivalent to no radix — false negative.

parseInt(str, null) is not caught here: ToInt32(null) === 0, so the spec treats it identically to omitting the radix (implicit hex-detection hazard). A companion check is needed:

// Literal null coerces to 0 — same hazard as literal 0
if (arg.type === "Literal" && arg.value === null) {
  return false;
}

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5a6dde. null is now treated as spec-equivalent to an omitted radix and covered by the new invalid-radix tests.

const firstArg = node.arguments[0];
const suggest =
node.arguments.length === 1
node.arguments.length === 1 && firstArg.type !== "SpreadElement"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No autofix suggestion offered when radix is 0 or undefined.

When parseInt(str, 0) or parseInt(str, undefined) is flagged, suggest is undefined because node.arguments.length === 1 is false. The user sees the error but gets no guidance on how to fix it.

Consider adding a suggestion that replaces the bad radix argument rather than inserting one:

const suggest: TSESLint.ReportSuggestionArray<"addRadix10"> | undefined =
  node.arguments.length >= 2 && !isValidSecondArg(node.arguments[1])
    ? [{ messageId: "addRadix10", fix: fixer => fixer.replaceText(node.arguments[1] as TSESTree.Node, "10") }]
    : node.arguments.length === 1 && firstArg.type !== "SpreadElement"
    ? [{ messageId: "addRadix10", fix: fixer => fixer.insertTextAfter(firstArg, ", 10") }]
    : undefined;

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5a6dde. Known-bad radix arguments now get an addRadix10 suggestion that replaces the invalid second argument with 10.

{
code: `globalThis.parseInt(str, undefined);`,
errors: [{ messageId: "requireRadix" }],
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete call-form coverage for undefined radix.

The radix 0 test covers all 5 call forms (parseInt, Number.parseInt, globalThis.parseInt, window["parseInt"], global.parseInt). The undefined test only covers 3 of them — window["parseInt"](str, undefined) and global.parseInt(str, undefined) are missing.

Please add:

{
  code: `window["parseInt"](str, undefined);`,
  errors: [{ messageId: "requireRadix" }],
},
{
  code: `global.parseInt(str, undefined);`,
  errors: [{ messageId: "requireRadix" }],
},

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5a6dde. The undefined-radix tests now include the missing window["parseInt"] and global.parseInt call forms.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Summary

Applied /diagnosing-bugs and /tdd — 5 inline comments filed, requesting changes.

📝 Issues found (5)
# File Issue Skill
1 require-parseInt-radix.ts:67 null radix not rejected — parseInt(str, null) silently passes /diagnosing-bugs
2 require-parseInt-radix.ts:87 No fix suggestion when known-bad radix (0/undefined) is detected /diagnosing-bugs
3 require-parseInt-radix.test.ts:170 Missing suggestions: [] assertion in radix-0 tests /tdd
4 require-parseInt-radix.test.ts:205 window["parseInt"] and global.parseInt missing from undefined-radix test /tdd
5 require-parseInt-radix.test.ts:218 Spread test missing member-expression variants (Number.parseInt(...args) etc.) /tdd

Issue #1 (null radix) is a correctness gap in the same category as the bug being fixed — worth addressing before merge. Issues #2–5 strengthen the implementation and test suite but are non-blocking if you prefer a follow-up.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 62.9 AIC · ⌖ 8.87 AIC · ⊞ 6.6K ·
Comment /matt to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on one correctness gap and a few test coverage issues.

📋 Key Themes & Highlights

Key Themes

  • Correctness gap — null radix: parseInt(str, null) is spec-equivalent to no radix (null coerces to 0) but is not rejected by isValidSecondArg. Same category as the 0 case this PR fixes.
  • Missing fix suggestion for known-bad radix: When 0 or undefined is passed, the rule correctly flags the call but offers no suggestion — unlike the no-radix case which proposes addRadix10. A replaceRadix10 suggestion would be consistent and helpful.
  • Test coverage gaps: The undefined radix test block covers only 3 of 5 global variants; the spread test only covers the bare parseInt form; and none of the new invalid tests explicitly assert suggestions: [].

Positive Highlights

  • ✅ Root cause correctly identified and fixed: the length >= 2 short-circuit was the right place to intervene.
  • isValidSecondArg is cleanly extracted, well-documented, and easy to extend.
  • ✅ Spread-argument guard prevents emitting broken autofix output — good defensive thinking.
  • ✅ Non-literal identifier radix (parseInt(str, base)) is correctly accepted with a clear policy comment.
  • ✅ The new tests are well-named and readable as specs.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 62.9 AIC · ⌖ 8.87 AIC · ⊞ 6.6K
Comment /matt to run again

Comment thread eslint-factory/src/rules/require-parseInt-radix.ts Outdated
Comment thread eslint-factory/src/rules/require-parseInt-radix.ts Outdated
Comment thread eslint-factory/src/rules/require-parseInt-radix.test.ts Outdated
Comment thread eslint-factory/src/rules/require-parseInt-radix.test.ts
Comment thread eslint-factory/src/rules/require-parseInt-radix.test.ts
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 81/100 — Excellent

Analyzed 11 test(s) in require-parseInt-radix.test.ts (TypeScript/vitest): 10 design, 1 implementation, 0 guideline violation(s). Test-to-production line ratio of 2.78:1 slightly exceeds the 2:1 threshold (−10 pts), but behavioral coverage is strong.

📊 Metrics & Test Classification (11 tests analyzed)
Metric Value
New/modified tests analyzed 11
✅ Design tests (behavioral contracts) 10 (91%)
⚠️ Implementation tests (low value) 1 (9%)
Tests with error/edge cases 9 (82%)
Duplicate test clusters 0
Test inflation detected YES — test +64 lines, prod +23 lines (2.78:1)
🚨 Coding-guideline violations 0

Note: Test file is *.test.ts (TypeScript vitest), not *.test.cjs or *.test.js. Analyzed under the JS/vitest rules; no scoring differences apply.

Test File Classification Issues Detected
uses the correct docs URL :20 ⚠️ Implementation Metadata check — no behavioral assertion
valid: explicit radix is accepted :24 ✅ Design
valid: non-parseInt calls not flagged :45 ✅ Design Edge: near-miss names
valid: aliased/destructured out of scope :52 ✅ Design Edge: scope design invariant
invalid: global parseInt without radix :65 ✅ Design Error coverage ✓
invalid: Number.parseInt without radix :81 ✅ Design Error coverage ✓, direct + computed
invalid: global-object parseInt access :97 ✅ Design Error coverage ✓, 6 cases
suggestion: inserts ', 10' :129 ✅ Design Fix-output verification
invalid: radix 0 rejected 🆕 :164 ✅ Design Bug-fix regression test ✓
invalid: radix undefined rejected 🆕 :192 ✅ Design Bug-fix regression test ✓
no broken fix for spread-element 🆕 :212 ✅ Design Edge: spread arg guard

Go: 0 (*_test.go); JavaScript/TypeScript: 11 (*.test.ts).

⚠️ Flagged Tests (1 issue)

uses the correct docs URL (require-parseInt-radix.test.ts:20) — implementation_test, low_value. Asserts a hardcoded URL string on meta.docs.url. If the URL changes for a legitimate reason this test breaks without any behavioral regression. Consider removing or converting to a check that verifies only URL structure, or simply accepting this as a stable low-cost contract.

Test inflation (2.78:1): The 64 lines of new tests vs 23 lines of new production code crosses the 2:1 guideline, but the extra coverage is justified — the PR adds two new edge-case branches (radix === 0 and radix === undefined) that each warrant dedicated multi-variant test groups. No action required.

Verdict

Check passed. 9% implementation tests (threshold: 30%). Three new tests directly cover the false-negative bug fix (radix 0, radix undefined, spread-element guard). No coding-guideline violations.

References:

Warning

Firewall blocked 1 domain

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

  • awmgmcpg

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

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 69.8 AIC · ⌖ 21.4 AIC · ⊞ 7K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 81/100. 9% implementation tests (threshold: 30%). Three new regression tests directly target the false-negative bug fix (radix 0, radix undefined, spread-element guard). No coding-guideline violations.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One additional false negative missed by the current isValidSecondArg implementation.

### Finding: `NaN` radix — same hazard class as `null` and `0`

parseInt(str, NaN) passes isValidSecondArg unchanged because the function only rejects Identifier nodes named "undefined". NaN is a different identifier that coerces to 0 via ToInt32, triggering the same implicit base detection. Fix: add arg.name === "NaN" to the identifier check on line 74.

This is the same pattern as the null false negative already flagged in the existing review — both are known-bad spec-equivalent values not caught by the current identifier blocklist.

Warning

Firewall blocked 1 domain

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

  • patchdiff.githubusercontent.com

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

network:
  allowed:
    - defaults
    - "patchdiff.githubusercontent.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer · 155.6 AIC · ⌖ 7.08 AIC · ⊞ 1.6K
Comment /review to run again

Comment thread eslint-factory/src/rules/require-parseInt-radix.ts
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §28486872548

Field Value
Category bug
Risk low
Score 44 / 100
Priority medium
Action 📦 batch_review

Score breakdown: Impact 20/50 · Urgency 10/30 · Quality 14/20

Batch: eslint-fixes (PRs #42568, #42580, #42581)

Rationale: Bug fix for false-negative radix detection in require-parseInt-radix (+95/-11, includes tests). Mixed reviews. Group with other eslint-factory PRs for single review pass.

Labels applied: pr-type:bug pr-risk:low pr-priority:medium pr-action:batch_review pr-batch:eslint-fixes pr-agent:copilot-swe-agent

Generated by 🔧 PR Triage Agent · 77.2 AIC · ⌖ 9.82 AIC · ⊞ 1.6K ·

@pelikhan

pelikhan commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Ran the PR-finisher pass in f5a6dde. I addressed the actionable review feedback, revalidated locally, and the remaining hand-off is a maintainer CI re-trigger on the new head.

@pelikhan pelikhan merged commit 43d1556 into main Jul 1, 2026
@pelikhan pelikhan deleted the copilot/eslint-factory-fix-parseint-radix branch July 1, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eslint-factory: require-parseInt-radix accepts radix 0 / undefined (false negative)

3 participants