Skip to content

Linter: Only classify an ERB tag's own calls in html-no-nested-forms - #2750

Merged
marcoroth merged 3 commits into
marcoroth:mainfrom
viktorianer:fix-nested-forms-block-helpers
Oct 6, 2026
Merged

marcoroth merged 3 commits into
marcoroth:mainfrom
viktorianer:fix-nested-forms-block-helpers

Conversation

@viktorianer

@viktorianer viktorianer commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2708
Fixes #2709

html-no-nested-forms collects form helper calls from an ERB tag's Prism node. For a tag that opens a block, that node is the whole call including its block, so the collector also walked into the calls of the tags nested inside the block. Any block helper wrapping a form helper was then classified as a form itself, and the real form helper inside it was reported as nested:

<%= panel do %>
  <%= form_with url: "/search" do |form| %>
    <%= form.submit %>
  <% end %>
<% end %>

The collector now ignores calls that start after the tag's own content (node.content.range.to, a byte offset like Prism's startOffset). A nested tag's calls are judged when the visitor reaches that tag, and a form helper in an inline block inside the same tag is still found.

New cases cover every shape from the issue: a block helper with and without a receiver, nested block helpers, a component render block, a component slot block (#2709), and two form helpers inside a partial render block. Controls keep a real button_to inside form_with inside a block helper, a block helper inside a <form> element, and a button_to in an inline block inside form_with reported.

The Prism node of a block-opening ERB tag spans the whole block, so the
form helper collector also found the form helpers of the tags nested inside
it. Any block helper wrapping a `form_with` or `button_to`, such as a
component render or a layout helper, was then treated as a form itself, and
the real form helper inside it was reported as nested.

The collector now ignores calls that start after the tag's own content, so
calls in nested tags are judged by those tags. A form helper in an inline
block inside the same tag is still found.

Fixes marcoroth#2708
Fixes marcoroth#2709

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added linter @herb-tools/linter for HTML+ERB templates typescript TypeScript source across the javascript/ packages linter-rule Individual linter rules and their documentation labels Oct 1, 2026
# Conflicts:
#	javascript/packages/linter/src/rules/html-no-nested-forms.ts

@marcoroth marcoroth left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks @viktorianer, looks like this overlapped with #2774. Sorry.

But I merged in main to make sure to keep the tests from this pull request, thank you! 🙏🏼

@marcoroth
marcoroth enabled auto-merge (squash) October 6, 2026 01:13
@marcoroth
marcoroth merged commit f01661d into marcoroth:main Oct 6, 2026
18 checks passed
@viktorianer

Copy link
Copy Markdown
Contributor Author

Oh, you found the same issue a bit later? 👌🏼

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

Labels

linter @herb-tools/linter for HTML+ERB templates linter-rule Individual linter rules and their documentation typescript TypeScript source across the javascript/ packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter: html-no-nested-forms with block html-no-nested-forms: false positive for button_to inside a render … do block (0.11.0)

2 participants