Repository: ruby/prism
Pull request #2046 (2023-12-11 20:35:04)

Add a `PM_KEYWORD_HASH_NODE_FLAGS_STATIC_LITERAL_ASSOC_NODE_KEYS` flag for `KeywordHashNode`s
The CRuby compiler changes how it handles keyword hashes depending on if:
1. All elements are `AssocNode`s with static literal keys, in which case these can be sent to the call as keyword arguments,
2. Otherwise, the elements need to be collected in a hash and sent as an extra positional argument.

https://github.com/ruby/ruby/pull/9147 implements that behaviour inside CRuby by looping over the elements of `KeywordHashNode` to detect either of these cases, but a better way to do that is to add a flag to `KeywordHashNode` to keep this state.

This PR implements that feature by making all `KeywordHashNode` elements start off with the `PM_KEYWORD_HASH_NODE_FLAGS_STATIC_LITERAL_ASSOC_NODE_KEYS` flag set. When an element is added to the keyword hash node, we check if the element is an `AssocNode` and if its key is a static literal. If we fail either of those conditions, then we clear the flag on the keyword hash node.

Ref: https://github.com/ruby/ruby/pull/9147#discussion_r1421126156


Patch:
diff --git a/config.yml b/config.yml
index 5f20c80f..7e71e4e5 100644
--- a/config.yml
+++ b/config.yml
@@ -364,6 +364,11 @@ flags:
       - name: HEXADECIMAL
         comment: "0x prefix"
     comment: Flags for integer nodes that correspond to the base of the integer.
+  - name: KeywordHashNodeFlags
+    values:
+      - name: STATIC_KEYS
+        comment: "a keyword hash which only has `AssocNode` elements all with static literal keys, which means the elements can be treated as keyword arguments"
+    comment: Flags for keyword hash nodes.
   - name: LoopFlags
     values:
       - name: BEGIN_MODIFIER
@@ -1821,6 +1826,9 @@ nodes:
           ^^^^^^^^^^^^^^^^
   - name: KeywordHashNode
     fields:
+      - name: flags
+        type: flags
+        kind: KeywordHashNodeFlags
       - name: elements
         type: node[]
     comment: |
diff --git a/src/prism.c b/src/prism.c
index 9bed7b19..1474a185 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -3921,7 +3921,8 @@ pm_keyword_hash_node_create(pm_parser_t *parser) {
     *node = (pm_keyword_hash_node_t) {
         .base = {
             .type = PM_KEYWORD_HASH_NODE,
-            .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
+            .location = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+            .flags = PM_KEYWORD_HASH_NODE_FLAGS_STATIC_KEYS
         },
         .elements = { 0 }
     };
@@ -3934,6 +3935,12 @@ pm_keyword_hash_node_create(pm_parser_t *parser) {
  */
 static void
 pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *element) {
+    // If the element being added is not an AssocNode or does not have a static literal key, then
+    // we want to turn the STATIC_KEYS flag off.
+    if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || (((pm_assoc_node_t *) element)->key->flags & PM_NODE_FLAG_STATIC_LITERAL) == 0) {
+        hash->base.flags &= (pm_node_flags_t) ~PM_KEYWORD_HASH_NODE_FLAGS_STATIC_KEYS;
+    }
+
     pm_node_list_append(&hash->elements, element);
     if (hash->base.location.start == NULL) {
         hash->base.location.start = element->location.start;


Test patch:
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index 816df466..e11a7bdc 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -367,7 +367,7 @@ module Prism
         Location(),
         Location(),
         ArgumentsNode(1, [
-          KeywordHashNode([AssocSplatNode(expression("kwargs"), Location())]),
+          KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
           SplatNode(Location(), expression("args"))
         ]),
         Location(),
@@ -414,13 +414,13 @@ module Prism
         Location(),
         Location(),
         ArgumentsNode(0, [
-          KeywordHashNode(
-            [AssocNode(
+          KeywordHashNode(1, [
+            AssocNode(
               SymbolNode(0, nil, Location(), Location(), "foo"),
               expression("bar"),
               nil
-            )]
-          ),
+            )
+          ]),
           SplatNode(Location(), expression("args"))
         ]),
         Location(),
diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt
index c970620a..7601fc38 100644
--- a/test/prism/snapshots/arrays.txt
+++ b/test/prism/snapshots/arrays.txt
@@ -79,6 +79,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (5,1)-(5,12))
+        │   │       ├── flags: static_keys
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocNode (location: (5,1)-(5,12))
         │   │               ├── key:
@@ -175,6 +176,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (28,1)-(28,11))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocNode (location: (28,1)-(28,11))
         │   │               ├── key:
@@ -632,6 +634,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (49,1)-(49,5))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocSplatNode (location: (49,1)-(49,5))
         │   │               ├── value:
@@ -646,6 +649,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (51,1)-(51,5))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocSplatNode (location: (51,1)-(51,5))
         │   │               ├── value:
@@ -668,6 +672,7 @@
         │   │   ├── @ IntegerNode (location: (53,1)-(53,2))
         │   │   │   └── flags: decimal
         │   │   └── @ KeywordHashNode (location: (53,4)-(53,8))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocSplatNode (location: (53,4)-(53,8))
         │   │               ├── value:
@@ -690,6 +695,7 @@
         │   │   ├── @ IntegerNode (location: (55,1)-(55,2))
         │   │   │   └── flags: decimal
         │   │   └── @ KeywordHashNode (location: (55,4)-(55,20))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 3)
         │   │           ├── @ AssocSplatNode (location: (55,4)-(55,8))
         │   │           │   ├── value:
@@ -730,6 +736,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (58,2)-(58,12))
+        │   │       ├── flags: ∅
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocNode (location: (58,2)-(58,12))
         │   │               ├── key:
diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt
index 7abd8cf6..2210e26c 100644
--- a/test/prism/snapshots/constants.txt
+++ b/test/prism/snapshots/constants.txt
@@ -117,6 +117,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (17,4)-(17,9))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocSplatNode (location: (17,4)-(17,9))
         │   │                   ├── value:
@@ -198,6 +199,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (23,9)-(23,14))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocSplatNode (location: (23,9)-(23,14))
         │   │                   ├── value:
diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt
index 965ca959..6733e57b 100644
--- a/test/prism/snapshots/if.txt
+++ b/test/prism/snapshots/if.txt
@@ -250,6 +250,7 @@
         │   │           │   ├── flags: ∅
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (25,4)-(25,6))
+        │   │           │           ├── flags: static_keys
         │   │           │           └── elements: (length: 1)
         │   │           │               └── @ AssocNode (location: (25,4)-(25,6))
         │   │           │                   ├── key:
diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt
index a60a9a18..772d4cad 100644
--- a/test/prism/snapshots/method_calls.txt
+++ b/test/prism/snapshots/method_calls.txt
@@ -307,6 +307,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (27,2)-(27,10))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocSplatNode (location: (27,2)-(27,10))
         │   │                   ├── value:
@@ -781,6 +782,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── unescaped: "a"
         │   │       └── @ KeywordHashNode (location: (60,8)-(60,32))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 2)
         │   │               ├── @ AssocNode (location: (60,8)-(60,22))
         │   │               │   ├── key:
@@ -912,6 +914,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── unescaped: "a"
         │   │       └── @ KeywordHashNode (location: (64,8)-(64,15))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (64,8)-(64,15))
         │   │                   ├── key:
@@ -980,6 +983,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (66,3)-(66,17))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (66,3)-(66,17))
         │   │                   ├── key:
@@ -1011,6 +1015,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (68,3)-(68,40))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 3)
         │   │               ├── @ AssocNode (location: (68,3)-(68,20))
         │   │               │   ├── key:
@@ -1065,6 +1070,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (70,3)-(70,40))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 3)
         │   │               ├── @ AssocNode (location: (70,3)-(70,20))
         │   │               │   ├── key:
@@ -1167,6 +1173,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (74,3)-(74,20))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (74,3)-(74,20))
         │   │                   ├── key:
@@ -1229,6 +1236,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── unescaped: "a"
         │   │       └── @ KeywordHashNode (location: (82,0)-(82,5))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (82,0)-(82,5))
         │   │                   ├── key:
@@ -1279,6 +1287,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (87,4)-(87,21))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 2)
         │   │               ├── @ AssocNode (location: (87,4)-(87,11))
         │   │               │   ├── key:
@@ -1327,6 +1336,7 @@
         │   │       ├── @ IntegerNode (location: (89,10)-(89,11))
         │   │       │   └── flags: decimal
         │   │       └── @ KeywordHashNode (location: (89,13)-(89,21))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (89,13)-(89,21))
         │   │                   ├── key:
@@ -1517,6 +1527,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (103,4)-(103,11))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (103,4)-(103,11))
         │   │                   ├── key:
@@ -1544,6 +1555,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (105,4)-(105,28))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (105,4)-(105,28))
         │   │                   ├── key:
@@ -1600,6 +1612,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (107,4)-(107,24))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (107,4)-(107,24))
         │   │                   ├── key:
diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt
index ae7adecf..62c117b8 100644
--- a/test/prism/snapshots/methods.txt
+++ b/test/prism/snapshots/methods.txt
@@ -1297,6 +1297,7 @@
         │   │           │   ├── flags: contains_keyword_splat
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (139,11)-(139,30))
+        │   │           │           ├── flags: ∅
         │   │           │           └── elements: (length: 3)
         │   │           │               ├── @ AssocSplatNode (location: (139,11)-(139,16))
         │   │           │               │   ├── value:
diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt
index ca47adce..3f9dc426 100644
--- a/test/prism/snapshots/rescue.txt
+++ b/test/prism/snapshots/rescue.txt
@@ -343,6 +343,7 @@
             │   │           │   ├── flags: ∅
             │   │           │   └── arguments: (length: 1)
             │   │           │       └── @ KeywordHashNode (location: (29,4)-(29,6))
+            │   │           │           ├── flags: static_keys
             │   │           │           └── elements: (length: 1)
             │   │           │               └── @ AssocNode (location: (29,4)-(29,6))
             │   │           │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/aref_args_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_assocs.txt
index b7f04469..8286751c 100644
--- a/test/prism/snapshots/seattlerb/aref_args_assocs.txt
+++ b/test/prism/snapshots/seattlerb/aref_args_assocs.txt
@@ -7,6 +7,7 @@
             ├── flags: ∅
             ├── elements: (length: 1)
             │   └── @ KeywordHashNode (location: (1,1)-(1,7))
+            │       ├── flags: static_keys
             │       └── elements: (length: 1)
             │           └── @ AssocNode (location: (1,1)-(1,7))
             │               ├── key:
diff --git a/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt
index 8573d1d7..c809f426 100644
--- a/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt
+++ b/test/prism/snapshots/seattlerb/aref_args_lit_assocs.txt
@@ -9,6 +9,7 @@
             │   ├── @ IntegerNode (location: (1,1)-(1,2))
             │   │   └── flags: decimal
             │   └── @ KeywordHashNode (location: (1,4)-(1,10))
+            │       ├── flags: static_keys
             │       └── elements: (length: 1)
             │           └── @ AssocNode (location: (1,4)-(1,10))
             │               ├── key:
diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt
index 17b695cf..1d65eef3 100644
--- a/test/prism/snapshots/seattlerb/assoc_label.txt
+++ b/test/prism/snapshots/seattlerb/assoc_label.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(1,5))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt
index 90ffa88f..4c684f32 100644
--- a/test/prism/snapshots/seattlerb/bug_249.txt
+++ b/test/prism/snapshots/seattlerb/bug_249.txt
@@ -66,6 +66,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── block: ∅
             │       └── @ KeywordHashNode (location: (4,11)-(4,28))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (4,11)-(4,28))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt
index c9491071..cea1fc8d 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt
@@ -21,6 +21,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── unescaped: "bar"
             │       └── @ KeywordHashNode (location: (1,10)-(1,18))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,10)-(1,18))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
index 04f4173f..e4c2a10f 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
@@ -21,6 +21,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── unescaped: "bar"
             │       └── @ KeywordHashNode (location: (1,10)-(1,18))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,10)-(1,18))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
index 4694b430..4b7666a3 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
@@ -17,6 +17,7 @@
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   └── flags: decimal
             │       └── @ KeywordHashNode (location: (1,5)-(1,9))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,5)-(1,9))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
index 6952d26f..9a052bef 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
@@ -17,6 +17,7 @@
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   └── flags: decimal
             │       └── @ KeywordHashNode (location: (1,5)-(1,15))
+            │           ├── flags: ∅
             │           └── elements: (length: 2)
             │               ├── @ AssocNode (location: (1,5)-(1,10))
             │               │   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
index da8d8b70..dab497db 100644
--- a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
@@ -25,6 +25,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── block: ∅
             │       └── @ KeywordHashNode (location: (1,5)-(1,8))
+            │           ├── flags: ∅
             │           └── elements: (length: 1)
             │               └── @ AssocSplatNode (location: (1,5)-(1,8))
             │                   ├── value:
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
index f47f5175..958231a2 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
@@ -15,6 +15,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,2)-(1,11))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,2)-(1,11))
         │   │                   ├── key:
@@ -56,6 +57,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (3,2)-(3,8))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (3,2)-(3,8))
         │   │                   ├── key:
@@ -83,6 +85,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (5,2)-(5,8))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (5,2)-(5,8))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
index b27cd84b..a21be7ff 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
@@ -17,6 +17,7 @@
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   └── flags: decimal
             │       └── @ KeywordHashNode (location: (1,5)-(1,9))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,5)-(1,9))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt
index 5099caf0..f8cde2f6 100644
--- a/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt
+++ b/test/prism/snapshots/seattlerb/call_array_lit_inline_hash.txt
@@ -24,6 +24,7 @@
             │           │   │   ├── closing_loc: ∅
             │           │   │   └── unescaped: "b"
             │           │   └── @ KeywordHashNode (location: (1,7)-(1,14))
+            │           │       ├── flags: static_keys
             │           │       └── elements: (length: 1)
             │           │           └── @ AssocNode (location: (1,7)-(1,14))
             │           │               ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt
index 530b25e4..ba458373 100644
--- a/test/prism/snapshots/seattlerb/call_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(1,6))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt
index 5488fb2c..6d121252 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(1,5))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
index 24477a76..1c079f9d 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(5,3))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(5,3))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
index ae3b5882..20e402ff 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(1,6))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt
index f2effca1..04eab025 100644
--- a/test/prism/snapshots/seattlerb/call_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt
@@ -15,6 +15,7 @@
             │   ├── flags: contains_keyword_splat
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
+            │           ├── flags: ∅
             │           └── elements: (length: 1)
             │               └── @ AssocSplatNode (location: (1,2)-(1,5))
             │                   ├── value:
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
index 8c43e4eb..e562b672 100644
--- a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
@@ -35,6 +35,7 @@
             │           │   ├── flags: contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,31)-(1,40))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 1)
             │           │               └── @ AssocSplatNode (location: (1,31)-(1,40))
             │           │                   ├── value:
diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt
index 182158b2..a81b0ce4 100644
--- a/test/prism/snapshots/seattlerb/difficult2_.txt
+++ b/test/prism/snapshots/seattlerb/difficult2_.txt
@@ -53,6 +53,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (2,2)-(2,6))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (2,2)-(2,6))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
index 1fa0510b..75ae0d99 100644
--- a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
@@ -25,6 +25,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,8))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,4)-(1,8))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
index 305949a7..efa6fa24 100644
--- a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
+++ b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
@@ -15,6 +15,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,2)-(3,1))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,2)-(3,1))
         │   │                   ├── key:
@@ -44,6 +45,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (5,2)-(6,1))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (5,2)-(6,1))
         │   │                   ├── key:
@@ -73,6 +75,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (8,2)-(8,11))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (8,2)-(8,11))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
index 528ade93..3dc68795 100644
--- a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
+++ b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
@@ -17,6 +17,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,2)-(1,6))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
index 1650acc2..59bd8f02 100644
--- a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
+++ b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,5)-(1,12))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,5)-(1,12))
             │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/return_call_assocs.txt b/test/prism/snapshots/seattlerb/return_call_assocs.txt
index 047eb1d9..8baf6c4c 100644
--- a/test/prism/snapshots/seattlerb/return_call_assocs.txt
+++ b/test/prism/snapshots/seattlerb/return_call_assocs.txt
@@ -12,6 +12,7 @@
         │           ├── @ IntegerNode (location: (1,7)-(1,8))
         │           │   └── flags: decimal
         │           └── @ KeywordHashNode (location: (1,10)-(1,17))
+        │               ├── flags: static_keys
         │               └── elements: (length: 1)
         │                   └── @ AssocNode (location: (1,10)-(1,17))
         │                       ├── key:
@@ -34,6 +35,7 @@
         │           ├── @ IntegerNode (location: (3,7)-(3,8))
         │           │   └── flags: decimal
         │           └── @ KeywordHashNode (location: (3,10)-(3,26))
+        │               ├── flags: static_keys
         │               └── elements: (length: 2)
         │                   ├── @ AssocNode (location: (3,10)-(3,17))
         │                   │   ├── key:
@@ -77,6 +79,7 @@
         │               │   ├── flags: ∅
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (5,9)-(5,14))
+        │               │           ├── flags: static_keys
         │               │           └── elements: (length: 1)
         │               │               └── @ AssocNode (location: (5,9)-(5,14))
         │               │                   ├── key:
@@ -110,6 +113,7 @@
         │               │   ├── flags: ∅
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (7,9)-(7,12))
+        │               │           ├── flags: static_keys
         │               │           └── elements: (length: 1)
         │               │               └── @ AssocNode (location: (7,9)-(7,12))
         │               │                   ├── key:
@@ -143,6 +147,7 @@
         │               │   ├── flags: ∅
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (9,9)-(9,12))
+        │               │           ├── flags: static_keys
         │               │           └── elements: (length: 1)
         │               │               └── @ AssocNode (location: (9,9)-(9,12))
         │               │                   ├── key:
@@ -176,6 +181,7 @@
                         │   ├── flags: ∅
                         │   └── arguments: (length: 1)
                         │       └── @ KeywordHashNode (location: (11,9)-(11,13))
+                        │           ├── flags: ∅
                         │           └── elements: (length: 1)
                         │               └── @ AssocNode (location: (11,9)-(11,13))
                         │                   ├── key:
diff --git a/test/prism/snapshots/seattlerb/yield_call_assocs.txt b/test/prism/snapshots/seattlerb/yield_call_assocs.txt
index 559309af..82d1765f 100644
--- a/test/prism/snapshots/seattlerb/yield_call_assocs.txt
+++ b/test/prism/snapshots/seattlerb/yield_call_assocs.txt
@@ -13,6 +13,7 @@
         │   │       ├── @ IntegerNode (location: (1,6)-(1,7))
         │   │       │   └── flags: decimal
         │   │       └── @ KeywordHashNode (location: (1,9)-(1,16))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,9)-(1,16))
         │   │                   ├── key:
@@ -37,6 +38,7 @@
         │   │       ├── @ IntegerNode (location: (3,6)-(3,7))
         │   │       │   └── flags: decimal
         │   │       └── @ KeywordHashNode (location: (3,9)-(3,25))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 2)
         │   │               ├── @ AssocNode (location: (3,9)-(3,16))
         │   │               │   ├── key:
@@ -82,6 +84,7 @@
         │   │           │   ├── flags: ∅
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (5,8)-(5,13))
+        │   │           │           ├── flags: static_keys
         │   │           │           └── elements: (length: 1)
         │   │           │               └── @ AssocNode (location: (5,8)-(5,13))
         │   │           │                   ├── key:
@@ -117,6 +120,7 @@
         │   │           │   ├── flags: ∅
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (7,8)-(7,11))
+        │   │           │           ├── flags: static_keys
         │   │           │           └── elements: (length: 1)
         │   │           │               └── @ AssocNode (location: (7,8)-(7,11))
         │   │           │                   ├── key:
@@ -152,6 +156,7 @@
         │   │           │   ├── flags: ∅
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (9,8)-(9,11))
+        │   │           │           ├── flags: static_keys
         │   │           │           └── elements: (length: 1)
         │   │           │               └── @ AssocNode (location: (9,8)-(9,11))
         │   │           │                   ├── key:
@@ -187,6 +192,7 @@
             │           │   ├── flags: ∅
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (11,8)-(11,12))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 1)
             │           │               └── @ AssocNode (location: (11,8)-(11,12))
             │           │                   ├── key:
diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt
index ccf332a3..e0e27bc5 100644
--- a/test/prism/snapshots/unparser/corpus/literal/send.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/send.txt
@@ -1249,6 +1249,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (63,8)-(63,16))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (63,8)-(63,16))
         │   │                   ├── key:
@@ -1304,6 +1305,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── block: ∅
         │   │       └── @ KeywordHashNode (location: (64,13)-(64,25))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (64,13)-(64,25))
         │   │                   ├── key:
@@ -1567,6 +1569,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (70,4)-(70,8))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (70,4)-(70,8))
         │   │                   ├── key:
@@ -1612,6 +1615,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (71,6)-(71,10))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (71,6)-(71,10))
         │   │                   ├── key:
@@ -1657,6 +1661,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (72,6)-(72,9))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocSplatNode (location: (72,6)-(72,9))
         │   │                   ├── value:
@@ -2073,6 +2078,7 @@
         │   │   ├── flags: contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (81,2)-(81,7))
+        │   │           ├── flags: ∅
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocSplatNode (location: (81,2)-(81,7))
         │   │                   ├── value:
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
index 09bc0e15..85d8e595 100644
--- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
@@ -40,6 +40,7 @@
         │   │           │       │   ├── name: :argument
         │   │           │       │   └── depth: 0
         │   │           │       └── @ KeywordHashNode (location: (2,16)-(2,18))
+        │   │           │           ├── flags: ∅
         │   │           │           └── elements: (length: 1)
         │   │           │               └── @ AssocSplatNode (location: (2,16)-(2,18))
         │   │           │                   ├── value: ∅
diff --git a/test/prism/snapshots/whitequark/args_args_assocs.txt b/test/prism/snapshots/whitequark/args_args_assocs.txt
index 52a3b5c1..d5c0d7d1 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs.txt
@@ -25,6 +25,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── block: ∅
         │   │       └── @ KeywordHashNode (location: (1,9)-(1,18))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,9)-(1,18))
         │   │                   ├── key:
@@ -62,6 +63,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── block: ∅
             │       └── @ KeywordHashNode (location: (3,9)-(3,18))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (3,9)-(3,18))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
index 7523d2d9..2aa16446 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
@@ -35,6 +35,7 @@
             │       │   ├── closing_loc: ∅
             │       │   └── block: ∅
             │       └── @ KeywordHashNode (location: (1,9)-(1,18))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,9)-(1,18))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/args_assocs.txt b/test/prism/snapshots/whitequark/args_assocs.txt
index 31c2827d..bf51dda1 100644
--- a/test/prism/snapshots/whitequark/args_assocs.txt
+++ b/test/prism/snapshots/whitequark/args_assocs.txt
@@ -15,6 +15,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,4)-(1,13))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,4)-(1,13))
         │   │                   ├── key:
@@ -42,6 +43,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (3,4)-(3,13))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (3,4)-(3,13))
         │   │                   ├── key:
@@ -93,6 +95,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── block: ∅
         │   │       └── @ KeywordHashNode (location: (5,14)-(5,21))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (5,14)-(5,21))
         │   │                   ├── key:
@@ -121,6 +124,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (7,5)-(7,14))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (7,5)-(7,14))
         │   │                   ├── key:
@@ -144,6 +148,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (9,6)-(9,16))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (9,6)-(9,16))
         │   │                   ├── key:
@@ -167,6 +172,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (11,6)-(11,16))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (11,6)-(11,16))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_assocs_comma.txt
index 3cd75b74..b98aa39c 100644
--- a/test/prism/snapshots/whitequark/args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_assocs_comma.txt
@@ -25,6 +25,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,13))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,4)-(1,13))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/args_assocs_legacy.txt b/test/prism/snapshots/whitequark/args_assocs_legacy.txt
index 31c2827d..bf51dda1 100644
--- a/test/prism/snapshots/whitequark/args_assocs_legacy.txt
+++ b/test/prism/snapshots/whitequark/args_assocs_legacy.txt
@@ -15,6 +15,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,4)-(1,13))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,4)-(1,13))
         │   │                   ├── key:
@@ -42,6 +43,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (3,4)-(3,13))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (3,4)-(3,13))
         │   │                   ├── key:
@@ -93,6 +95,7 @@
         │   │       │   ├── closing_loc: ∅
         │   │       │   └── block: ∅
         │   │       └── @ KeywordHashNode (location: (5,14)-(5,21))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (5,14)-(5,21))
         │   │                   ├── key:
@@ -121,6 +124,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (7,5)-(7,14))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (7,5)-(7,14))
         │   │                   ├── key:
@@ -144,6 +148,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (9,6)-(9,16))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (9,6)-(9,16))
         │   │                   ├── key:
@@ -167,6 +172,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (11,6)-(11,16))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (11,6)-(11,16))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/array_assocs.txt b/test/prism/snapshots/whitequark/array_assocs.txt
index 0ddd0451..2b374744 100644
--- a/test/prism/snapshots/whitequark/array_assocs.txt
+++ b/test/prism/snapshots/whitequark/array_assocs.txt
@@ -7,6 +7,7 @@
         │   ├── flags: ∅
         │   ├── elements: (length: 1)
         │   │   └── @ KeywordHashNode (location: (1,2)-(1,8))
+        │   │       ├── flags: static_keys
         │   │       └── elements: (length: 1)
         │   │           └── @ AssocNode (location: (1,2)-(1,8))
         │   │               ├── key:
@@ -24,6 +25,7 @@
             │   ├── @ IntegerNode (location: (3,2)-(3,3))
             │   │   └── flags: decimal
             │   └── @ KeywordHashNode (location: (3,5)-(3,11))
+            │       ├── flags: static_keys
             │       └── elements: (length: 1)
             │           └── @ AssocNode (location: (3,5)-(3,11))
             │               ├── key:
diff --git a/test/prism/snapshots/whitequark/bug_cmdarg.txt b/test/prism/snapshots/whitequark/bug_cmdarg.txt
index 4887d808..6779d117 100644
--- a/test/prism/snapshots/whitequark/bug_cmdarg.txt
+++ b/test/prism/snapshots/whitequark/bug_cmdarg.txt
@@ -15,6 +15,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,7)-(1,15))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,7)-(1,15))
         │   │                   ├── key:
@@ -64,6 +65,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (5,2)-(5,26))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (5,2)-(5,26))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
index ebefd06e..ee2ae74c 100644
--- a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
@@ -40,6 +40,7 @@
             │           │       │   ├── name: :argument
             │           │       │   └── depth: 0
             │           │       └── @ KeywordHashNode (location: (1,37)-(1,39))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 1)
             │           │               └── @ AssocSplatNode (location: (1,37)-(1,39))
             │           │                   ├── value: ∅
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
index 8df571db..cb09606e 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
@@ -35,6 +35,7 @@
             │           │   ├── flags: contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,17)-(1,19))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 1)
             │           │               └── @ AssocSplatNode (location: (1,17)-(1,19))
             │           │                   ├── value: ∅
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
index 2dbf5373..10c0cdc4 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
@@ -35,6 +35,7 @@
             │           │   ├── flags: contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,17)-(1,35))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 2)
             │           │               ├── @ AssocSplatNode (location: (1,17)-(1,19))
             │           │               │   ├── value: ∅
diff --git a/test/prism/snapshots/whitequark/keyword_argument_omission.txt b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
index 04f0c87c..3ff3f9a1 100644
--- a/test/prism/snapshots/whitequark/keyword_argument_omission.txt
+++ b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,10))
+            │           ├── flags: static_keys
             │           └── elements: (length: 2)
             │               ├── @ AssocNode (location: (1,4)-(1,6))
             │               │   ├── key:
diff --git a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
index d9c0826f..290d66b0 100644
--- a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
+++ b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
@@ -40,6 +40,7 @@
             │           │   ├── flags: contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,21)-(1,23))
+            │           │           ├── flags: ∅
             │           │           └── elements: (length: 1)
             │           │               └── @ AssocSplatNode (location: (1,21)-(1,23))
             │           │                   ├── value: ∅
diff --git a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
index 46cbb8b1..0e036fbd 100644
--- a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
+++ b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
@@ -93,6 +93,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (10,8)-(11,1))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (10,8)-(11,1))
         │   │                   ├── key:
@@ -130,6 +131,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (13,8)-(14,1))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (13,8)-(14,1))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/parser_bug_525.txt b/test/prism/snapshots/whitequark/parser_bug_525.txt
index bdcf61ab..087cc76e 100644
--- a/test/prism/snapshots/whitequark/parser_bug_525.txt
+++ b/test/prism/snapshots/whitequark/parser_bug_525.txt
@@ -15,6 +15,7 @@
             │   ├── flags: ∅
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,3)-(1,11))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,3)-(1,11))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11380.txt b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
index 59f375e8..cd2939f8 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11380.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
@@ -31,6 +31,7 @@
             │       │               ├── closing_loc: ∅
             │       │               └── unescaped: "hello"
             │       └── @ KeywordHashNode (location: (1,17)-(1,21))
+            │           ├── flags: static_keys
             │           └── elements: (length: 1)
             │               └── @ AssocNode (location: (1,17)-(1,21))
             │                   ├── key:
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12073.txt b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
index 86be1100..521df7a0 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_12073.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
@@ -23,6 +23,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,9)-(1,13))
+        │   │           ├── flags: static_keys
         │   │           └── elements: (length: 1)
         │   │               └── @ AssocNode (location: (1,9)-(1,13))
         │   │                   ├── key: