Skip to content

fix(linter/jsx-a11y): no-redundant-roles attribute-aware implicit roles for <input>, skip ancestor-conditional elements#23015

Merged
camc314 merged 4 commits into
oxc-project:mainfrom
gaurav0107:fix/22743-jsx-a11y-no-redundant-roles-input-header
Jun 11, 2026
Merged

fix(linter/jsx-a11y): no-redundant-roles attribute-aware implicit roles for <input>, skip ancestor-conditional elements#23015
camc314 merged 4 commits into
oxc-project:mainfrom
gaurav0107:fix/22743-jsx-a11y-no-redundant-roles-input-header

Conversation

@gaurav0107

@gaurav0107 gaurav0107 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #22743.

jsx-a11y/no-redundant-roles was checking an element's explicit role against every possible implicit role in the shared tag-to-role map. That produced false positives for roles that depend on attributes or ancestor context.

This PR narrows the rule at the call site:

  • Skip ancestor-dependent elements (header, footer, main, address) because a single JSXOpeningElement cannot determine whether their landmark role applies.
  • Resolve <input> implicit roles from type and list before reporting a redundant role.
  • Keep the shared get_element_implicit_roles helper unchanged, since other callers still need the full set of possible roles for a tag.

Details

The input handling now covers the HTML-AAM cases this rule can determine locally:

  • list on text-like input types maps to combobox.
  • text-like inputs without list map to textbox, except type="search" maps to searchbox.
  • checkbox, radio, range, and number map to their specific widget roles.
  • button, image, reset, and submit map to button.
  • static JSX string expression values such as type={"checkbox"} are handled the same as string literal attributes.

allowedRedundantRoles, img, and select behavior from the existing rule are preserved.

AI disclosure

This PR was prepared with AI assistance. The change set, tests, and HTML-AAM mapping were reviewed against the issue details and upstream eslint-plugin-jsx-a11y behavior before submission.

@gaurav0107 gaurav0107 marked this pull request as ready for review June 6, 2026 13:39
@gaurav0107 gaurav0107 requested a review from camc314 as a code owner June 6, 2026 13:39
@codspeed-hq

codspeed-hq Bot commented Jun 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 5 untouched benchmarks
⏩ 66 skipped benchmarks1


Comparing gaurav0107:fix/22743-jsx-a11y-no-redundant-roles-input-header (2153156) with main (0a0bc2f)

Open in CodSpeed

Footnotes

  1. 66 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@camc314 camc314 added the A-linter Area - Linter label Jun 10, 2026
@camc314 camc314 changed the title fix(linter): jsx-a11y/no-redundant-roles attribute-aware implicit roles for <input>, skip ancestor-conditional elements fix(linter/jsx-a11y): no-redundant-roles attribute-aware implicit roles for <input>, skip ancestor-conditional elements Jun 11, 2026
@camc314 camc314 self-assigned this Jun 11, 2026
gaurav0107 and others added 4 commits June 11, 2026 15:13
…es for `<input>`, skip ancestor-conditional elements

Closes oxc-project#22743.

The `no-redundant-roles` rule was reading the full set of *possible*
implicit roles from a flat `(tag, role)` map and flagging any explicit
`role=` that appeared anywhere in that set. For elements whose implicit
role depends on attributes or ancestor context this produced two
confirmed false positives:

1. `<input role="combobox" />` flagged as redundant. Per HTML-AAM
   §3.5.76 a plain `<input>` has implicit role `textbox`. `combobox` is
   implicit only when a `list` attribute is set (§3.5.77).
2. `<header role="banner" />` flagged as redundant. Per ARIA in HTML the
   implicit `banner` role applies only when `<header>` is not nested in
   `article`, `aside`, `main`, `nav`, or `section`. Ancestor context is
   not available from a single `JSXOpeningElement`.

This change narrows the rule:

* Skip ancestor-conditional elements (`header`, `footer`, `main`,
  `address`) entirely. This matches `eslint-plugin-jsx-a11y`'s reference
  implementation.
* Filter `<input>` matches by `type` and `list` attributes. Only flag
  the explicit role when it matches the attribute-narrowed implicit
  role.

Tests cover the reported reproductions plus the
attribute-narrowed positive cases (`<input type="checkbox"
role="checkbox" />` etc.) so legitimate redundant roles on `<input>`
are still caught.
… clippy gate

CI flagged `str::to_ascii_lowercase` in `is_input_implicit_role` via
`-D clippy::disallowed-methods`. Switch to
`cow_utils::CowUtils::cow_to_ascii_lowercase`, the project-wide
allocation-free convention already used in sibling jsx-a11y rules.
No behavior change.
@camc314 camc314 force-pushed the fix/22743-jsx-a11y-no-redundant-roles-input-header branch from 10a0063 to 2153156 Compare June 11, 2026 14:25

@camc314 camc314 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.

thank you!

@camc314 camc314 merged commit 0e4e59f into oxc-project:main Jun 11, 2026
31 checks passed
camc314 pushed a commit that referenced this pull request Jun 15, 2026
# Oxlint
### 🚀 Features

- 5e1627d linter/unicorn: Flag all Error() calls without new in
`throw-new-error` (#23363) (Cason Kervis)
- 2e8bda4 linter/vue: Implement no-dupe-keys rule (#23350) (bab)
- 1490a0a linter/react: Implement react-compiler rule (#23202) (Boshen)
- dd560ae linter/unicorn: Implement `no-array-fill-with-reference-type`
rule (#23397) (Mikhail Baev)
- af36c2f linter: Add schema for `react/jsx-curly-brace-presence`
(#23400) (WaterWhisperer)
- 47d34a3 linter: Add schema for `react/jsx-handler-names` (#23393)
(WaterWhisperer)
- f4250d0 linter: Add schema for `unicorn/import-style` (#23386)
(WaterWhisperer)
- 30c74ce linter: Add schema for
`jsx_a11y/no-noninteractive-element-to-interactive-role` (#23384)
(Sysix)
- cfbe8dc linter: Add schema for
`jsx_a11y/no-interactive-element-to-noninteractive-role` (#23382)
(WaterWhisperer)
- d15b7ff linter: Add schema for `typescript/no-restricted-types`
(#23381) (WaterWhisperer)
- 028a811 linter: Add schema for `jsx-a11y/media-has-caption` (#23377)
(Sysix)
- b3b1038 linter: Add schema for `jsx-a11y/label-has-associated-control`
(#23376) (Sysix)
- 7ada6b2 linter: Add schema for `jsx_a11y/no-distracting-elements`
(#23379) (WaterWhisperer)
- ee3dd49 linter: Add schema for `jsx-a11y/img-redundant-alt` (#23374)
(Sysix)
- df5f8dd linter: Add short descriptions to most lint rules. (#23365)
(Connor Shea)
- e3fd735 linter: Add schema for `jsx_a11y/alt-text` (#23369) (Sysix)
- 0f2fff4 linter: Add schema for `react/exhaustive-deps` (#23372)
(Mikhail Baev)
- e3e4e10 linter: Add schema for `react_perf/jsx-no-new-object-as-prop`
(#23368) (Mikhail Baev)
- 9366d44 linter: Add schema for `unicorn/prefer-at` (#23366)
(WaterWhisperer)
- f57b55d linter: Add schema for `typescript/array-type` (#23355)
(Sysix)
- 0dcf912 linter: Add schema for `typescript/ban-ts-comment` (#23354)
(Sysix)
- 51fa83e linter: Add schema for `react/no-did-update-set-state`
(#23357) (Mikhail Baev)
- 59db0bd linter: Add schema for `consistent-generic-constructors`
(#23353) (Sysix)
- c4775c0 linter: Add schema for `typescript/consistent-type-assertions`
(#23349) (Sysix)
- 6e516f7 linter: Add schema for `typescript/consistent-type-imports`
(#23348) (Sysix)
- 012134d linter: Add schema for `react/jsx-no-target-blank` (#23345)
(WaterWhisperer)
- 0806aae linter: Add schema for `jsx_a11y/no-noninteractive-tabindex`
(#23337) (Mikhail Baev)
- 0708b5a linter: Add schema for `react/jsx-filename-extension` (#23315)
(Mikhail Baev)
- 150bce1 linter: Add schema for `typescript/no-empty-object-type`
(#23309) (Sysix)
- f9e36f1 linter: Add schema for
`typescript/no-duplicate-type-constituents` (#23308) (Sysix)
- 937accf linter: Add schema for `typescript/no-invalid-void-type`
(#23307) (Sysix)
- 3e042b9 linter: Add schema for `typescript/no-misused-promises`
(#23306) (Sysix)
- da212d1 linter: Add schema for `typescript/no-unnecessary-condition`
(#23305) (Sysix)
- f8f0d38 linter: Add schema for `typescript/parameter-properties`
(#23304) (Sysix)
- 2275fc7 linter: Add schema for `typescript/prefer-nullish-coalescing`
(#23302) (Sysix)
- d353858 linter: Add schema for
`typescript/prefer-string-starts-ends-with` (#23301) (Sysix)
- 03060f5 linter: Add schema for `typescript/triple-slash-reference`
(#23300) (Sysix)
- 6619cee linter: Add schema for `promise/param-names` (#23298) (Sysix)
- 8bf108e linter: Add schema for `promise/catch-or-return` (#23297)
(Sysix)
- 48158d0 linter: Add schema for `vitest/consistent-each-for` (#23294)
(Sysix)
- 7e74c98 linter: Add schema for `vitest/consistent-test-filename`
(#23293) (Sysix)
- ff94d4a linter: Add schema for `vitest/consistent-vitest-vi` (#23292)
(Sysix)
- 2409a10 linter: Add schema for `vitest/prefer-import-in-mock` (#23291)
(Sysix)
- 3d782b7 linter: Add schema for `react/no-unstable-nested-components`
(#23287) (Mikhail Baev)
- 0a0bc2f linter/jsx-a11y: Add `allowedRedundantRoles` option to
`no-redundant-roles` (#22820) (bab)
- 80758a5 linter/vue: Implement no-side-effects-in-computed-properties
rule (#23282) (bab)
- e3869ac linter: Add schema for `react/no-object-type-as-default-prop`
(#23279) (Mikhail Baev)
- 4480609 linter: Add schema for `react/jsx-props-no-spreading` (#23276)
(Mikhail Baev)
- 08d68a5 linter/react: Implement `jsx-no-literals` rule (#23145)
(kapobajza)
- 9a2788b linter/unicorn: Implement `prefer-export-from` rule (#22935)
(AliceLanniste)
- bdb723c linter/unicorn: Implement prefer-single-call rule (#23235)
(Yuzhe Shi)
- 31543ed linter: Add schema for `vue/define-props-destructuring`
(#23252) (Sysix)
- 21b6c3d linter: Add schema for `oxc/no-async-endpoint-handlers`
(#23251) (Sysix)
- e77ff81 linter: Add schema for `unicorn/prefer-object-from-entries`
(#23249) (Mikhail Baev)
- bcac2d6 linter: Add schema for `jest/vitest/no-restricted-matchers`
(#23247) (Sysix)
- 539f036 linter: Add schema for `jest/vitest/no-restricted-*-methods`
(#23246) (Sysix)
- dd1b927 linter/vue: Implement require-default-prop rule (#22951) (bab)
- 3f018e7 linter: Add schema for `unicorn/no-instanceof-builtins`
(#23225) (Mikhail Baev)
- 5b9a793 linter/react-perf: Support `nativeAllowList` config (#22996)
(Cole Ellison)
- b4e19d4 linter: Improve promise/catch-or-return termination
diagnostics (#23218) (camc314)
- 9b72a41 linter/promise/catch-or-return: Add support for
`allowThenStrict` option (#23206) (camc314)
- e0d0f78 linter: Verify promise/no-callback-in-promise schema (#23141)
(beanscg)
- 123d4f4 linter: Add schema for `jest/vitest/valid-expect` (#23185)
(Sysix)
- 46c8a21 linter: Add schema for
`jest/vitest/require-top-level-describe` (#23184) (Sysix)
- 41465cf linter: Add schema for `jest/vitest/prefer-snapshot-hint`
(#23183) (Sysix)
- d068b9b linter: Add schema for `jest/vitest/prefer-expect-assertions`
(#23181) (Sysix)
- 064a1ee linter: Add schema for `jest/prefer-ending-with-an-expect`
(#23180) (Sysix)
- d046797 linter: Add schema for `jest/vitest/no-standalone-expect`
(#23179) (Sysix)
- 137b9a6 linter: Add schema for `jest/vitest/no-large-snapshots`
(#23178) (Sysix)
- 0f3e4a5 linter: Add schema for `jest/vitest/no-hooks` (#23177) (Sysix)
- 00244d8 parser: Report definite property initializer errors (#23160)
(camc314)
- cd0b384 linter: Add schema for `unicorn/explicit-length-check`
(#23155) (Mikhail Baev)
- 01b74c4 linter: Add schema for `jest/no-deprecated-functions` (#23136)
(Sysix)
- 9d6a387 linter: Add schema for `unicorn/catch-error-name` (#23137)
(Mikhail Baev)
- 0da8efa linter: Add schema for `jest/vitest/max-nested-describe`
(#23131) (Sysix)
- d71c9fd linter: Add schema for `eslint/no-use-before-define` (#23129)
(Sysix)

### 🐛 Bug Fixes

- 019be2e linter/unicorn: Align throw-new-error parity (#23437)
(camc314)
- b9e5e51 linter/jest: Validate no-restricted-jest-methods config
(#23436) (camc314)
- f302f41 linter: Normalize oxlint jsPlugin prefixes (#23335) (konh)
- 2bdf8ce linter/jest: Validate no-restricted-matchers config (#23434)
(camc314)
- 5c2fb45 linter/eslint: Validate max-depth config (#23430) (camc314)
- eafc280 linter/eslint: Validate no-unused-vars config (#23433)
(camc314)
- 25aebb0 linter/typescript: Validate no-empty-object-type config
(#23432) (camc314)
- 7cafe3b linter/unicorn: Validate catch-error-name config (#23431)
(camc314)
- af1b897 linter/unicorn: Validate prefer-object-from-entries config
(#23429) (camc314)
- 68d48fe linter/eslint: Validate max-classes-per-file config (#23428)
(camc314)
- 02932b6 linter/eslint: Validate `complexity` config (#23427) (camc314)
- 375e574 linter/eslint: Use shared id-length regex config (#23426)
(camc314)
- ba3e2e0 linter/eslint: Preserve used imports in no-unused-vars fixer
(#23425) (camc314)
- 26ddac6 linter: Avoid config schema generation for
`jsx_a11y/no-noninteractive-element-interactions` (#23385) (Sysix)
- 40556ad linter: Parse `jsx-a11y/control-has-associated-label` config
with `DefaultRuleConfig` (#23373) (Sysix)
- dacbda8 linter/unicorn/prefer-string-starts-ends-with: Mark rule as
deprecated (#23342) (camc314)
- 956309a lsp: Use protocol line breaks for positions (#23329) (konh)
- 71e9648 linter: Expose no-noninteractive-element-interactions schema
(#23283) (camc314)
- 4c4c19c linter: `no-noninteractive-element-interactions` fix default
values (#23288) (Sysix)
- e63a139 linter/jsx-a11y: Restore control label defaults (#22737)
(konh)
- 0e4e59f linter/jsx-a11y: No-redundant-roles attribute-aware implicit
roles for `<input>`, skip ancestor-conditional elements (#23015) (Gaurav
Dubey)
- 06e215e linter/vitest: Validate `consistent-each-for` config (#23278)
(camc314)
- cd278e1 linter/promise: Validate param-names config (#23277) (camc314)
- 7f46013 linter/react/display-name: Handle default export function
correctly (#23271) (camc314)
- 22ff6a6 linter/unicorn: Deserialize filename-case config with serde
(#23266) (camc314)
- b644da0 linter/eslint: Add fixer for `sort_vars` (#23262) (Cason
Kervis)
- 778af4d linter/unicorn: Support array of patterns for
`unicorn/filename-case` `ignore` option (#23260) (kapobajza)
- 127fb2c linter: Deserialize rule config with serde for
`unicorn/prefer-object-from-entries` (#23253) (Sysix)
- f89b909 linter/react/jsx-no-target-blank: Handle compound conditions
(#23245) (camc314)
- c30419a linter/typescript: Deserialize rule config with serde for
`consistent-generic-constructors` (#23238) (camc314)
- 7d17d37 linter/react: Report invalid exhaustive-deps config (#23239)
(camc314)
- 1060186 linter/eslint: Add `deny_unknown_fields` to
`prefer-destructuring` (#23237) (camc314)
- fd2e4eb linter/react: Add `deny_unknown_fields` to `exhaustive-deps`
(#23236) (camc314)
- 6c3c904 linter/react/jsx-curly-brace-presence: Avoid false positive
with comment within braces (#23234) (camc314)
- 6c86d1c linter/react-perf: Correct nativeAllowList all schema (#23229)
(camc314)
- c393760 linter/react/rules-of-hooks: Align try/catch semantics
(#23221) (camc314)
- 4dd52de linter/react-perf: Re-generate stale snapshots (#23228)
(camc314)
- de2d23b linter/unicorn/prefer-dom-node-dataset: Preserve optional
chaining in fixer (#23148) (Bruno Carvalho)
- d7038de linter/react/exhaustive-deps: Ignore non `React` namespace
hooks (#23175) (camc314)
- 7111903 linter/react/exhaustive-deps: FP with wrapped literal
expressions (#23172) (camc314)
- 93326e7 linter/react: Align hook errors with anonomous async callbacks
(#23158) (camc314)
- 8f3db61 linter: Allow options for `eslint/capitalized-comments`
(#23139) (Sysix)
- f533565 linter: Support nofunc string option in no-use-before-define
(#23115) (bab)

### ⚡ Performance

- f09707e linter: `jest/no-deprecated-functions` store config version as
`usize` (#23138) (Sysix)
- da1a6c6 diagnostics: Migrate to allocation-optimized oxc-miette
(#23094) (Boshen)

### 📚 Documentation

- f682e25 linter: Remove manually written options doc for
`eslint/prefer-arrow-callback` (#23438) (Mikhail Baev)
- 64c942c linter: Remove manually written options doc for
`eslint/no-sequences` (#23420) (Mikhail Baev)
- d6d1189 linter: Add `short_descriptions` to more rules (#23417)
(Connor Shea)
- b979722 linter: Clarify valid-define-emits description (#23408)
(camc314)
- ccd0a69 linter: Clarify only-used-in-recursion description (#23407)
(camc314)
- b0fd30b linter: Clarify no-sync-scripts description (#23406) (camc314)
- c23290d linter: Clarify no-cycle description (#23405) (camc314)
- 3612e0b linter: Clarify no-nodejs-modules description (#23403)
(camc314)
- c67c5b1 linter: Clarify no-misused-new description (#23402) (camc314)
- 88bf0bc linter: Clarify require-render-return description (#23401)
(camc314)
- 9fff263 linter: Add short descriptions to more rules that lacked them
(#23389) (Connor Shea)
- ac69042 linter: Add short_description fields to more lint rules
(#23391) (Connor Shea)
- 99a3c78 linter: Add info for more skipped rule schemas (#23380)
(Sysix)
- c966384 linter: Fix the example of `unicorn/prefer-string-replace-all`
(#23367) (Liang)
- 2120ecf linter: Describe problems on not verified rules schema
(#23351) (Sysix)
- 14abf32 linter/react-perf: Use autogenerated docs (#23227) (camc314)
# Oxfmt
### 🚀 Features

- 9a2788b linter/unicorn: Implement `prefer-export-from` rule (#22935)
(AliceLanniste)
- 0c6fb23 oxfmt: Format `parser:json-stringify` files by
`oxc_formatter_json` (#23194) (leaysgur)
- 8cc82c4 formatter_json: Implement json-stringify variant (#23192)
(leaysgur)

### 🐛 Bug Fixes

- 956309a lsp: Use protocol line breaks for positions (#23329) (konh)
- 769de65 oxfmt: Handle compound json extensions (#23198) (leaysgur)
- b0b5d39 formatter_json: Support JSON5/JSON6 value in json/jsonc/json5
(#23193) (leaysgur)

### 📚 Documentation

- 4986613 formatter_json: Update AGENTS.md (#23199) (leaysgur)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-linter Area - Linter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

linter: jsx-a11y/no-redundant-roles incorrect implicit roles for <input> and <header> cause false positives

2 participants