Repository: ruby/prism
Pull request #2049 (2023-12-11 22:33:50)

Add public macro and internal functions for working with node flags
The current way of setting/unsetting and testing flags on node was very verbose, and, as a result, was error prone.

This PR introduces the public macro `PM_NODE_FLAG_P(node, flag)` (exported from `ast.h`) to check if `flag` is set on `node`.

It also adds two methods in `prism.c` to set/unset flags on nodes: `pm_node_flag_set` and `pm_node_flag_unset`.

These make the code easier to read and less error-prone.

paracycle: > This looks good, but can you rebase first with the keyword hash changes and use the macro?

Yeap, that was what I was intending to do. Thanks for the review.

Patch:
diff --git a/config.yml b/config.yml
index d3b6b087..7e71e4e5 100644
--- a/config.yml
+++ b/config.yml
@@ -345,8 +345,6 @@ flags:
         comment: "&. operator"
       - name: VARIABLE_CALL
         comment: "a call that could have been a local variable"
-      - name: ATTRIBUTE_WRITE
-        comment: "a call that is an attribute write, so the value being written should be returned"
     comment: Flags for call nodes.
   - name: EncodingFlags
     values:
diff --git a/src/prism.c b/src/prism.c
index 2b74152c..5a6bef3e 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -864,6 +864,27 @@ pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_b
     pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
 }
 
+/******************************************************************************/
+/* Node flag handling functions                                               */
+/******************************************************************************/
+
+/**
+ * Set the given flag on the given node.
+ */
+static inline void
+pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
+    node->flags |= flag;
+}
+
+/**
+ * Remove the given flag from the given node.
+ */
+static inline void
+pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
+    node->flags &= (pm_node_flags_t) ~flag;
+}
+
+
 /******************************************************************************/
 /* Node creation functions                                                    */
 /******************************************************************************/
@@ -1152,12 +1173,12 @@ pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) {
 
     // If the element is not a static literal, then the array is not a static
     // literal. Turn that flag off.
-    if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || (element->flags & PM_NODE_FLAG_STATIC_LITERAL) == 0) {
-        node->base.flags &= (pm_node_flags_t) ~PM_NODE_FLAG_STATIC_LITERAL;
+    if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *)node, PM_NODE_FLAG_STATIC_LITERAL);
     }
 
     if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
-        node->base.flags |= PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT;
+        pm_node_flag_set((pm_node_t *)node, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
     }
 }
 
@@ -1720,7 +1741,7 @@ pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *o
     node->block = arguments->block;
 
     if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
-        node->base.flags |= PM_CALL_NODE_FLAGS_SAFE_NAVIGATION;
+        pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
     }
 
     node->name = pm_parser_constant_id_token(parser, message);
@@ -1794,7 +1815,7 @@ pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token
     node->block = arguments->block;
 
     if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
-        node->base.flags |= PM_CALL_NODE_FLAGS_SAFE_NAVIGATION;
+        pm_node_flag_set((pm_node_t *)node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
     }
 
     node->name = pm_parser_constant_id_constant(parser, "call", 4);
@@ -1841,7 +1862,7 @@ pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
  */
 static inline bool
 pm_call_node_variable_call_p(pm_call_node_t *node) {
-    return node->base.flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL;
+    return PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_VARIABLE_CALL);
 }
 
 /**
@@ -3329,8 +3350,8 @@ pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) {
 
     // If the element is not a static literal, then the hash is not a static
     // literal. Turn that flag off.
-    if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || (element->flags & PM_NODE_FLAG_STATIC_LITERAL) == 0) {
-        hash->base.flags &= (pm_node_flags_t) ~PM_NODE_FLAG_STATIC_LITERAL;
+    if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *)hash, PM_NODE_FLAG_STATIC_LITERAL);
     }
 }
 
@@ -3787,7 +3808,7 @@ static inline void
 pm_interpolated_regular_expression_node_closing_set(pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
     node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
     node->base.location.end = closing->end;
-    node->base.flags |= pm_regular_expression_flags_create(closing);
+    pm_node_flag_set((pm_node_t *)node, pm_regular_expression_flags_create(closing));
 }
 
 /**
@@ -3937,8 +3958,8 @@ 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;
+    if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_FLAG_P(((pm_assoc_node_t *) element)->key, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *)hash, PM_KEYWORD_HASH_NODE_FLAGS_STATIC_KEYS);
     }
 
     pm_node_list_append(&hash->elements, element);
@@ -5261,7 +5282,7 @@ pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement)
     pm_node_list_append(&node->body, statement);
 
     // Every statement gets marked as a place where a newline can occur.
-    statement->flags |= PM_NODE_FLAG_NEWLINE;
+    pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
 }
 
 /**
@@ -10856,7 +10877,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
                     call->base.location.end = arguments->base.location.end;
 
                     parse_write_name(parser, &call->name);
-                    call->base.flags |= PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE;
                     return (pm_node_t *) call;
                 }
             }
@@ -10874,7 +10894,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
 
                 // Replace the name with "[]=".
                 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
-                call->base.flags |= PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE;
                 return target;
             }
 
@@ -11194,7 +11213,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
                 parsed_bare_hash = true;
                 parse_arguments_append(parser, arguments, argument);
                 if (contains_keyword_splat) {
-                    arguments->arguments->base.flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
+                    pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
                 }
                 break;
             }
@@ -11314,7 +11333,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
 
                 parse_arguments_append(parser, arguments, argument);
                 if (contains_keyword_splat) {
-                    arguments->arguments->base.flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
+                    pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
                 }
                 break;
             }
@@ -12382,7 +12401,7 @@ parse_string_part(pm_parser_t *parser) {
             pm_token_t closing = not_provided(parser);
 
             pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
-            node->flags |= parse_unescaped_encoding(parser);
+            pm_node_flag_set(node, parse_unescaped_encoding(parser));
 
             parser_lex(parser);
             return node;
@@ -12767,7 +12786,7 @@ parse_variable_call(pm_parser_t *parser) {
     }
 
     pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
-    node->base.flags |= flags;
+    pm_node_flag_set((pm_node_t *)node, flags);
 
     return (pm_node_t *) node;
 }
@@ -13701,7 +13720,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
 
             if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
                 node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
-                node->flags |= parse_unescaped_encoding(parser);
+                pm_node_flag_set(node, parse_unescaped_encoding(parser));
                 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM);
             } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
                 node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
@@ -13713,7 +13732,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
                 pm_token_t string_closing = not_provided(parser);
 
                 pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped);
-                part->flags |= parse_unescaped_encoding(parser);
+                pm_node_flag_set(part, parse_unescaped_encoding(parser));
                 pm_node_list_append(&parts, part);
 
                 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
@@ -14047,7 +14066,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
 
             pm_token_t closing = not_provided(parser);
             pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing);
-            node->flags |= parse_unescaped_encoding(parser);
+            pm_node_flag_set(node, parse_unescaped_encoding(parser));
 
             // Characters can be followed by strings in which case they are
             // automatically concatenated.
@@ -14175,7 +14194,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
                 if (parse_arguments_list(parser, &arguments, true, accepts_command_call)) {
                     // Since we found arguments, we need to turn off the
                     // variable call bit in the flags.
-                    call->base.flags &= (pm_node_flags_t) ~PM_CALL_NODE_FLAGS_VARIABLE_CALL;
+                    pm_node_flag_unset((pm_node_t *)call, PM_CALL_NODE_FLAGS_VARIABLE_CALL);
 
                     call->opening_loc = arguments.opening_loc;
                     call->arguments = arguments.arguments;
@@ -14255,7 +14274,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
                 // content and we're at the end of the heredoc, so we can return
                 // just a string node with the heredoc opening and closing as
                 // its opening and closing.
-                part->flags |= parse_unescaped_encoding(parser);
+                pm_node_flag_set(part, parse_unescaped_encoding(parser));
                 pm_string_node_t *cast = (pm_string_node_t *) part;
 
                 cast->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
@@ -15610,7 +15629,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
                         pm_token_t closing = not_provided(parser);
 
                         pm_node_t *string = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &parser->current, &closing);
-                        string->flags |= parse_unescaped_encoding(parser);
+                        pm_node_flag_set(string, parse_unescaped_encoding(parser));
                         parser_lex(parser);
 
                         if (current == NULL) {
@@ -15829,7 +15848,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
 
                 if (match1(parser, PM_TOKEN_STRING_END)) {
                     pm_node_t *node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
-                    node->flags |= parse_unescaped_encoding(parser);
+                    pm_node_flag_set(node, parse_unescaped_encoding(parser));
                     parser_lex(parser);
                     return node;
                 }
@@ -15842,7 +15861,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
                 pm_token_t closing = not_provided(parser);
 
                 pm_node_t *part = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped);
-                part->flags |= parse_unescaped_encoding(parser);
+                pm_node_flag_set(part, parse_unescaped_encoding(parser));
 
                 pm_interpolated_xstring_node_append(node, part);
             } else {
diff --git a/templates/include/prism/ast.h.erb b/templates/include/prism/ast.h.erb
index 8670dc3d..c169fedf 100644
--- a/templates/include/prism/ast.h.erb
+++ b/templates/include/prism/ast.h.erb
@@ -116,6 +116,11 @@ static const pm_node_flags_t PM_NODE_FLAG_COMMON_MASK = (1 << (PM_NODE_FLAG_BITS
  */
 #define PM_NODE_TYPE_P(node, type) (PM_NODE_TYPE(node) == (type))
 
+/**
+ * Return true if the given flag is set on the given node.
+ */
+#define PM_NODE_FLAG_P(node, flag) ((((pm_node_t *)(node))->flags & (flag)) != 0)
+
 /**
  * This is the base structure that represents a node in the syntax tree. It is
  * embedded into every node type.


Test patch:
diff --git a/test/prism/attribute_write_test.rb b/test/prism/attribute_write_test.rb
deleted file mode 100644
index bd83d72d..00000000
--- a/test/prism/attribute_write_test.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-module Prism
-  class AttributeWriteTest < TestCase
-    module Target
-      def self.value
-        2
-      end
-
-      def self.value=(value)
-        2
-      end
-
-      def self.[]=(index, value)
-        2
-      end
-    end
-
-    def test_named_call_with_operator
-      assert_attribute_write("Target.value = 1")
-    end
-
-    def test_named_call_without_operator
-      assert_attribute_write("Target.value=(1)")
-    end
-
-    def test_indexed_call_with_operator
-      assert_attribute_write("Target[0] = 1")
-    end
-
-    def test_indexed_call_without_operator
-      refute_attribute_write("Target.[]=(0, 1)")
-    end
-
-    def test_comparison_operators
-      refute_attribute_write("Target.value == 1")
-      refute_attribute_write("Target.value === 1")
-    end
-
-    private
-
-    def parse(source)
-      Prism.parse(source).value.statements.body.first
-    end
-
-    def assert_attribute_write(source)
-      call = parse(source)
-      assert(call.attribute_write?)
-      assert_equal(1, eval(source))
-    end
-
-    def refute_attribute_write(source)
-      call = parse(source)
-      refute(call.attribute_write?)
-      refute_equal(1, eval(source))
-    end
-  end
-end
diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt
index d31cd7af..7601fc38 100644
--- a/test/prism/snapshots/arrays.txt
+++ b/test/prism/snapshots/arrays.txt
@@ -22,7 +22,7 @@
         │   ├── opening_loc: (1,0)-(1,1) = "["
         │   └── closing_loc: (1,3)-(1,4) = "]"
         ├── @ CallNode (location: (3,0)-(3,23))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (3,0)-(3,3))
         │   │   ├── flags: variable_call
@@ -205,7 +205,7 @@
         │   ├── opening_loc: (28,0)-(28,1) = "["
         │   └── closing_loc: (28,11)-(28,12) = "]"
         ├── @ CallNode (location: (30,0)-(30,19))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (30,0)-(30,8))
         │   │   ├── flags: ∅
@@ -375,7 +375,7 @@
         │   ├── closing_loc: (37,12)-(37,13) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (39,0)-(39,19))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (39,0)-(39,3))
         │   │   ├── flags: variable_call
@@ -510,7 +510,7 @@
         │   │   ├── flags: ∅
         │   │   └── arguments: (length: 1)
         │   │       └── @ CallNode (location: (43,4)-(43,18))
-        │   │           ├── flags: attribute_write
+        │   │           ├── flags: ∅
         │   │           ├── receiver:
         │   │           │   @ CallNode (location: (43,4)-(43,7))
         │   │           │   ├── flags: variable_call
@@ -588,7 +588,7 @@
         │   ├── closing_loc: (45,7)-(45,8) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (47,0)-(47,14))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (47,0)-(47,3))
         │   │   ├── flags: variable_call
@@ -932,7 +932,7 @@
         │   ├── opening_loc: (82,0)-(82,3) = "%w["
         │   └── closing_loc: (82,6)-(82,7) = "]"
         ├── @ CallNode (location: (84,0)-(84,13))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (84,0)-(84,3))
         │   │   ├── flags: variable_call
@@ -970,7 +970,7 @@
         │       │   └── block: ∅
         │       └── operator_loc: (84,4)-(84,5) = "&"
         ├── @ CallNode (location: (86,0)-(86,17))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (86,0)-(86,7))
         │   │   ├── flags: ∅
@@ -1038,7 +1038,7 @@
         │   │   @ StatementsNode (location: (89,2)-(89,12))
         │   │   └── body: (length: 1)
         │   │       └── @ CallNode (location: (89,2)-(89,12))
-        │   │           ├── flags: attribute_write
+        │   │           ├── flags: ∅
         │   │           ├── receiver:
         │   │           │   @ CallNode (location: (89,2)-(89,5))
         │   │           │   ├── flags: variable_call
@@ -1930,7 +1930,7 @@
         │   │   @ StatementsNode (location: (132,10)-(132,18))
         │   │   └── body: (length: 1)
         │   │       └── @ CallNode (location: (132,10)-(132,18))
-        │   │           ├── flags: attribute_write
+        │   │           ├── flags: ∅
         │   │           ├── receiver:
         │   │           │   @ CallNode (location: (132,10)-(132,11))
         │   │           │   ├── flags: variable_call
@@ -1986,7 +1986,7 @@
         │   │   @ StatementsNode (location: (134,10)-(134,21))
         │   │   └── body: (length: 1)
         │   │       └── @ CallNode (location: (134,10)-(134,21))
-        │   │           ├── flags: attribute_write
+        │   │           ├── flags: ∅
         │   │           ├── receiver:
         │   │           │   @ CallNode (location: (134,10)-(134,11))
         │   │           │   ├── flags: variable_call
diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt
index 65f0d1df..772d4cad 100644
--- a/test/prism/snapshots/method_calls.txt
+++ b/test/prism/snapshots/method_calls.txt
@@ -238,7 +238,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (21,0)-(21,11))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (21,0)-(21,3))
         │   │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt
index c869af46..c8478c36 100644
--- a/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt
+++ b/test/prism/snapshots/seattlerb/attr_asgn_colon_id.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,8))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,8))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ ConstantReadNode (location: (1,0)-(1,1))
             │   └── name: :A
diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt
index d9e55722..3b09e172 100644
--- a/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt
+++ b/test/prism/snapshots/seattlerb/attrasgn_array_arg.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,13))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,13))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt
index 9a41409d..13615477 100644
--- a/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt
+++ b/test/prism/snapshots/seattlerb/attrasgn_array_lhs.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,42))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,42))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ ArrayNode (location: (1,0)-(1,12))
             │   ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt
index 74faad33..4f22f338 100644
--- a/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt
+++ b/test/prism/snapshots/seattlerb/attrasgn_primary_dot_constant.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,7))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,7))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
index 545f29a5..3b53fee1 100644
--- a/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
+++ b/test/prism/snapshots/seattlerb/eq_begin_why_wont_people_use_their_spacebar.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(3,8))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(3,8))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/index_0.txt b/test/prism/snapshots/seattlerb/index_0.txt
index 38a7d5d5..3be81b54 100644
--- a/test/prism/snapshots/seattlerb/index_0.txt
+++ b/test/prism/snapshots/seattlerb/index_0.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,7))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,7))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/safe_attrasgn.txt b/test/prism/snapshots/seattlerb/safe_attrasgn.txt
index 530ae743..b29fef3f 100644
--- a/test/prism/snapshots/seattlerb/safe_attrasgn.txt
+++ b/test/prism/snapshots/seattlerb/safe_attrasgn.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,8))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,8))
-            ├── flags: safe_navigation, attribute_write
+            ├── flags: safe_navigation
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt b/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt
index c34b981d..39fb489e 100644
--- a/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt
+++ b/test/prism/snapshots/seattlerb/safe_attrasgn_constant.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,8))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,8))
-            ├── flags: safe_navigation, attribute_write
+            ├── flags: safe_navigation
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
index 18edafe4..7855e269 100644
--- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
@@ -596,7 +596,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (29,0)-(29,19))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (29,0)-(29,3))
         │   │   ├── name: :foo
@@ -635,7 +635,7 @@
         │   ├── closing_loc: (29,10)-(29,11) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (30,0)-(30,17))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (30,0)-(30,3))
         │   │   ├── name: :foo
@@ -670,7 +670,7 @@
         │   ├── closing_loc: (30,8)-(30,9) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (31,0)-(31,9))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (31,0)-(31,3))
         │   │   ├── name: :foo
@@ -688,7 +688,7 @@
         │   ├── closing_loc: (31,4)-(31,5) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (32,0)-(32,17))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (32,0)-(32,3))
         │   │   ├── name: :foo
@@ -720,7 +720,7 @@
         │   ├── closing_loc: (32,8)-(32,9) = "]"
         │   └── block: ∅
         ├── @ CallNode (location: (33,0)-(33,18))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (33,0)-(33,3))
         │   │   ├── name: :foo
@@ -768,7 +768,7 @@
         │   │   └── unescaped: ""
         │   └── operator_loc: (34,2)-(34,3) = "="
         ├── @ CallNode (location: (35,0)-(35,7))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (35,0)-(35,1))
         │   │   ├── name: :x
@@ -790,7 +790,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (36,0)-(36,12))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (36,0)-(36,1))
         │   │   ├── name: :x
@@ -891,7 +891,7 @@
         │   │   └── closing_loc: (41,0)-(42,0) = "HEREDOC\n"
         │   └── operator_loc: (39,2)-(39,3) = "="
         ├── @ CallNode (location: (42,0)-(42,14))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (42,0)-(42,1))
         │   │   ├── name: :x
@@ -927,7 +927,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (45,0)-(45,16))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ LocalVariableReadNode (location: (45,0)-(45,1))
         │   │   ├── name: :x
diff --git a/test/prism/snapshots/unparser/corpus/literal/case.txt b/test/prism/snapshots/unparser/corpus/literal/case.txt
index be095579..a228c4af 100644
--- a/test/prism/snapshots/unparser/corpus/literal/case.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/case.txt
@@ -404,7 +404,7 @@
             │       │       ├── operator_loc: (36,5)-(36,6) = "*"
             │       │       └── expression:
             │       │           @ CallNode (location: (36,6)-(36,15))
-            │       │           ├── flags: attribute_write
+            │       │           ├── flags: ∅
             │       │           ├── receiver:
             │       │           │   @ CallNode (location: (36,6)-(36,9))
             │       │           │   ├── flags: variable_call
diff --git a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
index cf7de993..764ae3ad 100644
--- a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
@@ -97,7 +97,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (9,0)-(9,17))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ ParenthesesNode (location: (9,0)-(9,10))
         │   │   ├── body:
diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt
index a96937e8..e0e27bc5 100644
--- a/test/prism/snapshots/unparser/corpus/literal/send.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/send.txt
@@ -1529,7 +1529,7 @@
         │   ├── closing_loc: (68,25)-(68,26) = ")"
         │   └── block: ∅
         ├── @ CallNode (location: (69,0)-(69,12))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (69,0)-(69,3))
         │   │   ├── flags: variable_call
@@ -1773,7 +1773,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (77,0)-(77,13))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ SelfNode (location: (77,0)-(77,4))
         │   ├── call_operator_loc: (77,4)-(77,5) = "."
diff --git a/test/prism/snapshots/whitequark/send_attr_asgn.txt b/test/prism/snapshots/whitequark/send_attr_asgn.txt
index 938fcf75..948e5280 100644
--- a/test/prism/snapshots/whitequark/send_attr_asgn.txt
+++ b/test/prism/snapshots/whitequark/send_attr_asgn.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(7,10))
     └── body: (length: 4)
         ├── @ CallNode (location: (1,0)-(1,9))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (1,0)-(1,3))
         │   │   ├── flags: variable_call
@@ -29,7 +29,7 @@
         │   ├── closing_loc: ∅
         │   └── block: ∅
         ├── @ CallNode (location: (3,0)-(3,9))
-        │   ├── flags: attribute_write
+        │   ├── flags: ∅
         │   ├── receiver:
         │   │   @ CallNode (location: (3,0)-(3,3))
         │   │   ├── flags: variable_call
@@ -76,7 +76,7 @@
         │       @ IntegerNode (location: (5,9)-(5,10))
         │       └── flags: decimal
         └── @ CallNode (location: (7,0)-(7,10))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (7,0)-(7,3))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt b/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt
index 530ae743..b29fef3f 100644
--- a/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt
+++ b/test/prism/snapshots/whitequark/send_attr_asgn_conditional.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,8))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,8))
-            ├── flags: safe_navigation, attribute_write
+            ├── flags: safe_navigation
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,1))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/whitequark/send_index_asgn.txt b/test/prism/snapshots/whitequark/send_index_asgn.txt
index 34adbb1d..3f550ea4 100644
--- a/test/prism/snapshots/whitequark/send_index_asgn.txt
+++ b/test/prism/snapshots/whitequark/send_index_asgn.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,13))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,13))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,3))
             │   ├── flags: variable_call
diff --git a/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt b/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt
index 34adbb1d..3f550ea4 100644
--- a/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt
+++ b/test/prism/snapshots/whitequark/send_index_asgn_legacy.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (1,0)-(1,13))
     └── body: (length: 1)
         └── @ CallNode (location: (1,0)-(1,13))
-            ├── flags: attribute_write
+            ├── flags: ∅
             ├── receiver:
             │   @ CallNode (location: (1,0)-(1,3))
             │   ├── flags: variable_call