Repository: ruby/yarp
Pull request #1285 (2023-08-18 19:23:27)

Refactor operator write nodes
* `{ClassVariable,Constant,ConstantPath,GlobalVariable,InstanceVariable,LocalVariable}OperatorAndWriteNode` instead become `AndWriteNode` with their target being the original value being written to. I decided to keep them as read nodes to save on space because they'll never end up having a value and technically you have to read them first before anything else.
* Same for `OrWrite`.
* Same for `OperatorWrite`.


Patch:
diff --git a/config.yml b/config.yml
index 30164733..fdc9117a 100644
--- a/config.yml
+++ b/config.yml
@@ -399,6 +399,19 @@ nodes:
 
           left and right
           ^^^^^^^^^^^^^^
+  - name: AndWriteNode
+    child_nodes:
+      - name: target
+        type: node
+      - name: value
+        type: node
+      - name: operator_loc
+        type: location
+    comment: |
+      Represents the use of the `&&=` operator.
+
+          target &&= value
+          ^^^^^^^^^^^^^^^^
   - name: ArgumentsNode
     child_nodes:
       - name: arguments
@@ -723,47 +736,6 @@ nodes:
 
           class Foo end
           ^^^^^^^^^^^^^
-  - name: ClassVariableOperatorAndWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `&&=` operator for assignment to a class variable.
-
-          @@target &&= value
-          ^^^^^^^^^^^^^^^^
-  - name: ClassVariableOperatorOrWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `||=` operator for assignment to a class variable.
-
-          @@target ||= value
-          ^^^^^^^^^^^^^^^^^^
-  - name: ClassVariableOperatorWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents assigning to a class variable using an operator that isn't `=`.
-
-          @@target += value
-          ^^^^^^^^^^^^^^^^^
   - name: ClassVariableReadNode
     comment: |
       Represents referencing a class variable.
@@ -783,47 +755,6 @@ nodes:
 
           @@foo = 1
           ^^^^^^^^^
-  - name: ConstantOperatorAndWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `&&=` operator for assignment to a constant.
-
-          Target &&= value
-          ^^^^^^^^^^^^^^^^
-  - name: ConstantOperatorOrWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `||=` operator for assignment to a constant.
-
-          Target ||= value
-          ^^^^^^^^^^^^^^^^
-  - name: ConstantOperatorWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents assigning to a constant using an operator that isn't `=`.
-
-          Target += value
-          ^^^^^^^^^^^^^^^
   - name: ConstantPathNode
     child_nodes:
       - name: parent
@@ -837,50 +768,6 @@ nodes:
 
           Foo::Bar
           ^^^^^^^^
-  - name: ConstantPathOperatorAndWriteNode
-    child_nodes:
-      - name: target
-        type: node
-        kind: ConstantPathNode
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `&&=` operator for assignment to a constant path.
-
-          Parent::Child &&= value
-          ^^^^^^^^^^^^^^^^^^^^^^^
-  - name: ConstantPathOperatorOrWriteNode
-    child_nodes:
-      - name: target
-        type: node
-        kind: ConstantPathNode
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `||=` operator for assignment to a constant path.
-
-          Parent::Child ||= value
-          ^^^^^^^^^^^^^^^^^^^^^^^
-  - name: ConstantPathOperatorWriteNode
-    child_nodes:
-      - name: target
-        type: node
-        kind: ConstantPathNode
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents assigning to a constant path using an operator that isn't `=`.
-
-          Parent::Child += value
-          ^^^^^^^^^^^^^^^^^^^^^^
   - name: ConstantPathWriteNode
     child_nodes:
       - name: target
@@ -1123,47 +1010,6 @@ nodes:
 
           super
           ^^^^^
-  - name: GlobalVariableOperatorAndWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `&&=` operator for assignment to a global variable.
-
-          $target &&= value
-          ^^^^^^^^^^^^^^^^^
-  - name: GlobalVariableOperatorOrWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `||=` operator for assignment to a global variable.
-
-          $target ||= value
-          ^^^^^^^^^^^^^^^^^
-  - name: GlobalVariableOperatorWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents assigning to a global variable using an operator that isn't `=`.
-
-          $target += value
-          ^^^^^^^^^^^^^^^^
   - name: GlobalVariableReadNode
     comment: |
       Represents referencing a global variable.
@@ -1263,47 +1109,6 @@ nodes:
 
           case a; in b then c end
                   ^^^^^^^^^^^
-  - name: InstanceVariableOperatorAndWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `&&=` operator for assignment to an instance variable.
-
-          @target &&= value
-          ^^^^^^^^^^^^^^^^^
-  - name: InstanceVariableOperatorOrWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-    comment: |
-      Represents the use of the `||=` operator for assignment to an instance variable.
-
-          @target ||= value
-          ^^^^^^^^^^^^^^^^^
-  - name: InstanceVariableOperatorWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents assigning to an instance variable using an operator that isn't `=`.
-
-          @target += value
-          ^^^^^^^^^^^^^^^^
   - name: InstanceVariableReadNode
     comment: |
       Represents referencing an instance variable.
@@ -1441,53 +1246,6 @@ nodes:
 
           ->(value) { value * 2 }
           ^^^^^^^^^^^^^^^^^^^^^^^
-  - name: LocalVariableOperatorAndWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: constant_id
-        type: constant
-    comment: |
-      Represents the use of the `&&=` operator for assignment to a local variable.
-
-          target &&= value
-          ^^^^^^^^^^^^^^^^
-  - name: LocalVariableOperatorOrWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: constant_id
-        type: constant
-    comment: |
-      Represents the use of the `||=` operator for assignment to a local variable.
-
-          target ||= value
-          ^^^^^^^^^^^^^^^^
-  - name: LocalVariableOperatorWriteNode
-    child_nodes:
-      - name: name_loc
-        type: location
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: constant_id
-        type: constant
-      - name: operator_id
-        type: constant
-    comment: |
-      Represents assigning to a local variable using an operator that isn't `=`.
-
-          target += value
-          ^^^^^^^^^^^^^^^
   - name: LocalVariableReadNode
     child_nodes:
       - name: constant_id
@@ -1618,6 +1376,21 @@ nodes:
 
           $1
           ^^
+  - name: OperatorWriteNode
+    child_nodes:
+      - name: target
+        type: node
+      - name: operator_loc
+        type: location
+      - name: operator
+        type: constant
+      - name: value
+        type: node
+    comment: |
+      Represents the use of an operator on a write.
+
+          target += value
+          ^^^^^^^^^^^^^^^
   - name: OptionalParameterNode
     child_nodes:
       - name: constant_id
@@ -1647,6 +1420,19 @@ nodes:
 
           left or right
           ^^^^^^^^^^^^^
+  - name: OrWriteNode
+    child_nodes:
+      - name: target
+        type: node
+      - name: value
+        type: node
+      - name: operator_loc
+        type: location
+    comment: |
+      Represents the use of the `||=` operator.
+
+          target ||= value
+          ^^^^^^^^^^^^^^^^
   - name: ParametersNode
     child_nodes:
       - name: requireds
diff --git a/src/yarp.c b/src/yarp.c
index a6a2087a..1871d825 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -658,6 +658,27 @@ yp_and_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *opera
     return node;
 }
 
+// Allocate and initialize a new AndWriteNode.
+static yp_and_write_node_t *
+yp_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    yp_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_and_write_node_t);
+
+    *node = (yp_and_write_node_t) {
+        {
+            .type = YP_NODE_AND_WRITE_NODE,
+            .location = {
+                .start = target->location.start,
+                .end = value->location.end
+            },
+        },
+        .target = target,
+        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
+        .value = value
+    };
+
+    return node;
+}
+
 // Allocate an initialize a new arguments node.
 static yp_arguments_node_t *
 yp_arguments_node_create(yp_parser_t *parser) {
@@ -1485,74 +1506,6 @@ yp_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const y
     return node;
 }
 
-// Allocate and initialize a new ClassVariableOperatorAndWriteNode node.
-static yp_class_variable_operator_and_write_node_t *
-yp_class_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_CLASS_VARIABLE_READ_NODE));
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_class_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_and_write_node_t);
-
-    *node = (yp_class_variable_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ClassVariableOperatorWriteNode node.
-static yp_class_variable_operator_write_node_t *
-yp_class_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    yp_class_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_write_node_t);
-
-    *node = (yp_class_variable_operator_write_node_t) {
-        {
-            .type = YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ClassVariableOperatorOrWriteNode node.
-static yp_class_variable_operator_or_write_node_t *
-yp_class_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_CLASS_VARIABLE_READ_NODE));
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_class_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_or_write_node_t);
-
-    *node = (yp_class_variable_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate and initialize a new ClassVariableReadNode node.
 static yp_class_variable_read_node_t *
 yp_class_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) {
@@ -1583,72 +1536,6 @@ yp_class_variable_read_node_to_class_variable_write_node(yp_parser_t *parser, yp
     return node;
 }
 
-// Allocate and initialize a new ConstantPathOperatorAndWriteNode node.
-static yp_constant_path_operator_and_write_node_t *
-yp_constant_path_operator_and_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_constant_path_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_and_write_node_t);
-
-    *node = (yp_constant_path_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->base.location.start,
-                .end = value->location.end
-            }
-        },
-        .target = target,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ConstantPathOperatorWriteNode node.
-static yp_constant_path_operator_write_node_t *
-yp_constant_path_operator_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    yp_constant_path_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_write_node_t);
-
-    *node = (yp_constant_path_operator_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->base.location.start,
-                .end = value->location.end
-            }
-        },
-        .target = target,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ConstantPathOperatorOrWriteNode node.
-static yp_constant_path_operator_or_write_node_t *
-yp_constant_path_operator_or_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_constant_path_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_or_write_node_t);
-
-    *node = (yp_constant_path_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->base.location.start,
-                .end = value->location.end
-            }
-        },
-        .target = target,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate and initialize a new ConstantPathNode node.
 static yp_constant_path_node_t *
 yp_constant_path_node_create(yp_parser_t *parser, yp_node_t *parent, const yp_token_t *delimiter, yp_node_t *child) {
@@ -1691,74 +1578,6 @@ yp_constant_path_write_node_create(yp_parser_t *parser, yp_constant_path_node_t
     return node;
 }
 
-// Allocate and initialize a new ConstantOperatorAndWriteNode node.
-static yp_constant_operator_and_write_node_t *
-yp_constant_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_CONSTANT_READ_NODE));
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_constant_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_and_write_node_t);
-
-    *node = (yp_constant_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ConstantOperatorWriteNode node.
-static yp_constant_operator_write_node_t *
-yp_constant_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    yp_constant_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_write_node_t);
-
-    *node = (yp_constant_operator_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new ConstantOperatorOrWriteNode node.
-static yp_constant_operator_or_write_node_t *
-yp_constant_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_CONSTANT_READ_NODE));
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_constant_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_or_write_node_t);
-
-    *node = (yp_constant_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate and initialize a new ConstantReadNode node.
 static yp_constant_read_node_t *
 yp_constant_read_node_create(yp_parser_t *parser, const yp_token_t *name) {
@@ -2193,74 +2012,6 @@ yp_hash_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *assoc
     return node;
 }
 
-// Allocate and initialize a new GlobalVariableOperatorAndWriteNode node.
-static yp_global_variable_operator_and_write_node_t *
-yp_global_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_GLOBAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_BACK_REFERENCE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_NUMBERED_REFERENCE_READ_NODE));
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_global_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_and_write_node_t);
-
-    *node = (yp_global_variable_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new GlobalVariableOperatorWriteNode node.
-static yp_global_variable_operator_write_node_t *
-yp_global_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    yp_global_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_write_node_t);
-
-    *node = (yp_global_variable_operator_write_node_t) {
-        {
-            .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new GlobalVariableOperatorOrWriteNode node.
-static yp_global_variable_operator_or_write_node_t *
-yp_global_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_GLOBAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_BACK_REFERENCE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_NUMBERED_REFERENCE_READ_NODE));
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_global_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_or_write_node_t);
-
-    *node = (yp_global_variable_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate a new GlobalVariableReadNode node.
 static yp_global_variable_read_node_t *
 yp_global_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name) {
@@ -2551,74 +2302,6 @@ yp_in_node_create(yp_parser_t *parser, yp_node_t *pattern, yp_statements_node_t
     return node;
 }
 
-// Allocate and initialize a new InstanceVariableOperatorAndWriteNode node.
-static yp_instance_variable_operator_and_write_node_t *
-yp_instance_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_INSTANCE_VARIABLE_READ_NODE));
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_instance_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_and_write_node_t);
-
-    *node = (yp_instance_variable_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new InstanceVariableOperatorWriteNode node.
-static yp_instance_variable_operator_write_node_t *
-yp_instance_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    yp_instance_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_write_node_t);
-
-    *node = (yp_instance_variable_operator_write_node_t) {
-        {
-            .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new InstanceVariableOperatorOrWriteNode node.
-static yp_instance_variable_operator_or_write_node_t *
-yp_instance_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_INSTANCE_VARIABLE_READ_NODE));
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_instance_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_or_write_node_t);
-
-    *node = (yp_instance_variable_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate and initialize a new InstanceVariableReadNode node.
 static yp_instance_variable_read_node_t *
 yp_instance_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) {
@@ -2892,77 +2575,6 @@ yp_lambda_node_create(
     return node;
 }
 
-// Allocate and initialize a new LocalVariableOperatorAndWriteNode node.
-static yp_local_variable_operator_and_write_node_t *
-yp_local_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_LOCAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_CALL_NODE));
-    assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_local_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_and_write_node_t);
-
-    *node = (yp_local_variable_operator_and_write_node_t) {
-        {
-            .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .constant_id = constant_id
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new LocalVariableOperatorWriteNode node.
-static yp_local_variable_operator_write_node_t *
-yp_local_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) {
-    yp_local_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_write_node_t);
-
-    *node = (yp_local_variable_operator_write_node_t) {
-        {
-            .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .constant_id = constant_id,
-        .operator_id = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
-    };
-
-    return node;
-}
-
-// Allocate and initialize a new LocalVariableOperatorOrWriteNode node.
-static yp_local_variable_operator_or_write_node_t *
-yp_local_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) {
-    assert(YP_NODE_TYPE_P(target, YP_NODE_LOCAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_CALL_NODE));
-    assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_local_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_or_write_node_t);
-
-    *node = (yp_local_variable_operator_or_write_node_t) {
-        {
-            .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE,
-            .location = {
-                .start = target->location.start,
-                .end = value->location.end
-            }
-        },
-        .name_loc = target->location,
-        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .constant_id = constant_id
-    };
-
-    return node;
-}
-
 // Allocate a new LocalVariableReadNode node.
 static yp_local_variable_read_node_t *
 yp_local_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name, uint32_t depth) {
@@ -3196,6 +2808,28 @@ yp_numbered_reference_read_node_create(yp_parser_t *parser, const yp_token_t *na
     return node;
 }
 
+// Allocate and initialize a new OperatorWriteNode.
+static yp_operator_write_node_t *
+yp_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    yp_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_operator_write_node_t);
+
+    *node = (yp_operator_write_node_t) {
+        {
+            .type = YP_NODE_OPERATOR_WRITE_NODE,
+            .location = {
+                .start = target->location.start,
+                .end = value->location.end
+            },
+        },
+        .target = target,
+        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
+        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1),
+        .value = value
+    };
+
+    return node;
+}
+
 // Allocate a new OptionalParameterNode node.
 static yp_optional_parameter_node_t *
 yp_optional_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator, yp_node_t *value) {
@@ -3239,6 +2873,27 @@ yp_or_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operat
     return node;
 }
 
+// Allocate and initialize a new OrWriteNode.
+static yp_or_write_node_t *
+yp_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    yp_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_or_write_node_t);
+
+    *node = (yp_or_write_node_t) {
+        {
+            .type = YP_NODE_OR_WRITE_NODE,
+            .location = {
+                .start = target->location.start,
+                .end = value->location.end
+            },
+        },
+        .target = target,
+        .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
+        .value = value
+    };
+
+    return node;
+}
+
 // Allocate and initialize a new ParametersNode node.
 static yp_parameters_node_t *
 yp_parameters_node_create(yp_parser_t *parser) {
@@ -12450,14 +12105,15 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                 case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
                     yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
                 /* fallthrough */
-                case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
+                case YP_NODE_CLASS_VARIABLE_READ_NODE:
+                case YP_NODE_CONSTANT_PATH_NODE:
+                case YP_NODE_CONSTANT_READ_NODE:
+                case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
+                case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
+                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
                     parser_lex(parser);
-
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    yp_node_t *result = (yp_node_t *) yp_global_variable_operator_and_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
+                    return (yp_node_t *) yp_and_write_node_create(parser, node, &token, value);
                 }
                 case YP_NODE_CALL_NODE: {
                     yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12474,13 +12130,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                         }
 
                         parser_lex(parser);
-
+                        yp_node_t *target = (yp_node_t *) yp_local_variable_read_node_create(parser, &(yp_token_t) { .type = YP_TOKEN_IDENTIFIER, .start = message_loc.start, .end = message_loc.end }, 0);
                         yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                        yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end);
-                        yp_node_t *result = (yp_node_t *) yp_local_variable_operator_and_write_node_create(parser, node, &token, value, constant_id);
 
                         yp_node_destroy(parser, node);
-                        return result;
+                        return (yp_node_t *) yp_and_write_node_create(parser, target, &token, value);
                     }
 
                     parser_lex(parser);
@@ -12491,49 +12145,6 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
                     return (yp_node_t *) yp_call_operator_and_write_node_create(parser, (yp_call_node_t *) node, &token, value);
                 }
-                case YP_NODE_CLASS_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    yp_node_t *result = (yp_node_t *) yp_class_variable_operator_and_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_CONSTANT_PATH_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    return (yp_node_t *) yp_constant_path_operator_and_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
-                }
-                case YP_NODE_CONSTANT_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    yp_node_t *result = (yp_node_t *) yp_constant_operator_and_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_and_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                    yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id;
-                    yp_node_t *result = (yp_node_t *) yp_local_variable_operator_and_write_node_create(parser, node, &token, value, constant_id);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
                 case YP_NODE_MULTI_WRITE_NODE: {
                     parser_lex(parser);
                     yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Cannot use `&&=' on a multi-write.");
@@ -12555,14 +12166,16 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                 case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
                     yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
                 /* fallthrough */
-                case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
+                case YP_NODE_CLASS_VARIABLE_READ_NODE:
+                case YP_NODE_CONSTANT_PATH_NODE:
+                case YP_NODE_CONSTANT_READ_NODE:
+                case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
+                case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
+                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
                     parser_lex(parser);
 
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    yp_node_t *result = (yp_node_t *) yp_global_variable_operator_or_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
+                    return (yp_node_t *) yp_or_write_node_create(parser, node, &token, value);
                 }
                 case YP_NODE_CALL_NODE: {
                     yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12579,13 +12192,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                         }
 
                         parser_lex(parser);
-
+                        yp_node_t *target = (yp_node_t *) yp_local_variable_read_node_create(parser, &(yp_token_t) { .type = YP_TOKEN_IDENTIFIER, .start = message_loc.start, .end = message_loc.end }, 0);
                         yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                        yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end);
-                        yp_node_t *result = (yp_node_t *) yp_local_variable_operator_or_write_node_create(parser, node, &token, value, constant_id);
 
                         yp_node_destroy(parser, node);
-                        return result;
+                        return (yp_node_t *) yp_or_write_node_create(parser, target, &token, value);
                     }
 
                     parser_lex(parser);
@@ -12596,49 +12207,6 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
                     return (yp_node_t *) yp_call_operator_or_write_node_create(parser, (yp_call_node_t *) node, &token, value);
                 }
-                case YP_NODE_CLASS_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    yp_node_t *result = (yp_node_t *) yp_class_variable_operator_or_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_CONSTANT_PATH_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    return (yp_node_t *) yp_constant_path_operator_or_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
-                }
-                case YP_NODE_CONSTANT_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    yp_node_t *result = (yp_node_t *) yp_constant_operator_or_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_or_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
-                    yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id;
-                    yp_node_t *result = (yp_node_t *) yp_local_variable_operator_or_write_node_create(parser, node, &token, value, constant_id);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
                 case YP_NODE_MULTI_WRITE_NODE: {
                     parser_lex(parser);
                     yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Cannot use `||=' on a multi-write.");
@@ -12670,14 +12238,16 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                 case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
                     yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
                 /* fallthrough */
-                case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
+                case YP_NODE_CLASS_VARIABLE_READ_NODE:
+                case YP_NODE_CONSTANT_PATH_NODE:
+                case YP_NODE_CONSTANT_READ_NODE:
+                case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
+                case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
+                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
                     parser_lex(parser);
 
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator");
-                    yp_node_t *result = (yp_node_t *) yp_global_variable_operator_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
+                    return (yp_node_t *) yp_operator_write_node_create(parser, node, &token, value);
                 }
                 case YP_NODE_CALL_NODE: {
                     yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12694,13 +12264,11 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                         }
 
                         parser_lex(parser);
-
+                        yp_node_t *target = (yp_node_t *) yp_local_variable_read_node_create(parser, &(yp_token_t) { .type = YP_TOKEN_IDENTIFIER, .start = message_loc.start, .end = message_loc.end }, 0);
                         yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
-                        yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end);
-                        yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, constant_id);
 
                         yp_node_destroy(parser, node);
-                        return result;
+                        return (yp_node_t *) yp_operator_write_node_create(parser, target, &token, value);
                     }
 
                     yp_token_t operator = not_provided(parser);
@@ -12710,49 +12278,6 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                     yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
                     return (yp_node_t *) yp_call_operator_write_node_create(parser, (yp_call_node_t *) node, &token, value);
                 }
-                case YP_NODE_CLASS_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
-                    yp_node_t *result = (yp_node_t *) yp_class_variable_operator_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_CONSTANT_PATH_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
-                    return (yp_node_t *) yp_constant_path_operator_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
-                }
-                case YP_NODE_CONSTANT_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
-                    yp_node_t *result = (yp_node_t *) yp_constant_operator_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
-                    yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_write_node_create(parser, node, &token, value);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
-                case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
-                    parser_lex(parser);
-
-                    yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
-                    yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id;
-                    yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, constant_id);
-
-                    yp_node_destroy(parser, node);
-                    return result;
-                }
                 case YP_NODE_MULTI_WRITE_NODE: {
                     parser_lex(parser);
                     yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected operator.");


Test patch:
diff --git a/test/location_test.rb b/test/location_test.rb
index 789bd49e..07bb48bb 100644
--- a/test/location_test.rb
+++ b/test/location_test.rb
@@ -17,6 +17,16 @@ module YARP
       assert_location(AndNode, "foo && bar")
     end
 
+    def test_AndWriteNode
+      assert_location(AndWriteNode, "foo &&= bar")
+      assert_location(AndWriteNode, "foo = 1; foo &&= bar", 9...20)
+      assert_location(AndWriteNode, "@@foo &&= bar")
+      assert_location(AndWriteNode, "Parent::Child &&= bar")
+      assert_location(AndWriteNode, "Foo &&= bar")
+      assert_location(AndWriteNode, "$foo &&= bar")
+      assert_location(AndWriteNode, "@foo &&= bar")
+    end
+
     def test_ArgumentsNode
       assert_location(ArgumentsNode, "foo(bar, baz, qux)", 4...17, &:arguments)
     end
@@ -199,18 +209,6 @@ module YARP
       assert_location(ClassNode, "class Foo < Bar end")
     end
 
-    def test_ClassVariableOperatorAndWriteNode
-      assert_location(ClassVariableOperatorAndWriteNode, "@@foo &&= bar")
-    end
-
-    def test_ClassVariableOperatorWriteNode
-      assert_location(ClassVariableOperatorWriteNode, "@@foo += bar")
-    end
-
-    def test_ClassVariableOperatorOrWriteNode
-      assert_location(ClassVariableOperatorOrWriteNode, "@@foo ||= bar")
-    end
-
     def test_ClassVariableReadNode
       assert_location(ClassVariableReadNode, "@@foo")
     end
@@ -231,30 +229,6 @@ module YARP
       assert_location(ConstantPathWriteNode, "::Foo::Bar = baz")
     end
 
-    def test_ConstantPathOperatorAndWriteNode
-      assert_location(ConstantPathOperatorAndWriteNode, "Parent::Child &&= bar")
-    end
-
-    def test_ConstantPathOperatorWriteNode
-      assert_location(ConstantPathOperatorWriteNode, "Parent::Child += bar")
-    end
-
-    def test_ConstantPathOperatorOrWriteNode
-      assert_location(ConstantPathOperatorOrWriteNode, "Parent::Child ||= bar")
-    end
-
-    def test_ConstantOperatorAndWriteNode
-      assert_location(ConstantOperatorAndWriteNode, "Foo &&= bar")
-    end
-
-    def test_ConstantOperatorWriteNode
-      assert_location(ConstantOperatorWriteNode, "Foo += bar")
-    end
-
-    def test_ConstantOperatorOrWriteNode
-      assert_location(ConstantOperatorOrWriteNode, "Foo ||= bar")
-    end
-
     def test_ConstantReadNode
       assert_location(ConstantReadNode, "Foo")
       assert_location(ConstantReadNode, "Foo::Bar", 5...8, &:child)
@@ -328,18 +302,6 @@ module YARP
       assert_location(ForwardingSuperNode, "super {}")
     end
 
-    def test_GlobalVariableOperatorAndWriteNode
-      assert_location(GlobalVariableOperatorAndWriteNode, "$foo &&= bar")
-    end
-
-    def test_GlobalVariableOperatorWriteNode
-      assert_location(GlobalVariableOperatorWriteNode, "$foo += bar")
-    end
-
-    def test_GlobalVariableOperatorOrWriteNode
-      assert_location(GlobalVariableOperatorOrWriteNode, "$foo ||= bar")
-    end
-
     def test_GlobalVariableReadNode
       assert_location(GlobalVariableReadNode, "$foo")
     end
@@ -374,18 +336,6 @@ module YARP
       end
     end
 
-    def test_InstanceVariableOperatorAndWriteNode
-      assert_location(InstanceVariableOperatorAndWriteNode, "@foo &&= bar")
-    end
-
-    def test_InstanceVariableOperatorWriteNode
-      assert_location(InstanceVariableOperatorWriteNode, "@foo += bar")
-    end
-
-    def test_InstanceVariableOperatorOrWriteNode
-      assert_location(InstanceVariableOperatorOrWriteNode, "@foo ||= bar")
-    end
-
     def test_InstanceVariableReadNode
       assert_location(InstanceVariableReadNode, "@foo")
     end
@@ -452,21 +402,6 @@ module YARP
       assert_location(LambdaNode, "-> do foo end")
     end
 
-    def test_LocalVariableOperatorAndWriteNode
-      assert_location(LocalVariableOperatorAndWriteNode, "foo &&= bar")
-      assert_location(LocalVariableOperatorAndWriteNode, "foo = 1; foo &&= bar", 9...20)
-    end
-
-    def test_LocalVariableOperatorWriteNode
-      assert_location(LocalVariableOperatorWriteNode, "foo += bar")
-      assert_location(LocalVariableOperatorWriteNode, "foo = 1; foo += bar", 9...19)
-    end
-
-    def test_LocalVariableOperatorOrWriteNode
-      assert_location(LocalVariableOperatorOrWriteNode, "foo ||= bar")
-      assert_location(LocalVariableOperatorOrWriteNode, "foo = 1; foo ||= bar", 9...20)
-    end
-
     def test_LocalVariableReadNode
       assert_location(LocalVariableReadNode, "foo = 1; foo", 9...12)
     end
@@ -510,6 +445,16 @@ module YARP
       assert_location(NumberedReferenceReadNode, "$1")
     end
 
+    def test_OperatorWriteNode
+      assert_location(OperatorWriteNode, "@@foo += bar")
+      assert_location(OperatorWriteNode, "Parent::Child += bar")
+      assert_location(OperatorWriteNode, "Foo += bar")
+      assert_location(OperatorWriteNode, "$foo += bar")
+      assert_location(OperatorWriteNode, "@foo += bar")
+      assert_location(OperatorWriteNode, "foo += bar")
+      assert_location(OperatorWriteNode, "foo = 1; foo += bar", 9...19)
+    end
+
     def test_OptionalParameterNode
       assert_location(OptionalParameterNode, "def foo(bar = nil); end", 8...17) do |node|
         node.parameters.optionals.first
@@ -521,6 +466,16 @@ module YARP
       assert_location(OrNode, "foo or bar")
     end
 
+    def test_OrWriteNode
+      assert_location(OrWriteNode, "@@foo ||= bar")
+      assert_location(OrWriteNode, "Parent::Child ||= bar")
+      assert_location(OrWriteNode, "Foo ||= bar")
+      assert_location(OrWriteNode, "$foo ||= bar")
+      assert_location(OrWriteNode, "@foo ||= bar")
+      assert_location(OrWriteNode, "foo ||= bar")
+      assert_location(OrWriteNode, "foo = 1; foo ||= bar", 9...20)
+    end
+
     def test_ParametersNode
       assert_location(ParametersNode, "def foo(bar, baz); end", 8...16, &:parameters)
     end
diff --git a/test/snapshots/blocks.txt b/test/snapshots/blocks.txt
index 3579d99b..214f5e54 100644
--- a/test/snapshots/blocks.txt
+++ b/test/snapshots/blocks.txt
@@ -88,12 +88,11 @@ ProgramNode(0...402)(
            (61...62)
          ),
          StatementsNode(63...72)(
-           [LocalVariableOperatorWriteNode(63...72)(
-              (63...67),
+           [OperatorWriteNode(63...72)(
+              LocalVariableReadNode(63...67)(:memo, 0),
               (68...70),
-              LocalVariableReadNode(71...72)(:x, 0),
-              :memo,
-              :+
+              :+,
+              LocalVariableReadNode(71...72)(:x, 0)
             )]
          ),
          (51...52),
diff --git a/test/snapshots/boolean_operators.txt b/test/snapshots/boolean_operators.txt
index 1b8a2aae..39a3b6ef 100644
--- a/test/snapshots/boolean_operators.txt
+++ b/test/snapshots/boolean_operators.txt
@@ -1,24 +1,21 @@
 ProgramNode(0...24)(
   [:a],
   StatementsNode(0...24)(
-    [LocalVariableOperatorAndWriteNode(0...7)(
-       (0...1),
-       (2...5),
+    [AndWriteNode(0...7)(
+       LocalVariableReadNode(0...1)(:a, 0),
        CallNode(6...7)(nil, nil, (6...7), nil, nil, nil, nil, 2, "b"),
-       :a
+       (2...5)
      ),
-     LocalVariableOperatorWriteNode(9...15)(
-       (9...10),
+     OperatorWriteNode(9...15)(
+       LocalVariableReadNode(9...10)(:a, 0),
        (11...13),
-       CallNode(14...15)(nil, nil, (14...15), nil, nil, nil, nil, 2, "b"),
-       :a,
-       :+
+       :+,
+       CallNode(14...15)(nil, nil, (14...15), nil, nil, nil, nil, 2, "b")
      ),
-     LocalVariableOperatorOrWriteNode(17...24)(
-       (17...18),
-       (19...22),
+     OrWriteNode(17...24)(
+       LocalVariableReadNode(17...18)(:a, 0),
        CallNode(23...24)(nil, nil, (23...24), nil, nil, nil, nil, 2, "b"),
-       :a
+       (19...22)
      )]
   )
 )
diff --git a/test/snapshots/defined.txt b/test/snapshots/defined.txt
index 4d0b377a..3d938b69 100644
--- a/test/snapshots/defined.txt
+++ b/test/snapshots/defined.txt
@@ -8,12 +8,11 @@ ProgramNode(0...78)(
      ),
      DefinedNode(27...43)(
        (35...36),
-       LocalVariableOperatorWriteNode(36...42)(
-         (36...37),
+       OperatorWriteNode(36...42)(
+         LocalVariableReadNode(36...37)(:x, 0),
          (38...40),
-         IntegerNode(41...42)(),
-         :x,
-         :%
+         :%,
+         IntegerNode(41...42)()
        ),
        (42...43),
        (27...35)
diff --git a/test/snapshots/seattlerb/bug_op_asgn_rescue.txt b/test/snapshots/seattlerb/bug_op_asgn_rescue.txt
index e54cde69..ca841c4e 100644
--- a/test/snapshots/seattlerb/bug_op_asgn_rescue.txt
+++ b/test/snapshots/seattlerb/bug_op_asgn_rescue.txt
@@ -1,15 +1,14 @@
 ProgramNode(0...18)(
   [:a],
   StatementsNode(0...18)(
-    [LocalVariableOperatorOrWriteNode(0...18)(
-       (0...1),
-       (2...5),
+    [OrWriteNode(0...18)(
+       LocalVariableReadNode(0...1)(:a, 0),
        RescueModifierNode(6...18)(
          CallNode(6...7)(nil, nil, (6...7), nil, nil, nil, nil, 2, "b"),
          (8...14),
          NilNode(15...18)()
        ),
-       :a
+       (2...5)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/const_2_op_asgn_or2.txt b/test/snapshots/seattlerb/const_2_op_asgn_or2.txt
index dfaac544..369adfb8 100644
--- a/test/snapshots/seattlerb/const_2_op_asgn_or2.txt
+++ b/test/snapshots/seattlerb/const_2_op_asgn_or2.txt
@@ -1,14 +1,14 @@
 ProgramNode(0...12)(
   [],
   StatementsNode(0...12)(
-    [ConstantPathOperatorOrWriteNode(0...12)(
+    [OrWriteNode(0...12)(
        ConstantPathNode(0...6)(
          ConstantPathNode(0...3)(nil, ConstantReadNode(2...3)(), (0...2)),
          ConstantReadNode(5...6)(),
          (3...5)
        ),
-       (7...10),
-       IntegerNode(11...12)()
+       IntegerNode(11...12)(),
+       (7...10)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/const_3_op_asgn_or.txt b/test/snapshots/seattlerb/const_3_op_asgn_or.txt
index c9d4dbfe..60b59b6f 100644
--- a/test/snapshots/seattlerb/const_3_op_asgn_or.txt
+++ b/test/snapshots/seattlerb/const_3_op_asgn_or.txt
@@ -1,10 +1,10 @@
 ProgramNode(0...9)(
   [],
   StatementsNode(0...9)(
-    [ConstantPathOperatorOrWriteNode(0...9)(
+    [OrWriteNode(0...9)(
        ConstantPathNode(0...3)(nil, ConstantReadNode(2...3)(), (0...2)),
-       (4...7),
-       IntegerNode(8...9)()
+       IntegerNode(8...9)(),
+       (4...7)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/const_op_asgn_and1.txt b/test/snapshots/seattlerb/const_op_asgn_and1.txt
index 1e7dce02..e2ba0e9a 100644
--- a/test/snapshots/seattlerb/const_op_asgn_and1.txt
+++ b/test/snapshots/seattlerb/const_op_asgn_and1.txt
@@ -1,11 +1,11 @@
 ProgramNode(0...8)(
   [],
   StatementsNode(0...8)(
-    [ConstantPathOperatorWriteNode(0...8)(
+    [OperatorWriteNode(0...8)(
        ConstantPathNode(0...3)(nil, ConstantReadNode(2...3)(), (0...2)),
        (4...6),
-       IntegerNode(7...8)(),
-       :&
+       :&,
+       IntegerNode(7...8)()
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/const_op_asgn_and2.txt b/test/snapshots/seattlerb/const_op_asgn_and2.txt
index 85739dac..6c9c8157 100644
--- a/test/snapshots/seattlerb/const_op_asgn_and2.txt
+++ b/test/snapshots/seattlerb/const_op_asgn_and2.txt
@@ -1,10 +1,10 @@
 ProgramNode(0...9)(
   [],
   StatementsNode(0...9)(
-    [ConstantPathOperatorAndWriteNode(0...9)(
+    [AndWriteNode(0...9)(
        ConstantPathNode(0...3)(nil, ConstantReadNode(2...3)(), (0...2)),
-       (4...7),
-       IntegerNode(8...9)()
+       IntegerNode(8...9)(),
+       (4...7)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/const_op_asgn_or.txt b/test/snapshots/seattlerb/const_op_asgn_or.txt
index d1bfe6cd..8a42b824 100644
--- a/test/snapshots/seattlerb/const_op_asgn_or.txt
+++ b/test/snapshots/seattlerb/const_op_asgn_or.txt
@@ -1,14 +1,14 @@
 ProgramNode(0...10)(
   [],
   StatementsNode(0...10)(
-    [ConstantPathOperatorOrWriteNode(0...10)(
+    [OrWriteNode(0...10)(
        ConstantPathNode(0...4)(
          ConstantReadNode(0...1)(),
          ConstantReadNode(3...4)(),
          (1...3)
        ),
-       (5...8),
-       IntegerNode(9...10)()
+       IntegerNode(9...10)(),
+       (5...8)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/messy_op_asgn_lineno.txt b/test/snapshots/seattlerb/messy_op_asgn_lineno.txt
index 71a28870..a27ed4b8 100644
--- a/test/snapshots/seattlerb/messy_op_asgn_lineno.txt
+++ b/test/snapshots/seattlerb/messy_op_asgn_lineno.txt
@@ -9,13 +9,14 @@ ProgramNode(0...15)(
        ArgumentsNode(2...15)(
          [ParenthesesNode(2...15)(
             StatementsNode(3...14)(
-              [ConstantPathOperatorWriteNode(3...14)(
+              [OperatorWriteNode(3...14)(
                  ConstantPathNode(3...7)(
                    ConstantReadNode(3...4)(),
                    ConstantReadNode(6...7)(),
                    (4...6)
                  ),
                  (8...10),
+                 :*,
                  CallNode(11...14)(
                    nil,
                    nil,
@@ -38,8 +39,7 @@ ProgramNode(0...15)(
                    nil,
                    0,
                    "d"
-                 ),
-                 :*
+                 )
                )]
             ),
             (2...3),
diff --git a/test/snapshots/seattlerb/op_asgn_command_call.txt b/test/snapshots/seattlerb/op_asgn_command_call.txt
index 1daadaab..6d7177dc 100644
--- a/test/snapshots/seattlerb/op_asgn_command_call.txt
+++ b/test/snapshots/seattlerb/op_asgn_command_call.txt
@@ -1,9 +1,8 @@
 ProgramNode(0...11)(
   [:a],
   StatementsNode(0...11)(
-    [LocalVariableOperatorOrWriteNode(0...11)(
-       (0...1),
-       (2...5),
+    [OrWriteNode(0...11)(
+       LocalVariableReadNode(0...1)(:a, 0),
        CallNode(6...11)(
          CallNode(6...7)(nil, nil, (6...7), nil, nil, nil, nil, 2, "b"),
          (7...8),
@@ -15,7 +14,7 @@ ProgramNode(0...11)(
          0,
          "c"
        ),
-       :a
+       (2...5)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt b/test/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
index 0cd37752..a69dc2e6 100644
--- a/test/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
+++ b/test/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
@@ -1,13 +1,14 @@
 ProgramNode(0...11)(
   [],
   StatementsNode(0...11)(
-    [ConstantPathOperatorWriteNode(0...11)(
+    [OperatorWriteNode(0...11)(
        ConstantPathNode(0...4)(
          ConstantReadNode(0...1)(),
          ConstantReadNode(3...4)(),
          (1...3)
        ),
        (5...7),
+       :*,
        CallNode(8...11)(
          nil,
          nil,
@@ -20,8 +21,7 @@ ProgramNode(0...11)(
          nil,
          0,
          "c"
-       ),
-       :*
+       )
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/parse_line_defn_complex.txt b/test/snapshots/seattlerb/parse_line_defn_complex.txt
index adf81b25..4da4f25e 100644
--- a/test/snapshots/seattlerb/parse_line_defn_complex.txt
+++ b/test/snapshots/seattlerb/parse_line_defn_complex.txt
@@ -25,12 +25,11 @@ ProgramNode(0...40)(
             0,
             "p"
           ),
-          LocalVariableOperatorWriteNode(18...24)(
-            (18...19),
+          OperatorWriteNode(18...24)(
+            LocalVariableReadNode(18...19)(:y, 0),
             (20...22),
-            IntegerNode(23...24)(),
-            :y,
-            :*
+            :*,
+            IntegerNode(23...24)()
           ),
           ReturnNode(27...35)(
             (27...33),
diff --git a/test/snapshots/seattlerb/parse_line_op_asgn.txt b/test/snapshots/seattlerb/parse_line_op_asgn.txt
index 011858c3..8763aab9 100644
--- a/test/snapshots/seattlerb/parse_line_op_asgn.txt
+++ b/test/snapshots/seattlerb/parse_line_op_asgn.txt
@@ -1,12 +1,11 @@
 ProgramNode(6...34)(
   [:foo],
   StatementsNode(6...34)(
-    [LocalVariableOperatorWriteNode(6...24)(
-       (6...9),
+    [OperatorWriteNode(6...24)(
+       LocalVariableReadNode(6...9)(:foo, 0),
        (10...12),
-       CallNode(21...24)(nil, nil, (21...24), nil, nil, nil, nil, 2, "bar"),
-       :foo,
-       :+
+       :+,
+       CallNode(21...24)(nil, nil, (21...24), nil, nil, nil, nil, 2, "bar")
      ),
      CallNode(31...34)(nil, nil, (31...34), nil, nil, nil, nil, 2, "baz")]
   )
diff --git a/test/snapshots/unparser/corpus/literal/assignment.txt b/test/snapshots/unparser/corpus/literal/assignment.txt
index 1423b7e4..63b60ba9 100644
--- a/test/snapshots/unparser/corpus/literal/assignment.txt
+++ b/test/snapshots/unparser/corpus/literal/assignment.txt
@@ -610,10 +610,10 @@ ProgramNode(0...704)(
        ),
        (543...546)
      ),
-     InstanceVariableOperatorOrWriteNode(551...561)(
-       (551...553),
-       (554...557),
-       StringNode(558...561)((558...560), (560...560), (560...561), "")
+     OrWriteNode(551...561)(
+       InstanceVariableReadNode(551...553)(),
+       StringNode(558...561)((558...560), (560...560), (560...561), ""),
+       (554...557)
      ),
      LocalVariableWriteNode(562...576)(
        :x,
@@ -703,16 +703,16 @@ ProgramNode(0...704)(
        ),
        (665...668)
      ),
-     InstanceVariableOperatorOrWriteNode(687...704)(
-       (687...689),
-       (690...693),
+     OrWriteNode(687...704)(
+       InstanceVariableReadNode(687...689)(),
        InterpolatedStringNode(694...704)(
          (694...704),
          [StringNode(705...707)(nil, (705...707), nil, "  "),
           EmbeddedStatementsNode(707...710)((707...709), nil, (709...710)),
           StringNode(710...711)(nil, (710...711), nil, "\n")],
          (711...719)
-       )
+       ),
+       (690...693)
      )]
   )
 )
diff --git a/test/snapshots/unparser/corpus/literal/opasgn.txt b/test/snapshots/unparser/corpus/literal/opasgn.txt
index 04f547fc..21933e30 100644
--- a/test/snapshots/unparser/corpus/literal/opasgn.txt
+++ b/test/snapshots/unparser/corpus/literal/opasgn.txt
@@ -1,61 +1,53 @@
 ProgramNode(0...233)(
   [:a, :h],
   StatementsNode(0...233)(
-    [LocalVariableOperatorWriteNode(0...6)(
-       (0...1),
+    [OperatorWriteNode(0...6)(
+       LocalVariableReadNode(0...1)(:a, 0),
        (2...4),
-       IntegerNode(5...6)(),
-       :a,
-       :+
+       :+,
+       IntegerNode(5...6)()
      ),
-     LocalVariableOperatorWriteNode(7...13)(
-       (7...8),
+     OperatorWriteNode(7...13)(
+       LocalVariableReadNode(7...8)(:a, 0),
        (9...11),
-       IntegerNode(12...13)(),
-       :a,
-       :-
+       :-,
+       IntegerNode(12...13)()
      ),
-     LocalVariableOperatorWriteNode(14...21)(
-       (14...15),
+     OperatorWriteNode(14...21)(
+       LocalVariableReadNode(14...15)(:a, 0),
        (16...19),
-       IntegerNode(20...21)(),
-       :a,
-       :**
+       :**,
+       IntegerNode(20...21)()
      ),
-     LocalVariableOperatorWriteNode(22...28)(
-       (22...23),
+     OperatorWriteNode(22...28)(
+       LocalVariableReadNode(22...23)(:a, 0),
        (24...26),
-       IntegerNode(27...28)(),
-       :a,
-       :*
+       :*,
+       IntegerNode(27...28)()
      ),
-     LocalVariableOperatorWriteNode(29...35)(
-       (29...30),
+     OperatorWriteNode(29...35)(
+       LocalVariableReadNode(29...30)(:a, 0),
        (31...33),
-       IntegerNode(34...35)(),
-       :a,
-       :/
+       :/,
+       IntegerNode(34...35)()
      ),
-     LocalVariableOperatorAndWriteNode(36...43)(
-       (36...37),
-       (38...41),
+     AndWriteNode(36...43)(
+       LocalVariableReadNode(36...37)(:a, 0),
        CallNode(42...43)(nil, nil, (42...43), nil, nil, nil, nil, 2, "b"),
-       :a
+       (38...41)
      ),
-     LocalVariableOperatorOrWriteNode(44...51)(
-       (44...45),
-       (46...49),
+     OrWriteNode(44...51)(
+       LocalVariableReadNode(44...45)(:a, 0),
        IntegerNode(50...51)(),
-       :a
+       (46...49)
      ),
      CallNode(52...65)(
        ParenthesesNode(52...61)(
          StatementsNode(53...60)(
-           [LocalVariableOperatorOrWriteNode(53...60)(
-              (53...54),
-              (55...58),
+           [OrWriteNode(53...60)(
+              LocalVariableReadNode(53...54)(:a, 0),
               IntegerNode(59...60)(),
-              :a
+              (55...58)
             )]
          ),
          (52...53),
@@ -73,11 +65,10 @@ ProgramNode(0...233)(
      CallNode(66...83)(
        ParenthesesNode(66...76)(
          StatementsNode(67...75)(
-           [LocalVariableOperatorOrWriteNode(67...75)(
-              (67...68),
-              (69...72),
+           [OrWriteNode(67...75)(
+              LocalVariableReadNode(67...68)(:h, 0),
               HashNode(73...75)((73...74), [], (74...75)),
-              :h
+              (69...72)
             )]
          ),
          (66...67),
diff --git a/test/snapshots/unparser/corpus/literal/send.txt b/test/snapshots/unparser/corpus/literal/send.txt
index d41d29f7..a3f5e988 100644
--- a/test/snapshots/unparser/corpus/literal/send.txt
+++ b/test/snapshots/unparser/corpus/literal/send.txt
@@ -6,9 +6,8 @@ ProgramNode(0...999)(
        (0...6),
        ConstantReadNode(7...8)(),
        StatementsNode(11...31)(
-         [LocalVariableOperatorOrWriteNode(11...31)(
-            (11...14),
-            (15...18),
+         [OrWriteNode(11...31)(
+            LocalVariableReadNode(11...14)(:foo, 0),
             ParenthesesNode(19...31)(
               StatementsNode(21...30)(
                 [MultiWriteNode(21...30)(
@@ -45,7 +44,7 @@ ProgramNode(0...999)(
               (19...20),
               (30...31)
             ),
-            :foo
+            (15...18)
           )]
        ),
        (32...35)
diff --git a/test/snapshots/whitequark/const_op_asgn.txt b/test/snapshots/whitequark/const_op_asgn.txt
index ef5e755c..a9da130f 100644
--- a/test/snapshots/whitequark/const_op_asgn.txt
+++ b/test/snapshots/whitequark/const_op_asgn.txt
@@ -1,41 +1,41 @@
 ProgramNode(0...77)(
   [],
   StatementsNode(0...77)(
-    [ConstantPathOperatorWriteNode(0...8)(
+    [OperatorWriteNode(0...8)(
        ConstantPathNode(0...3)(nil, ConstantReadNode(2...3)(), (0...2)),
        (4...6),
-       IntegerNode(7...8)(),
-       :+
+       :+,
+       IntegerNode(7...8)()
      ),
-     ConstantOperatorWriteNode(10...16)(
-       (10...11),
+     OperatorWriteNode(10...16)(
+       ConstantReadNode(10...11)(),
        (12...14),
-       IntegerNode(15...16)(),
-       :+
+       :+,
+       IntegerNode(15...16)()
      ),
-     ConstantPathOperatorWriteNode(18...27)(
+     OperatorWriteNode(18...27)(
        ConstantPathNode(18...22)(
          ConstantReadNode(18...19)(),
          ConstantReadNode(21...22)(),
          (19...21)
        ),
        (23...25),
-       IntegerNode(26...27)(),
-       :+
+       :+,
+       IntegerNode(26...27)()
      ),
      DefNode(29...50)(
        (33...34),
        nil,
        nil,
        StatementsNode(36...45)(
-         [ConstantPathOperatorOrWriteNode(36...45)(
+         [OrWriteNode(36...45)(
             ConstantPathNode(36...39)(
               nil,
               ConstantReadNode(38...39)(),
               (36...38)
             ),
-            (40...43),
-            IntegerNode(44...45)()
+            IntegerNode(44...45)(),
+            (40...43)
           )]
        ),
        [],
@@ -51,14 +51,14 @@ ProgramNode(0...77)(
        nil,
        nil,
        StatementsNode(59...72)(
-         [ConstantPathOperatorOrWriteNode(59...72)(
+         [OrWriteNode(59...72)(
             ConstantPathNode(59...66)(
               SelfNode(59...63)(),
               ConstantReadNode(65...66)(),
               (63...65)
             ),
-            (67...70),
-            IntegerNode(71...72)()
+            IntegerNode(71...72)(),
+            (67...70)
           )]
        ),
        [],
diff --git a/test/snapshots/whitequark/op_asgn_cmd.txt b/test/snapshots/whitequark/op_asgn_cmd.txt
index 409aa26e..116d641b 100644
--- a/test/snapshots/whitequark/op_asgn_cmd.txt
+++ b/test/snapshots/whitequark/op_asgn_cmd.txt
@@ -77,13 +77,14 @@ ProgramNode(0...64)(
        ),
        :+
      ),
-     ConstantPathOperatorWriteNode(32...47)(
+     OperatorWriteNode(32...47)(
        ConstantPathNode(32...38)(
          CallNode(32...35)(nil, nil, (32...35), nil, nil, nil, nil, 2, "foo"),
          ConstantReadNode(37...38)(),
          (35...37)
        ),
        (39...41),
+       :+,
        CallNode(42...47)(
          nil,
          nil,
@@ -106,8 +107,7 @@ ProgramNode(0...64)(
          nil,
          0,
          "m"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(49...64)(
        CallNode(49...55)(
diff --git a/test/snapshots/whitequark/rescue_mod_op_assign.txt b/test/snapshots/whitequark/rescue_mod_op_assign.txt
index 051ead12..14125cec 100644
--- a/test/snapshots/whitequark/rescue_mod_op_assign.txt
+++ b/test/snapshots/whitequark/rescue_mod_op_assign.txt
@@ -1,16 +1,15 @@
 ProgramNode(0...22)(
   [:foo],
   StatementsNode(0...22)(
-    [LocalVariableOperatorWriteNode(0...22)(
-       (0...3),
+    [OperatorWriteNode(0...22)(
+       LocalVariableReadNode(0...3)(:foo, 0),
        (4...6),
+       :+,
        RescueModifierNode(7...22)(
          CallNode(7...11)(nil, nil, (7...11), nil, nil, nil, nil, 2, "meth"),
          (12...18),
          CallNode(19...22)(nil, nil, (19...22), nil, nil, nil, nil, 2, "bar")
-       ),
-       :foo,
-       :+
+       )
      )]
   )
 )
diff --git a/test/snapshots/whitequark/ruby_bug_12402.txt b/test/snapshots/whitequark/ruby_bug_12402.txt
index fb00d826..9573dc5f 100644
--- a/test/snapshots/whitequark/ruby_bug_12402.txt
+++ b/test/snapshots/whitequark/ruby_bug_12402.txt
@@ -1,9 +1,10 @@
 ProgramNode(0...437)(
   [:foo],
   StatementsNode(0...437)(
-    [LocalVariableOperatorWriteNode(0...27)(
-       (0...3),
+    [OperatorWriteNode(0...27)(
+       LocalVariableReadNode(0...3)(:foo, 0),
        (4...6),
+       :+,
        CallNode(7...27)(
          nil,
          nil,
@@ -30,13 +31,12 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       ),
-       :foo,
-       :+
+       )
      ),
-     LocalVariableOperatorWriteNode(29...57)(
-       (29...32),
+     OperatorWriteNode(29...57)(
+       LocalVariableReadNode(29...32)(:foo, 0),
        (33...35),
+       :+,
        RescueModifierNode(36...57)(
          CallNode(36...46)(
            nil,
@@ -63,9 +63,7 @@ ProgramNode(0...437)(
          ),
          (47...53),
          NilNode(54...57)()
-       ),
-       :foo,
-       :+
+       )
      ),
      LocalVariableWriteNode(59...85)(
        :foo,
@@ -301,13 +299,12 @@ ProgramNode(0...437)(
        ),
        :+
      ),
-     ConstantPathOperatorOrWriteNode(242...273)(
+     OrWriteNode(242...273)(
        ConstantPathNode(242...248)(
          LocalVariableReadNode(242...245)(:foo, 0),
          ConstantReadNode(247...248)(),
          (245...247)
        ),
-       (249...252),
        CallNode(253...273)(
          nil,
          nil,
@@ -334,15 +331,15 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       )
+       ),
+       (249...252)
      ),
-     ConstantPathOperatorOrWriteNode(275...307)(
+     OrWriteNode(275...307)(
        ConstantPathNode(275...281)(
          LocalVariableReadNode(275...278)(:foo, 0),
          ConstantReadNode(280...281)(),
          (278...280)
        ),
-       (282...285),
        RescueModifierNode(286...307)(
          CallNode(286...296)(
            nil,
@@ -369,7 +366,8 @@ ProgramNode(0...437)(
          ),
          (297...303),
          NilNode(304...307)()
-       )
+       ),
+       (282...285)
      ),
      CallOperatorWriteNode(309...339)(
        CallNode(309...315)(
diff --git a/test/snapshots/whitequark/ruby_bug_12669.txt b/test/snapshots/whitequark/ruby_bug_12669.txt
index fc6b673c..c7932a40 100644
--- a/test/snapshots/whitequark/ruby_bug_12669.txt
+++ b/test/snapshots/whitequark/ruby_bug_12669.txt
@@ -1,12 +1,14 @@
 ProgramNode(0...74)(
   [:a, :b],
   StatementsNode(0...74)(
-    [LocalVariableOperatorWriteNode(0...18)(
-       (0...1),
+    [OperatorWriteNode(0...18)(
+       LocalVariableReadNode(0...1)(:a, 0),
        (2...4),
-       LocalVariableOperatorWriteNode(5...18)(
-         (5...6),
+       :+,
+       OperatorWriteNode(5...18)(
+         LocalVariableReadNode(5...6)(:b, 0),
          (7...9),
+         :+,
          CallNode(10...18)(
            nil,
            nil,
@@ -19,16 +21,13 @@ ProgramNode(0...74)(
            nil,
            0,
            "raise"
-         ),
-         :b,
-         :+
-       ),
-       :a,
-       :+
+         )
+       )
      ),
-     LocalVariableOperatorWriteNode(20...37)(
-       (20...21),
+     OperatorWriteNode(20...37)(
+       LocalVariableReadNode(20...21)(:a, 0),
        (22...24),
+       :+,
        LocalVariableWriteNode(25...37)(
          :b,
          0,
@@ -47,16 +46,15 @@ ProgramNode(0...74)(
          ),
          (25...26),
          (27...28)
-       ),
-       :a,
-       :+
+       )
      ),
      LocalVariableWriteNode(39...56)(
        :a,
        0,
-       LocalVariableOperatorWriteNode(43...56)(
-         (43...44),
+       OperatorWriteNode(43...56)(
+         LocalVariableReadNode(43...44)(:b, 0),
          (45...47),
+         :+,
          CallNode(48...56)(
            nil,
            nil,
@@ -69,9 +67,7 @@ ProgramNode(0...74)(
            nil,
            0,
            "raise"
-         ),
-         :b,
-         :+
+         )
        ),
        (39...40),
        (41...42)
diff --git a/test/snapshots/whitequark/var_and_asgn.txt b/test/snapshots/whitequark/var_and_asgn.txt
index 76469d6b..8d476a81 100644
--- a/test/snapshots/whitequark/var_and_asgn.txt
+++ b/test/snapshots/whitequark/var_and_asgn.txt
@@ -1,11 +1,10 @@
 ProgramNode(0...7)(
   [:a],
   StatementsNode(0...7)(
-    [LocalVariableOperatorAndWriteNode(0...7)(
-       (0...1),
-       (2...5),
+    [AndWriteNode(0...7)(
+       LocalVariableReadNode(0...1)(:a, 0),
        IntegerNode(6...7)(),
-       :a
+       (2...5)
      )]
   )
 )
diff --git a/test/snapshots/whitequark/var_op_asgn.txt b/test/snapshots/whitequark/var_op_asgn.txt
index 2bc8667b..863605eb 100644
--- a/test/snapshots/whitequark/var_op_asgn.txt
+++ b/test/snapshots/whitequark/var_op_asgn.txt
@@ -1,35 +1,34 @@
 ProgramNode(0...53)(
   [:a],
   StatementsNode(0...53)(
-    [ClassVariableOperatorWriteNode(0...11)(
-       (0...5),
+    [OperatorWriteNode(0...11)(
+       ClassVariableReadNode(0...5)(),
        (6...8),
-       IntegerNode(9...11)(),
-       :|
+       :|,
+       IntegerNode(9...11)()
      ),
-     InstanceVariableOperatorWriteNode(13...20)(
-       (13...15),
+     OperatorWriteNode(13...20)(
+       InstanceVariableReadNode(13...15)(),
        (16...18),
-       IntegerNode(19...20)(),
-       :|
+       :|,
+       IntegerNode(19...20)()
      ),
-     LocalVariableOperatorWriteNode(22...28)(
-       (22...23),
+     OperatorWriteNode(22...28)(
+       LocalVariableReadNode(22...23)(:a, 0),
        (24...26),
-       IntegerNode(27...28)(),
-       :a,
-       :+
+       :+,
+       IntegerNode(27...28)()
      ),
      DefNode(30...53)(
        (34...35),
        nil,
        nil,
        StatementsNode(37...48)(
-         [ClassVariableOperatorWriteNode(37...48)(
-            (37...42),
+         [OperatorWriteNode(37...48)(
+            ClassVariableReadNode(37...42)(),
             (43...45),
-            IntegerNode(46...48)(),
-            :|
+            :|,
+            IntegerNode(46...48)()
           )]
        ),
        [],
diff --git a/test/snapshots/whitequark/var_op_asgn_cmd.txt b/test/snapshots/whitequark/var_op_asgn_cmd.txt
index 4ef661f2..4d7ef7d4 100644
--- a/test/snapshots/whitequark/var_op_asgn_cmd.txt
+++ b/test/snapshots/whitequark/var_op_asgn_cmd.txt
@@ -1,9 +1,10 @@
 ProgramNode(0...12)(
   [:foo],
   StatementsNode(0...12)(
-    [LocalVariableOperatorWriteNode(0...12)(
-       (0...3),
+    [OperatorWriteNode(0...12)(
+       LocalVariableReadNode(0...3)(:foo, 0),
        (4...6),
+       :+,
        CallNode(7...12)(
          nil,
          nil,
@@ -14,9 +15,7 @@ ProgramNode(0...12)(
          nil,
          0,
          "m"
-       ),
-       :foo,
-       :+
+       )
      )]
   )
 )
diff --git a/test/snapshots/whitequark/var_or_asgn.txt b/test/snapshots/whitequark/var_or_asgn.txt
index 39cc8238..cc029042 100644
--- a/test/snapshots/whitequark/var_or_asgn.txt
+++ b/test/snapshots/whitequark/var_or_asgn.txt
@@ -1,11 +1,10 @@
 ProgramNode(0...7)(
   [:a],
   StatementsNode(0...7)(
-    [LocalVariableOperatorOrWriteNode(0...7)(
-       (0...1),
-       (2...5),
+    [OrWriteNode(0...7)(
+       LocalVariableReadNode(0...1)(:a, 0),
        IntegerNode(6...7)(),
-       :a
+       (2...5)
      )]
   )
 )