Repository: ruby/yarp
Pull request #1405 (2023-09-05 18:24:05)

Call and operator write ergonomics
Fixes https://github.com/ruby/yarp/issues/1236

This is definitely a tradeoff, because now we don't have a single call node anymore. However, I think this is acceptable considering the call node couldn't represent that actually 2 methods get called when you have these nodes. So it would have been confusing to client libraries anyway.


Patch:
diff --git a/config.yml b/config.yml
index a7fb8651..a2066b36 100644
--- a/config.yml
+++ b/config.yml
@@ -582,11 +582,42 @@ nodes:
 
           break foo
           ^^^^^^^^^
-  - name: CallNode
+  - name: CallAndWriteNode
     fields:
       - name: receiver
         type: node?
+      - name: call_operator_loc
+        type: location?
+      - name: message_loc
+        type: location?
+      - name: opening_loc
+        type: location?
+      - name: arguments
+        type: node?
+        kind: ArgumentsNode
+      - name: closing_loc
+        type: location?
+      - name: flags
+        type: flags
+        kind: CallNodeFlags
+      - name: read_name
+        type: string
+      - name: write_name
+        type: string
       - name: operator_loc
+        type: location
+      - name: value
+        type: node
+    comment: |
+      Represents the use of the `&&=` operator on a call.
+
+          foo.bar &&= value
+          ^^^^^^^^^^^^^^^^^
+  - name: CallNode
+    fields:
+      - name: receiver
+        type: node?
+      - name: call_operator_loc
         type: location?
       - name: message_loc
         type: location?
@@ -625,50 +656,70 @@ nodes:
 
           foo&.bar
           ^^^^^^^^
-  - name: CallOperatorAndWriteNode
+  - name: CallOperatorWriteNode
     fields:
-      - name: target
-        type: node
-        kind: CallNode
+      - name: receiver
+        type: node?
+      - name: call_operator_loc
+        type: location?
+      - name: message_loc
+        type: location?
+      - name: opening_loc
+        type: location?
+      - name: arguments
+        type: node?
+        kind: ArgumentsNode
+      - name: closing_loc
+        type: location?
+      - name: flags
+        type: flags
+        kind: CallNodeFlags
+      - name: read_name
+        type: string
+      - name: write_name
+        type: string
+      - name: operator
+        type: constant
       - name: operator_loc
         type: location
       - name: value
         type: node
     comment: |
-      Represents the use of the `&&=` operator on a call.
+      Represents the use of an assignment operator on a call.
 
-          foo.bar &&= value
-          ^^^^^^^^^^^^^^^^^
-  - name: CallOperatorOrWriteNode
+          foo.bar += baz
+          ^^^^^^^^^^^^^^
+  - name: CallOrWriteNode
     fields:
-      - name: target
-        type: node
-        kind: CallNode
-      - name: value
-        type: node
+      - name: receiver
+        type: node?
+      - name: call_operator_loc
+        type: location?
+      - name: message_loc
+        type: location?
+      - name: opening_loc
+        type: location?
+      - name: arguments
+        type: node?
+        kind: ArgumentsNode
+      - name: closing_loc
+        type: location?
+      - name: flags
+        type: flags
+        kind: CallNodeFlags
+      - name: read_name
+        type: string
+      - name: write_name
+        type: string
       - name: operator_loc
         type: location
+      - name: value
+        type: node
     comment: |
       Represents the use of the `||=` operator on a call.
 
           foo.bar ||= value
           ^^^^^^^^^^^^^^^^^
-  - name: CallOperatorWriteNode
-    fields:
-      - name: target
-        type: node
-        kind: CallNode
-      - name: operator_loc
-        type: location
-      - name: value
-        type: node
-      - name: operator
-        type: constant
-    comment: |
-      Represents the use of an assignment operator on a call.
-
-          foo.bar += baz
-          ^^^^^^^^^^^^^^
   - name: CapturePatternNode
     fields:
       - name: value
diff --git a/rust/yarp/src/lib.rs b/rust/yarp/src/lib.rs
index 9c5809b0..027f9826 100644
--- a/rust/yarp/src/lib.rs
+++ b/rust/yarp/src/lib.rs
@@ -370,8 +370,8 @@ end
         let call = asgn.as_local_variable_write_node().unwrap().value();
         let call = call.as_call_node().unwrap();
 
-        let operator_loc = call.operator_loc();
-        assert!(operator_loc.is_none());
+        let call_operator_loc = call.call_operator_loc();
+        assert!(call_operator_loc.is_none());
         let closing_loc = call.closing_loc();
         assert!(closing_loc.is_some());
 
@@ -379,8 +379,8 @@ end
         let call = asgn.as_local_variable_write_node().unwrap().value();
         let call = call.as_call_node().unwrap();
 
-        let operator_loc = call.operator_loc();
-        assert!(operator_loc.is_some());
+        let call_operator_loc = call.call_operator_loc();
+        assert!(call_operator_loc.is_some());
         let closing_loc = call.closing_loc();
         assert!(closing_loc.is_none());
     }
diff --git a/src/yarp.c b/src/yarp.c
index 45c26471..4bd158cf 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -1234,7 +1234,7 @@ yp_call_node_create(yp_parser_t *parser) {
             .location = YP_LOCATION_NULL_VALUE(parser),
         },
         .receiver = NULL,
-        .operator_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
+        .call_operator_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
         .message_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
         .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
         .arguments = NULL,
@@ -1307,7 +1307,7 @@ yp_call_node_call_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *o
     }
 
     node->receiver = receiver;
-    node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+    node->call_operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
     node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(message);
     node->opening_loc = arguments->opening_loc;
     node->arguments = arguments->arguments;
@@ -1384,7 +1384,7 @@ yp_call_node_shorthand_create(yp_parser_t *parser, yp_node_t *receiver, yp_token
     }
 
     node->receiver = receiver;
-    node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
+    node->call_operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
     node->opening_loc = arguments->opening_loc;
     node->arguments = arguments->arguments;
     node->closing_loc = arguments->closing_loc;
@@ -1435,69 +1435,128 @@ yp_call_node_variable_call_p(yp_call_node_t *node) {
     return node->base.flags & YP_CALL_NODE_FLAGS_VARIABLE_CALL;
 }
 
-// Allocate and initialize a new CallOperatorAndWriteNode node.
-static yp_call_operator_and_write_node_t *
-yp_call_operator_and_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+// Initialize the read name by reading the write name and chopping off the '='.
+static void
+yp_call_write_read_name_init(yp_string_t *read_name, yp_string_t *write_name) {
+    size_t length = write_name->length - 1;
+
+    void *memory = malloc(length);
+    memcpy(memory, write_name->source, length);
+
+    yp_string_owned_init(read_name, (uint8_t *) memory, length);
+}
+
+// Allocate and initialize a new CallAndWriteNode node.
+static yp_call_and_write_node_t *
+yp_call_and_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    assert(target->block == NULL);
     assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
-    yp_call_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_and_write_node_t);
+    yp_call_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_and_write_node_t);
 
-    *node = (yp_call_operator_and_write_node_t) {
+    *node = (yp_call_and_write_node_t) {
         {
-            .type = YP_NODE_CALL_OPERATOR_AND_WRITE_NODE,
+            .type = YP_NODE_CALL_AND_WRITE_NODE,
+            .flags = target->base.flags,
             .location = {
                 .start = target->base.location.start,
                 .end = value->location.end
             }
         },
-        .target = target,
+        .receiver = target->receiver,
+        .call_operator_loc = target->call_operator_loc,
+        .message_loc = target->message_loc,
+        .opening_loc = target->opening_loc,
+        .arguments = target->arguments,
+        .closing_loc = target->closing_loc,
+        .read_name = YP_EMPTY_STRING,
+        .write_name = target->name,
         .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
         .value = value
     };
 
+    yp_call_write_read_name_init(&node->read_name, &node->write_name);
+
+    // Here we're going to free the target, since it is no longer necessary.
+    // However, we don't want to call `yp_node_destroy` because we want to keep
+    // around all of its children since we just reused them.
+    free(target);
+
     return node;
 }
 
 // Allocate a new CallOperatorWriteNode node.
 static yp_call_operator_write_node_t *
 yp_call_operator_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    assert(target->block == NULL);
     yp_call_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_write_node_t);
 
     *node = (yp_call_operator_write_node_t) {
         {
             .type = YP_NODE_CALL_OPERATOR_WRITE_NODE,
+            .flags = target->base.flags,
             .location = {
                 .start = target->base.location.start,
                 .end = value->location.end
             }
         },
-        .target = target,
+        .receiver = target->receiver,
+        .call_operator_loc = target->call_operator_loc,
+        .message_loc = target->message_loc,
+        .opening_loc = target->opening_loc,
+        .arguments = target->arguments,
+        .closing_loc = target->closing_loc,
+        .read_name = YP_EMPTY_STRING,
+        .write_name = target->name,
+        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1),
         .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
-        .value = value,
-        .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
+        .value = value
     };
 
+    yp_call_write_read_name_init(&node->read_name, &node->write_name);
+
+    // Here we're going to free the target, since it is no longer necessary.
+    // However, we don't want to call `yp_node_destroy` because we want to keep
+    // around all of its children since we just reused them.
+    free(target);
+
     return node;
 }
 
 // Allocate and initialize a new CallOperatorOrWriteNode node.
-static yp_call_operator_or_write_node_t *
-yp_call_operator_or_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+static yp_call_or_write_node_t *
+yp_call_or_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) {
+    assert(target->block == NULL);
     assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
-    yp_call_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_or_write_node_t);
+    yp_call_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_or_write_node_t);
 
-    *node = (yp_call_operator_or_write_node_t) {
+    *node = (yp_call_or_write_node_t) {
         {
-            .type = YP_NODE_CALL_OPERATOR_OR_WRITE_NODE,
+            .type = YP_NODE_CALL_OR_WRITE_NODE,
+            .flags = target->base.flags,
             .location = {
                 .start = target->base.location.start,
                 .end = value->location.end
             }
         },
-        .target = target,
+        .receiver = target->receiver,
+        .call_operator_loc = target->call_operator_loc,
+        .message_loc = target->message_loc,
+        .opening_loc = target->opening_loc,
+        .arguments = target->arguments,
+        .closing_loc = target->closing_loc,
+        .read_name = YP_EMPTY_STRING,
+        .write_name = target->name,
         .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
         .value = value
     };
 
+    yp_call_write_read_name_init(&node->read_name, &node->write_name);
+
+    // Here we're going to free the target, since it is no longer necessary.
+    // However, we don't want to call `yp_node_destroy` because we want to keep
+    // around all of its children since we just reused them.
+    free(target);
+
     return node;
 }
 
@@ -8057,7 +8116,7 @@ parse_target(yp_parser_t *parser, yp_node_t *target) {
             // an aref expression, and we can transform it into an aset
             // expression.
             if (
-                (call->operator_loc.start == NULL) &&
+                (call->call_operator_loc.start == NULL) &&
                 (call->message_loc.start[0] == '[') &&
                 (call->message_loc.end[-1] == ']') &&
                 (call->block == NULL)
@@ -8213,7 +8272,7 @@ parse_write(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_nod
             // an aref expression, and we can transform it into an aset
             // expression.
             if (
-                (call->operator_loc.start == NULL) &&
+                (call->call_operator_loc.start == NULL) &&
                 (call->message_loc.start[0] == '[') &&
                 (call->message_loc.end[-1] == ']') &&
                 (call->block == NULL)
@@ -12978,7 +13037,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                     node = parse_target(parser, node);
 
                     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);
+                    return (yp_node_t *) yp_call_and_write_node_create(parser, (yp_call_node_t *) node, &token, value);
                 }
                 case YP_NODE_MULTI_WRITE_NODE: {
                     parser_lex(parser);
@@ -13079,7 +13138,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
                     node = parse_target(parser, node);
 
                     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);
+                    return (yp_node_t *) yp_call_or_write_node_create(parser, (yp_call_node_t *) node, &token, value);
                 }
                 case YP_NODE_MULTI_WRITE_NODE: {
                     parser_lex(parser);


Test patch:
diff --git a/test/yarp/location_test.rb b/test/yarp/location_test.rb
index 192cfdd7..0902c122 100644
--- a/test/yarp/location_test.rb
+++ b/test/yarp/location_test.rb
@@ -166,9 +166,9 @@ module YARP
       assert_location(CallNode, "foo bar('baz')")
     end
 
-    def test_CallOperatorAndWriteNode
-      assert_location(CallOperatorAndWriteNode, "foo.foo &&= bar")
-      assert_location(CallOperatorAndWriteNode, "foo[foo] &&= bar")
+    def test_CallAndWriteNode
+      assert_location(CallAndWriteNode, "foo.foo &&= bar")
+      assert_location(CallAndWriteNode, "foo[foo] &&= bar")
     end
 
     def test_CallOperatorWriteNode
@@ -176,9 +176,9 @@ module YARP
       assert_location(CallOperatorWriteNode, "foo[foo] += bar")
     end
 
-    def test_CallOperatorOrWriteNode
-      assert_location(CallOperatorOrWriteNode, "foo.foo ||= bar")
-      assert_location(CallOperatorOrWriteNode, "foo[foo] ||= bar")
+    def test_CallOrWriteNode
+      assert_location(CallOrWriteNode, "foo.foo ||= bar")
+      assert_location(CallOrWriteNode, "foo[foo] ||= bar")
     end
 
     def test_CapturePatternNode
diff --git a/test/yarp/snapshots/seattlerb/index_0_opasgn.txt b/test/yarp/snapshots/seattlerb/index_0_opasgn.txt
index 1f6d2797..60c76d0d 100644
--- a/test/yarp/snapshots/seattlerb/index_0_opasgn.txt
+++ b/test/yarp/snapshots/seattlerb/index_0_opasgn.txt
@@ -2,20 +2,18 @@ ProgramNode(0...8)(
   [],
   StatementsNode(0...8)(
     [CallOperatorWriteNode(0...8)(
-       CallNode(0...3)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         nil,
-         (1...3),
-         (1...2),
-         nil,
-         (2...3),
-         nil,
-         0,
-         "[]="
-       ),
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       nil,
+       (1...3),
+       (1...2),
+       nil,
+       (2...3),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (4...6),
-       CallNode(7...8)(nil, nil, (7...8), nil, nil, nil, nil, 2, "b"),
-       :+
+       CallNode(7...8)(nil, nil, (7...8), nil, nil, nil, nil, 2, "b")
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt b/test/yarp/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt
index 0bb9c07e..a33ceae8 100644
--- a/test/yarp/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt
+++ b/test/yarp/snapshots/seattlerb/op_asgn_dot_ident_command_call.txt
@@ -1,18 +1,17 @@
 ProgramNode(0...11)(
   [],
   StatementsNode(0...11)(
-    [CallOperatorOrWriteNode(0...11)(
-       CallNode(0...3)(
-         ConstantReadNode(0...1)(),
-         (1...2),
-         (2...3),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "B="
-       ),
+    [CallOrWriteNode(0...11)(
+       ConstantReadNode(0...1)(),
+       (1...2),
+       (2...3),
+       nil,
+       nil,
+       nil,
+       0,
+       "B",
+       "B=",
+       (4...7),
        CallNode(8...11)(
          nil,
          nil,
@@ -23,8 +22,7 @@ ProgramNode(0...11)(
          nil,
          0,
          "c"
-       ),
-       (4...7)
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/op_asgn_index_command_call.txt b/test/yarp/snapshots/seattlerb/op_asgn_index_command_call.txt
index 5ae2a484..e166f958 100644
--- a/test/yarp/snapshots/seattlerb/op_asgn_index_command_call.txt
+++ b/test/yarp/snapshots/seattlerb/op_asgn_index_command_call.txt
@@ -1,18 +1,17 @@
 ProgramNode(0...16)(
   [],
   StatementsNode(0...16)(
-    [CallOperatorOrWriteNode(0...16)(
-       CallNode(0...5)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         nil,
-         (1...5),
-         (1...2),
-         ArgumentsNode(2...4)([SymbolNode(2...4)((2...3), (3...4), nil, "b")]),
-         (4...5),
-         nil,
-         0,
-         "[]="
-       ),
+    [CallOrWriteNode(0...16)(
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       nil,
+       (1...5),
+       (1...2),
+       ArgumentsNode(2...4)([SymbolNode(2...4)((2...3), (3...4), nil, "b")]),
+       (4...5),
+       0,
+       "[]",
+       "[]=",
+       (6...9),
        CallNode(10...16)(
          nil,
          nil,
@@ -25,8 +24,7 @@ ProgramNode(0...16)(
          nil,
          0,
          "c"
-       ),
-       (6...9)
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt b/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
index 88847480..85673bab 100644
--- a/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
+++ b/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
@@ -2,20 +2,18 @@ ProgramNode(0...9)(
   [],
   StatementsNode(0...9)(
     [CallOperatorWriteNode(0...9)(
-       CallNode(0...4)(
-         ConstantReadNode(0...1)(),
-         (1...3),
-         (3...4),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       ConstantReadNode(0...1)(),
+       (1...3),
+       (3...4),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :+,
        (5...7),
-       IntegerNode(8...9)(),
-       :+
+       IntegerNode(8...9)()
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt b/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
index 5f369122..741620d8 100644
--- a/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
+++ b/test/yarp/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
@@ -2,17 +2,16 @@ ProgramNode(0...11)(
   [],
   StatementsNode(0...11)(
     [CallOperatorWriteNode(0...11)(
-       CallNode(0...4)(
-         ConstantReadNode(0...1)(),
-         (1...3),
-         (3...4),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       ConstantReadNode(0...1)(),
+       (1...3),
+       (3...4),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :*,
        (5...7),
        CallNode(8...11)(
          nil,
@@ -26,8 +25,7 @@ ProgramNode(0...11)(
          nil,
          0,
          "c"
-       ),
-       :*
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt b/test/yarp/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt
index 2d5ef0f0..f075b4bb 100644
--- a/test/yarp/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt
+++ b/test/yarp/snapshots/seattlerb/op_asgn_val_dot_ident_command_call.txt
@@ -1,18 +1,17 @@
 ProgramNode(0...11)(
   [],
   StatementsNode(0...11)(
-    [CallOperatorOrWriteNode(0...11)(
-       CallNode(0...3)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         (1...2),
-         (2...3),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+    [CallOrWriteNode(0...11)(
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       (1...2),
+       (2...3),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       (4...7),
        CallNode(8...11)(
          nil,
          nil,
@@ -23,8 +22,7 @@ ProgramNode(0...11)(
          nil,
          0,
          "c"
-       ),
-       (4...7)
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/safe_op_asgn.txt b/test/yarp/snapshots/seattlerb/safe_op_asgn.txt
index 50e03a15..e5bc6176 100644
--- a/test/yarp/snapshots/seattlerb/safe_op_asgn.txt
+++ b/test/yarp/snapshots/seattlerb/safe_op_asgn.txt
@@ -2,17 +2,16 @@ ProgramNode(0...11)(
   [],
   StatementsNode(0...11)(
     [CallOperatorWriteNode(0...11)(
-       CallNode(0...4)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         (1...3),
-         (3...4),
-         nil,
-         nil,
-         nil,
-         nil,
-         1,
-         "b="
-       ),
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       (1...3),
+       (3...4),
+       nil,
+       nil,
+       nil,
+       1,
+       "b",
+       "b=",
+       :+,
        (5...7),
        CallNode(8...11)(
          nil,
@@ -24,8 +23,7 @@ ProgramNode(0...11)(
          nil,
          0,
          "x"
-       ),
-       :+
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/seattlerb/safe_op_asgn2.txt b/test/yarp/snapshots/seattlerb/safe_op_asgn2.txt
index 2b02d02c..c1175989 100644
--- a/test/yarp/snapshots/seattlerb/safe_op_asgn2.txt
+++ b/test/yarp/snapshots/seattlerb/safe_op_asgn2.txt
@@ -1,20 +1,18 @@
 ProgramNode(0...10)(
   [],
   StatementsNode(0...10)(
-    [CallOperatorOrWriteNode(0...10)(
-       CallNode(0...4)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         (1...3),
-         (3...4),
-         nil,
-         nil,
-         nil,
-         nil,
-         1,
-         "b="
-       ),
-       CallNode(9...10)(nil, nil, (9...10), nil, nil, nil, nil, 2, "x"),
-       (5...8)
+    [CallOrWriteNode(0...10)(
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       (1...3),
+       (3...4),
+       nil,
+       nil,
+       nil,
+       1,
+       "b",
+       "b=",
+       (5...8),
+       CallNode(9...10)(nil, nil, (9...10), nil, nil, nil, nil, 2, "x")
      )]
   )
 )
diff --git a/test/yarp/snapshots/unparser/corpus/literal/assignment.txt b/test/yarp/snapshots/unparser/corpus/literal/assignment.txt
index c08204cf..ab443e43 100644
--- a/test/yarp/snapshots/unparser/corpus/literal/assignment.txt
+++ b/test/yarp/snapshots/unparser/corpus/literal/assignment.txt
@@ -584,32 +584,20 @@ ProgramNode(0...704)(
        0,
        "[]="
      ),
-     CallOperatorOrWriteNode(536...550)(
-       CallNode(536...542)(
-         LocalVariableReadNode(536...537)(:a, 0),
-         nil,
-         (537...542),
-         (537...538),
-         ArgumentsNode(538...541)(
-           [StringNode(538...541)((538...540), (540...540), (540...541), "")]
-         ),
-         (541...542),
-         nil,
-         0,
-         "[]="
-       ),
-       CallNode(547...550)(
-         nil,
-         nil,
-         (547...550),
-         nil,
-         nil,
-         nil,
-         nil,
-         2,
-         "bar"
+     CallOrWriteNode(536...550)(
+       LocalVariableReadNode(536...537)(:a, 0),
+       nil,
+       (537...542),
+       (537...538),
+       ArgumentsNode(538...541)(
+         [StringNode(538...541)((538...540), (540...540), (540...541), "")]
        ),
-       (543...546)
+       (541...542),
+       0,
+       "[]",
+       "[]=",
+       (543...546),
+       CallNode(547...550)(nil, nil, (547...550), nil, nil, nil, nil, 2, "bar")
      ),
      InstanceVariableOrWriteNode(551...561)(
        :@a,
@@ -668,42 +656,26 @@ ProgramNode(0...704)(
        0,
        "[]="
      ),
-     CallOperatorOrWriteNode(651...672)(
-       CallNode(651...664)(
-         LocalVariableReadNode(651...652)(:a, 0),
-         nil,
-         (652...664),
-         (652...653),
-         ArgumentsNode(653...663)(
-           [InterpolatedStringNode(653...663)(
-              (653...663),
-              [StringNode(673...675)(nil, (673...675), nil, "  "),
-               EmbeddedStatementsNode(675...678)(
-                 (675...677),
-                 nil,
-                 (677...678)
-               ),
-               StringNode(678...679)(nil, (678...679), nil, "\n")],
-              (679...687)
-            )]
-         ),
-         (663...664),
-         nil,
-         0,
-         "[]="
-       ),
-       CallNode(669...672)(
-         nil,
-         nil,
-         (669...672),
-         nil,
-         nil,
-         nil,
-         nil,
-         2,
-         "bar"
+     CallOrWriteNode(651...672)(
+       LocalVariableReadNode(651...652)(:a, 0),
+       nil,
+       (652...664),
+       (652...653),
+       ArgumentsNode(653...663)(
+         [InterpolatedStringNode(653...663)(
+            (653...663),
+            [StringNode(673...675)(nil, (673...675), nil, "  "),
+             EmbeddedStatementsNode(675...678)((675...677), nil, (677...678)),
+             StringNode(678...679)(nil, (678...679), nil, "\n")],
+            (679...687)
+          )]
        ),
-       (665...668)
+       (663...664),
+       0,
+       "[]",
+       "[]=",
+       (665...668),
+       CallNode(669...672)(nil, nil, (669...672), nil, nil, nil, nil, 2, "bar")
      ),
      InstanceVariableOrWriteNode(687...704)(
        :@a,
diff --git a/test/yarp/snapshots/unparser/corpus/literal/opasgn.txt b/test/yarp/snapshots/unparser/corpus/literal/opasgn.txt
index 15d621cb..149bbc8b 100644
--- a/test/yarp/snapshots/unparser/corpus/literal/opasgn.txt
+++ b/test/yarp/snapshots/unparser/corpus/literal/opasgn.txt
@@ -105,334 +105,304 @@ ProgramNode(0...233)(
        "[]="
      ),
      CallOperatorWriteNode(84...92)(
-       CallNode(84...87)(
-         LocalVariableReadNode(84...85)(:a, 0),
-         (85...86),
-         (86...87),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       LocalVariableReadNode(84...85)(:a, 0),
+       (85...86),
+       (86...87),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :+,
        (88...90),
-       IntegerNode(91...92)(),
-       :+
+       IntegerNode(91...92)()
      ),
      CallOperatorWriteNode(93...101)(
-       CallNode(93...96)(
-         LocalVariableReadNode(93...94)(:a, 0),
-         (94...95),
-         (95...96),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       LocalVariableReadNode(93...94)(:a, 0),
+       (94...95),
+       (95...96),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :-,
        (97...99),
-       IntegerNode(100...101)(),
-       :-
+       IntegerNode(100...101)()
      ),
      CallOperatorWriteNode(102...111)(
-       CallNode(102...105)(
-         LocalVariableReadNode(102...103)(:a, 0),
-         (103...104),
-         (104...105),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       LocalVariableReadNode(102...103)(:a, 0),
+       (103...104),
+       (104...105),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :**,
        (106...109),
-       IntegerNode(110...111)(),
-       :**
+       IntegerNode(110...111)()
      ),
      CallOperatorWriteNode(112...120)(
-       CallNode(112...115)(
-         LocalVariableReadNode(112...113)(:a, 0),
-         (113...114),
-         (114...115),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       LocalVariableReadNode(112...113)(:a, 0),
+       (113...114),
+       (114...115),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :*,
        (116...118),
-       IntegerNode(119...120)(),
-       :*
+       IntegerNode(119...120)()
      ),
      CallOperatorWriteNode(121...129)(
-       CallNode(121...124)(
-         LocalVariableReadNode(121...122)(:a, 0),
-         (122...123),
-         (123...124),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+       LocalVariableReadNode(121...122)(:a, 0),
+       (122...123),
+       (123...124),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       :/,
        (125...127),
-       IntegerNode(128...129)(),
-       :/
+       IntegerNode(128...129)()
      ),
-     CallOperatorAndWriteNode(130...139)(
-       CallNode(130...133)(
-         LocalVariableReadNode(130...131)(:a, 0),
-         (131...132),
-         (132...133),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
+     CallAndWriteNode(130...139)(
+       LocalVariableReadNode(130...131)(:a, 0),
+       (131...132),
+       (132...133),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
        (134...137),
        CallNode(138...139)(nil, nil, (138...139), nil, nil, nil, nil, 2, "b")
      ),
-     CallOperatorOrWriteNode(140...149)(
-       CallNode(140...143)(
-         LocalVariableReadNode(140...141)(:a, 0),
-         (141...142),
-         (142...143),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "b="
-       ),
-       IntegerNode(148...149)(),
-       (144...147)
+     CallOrWriteNode(140...149)(
+       LocalVariableReadNode(140...141)(:a, 0),
+       (141...142),
+       (142...143),
+       nil,
+       nil,
+       nil,
+       0,
+       "b",
+       "b=",
+       (144...147),
+       IntegerNode(148...149)()
      ),
      CallOperatorWriteNode(150...159)(
-       CallNode(150...154)(
-         LocalVariableReadNode(150...151)(:a, 0),
-         nil,
-         (151...154),
-         (151...152),
-         ArgumentsNode(152...153)(
-           [CallNode(152...153)(
-              nil,
-              nil,
-              (152...153),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (153...154),
-         nil,
-         0,
-         "[]="
+       LocalVariableReadNode(150...151)(:a, 0),
+       nil,
+       (151...154),
+       (151...152),
+       ArgumentsNode(152...153)(
+         [CallNode(152...153)(
+            nil,
+            nil,
+            (152...153),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (153...154),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (155...157),
-       IntegerNode(158...159)(),
-       :+
+       IntegerNode(158...159)()
      ),
      CallOperatorWriteNode(160...169)(
-       CallNode(160...164)(
-         LocalVariableReadNode(160...161)(:a, 0),
-         nil,
-         (161...164),
-         (161...162),
-         ArgumentsNode(162...163)(
-           [CallNode(162...163)(
-              nil,
-              nil,
-              (162...163),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (163...164),
-         nil,
-         0,
-         "[]="
+       LocalVariableReadNode(160...161)(:a, 0),
+       nil,
+       (161...164),
+       (161...162),
+       ArgumentsNode(162...163)(
+         [CallNode(162...163)(
+            nil,
+            nil,
+            (162...163),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (163...164),
+       0,
+       "[]",
+       "[]=",
+       :-,
        (165...167),
-       IntegerNode(168...169)(),
-       :-
+       IntegerNode(168...169)()
      ),
      CallOperatorWriteNode(170...180)(
-       CallNode(170...174)(
-         LocalVariableReadNode(170...171)(:a, 0),
-         nil,
-         (171...174),
-         (171...172),
-         ArgumentsNode(172...173)(
-           [CallNode(172...173)(
-              nil,
-              nil,
-              (172...173),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (173...174),
-         nil,
-         0,
-         "[]="
+       LocalVariableReadNode(170...171)(:a, 0),
+       nil,
+       (171...174),
+       (171...172),
+       ArgumentsNode(172...173)(
+         [CallNode(172...173)(
+            nil,
+            nil,
+            (172...173),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (173...174),
+       0,
+       "[]",
+       "[]=",
+       :**,
        (175...178),
-       IntegerNode(179...180)(),
-       :**
+       IntegerNode(179...180)()
      ),
      CallOperatorWriteNode(181...190)(
-       CallNode(181...185)(
-         LocalVariableReadNode(181...182)(:a, 0),
-         nil,
-         (182...185),
-         (182...183),
-         ArgumentsNode(183...184)(
-           [CallNode(183...184)(
-              nil,
-              nil,
-              (183...184),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (184...185),
-         nil,
-         0,
-         "[]="
+       LocalVariableReadNode(181...182)(:a, 0),
+       nil,
+       (182...185),
+       (182...183),
+       ArgumentsNode(183...184)(
+         [CallNode(183...184)(
+            nil,
+            nil,
+            (183...184),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (184...185),
+       0,
+       "[]",
+       "[]=",
+       :*,
        (186...188),
-       IntegerNode(189...190)(),
-       :*
+       IntegerNode(189...190)()
      ),
      CallOperatorWriteNode(191...200)(
-       CallNode(191...195)(
-         LocalVariableReadNode(191...192)(:a, 0),
-         nil,
-         (192...195),
-         (192...193),
-         ArgumentsNode(193...194)(
-           [CallNode(193...194)(
-              nil,
-              nil,
-              (193...194),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (194...195),
-         nil,
-         0,
-         "[]="
+       LocalVariableReadNode(191...192)(:a, 0),
+       nil,
+       (192...195),
+       (192...193),
+       ArgumentsNode(193...194)(
+         [CallNode(193...194)(
+            nil,
+            nil,
+            (193...194),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (194...195),
+       0,
+       "[]",
+       "[]=",
+       :/,
        (196...198),
-       IntegerNode(199...200)(),
-       :/
+       IntegerNode(199...200)()
      ),
-     CallOperatorAndWriteNode(201...211)(
-       CallNode(201...205)(
-         LocalVariableReadNode(201...202)(:a, 0),
-         nil,
-         (202...205),
-         (202...203),
-         ArgumentsNode(203...204)(
-           [CallNode(203...204)(
-              nil,
-              nil,
-              (203...204),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (204...205),
-         nil,
-         0,
-         "[]="
+     CallAndWriteNode(201...211)(
+       LocalVariableReadNode(201...202)(:a, 0),
+       nil,
+       (202...205),
+       (202...203),
+       ArgumentsNode(203...204)(
+         [CallNode(203...204)(
+            nil,
+            nil,
+            (203...204),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
+       (204...205),
+       0,
+       "[]",
+       "[]=",
        (206...209),
        CallNode(210...211)(nil, nil, (210...211), nil, nil, nil, nil, 2, "b")
      ),
-     CallOperatorOrWriteNode(212...222)(
-       CallNode(212...216)(
-         LocalVariableReadNode(212...213)(:a, 0),
-         nil,
-         (213...216),
-         (213...214),
-         ArgumentsNode(214...215)(
-           [CallNode(214...215)(
-              nil,
-              nil,
-              (214...215),
-              nil,
-              nil,
-              nil,
-              nil,
-              2,
-              "b"
-            )]
-         ),
-         (215...216),
-         nil,
-         0,
-         "[]="
+     CallOrWriteNode(212...222)(
+       LocalVariableReadNode(212...213)(:a, 0),
+       nil,
+       (213...216),
+       (213...214),
+       ArgumentsNode(214...215)(
+         [CallNode(214...215)(
+            nil,
+            nil,
+            (214...215),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
        ),
-       IntegerNode(221...222)(),
-       (217...220)
+       (215...216),
+       0,
+       "[]",
+       "[]=",
+       (217...220),
+       IntegerNode(221...222)()
      ),
      CallOperatorWriteNode(223...233)(
-       CallNode(223...228)(
-         CallNode(223...226)(
-           nil,
-           nil,
-           (223...226),
-           nil,
-           nil,
-           nil,
-           nil,
-           2,
-           "foo"
-         ),
-         (226...227),
-         (227...228),
+       CallNode(223...226)(
          nil,
          nil,
+         (223...226),
          nil,
          nil,
-         0,
-         "A="
+         nil,
+         nil,
+         2,
+         "foo"
        ),
+       (226...227),
+       (227...228),
+       nil,
+       nil,
+       nil,
+       0,
+       "A",
+       "A=",
+       :+,
        (229...231),
-       IntegerNode(232...233)(),
-       :+
+       IntegerNode(232...233)()
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/and_asgn.txt b/test/yarp/snapshots/whitequark/and_asgn.txt
index d42bcc35..fab12349 100644
--- a/test/yarp/snapshots/whitequark/and_asgn.txt
+++ b/test/yarp/snapshots/whitequark/and_asgn.txt
@@ -1,35 +1,31 @@
 ProgramNode(0...28)(
   [],
   StatementsNode(0...28)(
-    [CallOperatorAndWriteNode(0...11)(
-       CallNode(0...5)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         (3...4),
-         (4...5),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
+    [CallAndWriteNode(0...11)(
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       (3...4),
+       (4...5),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
        (6...9),
        IntegerNode(10...11)()
      ),
-     CallOperatorAndWriteNode(13...28)(
-       CallNode(13...22)(
-         CallNode(13...16)(nil, nil, (13...16), nil, nil, nil, nil, 2, "foo"),
-         nil,
-         (16...22),
-         (16...17),
-         ArgumentsNode(17...21)(
-           [IntegerNode(17...18)(), IntegerNode(20...21)()]
-         ),
-         (21...22),
-         nil,
-         0,
-         "[]="
+     CallAndWriteNode(13...28)(
+       CallNode(13...16)(nil, nil, (13...16), nil, nil, nil, nil, 2, "foo"),
+       nil,
+       (16...22),
+       (16...17),
+       ArgumentsNode(17...21)(
+         [IntegerNode(17...18)(), IntegerNode(20...21)()]
        ),
+       (21...22),
+       0,
+       "[]",
+       "[]=",
        (23...26),
        IntegerNode(27...28)()
      )]
diff --git a/test/yarp/snapshots/whitequark/op_asgn.txt b/test/yarp/snapshots/whitequark/op_asgn.txt
index c0a4e047..44e36943 100644
--- a/test/yarp/snapshots/whitequark/op_asgn.txt
+++ b/test/yarp/snapshots/whitequark/op_asgn.txt
@@ -2,52 +2,46 @@ ProgramNode(0...35)(
   [],
   StatementsNode(0...35)(
     [CallOperatorWriteNode(0...10)(
-       CallNode(0...5)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         (3...4),
-         (4...5),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "A="
-       ),
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       (3...4),
+       (4...5),
+       nil,
+       nil,
+       nil,
+       0,
+       "A",
+       "A=",
+       :+,
        (6...8),
-       IntegerNode(9...10)(),
-       :+
+       IntegerNode(9...10)()
      ),
      CallOperatorWriteNode(12...22)(
-       CallNode(12...17)(
-         CallNode(12...15)(nil, nil, (12...15), nil, nil, nil, nil, 2, "foo"),
-         (15...16),
-         (16...17),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
+       CallNode(12...15)(nil, nil, (12...15), nil, nil, nil, nil, 2, "foo"),
+       (15...16),
+       (16...17),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
+       :+,
        (18...20),
-       IntegerNode(21...22)(),
-       :+
+       IntegerNode(21...22)()
      ),
      CallOperatorWriteNode(24...35)(
-       CallNode(24...30)(
-         CallNode(24...27)(nil, nil, (24...27), nil, nil, nil, nil, 2, "foo"),
-         (27...29),
-         (29...30),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
+       CallNode(24...27)(nil, nil, (24...27), nil, nil, nil, nil, 2, "foo"),
+       (27...29),
+       (29...30),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
+       :+,
        (31...33),
-       IntegerNode(34...35)(),
-       :+
+       IntegerNode(34...35)()
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/op_asgn_cmd.txt b/test/yarp/snapshots/whitequark/op_asgn_cmd.txt
index 409aa26e..9bf83ad9 100644
--- a/test/yarp/snapshots/whitequark/op_asgn_cmd.txt
+++ b/test/yarp/snapshots/whitequark/op_asgn_cmd.txt
@@ -2,17 +2,16 @@ ProgramNode(0...64)(
   [],
   StatementsNode(0...64)(
     [CallOperatorWriteNode(0...14)(
-       CallNode(0...5)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         (3...4),
-         (4...5),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "A="
-       ),
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       (3...4),
+       (4...5),
+       nil,
+       nil,
+       nil,
+       0,
+       "A",
+       "A=",
+       :+,
        (6...8),
        CallNode(9...14)(
          nil,
@@ -36,21 +35,19 @@ ProgramNode(0...64)(
          nil,
          0,
          "m"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(16...30)(
-       CallNode(16...21)(
-         CallNode(16...19)(nil, nil, (16...19), nil, nil, nil, nil, 2, "foo"),
-         (19...20),
-         (20...21),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
+       CallNode(16...19)(nil, nil, (16...19), nil, nil, nil, nil, 2, "foo"),
+       (19...20),
+       (20...21),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
+       :+,
        (22...24),
        CallNode(25...30)(
          nil,
@@ -74,8 +71,7 @@ ProgramNode(0...64)(
          nil,
          0,
          "m"
-       ),
-       :+
+       )
      ),
      ConstantPathOperatorWriteNode(32...47)(
        ConstantPathNode(32...38)(
@@ -110,17 +106,16 @@ ProgramNode(0...64)(
        :+
      ),
      CallOperatorWriteNode(49...64)(
-       CallNode(49...55)(
-         CallNode(49...52)(nil, nil, (49...52), nil, nil, nil, nil, 2, "foo"),
-         (52...54),
-         (54...55),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
+       CallNode(49...52)(nil, nil, (49...52), nil, nil, nil, nil, 2, "foo"),
+       (52...54),
+       (54...55),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
+       :+,
        (56...58),
        CallNode(59...64)(
          nil,
@@ -144,8 +139,7 @@ ProgramNode(0...64)(
          nil,
          0,
          "m"
-       ),
-       :+
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/op_asgn_index.txt b/test/yarp/snapshots/whitequark/op_asgn_index.txt
index 9b31652c..153077be 100644
--- a/test/yarp/snapshots/whitequark/op_asgn_index.txt
+++ b/test/yarp/snapshots/whitequark/op_asgn_index.txt
@@ -2,20 +2,18 @@ ProgramNode(0...14)(
   [],
   StatementsNode(0...14)(
     [CallOperatorWriteNode(0...14)(
-       CallNode(0...9)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         nil,
-         (3...9),
-         (3...4),
-         ArgumentsNode(4...8)([IntegerNode(4...5)(), IntegerNode(7...8)()]),
-         (8...9),
-         nil,
-         0,
-         "[]="
-       ),
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       nil,
+       (3...9),
+       (3...4),
+       ArgumentsNode(4...8)([IntegerNode(4...5)(), IntegerNode(7...8)()]),
+       (8...9),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (10...12),
-       IntegerNode(13...14)(),
-       :+
+       IntegerNode(13...14)()
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/op_asgn_index_cmd.txt b/test/yarp/snapshots/whitequark/op_asgn_index_cmd.txt
index 031532b8..9dda56d6 100644
--- a/test/yarp/snapshots/whitequark/op_asgn_index_cmd.txt
+++ b/test/yarp/snapshots/whitequark/op_asgn_index_cmd.txt
@@ -2,17 +2,16 @@ ProgramNode(0...18)(
   [],
   StatementsNode(0...18)(
     [CallOperatorWriteNode(0...18)(
-       CallNode(0...9)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         nil,
-         (3...9),
-         (3...4),
-         ArgumentsNode(4...8)([IntegerNode(4...5)(), IntegerNode(7...8)()]),
-         (8...9),
-         nil,
-         0,
-         "[]="
-       ),
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       nil,
+       (3...9),
+       (3...4),
+       ArgumentsNode(4...8)([IntegerNode(4...5)(), IntegerNode(7...8)()]),
+       (8...9),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (10...12),
        CallNode(13...18)(
          nil,
@@ -36,8 +35,7 @@ ProgramNode(0...18)(
          nil,
          0,
          "m"
-       ),
-       :+
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/or_asgn.txt b/test/yarp/snapshots/whitequark/or_asgn.txt
index 35d7d76a..f681a319 100644
--- a/test/yarp/snapshots/whitequark/or_asgn.txt
+++ b/test/yarp/snapshots/whitequark/or_asgn.txt
@@ -1,37 +1,33 @@
 ProgramNode(0...28)(
   [],
   StatementsNode(0...28)(
-    [CallOperatorOrWriteNode(0...11)(
-       CallNode(0...5)(
-         CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
-         (3...4),
-         (4...5),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "a="
-       ),
-       IntegerNode(10...11)(),
-       (6...9)
+    [CallOrWriteNode(0...11)(
+       CallNode(0...3)(nil, nil, (0...3), nil, nil, nil, nil, 2, "foo"),
+       (3...4),
+       (4...5),
+       nil,
+       nil,
+       nil,
+       0,
+       "a",
+       "a=",
+       (6...9),
+       IntegerNode(10...11)()
      ),
-     CallOperatorOrWriteNode(13...28)(
-       CallNode(13...22)(
-         CallNode(13...16)(nil, nil, (13...16), nil, nil, nil, nil, 2, "foo"),
-         nil,
-         (16...22),
-         (16...17),
-         ArgumentsNode(17...21)(
-           [IntegerNode(17...18)(), IntegerNode(20...21)()]
-         ),
-         (21...22),
-         nil,
-         0,
-         "[]="
+     CallOrWriteNode(13...28)(
+       CallNode(13...16)(nil, nil, (13...16), nil, nil, nil, nil, 2, "foo"),
+       nil,
+       (16...22),
+       (16...17),
+       ArgumentsNode(17...21)(
+         [IntegerNode(17...18)(), IntegerNode(20...21)()]
        ),
-       IntegerNode(27...28)(),
-       (23...26)
+       (21...22),
+       0,
+       "[]",
+       "[]=",
+       (23...26),
+       IntegerNode(27...28)()
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/ruby_bug_12402.txt b/test/yarp/snapshots/whitequark/ruby_bug_12402.txt
index 7bcef829..396349c5 100644
--- a/test/yarp/snapshots/whitequark/ruby_bug_12402.txt
+++ b/test/yarp/snapshots/whitequark/ruby_bug_12402.txt
@@ -136,17 +136,16 @@ ProgramNode(0...437)(
        (91...92)
      ),
      CallOperatorWriteNode(116...145)(
-       CallNode(116...121)(
-         LocalVariableReadNode(116...119)(:foo, 0),
-         (119...120),
-         (120...121),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "C="
-       ),
+       LocalVariableReadNode(116...119)(:foo, 0),
+       (119...120),
+       (120...121),
+       nil,
+       nil,
+       nil,
+       0,
+       "C",
+       "C=",
+       :+,
        (122...124),
        CallNode(125...145)(
          nil,
@@ -174,21 +173,19 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(147...177)(
-       CallNode(147...152)(
-         LocalVariableReadNode(147...150)(:foo, 0),
-         (150...151),
-         (151...152),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "C="
-       ),
+       LocalVariableReadNode(147...150)(:foo, 0),
+       (150...151),
+       (151...152),
+       nil,
+       nil,
+       nil,
+       0,
+       "C",
+       "C=",
+       :+,
        (153...155),
        RescueModifierNode(156...177)(
          CallNode(156...166)(
@@ -216,21 +213,19 @@ ProgramNode(0...437)(
          ),
          (167...173),
          NilNode(174...177)()
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(179...208)(
-       CallNode(179...184)(
-         LocalVariableReadNode(179...182)(:foo, 0),
-         (182...183),
-         (183...184),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "m="
-       ),
+       LocalVariableReadNode(179...182)(:foo, 0),
+       (182...183),
+       (183...184),
+       nil,
+       nil,
+       nil,
+       0,
+       "m",
+       "m=",
+       :+,
        (185...187),
        CallNode(188...208)(
          nil,
@@ -258,21 +253,19 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(210...240)(
-       CallNode(210...215)(
-         LocalVariableReadNode(210...213)(:foo, 0),
-         (213...214),
-         (214...215),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "m="
-       ),
+       LocalVariableReadNode(210...213)(:foo, 0),
+       (213...214),
+       (214...215),
+       nil,
+       nil,
+       nil,
+       0,
+       "m",
+       "m=",
+       :+,
        (216...218),
        RescueModifierNode(219...240)(
          CallNode(219...229)(
@@ -300,8 +293,7 @@ ProgramNode(0...437)(
          ),
          (230...236),
          NilNode(237...240)()
-       ),
-       :+
+       )
      ),
      ConstantPathOrWriteNode(242...273)(
        ConstantPathNode(242...248)(
@@ -374,17 +366,16 @@ ProgramNode(0...437)(
        )
      ),
      CallOperatorWriteNode(309...339)(
-       CallNode(309...315)(
-         LocalVariableReadNode(309...312)(:foo, 0),
-         (312...314),
-         (314...315),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "m="
-       ),
+       LocalVariableReadNode(309...312)(:foo, 0),
+       (312...314),
+       (314...315),
+       nil,
+       nil,
+       nil,
+       0,
+       "m",
+       "m=",
+       :+,
        (316...318),
        CallNode(319...339)(
          nil,
@@ -412,21 +403,19 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(341...372)(
-       CallNode(341...347)(
-         LocalVariableReadNode(341...344)(:foo, 0),
-         (344...346),
-         (346...347),
-         nil,
-         nil,
-         nil,
-         nil,
-         0,
-         "m="
-       ),
+       LocalVariableReadNode(341...344)(:foo, 0),
+       (344...346),
+       (346...347),
+       nil,
+       nil,
+       nil,
+       0,
+       "m",
+       "m=",
+       :+,
        (348...350),
        RescueModifierNode(351...372)(
          CallNode(351...361)(
@@ -454,21 +443,19 @@ ProgramNode(0...437)(
          ),
          (362...368),
          NilNode(369...372)()
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(374...404)(
-       CallNode(374...380)(
-         LocalVariableReadNode(374...377)(:foo, 0),
-         nil,
-         (377...380),
-         (377...378),
-         ArgumentsNode(378...379)([IntegerNode(378...379)()]),
-         (379...380),
-         nil,
-         0,
-         "[]="
-       ),
+       LocalVariableReadNode(374...377)(:foo, 0),
+       nil,
+       (377...380),
+       (377...378),
+       ArgumentsNode(378...379)([IntegerNode(378...379)()]),
+       (379...380),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (381...383),
        CallNode(384...404)(
          nil,
@@ -496,21 +483,19 @@ ProgramNode(0...437)(
          nil,
          0,
          "raise"
-       ),
-       :+
+       )
      ),
      CallOperatorWriteNode(406...437)(
-       CallNode(406...412)(
-         LocalVariableReadNode(406...409)(:foo, 0),
-         nil,
-         (409...412),
-         (409...410),
-         ArgumentsNode(410...411)([IntegerNode(410...411)()]),
-         (411...412),
-         nil,
-         0,
-         "[]="
-       ),
+       LocalVariableReadNode(406...409)(:foo, 0),
+       nil,
+       (409...412),
+       (409...410),
+       ArgumentsNode(410...411)([IntegerNode(410...411)()]),
+       (411...412),
+       0,
+       "[]",
+       "[]=",
+       :+,
        (413...415),
        RescueModifierNode(416...437)(
          CallNode(416...426)(
@@ -538,8 +523,7 @@ ProgramNode(0...437)(
          ),
          (427...433),
          NilNode(434...437)()
-       ),
-       :+
+       )
      )]
   )
 )
diff --git a/test/yarp/snapshots/whitequark/send_op_asgn_conditional.txt b/test/yarp/snapshots/whitequark/send_op_asgn_conditional.txt
index 8403b1c7..767a6e7b 100644
--- a/test/yarp/snapshots/whitequark/send_op_asgn_conditional.txt
+++ b/test/yarp/snapshots/whitequark/send_op_asgn_conditional.txt
@@ -1,18 +1,16 @@
 ProgramNode(0...10)(
   [],
   StatementsNode(0...10)(
-    [CallOperatorAndWriteNode(0...10)(
-       CallNode(0...4)(
-         CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-         (1...3),
-         (3...4),
-         nil,
-         nil,
-         nil,
-         nil,
-         1,
-         "b="
-       ),
+    [CallAndWriteNode(0...10)(
+       CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
+       (1...3),
+       (3...4),
+       nil,
+       nil,
+       nil,
+       1,
+       "b",
+       "b=",
        (5...8),
        IntegerNode(9...10)()
      )]