Skip to content

fix: allow keywords as block type names and labels - #355

Open
aoskotsky-amplify wants to merge 1 commit into
mainfrom
fix/keyword-block-names
Open

fix: allow keywords as block type names and labels#355
aoskotsky-amplify wants to merge 1 commit into
mainfrom
fix/keyword-block-names

Conversation

@aoskotsky-amplify

Copy link
Copy Markdown
Member

Problem

Keywords parse as attribute names but not as block names:

in = 1        # works today

in { }        # UnexpectedToken
for_each { }  # UnexpectedToken
resource in { }  # UnexpectedToken

Keywords (in, for, if, for_each, else, endif, endfor) and literal keywords (true, false, null) are reserved only inside expressions. In a body they are ordinary names, valid as a block type or label just as they already are as attribute names.

The grammar already encodes this for attributes and simply never carried it to blocks:

attribute : _attribute_name EQ expression
_attribute_name : identifier | keyword | literal_valuekeywords allowed
block : identifier (identifier | string)* ...            ← but not here

This continues the line of #164 (attributes named in) and #168 (if/for_each as identifiers), which fixed the attribute side only.

Fix

One line of grammar — reuse the existing _attribute_name rule in the block type and label positions:

block : _attribute_name (_attribute_name | string)* new_line_or_comment? LBRACE body RBRACE

No new rule, so nothing to keep in sync. Because the rule is inlined, block() now receives KeywordRule/LiteralValueRule for bare names, so the transformer normalizes them to IdentifierRule — exactly what attribute() already does — keeping BlockRule.labels a single type for downstream consumers.

Testing

  • New resource_keyword_block integration suite (the block-side sibling of the existing resource_keyword_attribute), covering keyword block types, a keyword label (resource in), nesting, and a string label. All four pipeline files included.
  • Verified the fixture genuinely guards the fix: reverting the source with the fixture present produces 11 failures.
  • Full suite green: 1518 tests, 0 failures, unchanged from baseline.
  • Expression keywords confirmed still reserved: for-tuple, for-object, conditional, for_each =, and template %{ for } / %{ if } directives all parse and reconstruct as before.
  • pre-commit clean (ruff, ruff format, mypy).

Note for reviewers

Sharing _attribute_name also permits true { } / null { } as block names. That matches HCL, where a block type is an Identifier and these are contextual keywords, and it matches how they are already accepted as attribute names. Easy to restrict to identifier | keyword at the block position if you would rather not accept it.

🤖 Generated with Claude Code

@aoskotsky-amplify
aoskotsky-amplify requested a review from a team as a code owner September 4, 2026 17:13
`in { ... }`, `for { ... }`, and `resource in { ... }` failed to parse
while the matching attribute form `in = 1` already worked. Keywords are
reserved only inside expressions; in a body they are ordinary names.

Reuse the existing `_attribute_name` rule for the block type and label
positions, and normalize KeywordRule/LiteralValueRule to IdentifierRule
in the block transformer so labels stay a single type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant