Repository: ruby/prism
Pull request #2766 (2024-05-03 16:15:15)

Disallow keywords and blocks in index expressions in 3.4+
Fixes https://github.com/ruby/prism/issues/2438


Patch:
diff --git a/config.yml b/config.yml
index 9ef9eddc..4c5228ae 100644
--- a/config.yml
+++ b/config.yml
@@ -250,6 +250,8 @@ errors:
   - UNARY_RECEIVER
   - UNDEF_ARGUMENT
   - UNEXPECTED_BLOCK_ARGUMENT
+  - UNEXPECTED_INDEX_BLOCK
+  - UNEXPECTED_INDEX_KEYWORDS
   - UNEXPECTED_TOKEN_CLOSE_CONTEXT
   - UNEXPECTED_TOKEN_IGNORE
   - UNTIL_TERM
@@ -620,6 +622,8 @@ tokens:
 flags:
   - name: ArgumentsNodeFlags
     values:
+      - name: CONTAINS_KEYWORDS
+        comment: "if arguments contain keywords"
       - name: CONTAINS_KEYWORD_SPLAT
         comment: "if arguments contain keyword splat"
     comment: Flags for arguments nodes.
diff --git a/src/prism.c b/src/prism.c
index 859b5a28..316cb7be 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -2943,6 +2943,29 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const
     return node;
 }
 
+/**
+ * Validate that index expressions do not have keywords or blocks if we are
+ * parsing as Ruby 3.4+.
+ */
+static void
+pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
+    if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) {
+        if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
+            pm_node_t *node;
+            PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
+                if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
+                    pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
+                    break;
+                }
+            }
+        }
+
+        if (block != NULL) {
+            pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
+        }
+    }
+}
+
 /**
  * Allocate and initialize a new IndexAndWriteNode node.
  */
@@ -2951,6 +2974,8 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons
     assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
     pm_index_and_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_and_write_node_t);
 
+    pm_index_arguments_check(parser, target->arguments, target->block);
+
     *node = (pm_index_and_write_node_t) {
         {
             .type = PM_INDEX_AND_WRITE_NODE,
@@ -3022,6 +3047,8 @@ static pm_index_operator_write_node_t *
 pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
     pm_index_operator_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_operator_write_node_t);
 
+    pm_index_arguments_check(parser, target->arguments, target->block);
+
     *node = (pm_index_operator_write_node_t) {
         {
             .type = PM_INDEX_OPERATOR_WRITE_NODE,
@@ -3095,6 +3122,8 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const
     assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
     pm_index_or_write_node_t *node = PM_ALLOC_NODE(parser, pm_index_or_write_node_t);
 
+    pm_index_arguments_check(parser, target->arguments, target->block);
+
     *node = (pm_index_or_write_node_t) {
         {
             .type = PM_INDEX_OR_WRITE_NODE,
@@ -3159,6 +3188,8 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
     pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t);
     pm_node_flags_t flags = target->base.flags;
 
+    pm_index_arguments_check(parser, target->arguments, target->block);
+
     *node = (pm_index_target_node_t) {
         {
             .type = PM_INDEX_TARGET_NODE,
@@ -13733,9 +13764,10 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
                 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, (pm_node_t *) hash);
 
                 parse_arguments_append(parser, arguments, argument);
-                if (contains_keyword_splat) {
-                    pm_node_flag_set((pm_node_t *) arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
-                }
+
+                pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
+                if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
+                pm_node_flag_set((pm_node_t *) arguments->arguments, flags);
 
                 pm_static_literals_free(&hash_keys);
                 parsed_bare_hash = true;
@@ -13813,7 +13845,9 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
                     argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, PM_ERR_EXPECT_ARGUMENT);
                 }
 
+                bool contains_keywords = false;
                 bool contains_keyword_splat = false;
+
                 if (pm_symbol_node_label_p(argument) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
                     if (parsed_bare_hash) {
                         pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
@@ -13827,6 +13861,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
                     }
 
                     pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
+                    contains_keywords = true;
 
                     // Create the set of static literals for this hash.
                     pm_static_literals_t hash_keys = { 0 };
@@ -13855,9 +13890,12 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
                 }
 
                 parse_arguments_append(parser, arguments, argument);
-                if (contains_keyword_splat) {
-                    pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
-                }
+
+                pm_node_flags_t flags = 0;
+                if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
+                if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
+                pm_node_flag_set((pm_node_t *) arguments->arguments, flags);
+
                 break;
             }
         }
diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb
index 6a93ba58..ea860265 100644
--- a/templates/src/diagnostic.c.erb
+++ b/templates/src/diagnostic.c.erb
@@ -332,6 +332,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
     [PM_ERR_UNDEF_ARGUMENT]                     = { "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", PM_ERROR_LEVEL_SYNTAX },
     [PM_ERR_UNARY_RECEIVER]                     = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX },
     [PM_ERR_UNEXPECTED_BLOCK_ARGUMENT]          = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX },
+    [PM_ERR_UNEXPECTED_INDEX_BLOCK]             = { "unexpected block arg given in index; blocks are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
+    [PM_ERR_UNEXPECTED_INDEX_KEYWORDS]          = { "unexpected keyword arg given in index; keywords are not allowed in index expressions", PM_ERROR_LEVEL_SYNTAX },
     [PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT]     = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX },
     [PM_ERR_UNEXPECTED_TOKEN_IGNORE]            = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX },
     [PM_ERR_UNTIL_TERM]                         = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX },


Test patch:
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index 4d35d057..280bf63a 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -378,10 +378,13 @@ module Prism
         :a,
         Location(),
         Location(),
-        ArgumentsNode(1, [
-          KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
-          SplatNode(Location(), expression("args"))
-        ]),
+        ArgumentsNode(
+          ArgumentsNodeFlags::CONTAINS_KEYWORDS | ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT,
+          [
+            KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
+            SplatNode(Location(), expression("args"))
+          ]
+        ),
         Location(),
         nil
       )
@@ -425,7 +428,7 @@ module Prism
         :a,
         Location(),
         Location(),
-        ArgumentsNode(0, [
+        ArgumentsNode(ArgumentsNodeFlags::CONTAINS_KEYWORDS, [
           KeywordHashNode(1, [
             AssocNode(
               SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"),
diff --git a/test/prism/index_write_test.rb b/test/prism/index_write_test.rb
index 4c387da1..cf90eb08 100644
--- a/test/prism/index_write_test.rb
+++ b/test/prism/index_write_test.rb
@@ -40,41 +40,41 @@ module Prism
       RUBY
     end
 
-    # def test_keywords_latest
-    #   assert_parse_failure(<<~RUBY)
-    #     foo[bar: 1] = 1
-    #     foo[bar: 1] &&= 1
-    #     foo[bar: 1] ||= 1
-    #     foo[bar: 1] += 1
-    #   RUBY
+    def test_keywords_latest
+      assert_parse_failure(<<~RUBY)
+        foo[bar: 1] = 1
+        foo[bar: 1] &&= 1
+        foo[bar: 1] ||= 1
+        foo[bar: 1] += 1
+      RUBY
 
-    #   assert_parse_failure(<<~RUBY)
-    #     def foo(**)
-    #       bar[**] = 1
-    #       bar[**] &&= 1
-    #       bar[**] ||= 1
-    #       bar[**] += 1
-    #     end
-    #   RUBY
-    # end
+      assert_parse_failure(<<~RUBY)
+        def foo(**)
+          bar[**] = 1
+          bar[**] &&= 1
+          bar[**] ||= 1
+          bar[**] += 1
+        end
+      RUBY
+    end
 
-    # def test_block_latest
-    #   assert_parse_failure(<<~RUBY)
-    #     foo[&bar] = 1
-    #     foo[&bar] &&= 1
-    #     foo[&bar] ||= 1
-    #     foo[&bar] += 1
-    #   RUBY
+    def test_block_latest
+      assert_parse_failure(<<~RUBY)
+        foo[&bar] = 1
+        foo[&bar] &&= 1
+        foo[&bar] ||= 1
+        foo[&bar] += 1
+      RUBY
 
-    #   assert_parse_failure(<<~RUBY)
-    #     def foo(&)
-    #       bar[&] = 1
-    #       bar[&] &&= 1
-    #       bar[&] ||= 1
-    #       bar[&] += 1
-    #     end
-    #   RUBY
-    # end
+      assert_parse_failure(<<~RUBY)
+        def foo(&)
+          bar[&] = 1
+          bar[&] &&= 1
+          bar[&] ||= 1
+          bar[&] += 1
+        end
+      RUBY
+    end
 
     private
 
diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb
index 24976f48..07249956 100644
--- a/test/prism/location_test.rb
+++ b/test/prism/location_test.rb
@@ -477,7 +477,7 @@ module Prism
     end
 
     def test_IndexTargetNode
-      assert_location(IndexTargetNode, "foo[bar, &baz], = qux", 0...14) do |node|
+      assert_location(IndexTargetNode, "foo[bar], = qux", 0...8) do |node|
         node.lefts.first
       end
     end
diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt
index 89399b55..12518336 100644
--- a/test/prism/snapshots/constants.txt
+++ b/test/prism/snapshots/constants.txt
@@ -112,7 +112,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (17,4)-(17,9))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (17,4)-(17,9))
         │   │           ├── flags: ∅
@@ -194,7 +194,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (23,9)-(23,14))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (23,9)-(23,14))
         │   │           ├── flags: ∅
diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt
index 31c33d36..4114d227 100644
--- a/test/prism/snapshots/if.txt
+++ b/test/prism/snapshots/if.txt
@@ -307,7 +307,7 @@
         │   │           ├── opening_loc: ∅
         │   │           ├── arguments:
         │   │           │   @ ArgumentsNode (location: (25,4)-(25,6))
-        │   │           │   ├── flags: ∅
+        │   │           │   ├── flags: contains_keywords
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (25,4)-(25,6))
         │   │           │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt
index 4efc8106..6082b567 100644
--- a/test/prism/snapshots/method_calls.txt
+++ b/test/prism/snapshots/method_calls.txt
@@ -308,7 +308,7 @@
         │   ├── opening_loc: (27,1)-(27,2) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (27,2)-(27,10))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (27,2)-(27,10))
         │   │           ├── flags: ∅
@@ -779,7 +779,7 @@
         │   ├── opening_loc: (60,3)-(60,4) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (60,4)-(60,32))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ SymbolNode (location: (60,4)-(60,6))
         │   │       │   ├── flags: forced_us_ascii_encoding
@@ -912,7 +912,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (64,4)-(64,15))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ SymbolNode (location: (64,4)-(64,6))
         │   │       │   ├── flags: forced_us_ascii_encoding
@@ -988,7 +988,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (66,3)-(66,17))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (66,3)-(66,17))
         │   │           ├── flags: symbol_keys
@@ -1020,7 +1020,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (68,3)-(68,40))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (68,3)-(68,40))
         │   │           ├── flags: ∅
@@ -1075,7 +1075,7 @@
         │   ├── opening_loc: (70,2)-(70,3) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (70,3)-(70,40))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (70,3)-(70,40))
         │   │           ├── flags: ∅
@@ -1178,7 +1178,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (74,3)-(74,20))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (74,3)-(74,20))
         │   │           ├── flags: symbol_keys
@@ -1235,7 +1235,7 @@
         │   ├── opening_loc: (80,3)-(80,4) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (81,0)-(82,5))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ SymbolNode (location: (81,0)-(81,2))
         │   │       │   ├── flags: forced_us_ascii_encoding
@@ -1292,7 +1292,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (87,4)-(87,21))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (87,4)-(87,21))
         │   │           ├── flags: symbol_keys
@@ -1339,7 +1339,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (89,10)-(89,21))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ IntegerNode (location: (89,10)-(89,11))
         │   │       │   ├── flags: decimal
@@ -1529,7 +1529,7 @@
         │   ├── opening_loc: (103,3)-(103,4) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (103,4)-(103,11))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (103,4)-(103,11))
         │   │           ├── flags: symbol_keys
@@ -1558,7 +1558,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (105,4)-(105,28))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (105,4)-(105,28))
         │   │           ├── flags: symbol_keys
@@ -1614,7 +1614,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (107,4)-(107,24))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (107,4)-(107,24))
         │   │           ├── flags: symbol_keys
@@ -2414,7 +2414,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (156,5)-(156,19))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (156,5)-(156,19))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt
index 76c03618..b3864039 100644
--- a/test/prism/snapshots/methods.txt
+++ b/test/prism/snapshots/methods.txt
@@ -1296,7 +1296,7 @@
         │   │           ├── opening_loc: ∅
         │   │           ├── arguments:
         │   │           │   @ ArgumentsNode (location: (139,11)-(139,30))
-        │   │           │   ├── flags: contains_keyword_splat
+        │   │           │   ├── flags: contains_keywords, contains_keyword_splat
         │   │           │   └── arguments: (length: 1)
         │   │           │       └── @ KeywordHashNode (location: (139,11)-(139,30))
         │   │           │           ├── flags: ∅
diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt
index 2bdbfdaf..390b08ae 100644
--- a/test/prism/snapshots/rescue.txt
+++ b/test/prism/snapshots/rescue.txt
@@ -380,7 +380,7 @@
         │   │   │           ├── opening_loc: ∅
         │   │   │           ├── arguments:
         │   │   │           │   @ ArgumentsNode (location: (29,4)-(29,6))
-        │   │   │           │   ├── flags: ∅
+        │   │   │           │   ├── flags: contains_keywords
         │   │   │           │   └── arguments: (length: 1)
         │   │   │           │       └── @ KeywordHashNode (location: (29,4)-(29,6))
         │   │   │           │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt
index 923f5450..70490c0d 100644
--- a/test/prism/snapshots/seattlerb/assoc_label.txt
+++ b/test/prism/snapshots/seattlerb/assoc_label.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,5))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt
index 569bea14..ad61501a 100644
--- a/test/prism/snapshots/seattlerb/bug_249.txt
+++ b/test/prism/snapshots/seattlerb/bug_249.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (1,6)-(4,28))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ CallNode (location: (1,6)-(4,9))
             │       │   ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt
index 6f17e887..e138db4d 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,3)-(1,4) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,18))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ SymbolNode (location: (1,4)-(1,8))
             │       │   ├── flags: forced_us_ascii_encoding
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 e7256b33..fe2d7f73 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,3)-(1,4) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,18))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ SymbolNode (location: (1,4)-(1,8))
             │       │   ├── flags: forced_us_ascii_encoding
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
index 27c19fd3..f489bc7f 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,9))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
index 0193eb1d..5b191396 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,15))
-            │   ├── flags: contains_keyword_splat
+            │   ├── flags: contains_keywords, contains_keyword_splat
             │   └── arguments: (length: 2)
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
index 91c77255..f95b80cf 100644
--- a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,8))
-            │   ├── flags: contains_keyword_splat
+            │   ├── flags: contains_keywords, contains_keyword_splat
             │   └── arguments: (length: 2)
             │       ├── @ CallNode (location: (1,2)-(1,3))
             │       │   ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
index 2d6f81c8..8946206a 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
@@ -12,7 +12,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (1,2)-(1,11))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,2)-(1,11))
         │   │           ├── flags: ∅
@@ -55,7 +55,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (3,2)-(3,8))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (3,2)-(3,8))
         │   │           ├── flags: symbol_keys
@@ -84,7 +84,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (5,2)-(5,8))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (5,2)-(5,8))
             │           ├── flags: symbol_keys
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 312a1981..0ba5891c 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,9))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ IntegerNode (location: (1,2)-(1,3))
             │       │   ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt
index 438c2565..60784e60 100644
--- a/test/prism/snapshots/seattlerb/call_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,6))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt
index b4d7e0bf..dc25fb24 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,5))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
             │           ├── flags: symbol_keys
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 9587e2e0..b3d652e8 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(5,3))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(5,3))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
index 8d0b2851..b2012f0f 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,6))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt
index 4199e97a..e0620dc5 100644
--- a/test/prism/snapshots/seattlerb/call_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,1)-(1,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,5))
-            │   ├── flags: contains_keyword_splat
+            │   ├── flags: contains_keywords, contains_keyword_splat
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,5))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
index f420420f..2aadedd9 100644
--- a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
@@ -33,7 +33,7 @@
             │           ├── opening_loc: (1,30)-(1,31) = "("
             │           ├── arguments:
             │           │   @ ArgumentsNode (location: (1,31)-(1,40))
-            │           │   ├── flags: contains_keyword_splat
+            │           │   ├── flags: contains_keywords, contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,31)-(1,40))
             │           │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt
index a9b3736f..b53d4cad 100644
--- a/test/prism/snapshots/seattlerb/difficult2_.txt
+++ b/test/prism/snapshots/seattlerb/difficult2_.txt
@@ -52,7 +52,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (2,2)-(2,6))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (2,2)-(2,6))
             │           ├── flags: symbol_keys
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 da10a474..1bb8bd0b 100644
--- a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
@@ -22,7 +22,7 @@
             ├── opening_loc: (1,3)-(1,4) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,8))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,8))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
index 79b0ef5d..ff28a179 100644
--- a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
+++ b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
@@ -12,7 +12,7 @@
         │   ├── opening_loc: (1,1)-(1,2) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (1,2)-(3,1))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,2)-(3,1))
         │   │           ├── flags: symbol_keys
@@ -42,7 +42,7 @@
         │   ├── opening_loc: (5,1)-(5,2) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (5,2)-(6,1))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (5,2)-(6,1))
         │   │           ├── flags: symbol_keys
@@ -72,7 +72,7 @@
             ├── opening_loc: (8,1)-(8,2) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (8,2)-(8,11))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (8,2)-(8,11))
             │           ├── flags: symbol_keys
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 b767a5c1..dc11e2ca 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
@@ -15,7 +15,7 @@
             ├── opening_loc: (1,1)-(1,2) = "["
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,6))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,2)-(1,6))
             │           ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
index 64caf51b..bbc19d50 100644
--- a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
+++ b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (1,5)-(1,12))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,5)-(1,12))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/return_call_assocs.txt b/test/prism/snapshots/seattlerb/return_call_assocs.txt
index 66b3cb2f..8948f787 100644
--- a/test/prism/snapshots/seattlerb/return_call_assocs.txt
+++ b/test/prism/snapshots/seattlerb/return_call_assocs.txt
@@ -8,7 +8,7 @@
         │   ├── keyword_loc: (1,0)-(1,6) = "return"
         │   └── arguments:
         │       @ ArgumentsNode (location: (1,7)-(1,17))
-        │       ├── flags: ∅
+        │       ├── flags: contains_keywords
         │       └── arguments: (length: 2)
         │           ├── @ IntegerNode (location: (1,7)-(1,8))
         │           │   ├── flags: decimal
@@ -34,7 +34,7 @@
         │   ├── keyword_loc: (3,0)-(3,6) = "return"
         │   └── arguments:
         │       @ ArgumentsNode (location: (3,7)-(3,26))
-        │       ├── flags: ∅
+        │       ├── flags: contains_keywords
         │       └── arguments: (length: 2)
         │           ├── @ IntegerNode (location: (3,7)-(3,8))
         │           │   ├── flags: decimal
@@ -84,7 +84,7 @@
         │               ├── opening_loc: ∅
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (5,9)-(5,14))
-        │               │   ├── flags: ∅
+        │               │   ├── flags: contains_keywords
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (5,9)-(5,14))
         │               │           ├── flags: symbol_keys
@@ -120,7 +120,7 @@
         │               ├── opening_loc: ∅
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (7,9)-(7,12))
-        │               │   ├── flags: ∅
+        │               │   ├── flags: contains_keywords
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (7,9)-(7,12))
         │               │           ├── flags: symbol_keys
@@ -156,7 +156,7 @@
         │               ├── opening_loc: (9,8)-(9,9) = "("
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (9,9)-(9,12))
-        │               │   ├── flags: ∅
+        │               │   ├── flags: contains_keywords
         │               │   └── arguments: (length: 1)
         │               │       └── @ KeywordHashNode (location: (9,9)-(9,12))
         │               │           ├── flags: symbol_keys
@@ -192,7 +192,7 @@
                         ├── opening_loc: (11,8)-(11,9) = "("
                         ├── arguments:
                         │   @ ArgumentsNode (location: (11,9)-(11,13))
-                        │   ├── flags: ∅
+                        │   ├── flags: contains_keywords
                         │   └── arguments: (length: 1)
                         │       └── @ KeywordHashNode (location: (11,9)-(11,13))
                         │           ├── flags: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt
index b7eb0647..3fd7f719 100644
--- a/test/prism/snapshots/unparser/corpus/literal/send.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/send.txt
@@ -1251,7 +1251,7 @@
         │   ├── opening_loc: (63,7)-(63,8) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (63,8)-(63,16))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (63,8)-(63,16))
         │   │           ├── flags: symbol_keys
@@ -1297,7 +1297,7 @@
         │   ├── opening_loc: (64,7)-(64,8) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (64,8)-(64,25))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ CallNode (location: (64,8)-(64,11))
         │   │       │   ├── flags: variable_call, ignore_visibility
@@ -1571,7 +1571,7 @@
         │   ├── opening_loc: (70,3)-(70,4) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (70,4)-(70,8))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (70,4)-(70,8))
         │   │           ├── flags: symbol_keys
@@ -1617,7 +1617,7 @@
         │   ├── opening_loc: (71,5)-(71,6) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (71,6)-(71,10))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (71,6)-(71,10))
         │   │           ├── flags: symbol_keys
@@ -1663,7 +1663,7 @@
         │   ├── opening_loc: (72,5)-(72,6) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (72,6)-(72,9))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (72,6)-(72,9))
         │   │           ├── flags: ∅
@@ -2082,7 +2082,7 @@
         │   ├── opening_loc: (81,1)-(81,2) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (81,2)-(81,7))
-        │   │   ├── flags: contains_keyword_splat
+        │   │   ├── flags: contains_keywords, contains_keyword_splat
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (81,2)-(81,7))
         │   │           ├── flags: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
index e72be6d8..2b28be2f 100644
--- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
@@ -36,7 +36,7 @@
         │   │           ├── opening_loc: (2,5)-(2,6) = "("
         │   │           ├── arguments:
         │   │           │   @ ArgumentsNode (location: (2,6)-(2,18))
-        │   │           │   ├── flags: contains_keyword_splat
+        │   │           │   ├── flags: contains_keywords, contains_keyword_splat
         │   │           │   └── arguments: (length: 2)
         │   │           │       ├── @ LocalVariableReadNode (location: (2,6)-(2,14))
         │   │           │       │   ├── name: :argument
diff --git a/test/prism/snapshots/whitequark/args_args_assocs.txt b/test/prism/snapshots/whitequark/args_args_assocs.txt
index 6297f212..d257a885 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs.txt
@@ -12,7 +12,7 @@
         │   ├── opening_loc: (1,3)-(1,4) = "("
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (1,4)-(1,18))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 2)
         │   │       ├── @ CallNode (location: (1,4)-(1,7))
         │   │       │   ├── flags: variable_call, ignore_visibility
@@ -51,7 +51,7 @@
             ├── opening_loc: (3,3)-(3,4) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (3,4)-(3,18))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ CallNode (location: (3,4)-(3,7))
             │       │   ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
index 969514a5..2d986dd9 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
@@ -22,7 +22,7 @@
             ├── opening_loc: (1,3)-(1,4) = "["
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,18))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ CallNode (location: (1,4)-(1,7))
             │       │   ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/whitequark/args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_assocs_comma.txt
index b1b9fbee..64a25bbc 100644
--- a/test/prism/snapshots/whitequark/args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_assocs_comma.txt
@@ -22,7 +22,7 @@
             ├── opening_loc: (1,3)-(1,4) = "["
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,13))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,13))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/bug_cmdarg.txt b/test/prism/snapshots/whitequark/bug_cmdarg.txt
index 509dd7e8..32d05746 100644
--- a/test/prism/snapshots/whitequark/bug_cmdarg.txt
+++ b/test/prism/snapshots/whitequark/bug_cmdarg.txt
@@ -12,7 +12,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (1,7)-(1,15))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,7)-(1,15))
         │   │           ├── flags: symbol_keys
@@ -62,7 +62,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (5,2)-(5,26))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (5,2)-(5,26))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
index c06818a9..acaf9c05 100644
--- a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
@@ -36,7 +36,7 @@
             │           ├── opening_loc: (1,26)-(1,27) = "("
             │           ├── arguments:
             │           │   @ ArgumentsNode (location: (1,27)-(1,39))
-            │           │   ├── flags: contains_keyword_splat
+            │           │   ├── flags: contains_keywords, contains_keyword_splat
             │           │   └── arguments: (length: 2)
             │           │       ├── @ LocalVariableReadNode (location: (1,27)-(1,35))
             │           │       │   ├── name: :argument
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
index adaf111f..b4235fb2 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
@@ -33,7 +33,7 @@
             │           ├── opening_loc: (1,16)-(1,17) = "("
             │           ├── arguments:
             │           │   @ ArgumentsNode (location: (1,17)-(1,19))
-            │           │   ├── flags: contains_keyword_splat
+            │           │   ├── flags: contains_keywords, contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,17)-(1,19))
             │           │           ├── flags: ∅
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 a0342145..33779abc 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
@@ -33,7 +33,7 @@
             │           ├── opening_loc: (1,16)-(1,17) = "("
             │           ├── arguments:
             │           │   @ ArgumentsNode (location: (1,17)-(1,35))
-            │           │   ├── flags: contains_keyword_splat
+            │           │   ├── flags: contains_keywords, contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,17)-(1,35))
             │           │           ├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/keyword_argument_omission.txt b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
index 62e8fecf..446b45b5 100644
--- a/test/prism/snapshots/whitequark/keyword_argument_omission.txt
+++ b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: (1,3)-(1,4) = "("
             ├── arguments:
             │   @ ArgumentsNode (location: (1,4)-(1,10))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,4)-(1,10))
             │           ├── flags: symbol_keys
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 6d0bdfb8..db281e2f 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
@@ -39,7 +39,7 @@
             │           ├── opening_loc: (1,20)-(1,21) = "("
             │           ├── arguments:
             │           │   @ ArgumentsNode (location: (1,21)-(1,23))
-            │           │   ├── flags: contains_keyword_splat
+            │           │   ├── flags: contains_keywords, contains_keyword_splat
             │           │   └── arguments: (length: 1)
             │           │       └── @ KeywordHashNode (location: (1,21)-(1,23))
             │           │           ├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
index d5e89d87..7ef00664 100644
--- a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
+++ b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
@@ -102,7 +102,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (10,8)-(11,1))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (10,8)-(11,1))
         │   │           ├── flags: symbol_keys
@@ -141,7 +141,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (13,8)-(14,1))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (13,8)-(14,1))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/parser_bug_525.txt b/test/prism/snapshots/whitequark/parser_bug_525.txt
index a69b8a20..3a31a97c 100644
--- a/test/prism/snapshots/whitequark/parser_bug_525.txt
+++ b/test/prism/snapshots/whitequark/parser_bug_525.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (1,3)-(1,11))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 1)
             │       └── @ KeywordHashNode (location: (1,3)-(1,11))
             │           ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11380.txt b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
index b7a7ef9a..d5ec10b0 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11380.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
@@ -12,7 +12,7 @@
             ├── opening_loc: ∅
             ├── arguments:
             │   @ ArgumentsNode (location: (1,2)-(1,21))
-            │   ├── flags: ∅
+            │   ├── flags: contains_keywords
             │   └── arguments: (length: 2)
             │       ├── @ LambdaNode (location: (1,2)-(1,15))
             │       │   ├── locals: []
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12073.txt b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
index eb7d5f1e..2b4d3eab 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_12073.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
@@ -21,7 +21,7 @@
         │   ├── opening_loc: ∅
         │   ├── arguments:
         │   │   @ ArgumentsNode (location: (1,9)-(1,13))
-        │   │   ├── flags: ∅
+        │   │   ├── flags: contains_keywords
         │   │   └── arguments: (length: 1)
         │   │       └── @ KeywordHashNode (location: (1,9)-(1,13))
         │   │           ├── flags: symbol_keys