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).
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.
Most of these offenses are corrected by --fix. The rewrites are safe because the declaration they replace is one Rails rejects outright.
Before
After
<%# 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.
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)
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)
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.
Linter Rule: Enforce strict locals comment syntax
Rule:
erb-strict-locals-comment-syntaxDescription
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::Errorat 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.<%# 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:
Keyword argument with default value:
Complex default values:
No locals (empty):
Double-splat for optional keyword arguments:
🚫 Bad
Wrong comment syntax
Missing colon after
locals:Singular
localinstead oflocals:Missing colon before parentheses:
Missing parentheses around parameters:
Empty
locals:without parentheses:Missing space after the colon:
Unbalanced parentheses:
Wrong tag type (must use ERB comment tag)
Ruby comment in execution tag:
Unsupported argument types
Positional argument (use
user:instead):Block argument:
Single splat argument:
Note: Double-splat (
**attributes) IS supported for optional keyword arguments.Invalid Ruby syntax
Trailing comma:
Leading comma:
Double comma:
Duplicate declarations
Only one
locals:comment is allowed per partial:References