Skip to content

Linter Rule: Enforce strict locals comment syntax

Rule: erb-strict-locals-comment-syntax

Description

Ensures that strict locals comments use the exact locals: ( ... ) syntax so they are properly recognized by Rails and tooling. Also validates that only keyword arguments are used (no positional, block, or splat arguments).

Rationale

Strict locals comments declare which locals are expected in a template. Misspellings or malformed syntax silently disable the declaration, leading to confusing runtime errors when required locals are missing.

Additionally, Rails only supports keyword arguments in strict locals declarations. Positional, block, and splat arguments will raise an ActionView::Error at render-time.

This rule catches invalid comment forms and argument types early during development.

Autofix

Most of these offenses are corrected by --fix. The rewrites are safe because the declaration they replace is one Rails rejects outright.

BeforeAfter
<%# locals() %><%# locals: () %>
<%# local: (user:) %><%# locals: (user:) %>
<%# locals (user:) %><%# locals: (user:) %>
<%# locals:(user:) %><%# locals: (user:) %>
<%# locals: %><%# locals: () %>
<%# locals: user:, admin: false %><%# locals: (user:, admin: false) %>
<%# locals: (user: %><%# locals: (user:) %>
<% # locals: (user:) %><%# locals: (user:) %>
<%# locals: (user) %><%# locals: (user:) %>
<%# locals: (user:,) %><%# locals: (user:) %>

A parameter list written without parentheses is wrapped in them, and any bare name in it becomes a required keyword argument, since that is the only form Rails accepts.

Block arguments, splat arguments, and duplicate declarations are reported but not corrected. Each has more than one reasonable rewrite, so the choice is left to you.

Examples

✅ Good

Required keyword argument:

erb
<%# locals: (user:) %>
Strict local `user` is never used in this partial. Callers have to pass `user:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)

Keyword argument with default value:

erb
<%# locals: (user:, admin: false) %>
Strict local `admin` is never used in this partial. Callers can pass `admin:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)
Strict local `user` is never used in this partial. Callers have to pass `user:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)

Complex default values:

erb
<%# locals: (items: [], config: {}) %>
Strict local `config` is never used in this partial. Callers can pass `config:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)
Strict local `items` is never used in this partial. Callers can pass `items:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)

No locals (empty):

erb
<%# locals: () %>

Double-splat for optional keyword arguments:

erb
<%# locals: (message: "Hello", **attributes) %>
Strict local `message` is never used in this partial. Callers can pass `message:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)

🚫 Bad

Wrong comment syntax

Missing colon after locals:

erb
<%# locals() %>
Use `locals:` with a colon, not `locals()`. Correct format: `<%# locals: (...) %>`. (erb-strict-locals-comment-syntax)

Singular local instead of locals:

erb
<%# local: (user:) %>
Use `locals:` (plural), not `local:`. (erb-strict-locals-comment-syntax)

Missing colon before parentheses:

erb
<%# locals (user:) %>
Use `locals:` with a colon before the parentheses, not `locals (`. (erb-strict-locals-comment-syntax)

Missing parentheses around parameters:

erb
<%# locals: user %>
Strict locals parameters must be wrapped in parentheses. Use `<%# locals: (user:) %>`. (erb-strict-locals-comment-syntax)

Empty locals: without parentheses:

erb
<%# locals: %>
Strict locals declarations always need parentheses. Use `<%# locals: () %>` for a partial without locals. (erb-strict-locals-comment-syntax)

Missing space after the colon:

erb
<%# locals:(user:) %>
Missing space after `locals:`. Rails Strict Locals require a space after the colon: `<%# locals: (...) %>`. (erb-strict-locals-comment-syntax)
Strict local `user` is never used in this partial. Callers have to pass `user:` for a value the template never renders. Remove it from the `locals:` declaration and from the call sites. (actionview-no-unused-strict-locals)

Unbalanced parentheses:

erb
<%# locals: (user: %>
Unbalanced parentheses in the strict locals declaration. Add the missing closing `)`. (erb-strict-locals-comment-syntax)

Wrong tag type (must use ERB comment tag)

Ruby comment in execution tag:

erb
<% # locals: (user:) %>
Use `<%#` instead of `<% #` for strict locals comments. Only ERB comment syntax is recognized by Rails. (erb-strict-locals-comment-syntax)
Use `<%#` instead of `<% #`. Ruby comments immediately after ERB tags can cause parsing issues. (erb-comment-syntax)

Unsupported argument types

Positional argument (use user: instead):

erb
<%# locals: (user) %>
Strict locals only support keyword arguments. Use `user:` instead of the positional argument `user`. (erb-strict-locals-comment-syntax)

Block argument:

erb
<%# locals: (&block) %>
Strict locals only support keyword arguments. The block argument `&block` is not supported. (erb-strict-locals-comment-syntax)

Single splat argument:

erb
<%# locals: (*args) %>
Strict locals only support keyword arguments. The splat argument `*args` is not supported. Use `**args` to accept arbitrary keyword arguments. (erb-strict-locals-comment-syntax)

Note: Double-splat (**attributes) IS supported for optional keyword arguments.

Invalid Ruby syntax

Trailing comma:

erb
<%# locals: (user:,) %>
Remove the extra comma from the strict locals parameters. (erb-strict-locals-comment-syntax)

Leading comma:

erb
<%# locals: (, user:) %>
Remove the extra comma from the strict locals parameters. (erb-strict-locals-comment-syntax)

Double comma:

erb
<%# locals: (user:,, admin:) %>
Remove the extra comma from the strict locals parameters. (erb-strict-locals-comment-syntax)

Duplicate declarations

Only one locals: comment is allowed per partial:

erb
<%# locals: (user:) %>
<p>Content</p>
<%# locals: (admin:) %>
Duplicate strict locals declaration. Rails only uses the first `<%# locals: (...) %>` declaration in a partial. (erb-strict-locals-comment-syntax)

References

Released under the MIT License.