Linter Rule: Disallow locals that shadow a render option name
Rule: actionview-no-render-option-shadowing
Description
Detects locals passed to the shorthand render "partial", ... form whose name matches a render option, such as collection:, object: or layout:.
Rationale
render accepts a partial two ways, and the same keyword means different things in each. In the shorthand form the first argument names the partial and everything after it becomes the locals hash:
render "card", collection: @products
# => render_partial(partial: "card", locals: { collection: @products })In the keyword form it is a render option, and the partial is rendered once per item:
render partial: "card", collection: @productsBoth are valid and neither raises. A reader cannot tell from the call alone which was meant, and switching a call between the two forms during a refactor silently changes what it does.
Passing the value inside an explicit locals: hash removes the ambiguity. It produces exactly the same locals as the shorthand form, so the change is safe to make mechanically:
render partial: "card", locals: { collection: @products }This is a naming collision rather than a mistake. A partial is free to take a local called object or collection, and plenty do, which is why the rule reports at info and points at the explicit form instead of asking you to rename anything.
Locals already written inside a locals: hash are never reported.
Examples
✅ Good
<%= render partial: "card", locals: { collection: @products } %><%= render partial: "card", collection: @products, as: :item %><%= render "card", title: "Featured", product: @product %>🚫 Bad
<%= render "card", collection: @products %><%= render "shared/error_messages", object: @user %><%= render "card", layout: "wide" %>