Repository: ruby/yarp
Pull request #1520 (2023-09-15 17:09:48)

separate `ruby_memcheck` into its own group
This PR implements @eregon 's idea (thank you!) from the discussion in #1498: we can separate `ruby_memcheck` into its own group, and handle its non-presence.  This change enables people to install a smaller set of gems if they so choose.


Patch:
diff --git a/Gemfile b/Gemfile
index c5bdf88e..662bff52 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,5 +7,7 @@ gemspec
 gem "rake"
 gem "rake-compiler"
 gem "test-unit"
-gem "ruby_memcheck", platform: %i[mri truffleruby mswin mingw x64_mingw]
 gem "ffi", platform: %i[mri mswin mingw x64_mingw]
+group :memcheck do
+  gem "ruby_memcheck", platform: %i[mri truffleruby mswin mingw x64_mingw]
+end
diff --git a/config.yml b/config.yml
index fbaea52f..b446742e 100644
--- a/config.yml
+++ b/config.yml
@@ -1413,19 +1413,6 @@ nodes:
 
           1.0i
           ^^^^
-  - name: ImplicitNode
-    fields:
-      - name: value
-        type: node
-    comment: |
-      Represents a node that is implicitly being added to the tree but doesn't
-      correspond directly to a node in the source.
-
-          { foo: }
-            ^^^^
-
-          { Foo: }
-            ^^^^
   - name: InNode
     fields:
       - name: pattern
diff --git a/include/yarp/diagnostic.h b/include/yarp/diagnostic.h
index 6f17a23d..9a8975f0 100644
--- a/include/yarp/diagnostic.h
+++ b/include/yarp/diagnostic.h
@@ -150,8 +150,6 @@ typedef enum {
     YP_ERR_MULTI_ASSIGN_MULTI_SPLATS,
     YP_ERR_NOT_EXPRESSION,
     YP_ERR_NUMBER_LITERAL_UNDERSCORE,
-    YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED,
-    YP_ERR_NUMBERED_PARAMETER_OUTER_SCOPE,
     YP_ERR_OPERATOR_MULTI_ASSIGN,
     YP_ERR_OPERATOR_WRITE_BLOCK,
     YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI,
diff --git a/include/yarp/parser.h b/include/yarp/parser.h
index 89b0f274..2d6ba0c4 100644
--- a/include/yarp/parser.h
+++ b/include/yarp/parser.h
@@ -277,22 +277,12 @@ typedef struct yp_scope {
     // The IDs of the locals in the given scope.
     yp_constant_id_list_t locals;
 
-    // A pointer to the previous scope in the linked list.
-    struct yp_scope *previous;
-
     // A boolean indicating whether or not this scope can see into its parent.
     // If closed is true, then the scope cannot see into its parent.
     bool closed;
 
-    // A boolean indicating whether or not this scope has explicit parameters.
-    // This is necessary to determine whether or not numbered parameters are
-    // allowed.
-    bool explicit_params;
-
-    // A boolean indicating whether or not this scope has numbered parameters.
-    // This is necessary to determine if child blocks are allowed to use
-    // numbered parameters.
-    bool numbered_params;
+    // A pointer to the previous scope in the linked list.
+    struct yp_scope *previous;
 } yp_scope_t;
 
 // This struct represents the overall parser. It contains a reference to the
diff --git a/lib/yarp.rb b/lib/yarp.rb
index c1043c32..b81c9881 100644
--- a/lib/yarp.rb
+++ b/lib/yarp.rb
@@ -440,6 +440,10 @@ module YARP
             # arguments. We get rid of that here.
             names = names.grep_v(Integer)
 
+            # TODO: We don't support numbered local variables yet, so we get rid
+            # of those here.
+            names = names.grep_v(/^_\d$/)
+
             # For some reason, CRuby occasionally pushes this special local
             # variable when there are splat arguments. We get rid of that here.
             names = names.grep_v(:"#arg_rest")
diff --git a/src/diagnostic.c b/src/diagnostic.c
index e89d96cb..7fd658d5 100644
--- a/src/diagnostic.c
+++ b/src/diagnostic.c
@@ -184,10 +184,8 @@ static const char* const diagnostic_messages[YP_DIAGNOSTIC_ID_LEN] = {
     [YP_ERR_MULTI_ASSIGN_MULTI_SPLATS]          = "Multiple splats in multiple assignment",
     [YP_ERR_NOT_EXPRESSION]                     = "Expected an expression after `not`",
     [YP_ERR_NUMBER_LITERAL_UNDERSCORE]          = "Number literal ending with a `_`",
-    [YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED]     = "Numbered parameters are not allowed alongside explicit parameters",
-    [YP_ERR_NUMBERED_PARAMETER_OUTER_SCOPE]     = "Numbered parameter is already used in outer scope",
-    [YP_ERR_OPERATOR_MULTI_ASSIGN]              = "Unexpected operator for a multiple assignment",
     [YP_ERR_OPERATOR_WRITE_BLOCK]               = "Unexpected operator after a call with a block",
+    [YP_ERR_OPERATOR_MULTI_ASSIGN]              = "Unexpected operator for a multiple assignment",
     [YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI]        = "Unexpected multiple `**` splat parameters",
     [YP_ERR_PARAMETER_BLOCK_MULTI]              = "Multiple block parameters; only one block is allowed",
     [YP_ERR_PARAMETER_NAME_REPEAT]              = "Repeated parameter name",
diff --git a/src/util/yp_constant_pool.c b/src/util/yp_constant_pool.c
index 7ae5ea1f..8be96138 100644
--- a/src/util/yp_constant_pool.c
+++ b/src/util/yp_constant_pool.c
@@ -107,10 +107,10 @@ yp_constant_pool_init(yp_constant_pool_t *pool, size_t capacity) {
 }
 
 // Insert a constant into a constant pool and return its index in the pool.
-static inline yp_constant_id_t
-yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t length, bool owned) {
+static size_t
+yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
     if (pool->size >= (pool->capacity / 4 * 3)) {
-        if (!yp_constant_pool_resize(pool)) return 0;
+        if (!yp_constant_pool_resize(pool)) return pool->capacity;
     }
 
     size_t hash = yp_constant_pool_hash(start, length);
@@ -122,25 +122,7 @@ yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t l
         // same as the content we are trying to insert. If it is, then we can
         // return the id of the existing constant.
         if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
-            // Since we have found a match, we need to check if this is
-            // attempting to insert a shared or an owned constant. We want to
-            // prefer shared constants since they don't require allocations.
-            if (owned) {
-                // If we're attempting to insert an owned constant and we have
-                // an existing constant, then either way we don't want the given
-                // memory. Either it's duplicated with the existing constant or
-                // it's not necessary because we have a shared version.
-                free((void *) start);
-            } else if (constant->owned) {
-                // If we're attempting to insert a shared constant and the
-                // existing constant is owned, then we can free the owned
-                // constant and replace it with the shared constant.
-                free((void *) constant->start);
-                constant->start = start;
-                constant->owned = false;
-            }
-
-            return constant->id;
+            return index;
         }
 
         index = (index + 1) % pool->capacity;
@@ -149,22 +131,22 @@ yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t l
     pool->size++;
     assert(pool->size < ((size_t) (1 << 31)));
 
-    *constant = (yp_constant_t) {
+    pool->constants[index] = (yp_constant_t) {
         .id = (unsigned int) (pool->size & 0x7FFFFFFF),
-        .owned = owned,
         .start = start,
         .length = length,
         .hash = hash
     };
 
-    return constant->id;
+    return index;
 }
 
 // Insert a constant into a constant pool. Returns the id of the constant, or 0
 // if any potential calls to resize fail.
 yp_constant_id_t
 yp_constant_pool_insert_shared(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
-    return yp_constant_pool_insert(pool, start, length, false);
+    size_t index = yp_constant_pool_insert(pool, start, length);
+    return index == pool->capacity ? 0 : ((yp_constant_id_t) pool->constants[index].id);
 }
 
 // Insert a constant into a constant pool from memory that is now owned by the
@@ -172,7 +154,12 @@ yp_constant_pool_insert_shared(yp_constant_pool_t *pool, const uint8_t *start, s
 // resize fail.
 yp_constant_id_t
 yp_constant_pool_insert_owned(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
-    return yp_constant_pool_insert(pool, start, length, true);
+    size_t index = yp_constant_pool_insert(pool, start, length);
+    if (index == pool->capacity) return 0;
+
+    yp_constant_t *constant = &pool->constants[index];
+    constant->owned = true;
+    return ((yp_constant_id_t) constant->id);
 }
 
 // Free the memory associated with a constant pool.
diff --git a/src/yarp.c b/src/yarp.c
index 3eabc556..924abfa8 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -2737,22 +2737,6 @@ yp_else_node_end_keyword_loc_set(yp_else_node_t *node, const yp_token_t *keyword
     node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword);
 }
 
-// Allocate and initialize a new ImplicitNode node.
-static yp_implicit_node_t *
-yp_implicit_node_create(yp_parser_t *parser, yp_node_t *value) {
-    yp_implicit_node_t *node = YP_ALLOC_NODE(parser, yp_implicit_node_t);
-
-    *node = (yp_implicit_node_t) {
-        {
-            .type = YP_IMPLICIT_NODE,
-            .location = value->location
-        },
-        .value = value
-    };
-
-    return node;
-}
-
 // Allocate and initialize a new IntegerNode node.
 static yp_integer_node_t *
 yp_integer_node_create(yp_parser_t *parser, yp_node_flags_t base, const yp_token_t *token) {
@@ -4736,16 +4720,10 @@ yp_parser_scope_push(yp_parser_t *parser, bool closed) {
     yp_scope_t *scope = (yp_scope_t *) malloc(sizeof(yp_scope_t));
     if (scope == NULL) return false;
 
-    *scope = (yp_scope_t) {
-        .previous = parser->current_scope,
-        .closed = closed,
-        .explicit_params = false,
-        .numbered_params = false
-    };
-
+    *scope = (yp_scope_t) { .closed = closed, .previous = parser->current_scope };
     yp_constant_id_list_init(&scope->locals);
-    parser->current_scope = scope;
 
+    parser->current_scope = scope;
     return true;
 }
 
@@ -8323,13 +8301,8 @@ parse_target(yp_parser_t *parser, yp_node_t *target) {
             target->type = YP_GLOBAL_VARIABLE_TARGET_NODE;
             return target;
         case YP_LOCAL_VARIABLE_READ_NODE:
-            if (token_is_numbered_parameter(target->location.start, target->location.end)) {
-                yp_diagnostic_list_append(&parser->error_list, target->location.start, target->location.end, YP_ERR_PARAMETER_NUMBERED_RESERVED);
-            } else {
-                assert(sizeof(yp_local_variable_target_node_t) == sizeof(yp_local_variable_read_node_t));
-                target->type = YP_LOCAL_VARIABLE_TARGET_NODE;
-            }
-
+            assert(sizeof(yp_local_variable_target_node_t) == sizeof(yp_local_variable_read_node_t));
+            target->type = YP_LOCAL_VARIABLE_TARGET_NODE;
             return target;
         case YP_INSTANCE_VARIABLE_READ_NODE:
             assert(sizeof(yp_instance_variable_target_node_t) == sizeof(yp_instance_variable_read_node_t));
@@ -8449,10 +8422,6 @@ parse_write(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_nod
             return (yp_node_t *) node;
         }
         case YP_LOCAL_VARIABLE_READ_NODE: {
-            if (token_is_numbered_parameter(target->location.start, target->location.end)) {
-                yp_diagnostic_list_append(&parser->error_list, target->location.start, target->location.end, YP_ERR_PARAMETER_NUMBERED_RESERVED);
-            }
-
             yp_local_variable_read_node_t *local_read = (yp_local_variable_read_node_t *) target;
 
             yp_constant_id_t constant_id = local_read->name;
@@ -8731,32 +8700,14 @@ parse_assocs(yp_parser_t *parser, yp_node_t *node) {
                 break;
             }
             case YP_TOKEN_LABEL: {
-                yp_token_t label = parser->current;
                 parser_lex(parser);
 
-                yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &label);
+                yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous);
                 yp_token_t operator = not_provided(parser);
                 yp_node_t *value = NULL;
 
                 if (token_begins_expression_p(parser->current.type)) {
                     value = parse_expression(parser, YP_BINDING_POWER_DEFINED, YP_ERR_HASH_EXPRESSION_AFTER_LABEL);
-                } else {
-                    if (parser->encoding.isupper_char(label.start, (label.end - 1) - label.start)) {
-                        yp_token_t constant = { .type = YP_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
-                        value = (yp_node_t *) yp_constant_read_node_create(parser, &constant);
-                    } else {
-                        int depth = yp_parser_local_depth(parser, &((yp_token_t) { .type = YP_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }));
-                        yp_token_t identifier = { .type = YP_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
-
-                        if (depth == -1) {
-                            value = (yp_node_t *) yp_call_node_variable_call_create(parser, &identifier);
-                        } else {
-                            value = (yp_node_t *) yp_local_variable_read_node_create(parser, &identifier, (uint32_t) depth);
-                        }
-                    }
-
-                    value->location.end++;
-                    value = (yp_node_t *) yp_implicit_node_create(parser, value);
                 }
 
                 element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value);
@@ -9581,7 +9532,6 @@ parse_block(yp_parser_t *parser) {
     yp_block_parameters_node_t *parameters = NULL;
 
     if (accept1(parser, YP_TOKEN_PIPE)) {
-        parser->current_scope->explicit_params = true;
         yp_token_t block_parameters_opening = parser->previous;
 
         if (match1(parser, YP_TOKEN_PIPE)) {
@@ -9824,6 +9774,7 @@ parse_conditional(yp_parser_t *parser, yp_context_t context) {
     case YP_TOKEN_KEYWORD_TRUE: case YP_TOKEN_KEYWORD_UNDEF: case YP_TOKEN_KEYWORD_UNLESS: case YP_TOKEN_KEYWORD_UNTIL: \
     case YP_TOKEN_KEYWORD_WHEN: case YP_TOKEN_KEYWORD_WHILE: case YP_TOKEN_KEYWORD_YIELD
 
+
 // This macro allows you to define a case statement for all of the operators.
 // It's meant to be used in a switch statement.
 #define YP_CASE_OPERATOR YP_TOKEN_AMPERSAND: case YP_TOKEN_BACKTICK: case YP_TOKEN_BANG_EQUAL: \
@@ -10148,17 +10099,6 @@ parse_alias_argument(yp_parser_t *parser, bool first) {
     }
 }
 
-// Return true if any of the visible scopes to the current context are using
-// numbered parameters.
-static bool
-outer_scope_using_numbered_params_p(yp_parser_t *parser) {
-    for (yp_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
-        if (scope->numbered_params) return true;
-    }
-
-    return false;
-}
-
 // Parse an identifier into either a local variable read or a call.
 static yp_node_t *
 parse_variable_call(yp_parser_t *parser) {
@@ -10170,44 +10110,6 @@ parse_variable_call(yp_parser_t *parser) {
             return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
         }
 
-        if (!parser->current_scope->closed && token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
-            // Indicate that this scope is using numbered params so that child
-            // scopes cannot.
-            parser->current_scope->numbered_params = true;
-
-            // Now that we know we have a numbered parameter, we need to check
-            // if it's allowed in this context. If it is, then we will create a
-            // local variable read. If it's not, then we'll create a normal call
-            // node but add an error.
-            if (parser->current_scope->explicit_params) {
-                yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED);
-            } else if (outer_scope_using_numbered_params_p(parser)) {
-                yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
-            } else {
-                // When you use a numbered parameter, it implies the existence
-                // of all of the locals that exist before it. For example,
-                // referencing _2 means that _1 must exist. Therefore here we
-                // loop through all of the possibilities and add them into the
-                // constant pool.
-                uint8_t number = parser->previous.start[1];
-                uint8_t current = '1';
-                uint8_t *value;
-
-                while (current < number) {
-                    value = malloc(2);
-                    value[0] = '_';
-                    value[1] = current++;
-                    yp_parser_local_add_owned(parser, value, 2);
-                }
-
-                // Now we can add the actual token that is being used. For
-                // this one we can add a shared version since it is directly
-                // referenced in the source.
-                yp_parser_local_add_token(parser, &parser->previous);
-                return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, 0);
-            }
-        }
-
         flags |= YP_CALL_NODE_FLAGS_VARIABLE_CALL;
     }
 
@@ -10230,7 +10132,7 @@ parse_method_definition_name(yp_parser_t *parser) {
             parser_lex(parser);
             return parser->previous;
         default:
-            return (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end };
+            return not_provided(parser);
     }
 }
 
@@ -12130,7 +12032,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
 
             yp_node_t *receiver = NULL;
             yp_token_t operator = not_provided(parser);
-            yp_token_t name = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = def_keyword.end, .end = def_keyword.end };
+            yp_token_t name = not_provided(parser);
 
             context_push(parser, YP_CONTEXT_DEF_PARAMS);
             parser_lex(parser);
@@ -12154,6 +12056,10 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
 
                         operator = parser->previous;
                         name = parse_method_definition_name(parser);
+
+                        if (name.type == YP_TOKEN_MISSING) {
+                            yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_DEF_NAME_AFTER_RECEIVER);
+                        }
                     } else {
                         yp_parser_scope_push(parser, true);
                         name = parser->previous;
@@ -12220,6 +12126,9 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
                         }
 
                         name = parse_method_definition_name(parser);
+                        if (name.type == YP_TOKEN_MISSING) {
+                            yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_DEF_NAME_AFTER_RECEIVER);
+                        }
                     } else {
                         name = identifier;
                     }
@@ -12246,13 +12155,11 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
                 default:
                     yp_parser_scope_push(parser, true);
                     name = parse_method_definition_name(parser);
-                    break;
-            }
 
-            // If, after all that, we were unable to find a method name, add an
-            // error to the error list.
-            if (name.type == YP_TOKEN_MISSING) {
-                yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_DEF_NAME);
+                    if (name.type == YP_TOKEN_MISSING) {
+                        yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_DEF_NAME);
+                    }
+                    break;
             }
 
             yp_token_t lparen;
@@ -13181,7 +13088,6 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
 
             switch (parser->current.type) {
                 case YP_TOKEN_PARENTHESIS_LEFT: {
-                    parser->current_scope->explicit_params = true;
                     yp_token_t opening = parser->current;
                     parser_lex(parser);
 
@@ -13198,7 +13104,6 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
                     break;
                 }
                 case YP_CASE_PARAMETER: {
-                    parser->current_scope->explicit_params = true;
                     yp_accepts_block_stack_push(parser, false);
                     yp_token_t opening = not_provided(parser);
                     params = parse_block_parameters(parser, false, &opening, true);
diff --git a/templates/java/org/yarp/Loader.java.erb b/templates/java/org/yarp/Loader.java.erb
index 5cdb8c2b..7ef81b26 100644
--- a/templates/java/org/yarp/Loader.java.erb
+++ b/templates/java/org/yarp/Loader.java.erb
@@ -38,11 +38,11 @@ public class Loader {
 
                 constant = new byte[length];
 
-                if (Integer.compareUnsigned(start, 0x7FFFFFFF) <= 0) {
+                if (Integer.compareUnsigned(start, 1 << 31) <= 0) {
                     System.arraycopy(source, start, constant, 0, length);
                 } else {
                     int position = buffer.position();
-                    buffer.position(start & 0x7FFFFFFF);
+                    buffer.position(Math.abs(start));
                     buffer.get(constant, 0, length);
                     buffer.position(position);
                 }


Test patch:
diff --git a/rakelib/test.rake b/rakelib/test.rake
index 739bebd1..cbbcb045 100644
--- a/rakelib/test.rake
+++ b/rakelib/test.rake
@@ -17,20 +17,28 @@ return if RUBY_ENGINE == "jruby" || RUBY_ENGINE == "truffleruby"
 # Don't bother trying to configure memcheck on old versions of Ruby.
 return if RUBY_VERSION < "3.0"
 
-require "ruby_memcheck"
+
+begin
+  require "ruby_memcheck"
+  have_memcheck = true
+rescue LoadError
+  have_memcheck = false
+end
 
 namespace :test do
-  RubyMemcheck::TestTask.new(valgrind_internal: :compile, &config)
+  if have_memcheck
+    RubyMemcheck::TestTask.new(valgrind_internal: :compile, &config)
 
-  # Hide test:valgrind_internal from rake -T
-  Rake::Task["test:valgrind_internal"].clear_comments
+    # Hide test:valgrind_internal from rake -T
+    Rake::Task["test:valgrind_internal"].clear_comments
 
-  desc "Run tests under valgrind"
-  task :valgrind do
-    # Recompile with YARP_DEBUG_MODE_BUILD=1
-    ENV["YARP_DEBUG_MODE_BUILD"] = "1"
-    Rake::Task["clobber"].invoke
-    Rake::Task["test:valgrind_internal"].invoke
+    desc "Run tests under valgrind"
+    task :valgrind do
+      # Recompile with YARP_DEBUG_MODE_BUILD=1
+      ENV["YARP_DEBUG_MODE_BUILD"] = "1"
+      Rake::Task["clobber"].invoke
+      Rake::Task["test:valgrind_internal"].invoke
+    end
   end
 
   class GdbTestTask < Rake::TestTask
diff --git a/test/yarp/errors_test.rb b/test/yarp/errors_test.rb
index 89fafdf2..feecad7c 100644
--- a/test/yarp/errors_test.rb
+++ b/test/yarp/errors_test.rb
@@ -283,8 +283,7 @@ module YARP
 
     def test_def_with_expression_receiver_and_no_identifier
       assert_errors expression("def (a); end"), "def (a); end", [
-        ["Expected a `.` or `::` after the receiver in a method definition", 7..7],
-        ["Expected a method name", 7..7]
+        ["Expected a `.` or `::` after the receiver in a method definition", 7..7]
       ]
     end
 
@@ -292,7 +291,6 @@ module YARP
       assert_errors expression("def (\na\nb\n).c; end"), "def (\na\nb\n).c; end", [
         ["Expected a matching `)`", 7..7],
         ["Expected a `.` or `::` after the receiver in a method definition", 7..7],
-        ["Expected a method name", 7..7],
         ["Cannot parse the expression", 10..10],
         ["Cannot parse the expression", 11..11]
       ]
@@ -1194,30 +1192,11 @@ module YARP
       ]
     end
 
-    def test_writing_numbered_parameter
-      assert_errors expression("-> { _1 = 0 }"), "-> { _1 = 0 }", [
-        ["Token reserved for a numbered parameter", 5..7]
-      ]
-    end
-
-    def test_targeting_numbered_parameter
-      assert_errors expression("-> { _1, = 0 }"), "-> { _1, = 0 }", [
-        ["Token reserved for a numbered parameter", 5..7]
-      ]
-    end
-
-    def test_double_scope_numbered_parameters
-      source = "-> { _1 + -> { _2 } }"
-      errors = [["Numbered parameter is already used in outer scope", 15..17]]
-
-      assert_errors expression(source), source, errors, compare_ripper: false
-    end
-
     private
 
-    def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")
+    def assert_errors(expected, source, errors)
       # Ripper behaves differently on JRuby/TruffleRuby, so only check this on CRuby
-      assert_nil Ripper.sexp_raw(source) if compare_ripper
+      assert_nil Ripper.sexp_raw(source) if RUBY_ENGINE == "ruby"
 
       result = YARP.parse(source)
       node = result.value.statements.body.last
diff --git a/test/yarp/fixtures/hashes.txt b/test/yarp/fixtures/hashes.txt
index 443b2359..42c430e4 100644
--- a/test/yarp/fixtures/hashes.txt
+++ b/test/yarp/fixtures/hashes.txt
@@ -18,9 +18,3 @@
 { a: b, c: d, **e, f: g }
 
 { "a": !b? }
-
-a = 1
-tap do
-  b = 1
-  { a:, b:, c:, D: }
-end
diff --git a/test/yarp/location_test.rb b/test/yarp/location_test.rb
index 1c9aa104..3ef3da3f 100644
--- a/test/yarp/location_test.rb
+++ b/test/yarp/location_test.rb
@@ -410,20 +410,6 @@ module YARP
       assert_location(ImaginaryNode, "1ri")
     end
 
-    def test_ImplicitNode
-      assert_location(ImplicitNode, "{ foo: }", 2...6) do |node|
-        node.elements.first.value
-      end
-
-      assert_location(ImplicitNode, "{ Foo: }", 2..6) do |node|
-        node.elements.first.value
-      end
-
-      assert_location(ImplicitNode, "foo = 1; { foo: }", 11..15) do |node|
-        node.elements.first.value
-      end
-    end
-
     def test_InNode
       assert_location(InNode, "case foo; in bar; end", 10...16) do |node|
         node.conditions.first
diff --git a/test/yarp/snapshots/hashes.txt b/test/yarp/snapshots/hashes.txt
index b49db786..dde8ba01 100644
--- a/test/yarp/snapshots/hashes.txt
+++ b/test/yarp/snapshots/hashes.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (0...167))
-├── locals: [:a]
+@ ProgramNode (location: (0...120))
+├── locals: []
 └── statements:
-    @ StatementsNode (location: (0...167))
-    └── body: (length: 9)
+    @ StatementsNode (location: (0...120))
+    └── body: (length: 7)
         ├── @ HashNode (location: (0...2))
         │   ├── opening_loc: (0...1) = "{"
         │   ├── elements: (length: 0)
@@ -220,136 +220,36 @@
         │   │       │   └── name: "g"
         │   │       └── operator_loc: ∅
         │   └── closing_loc: (105...106) = "}"
-        ├── @ HashNode (location: (108...120))
-        │   ├── opening_loc: (108...109) = "{"
-        │   ├── elements: (length: 1)
-        │   │   └── @ AssocNode (location: (110...118))
-        │   │       ├── key:
-        │   │       │   @ SymbolNode (location: (110...114))
-        │   │       │   ├── opening_loc: (110...111) = "\""
-        │   │       │   ├── value_loc: (111...112) = "a"
-        │   │       │   ├── closing_loc: (112...114) = "\":"
-        │   │       │   └── unescaped: "a"
-        │   │       ├── value:
-        │   │       │   @ CallNode (location: (115...118))
-        │   │       │   ├── receiver:
-        │   │       │   │   @ CallNode (location: (116...118))
-        │   │       │   │   ├── receiver: ∅
-        │   │       │   │   ├── call_operator_loc: ∅
-        │   │       │   │   ├── message_loc: (116...118) = "b?"
-        │   │       │   │   ├── opening_loc: ∅
-        │   │       │   │   ├── arguments: ∅
-        │   │       │   │   ├── closing_loc: ∅
-        │   │       │   │   ├── block: ∅
-        │   │       │   │   ├── flags: ∅
-        │   │       │   │   └── name: "b?"
-        │   │       │   ├── call_operator_loc: ∅
-        │   │       │   ├── message_loc: (115...116) = "!"
-        │   │       │   ├── opening_loc: ∅
-        │   │       │   ├── arguments: ∅
-        │   │       │   ├── closing_loc: ∅
-        │   │       │   ├── block: ∅
-        │   │       │   ├── flags: ∅
-        │   │       │   └── name: "!"
-        │   │       └── operator_loc: ∅
-        │   └── closing_loc: (119...120) = "}"
-        ├── @ LocalVariableWriteNode (location: (122...127))
-        │   ├── name: :a
-        │   ├── depth: 0
-        │   ├── name_loc: (122...123) = "a"
-        │   ├── value:
-        │   │   @ IntegerNode (location: (126...127))
-        │   │   └── flags: decimal
-        │   └── operator_loc: (124...125) = "="
-        └── @ CallNode (location: (128...167))
-            ├── receiver: ∅
-            ├── call_operator_loc: ∅
-            ├── message_loc: (128...131) = "tap"
-            ├── opening_loc: ∅
-            ├── arguments: ∅
-            ├── closing_loc: ∅
-            ├── block:
-            │   @ BlockNode (location: (132...167))
-            │   ├── locals: [:b]
-            │   ├── parameters: ∅
-            │   ├── body:
-            │   │   @ StatementsNode (location: (137...163))
-            │   │   └── body: (length: 2)
-            │   │       ├── @ LocalVariableWriteNode (location: (137...142))
-            │   │       │   ├── name: :b
-            │   │       │   ├── depth: 0
-            │   │       │   ├── name_loc: (137...138) = "b"
-            │   │       │   ├── value:
-            │   │       │   │   @ IntegerNode (location: (141...142))
-            │   │       │   │   └── flags: decimal
-            │   │       │   └── operator_loc: (139...140) = "="
-            │   │       └── @ HashNode (location: (145...163))
-            │   │           ├── opening_loc: (145...146) = "{"
-            │   │           ├── elements: (length: 4)
-            │   │           │   ├── @ AssocNode (location: (147...149))
-            │   │           │   │   ├── key:
-            │   │           │   │   │   @ SymbolNode (location: (147...149))
-            │   │           │   │   │   ├── opening_loc: ∅
-            │   │           │   │   │   ├── value_loc: (147...148) = "a"
-            │   │           │   │   │   ├── closing_loc: (148...149) = ":"
-            │   │           │   │   │   └── unescaped: "a"
-            │   │           │   │   ├── value:
-            │   │           │   │   │   @ ImplicitNode (location: (147...149))
-            │   │           │   │   │   └── value:
-            │   │           │   │   │       @ LocalVariableReadNode (location: (147...149))
-            │   │           │   │   │       ├── name: :a
-            │   │           │   │   │       └── depth: 1
-            │   │           │   │   └── operator_loc: ∅
-            │   │           │   ├── @ AssocNode (location: (151...153))
-            │   │           │   │   ├── key:
-            │   │           │   │   │   @ SymbolNode (location: (151...153))
-            │   │           │   │   │   ├── opening_loc: ∅
-            │   │           │   │   │   ├── value_loc: (151...152) = "b"
-            │   │           │   │   │   ├── closing_loc: (152...153) = ":"
-            │   │           │   │   │   └── unescaped: "b"
-            │   │           │   │   ├── value:
-            │   │           │   │   │   @ ImplicitNode (location: (151...153))
-            │   │           │   │   │   └── value:
-            │   │           │   │   │       @ LocalVariableReadNode (location: (151...153))
-            │   │           │   │   │       ├── name: :b
-            │   │           │   │   │       └── depth: 0
-            │   │           │   │   └── operator_loc: ∅
-            │   │           │   ├── @ AssocNode (location: (155...157))
-            │   │           │   │   ├── key:
-            │   │           │   │   │   @ SymbolNode (location: (155...157))
-            │   │           │   │   │   ├── opening_loc: ∅
-            │   │           │   │   │   ├── value_loc: (155...156) = "c"
-            │   │           │   │   │   ├── closing_loc: (156...157) = ":"
-            │   │           │   │   │   └── unescaped: "c"
-            │   │           │   │   ├── value:
-            │   │           │   │   │   @ ImplicitNode (location: (155...157))
-            │   │           │   │   │   └── value:
-            │   │           │   │   │       @ CallNode (location: (155...157))
-            │   │           │   │   │       ├── receiver: ∅
-            │   │           │   │   │       ├── call_operator_loc: ∅
-            │   │           │   │   │       ├── message_loc: (155...156) = "c"
-            │   │           │   │   │       ├── opening_loc: ∅
-            │   │           │   │   │       ├── arguments: ∅
-            │   │           │   │   │       ├── closing_loc: ∅
-            │   │           │   │   │       ├── block: ∅
-            │   │           │   │   │       ├── flags: ∅
-            │   │           │   │   │       └── name: "c"
-            │   │           │   │   └── operator_loc: ∅
-            │   │           │   └── @ AssocNode (location: (159...161))
-            │   │           │       ├── key:
-            │   │           │       │   @ SymbolNode (location: (159...161))
-            │   │           │       │   ├── opening_loc: ∅
-            │   │           │       │   ├── value_loc: (159...160) = "D"
-            │   │           │       │   ├── closing_loc: (160...161) = ":"
-            │   │           │       │   └── unescaped: "D"
-            │   │           │       ├── value:
-            │   │           │       │   @ ImplicitNode (location: (159...161))
-            │   │           │       │   └── value:
-            │   │           │       │       @ ConstantReadNode (location: (159...161))
-            │   │           │       │       └── name: :D
-            │   │           │       └── operator_loc: ∅
-            │   │           └── closing_loc: (162...163) = "}"
-            │   ├── opening_loc: (132...134) = "do"
-            │   └── closing_loc: (164...167) = "end"
-            ├── flags: ∅
-            └── name: "tap"
+        └── @ HashNode (location: (108...120))
+            ├── opening_loc: (108...109) = "{"
+            ├── elements: (length: 1)
+            │   └── @ AssocNode (location: (110...118))
+            │       ├── key:
+            │       │   @ SymbolNode (location: (110...114))
+            │       │   ├── opening_loc: (110...111) = "\""
+            │       │   ├── value_loc: (111...112) = "a"
+            │       │   ├── closing_loc: (112...114) = "\":"
+            │       │   └── unescaped: "a"
+            │       ├── value:
+            │       │   @ CallNode (location: (115...118))
+            │       │   ├── receiver:
+            │       │   │   @ CallNode (location: (116...118))
+            │       │   │   ├── receiver: ∅
+            │       │   │   ├── call_operator_loc: ∅
+            │       │   │   ├── message_loc: (116...118) = "b?"
+            │       │   │   ├── opening_loc: ∅
+            │       │   │   ├── arguments: ∅
+            │       │   │   ├── closing_loc: ∅
+            │       │   │   ├── block: ∅
+            │       │   │   ├── flags: ∅
+            │       │   │   └── name: "b?"
+            │       │   ├── call_operator_loc: ∅
+            │       │   ├── message_loc: (115...116) = "!"
+            │       │   ├── opening_loc: ∅
+            │       │   ├── arguments: ∅
+            │       │   ├── closing_loc: ∅
+            │       │   ├── block: ∅
+            │       │   ├── flags: ∅
+            │       │   └── name: "!"
+            │       └── operator_loc: ∅
+            └── closing_loc: (119...120) = "}"
diff --git a/test/yarp/snapshots/if.txt b/test/yarp/snapshots/if.txt
index 7feb336d..ebafafe1 100644
--- a/test/yarp/snapshots/if.txt
+++ b/test/yarp/snapshots/if.txt
@@ -240,19 +240,7 @@
         │   │           │                   │   ├── value_loc: (248...249) = "b"
         │   │           │                   │   ├── closing_loc: (249...250) = ":"
         │   │           │                   │   └── unescaped: "b"
-        │   │           │                   ├── value:
-        │   │           │                   │   @ ImplicitNode (location: (248...250))
-        │   │           │                   │   └── value:
-        │   │           │                   │       @ CallNode (location: (248...250))
-        │   │           │                   │       ├── receiver: ∅
-        │   │           │                   │       ├── call_operator_loc: ∅
-        │   │           │                   │       ├── message_loc: (248...249) = "b"
-        │   │           │                   │       ├── opening_loc: ∅
-        │   │           │                   │       ├── arguments: ∅
-        │   │           │                   │       ├── closing_loc: ∅
-        │   │           │                   │       ├── block: ∅
-        │   │           │                   │       ├── flags: ∅
-        │   │           │                   │       └── name: "b"
+        │   │           │                   ├── value: ∅
         │   │           │                   └── operator_loc: ∅
         │   │           ├── closing_loc: ∅
         │   │           ├── block: ∅
diff --git a/test/yarp/snapshots/rescue.txt b/test/yarp/snapshots/rescue.txt
index 40b84f7b..3176c864 100644
--- a/test/yarp/snapshots/rescue.txt
+++ b/test/yarp/snapshots/rescue.txt
@@ -340,19 +340,7 @@
             │   │           │                   │   ├── value_loc: (303...304) = "b"
             │   │           │                   │   ├── closing_loc: (304...305) = ":"
             │   │           │                   │   └── unescaped: "b"
-            │   │           │                   ├── value:
-            │   │           │                   │   @ ImplicitNode (location: (303...305))
-            │   │           │                   │   └── value:
-            │   │           │                   │       @ CallNode (location: (303...305))
-            │   │           │                   │       ├── receiver: ∅
-            │   │           │                   │       ├── call_operator_loc: ∅
-            │   │           │                   │       ├── message_loc: (303...304) = "b"
-            │   │           │                   │       ├── opening_loc: ∅
-            │   │           │                   │       ├── arguments: ∅
-            │   │           │                   │       ├── closing_loc: ∅
-            │   │           │                   │       ├── block: ∅
-            │   │           │                   │       ├── flags: ∅
-            │   │           │                   │       └── name: "b"
+            │   │           │                   ├── value: ∅
             │   │           │                   └── operator_loc: ∅
             │   │           ├── closing_loc: ∅
             │   │           ├── block: ∅
diff --git a/test/yarp/snapshots/seattlerb/assoc__bare.txt b/test/yarp/snapshots/seattlerb/assoc__bare.txt
index fa86e4fa..87ae0f72 100644
--- a/test/yarp/snapshots/seattlerb/assoc__bare.txt
+++ b/test/yarp/snapshots/seattlerb/assoc__bare.txt
@@ -13,18 +13,6 @@
             │       │   ├── value_loc: (2...3) = "y"
             │       │   ├── closing_loc: (3...4) = ":"
             │       │   └── unescaped: "y"
-            │       ├── value:
-            │       │   @ ImplicitNode (location: (2...4))
-            │       │   └── value:
-            │       │       @ CallNode (location: (2...4))
-            │       │       ├── receiver: ∅
-            │       │       ├── call_operator_loc: ∅
-            │       │       ├── message_loc: (2...3) = "y"
-            │       │       ├── opening_loc: ∅
-            │       │       ├── arguments: ∅
-            │       │       ├── closing_loc: ∅
-            │       │       ├── block: ∅
-            │       │       ├── flags: ∅
-            │       │       └── name: "y"
+            │       ├── value: ∅
             │       └── operator_loc: ∅
             └── closing_loc: (5...6) = "}"
diff --git a/test/yarp/snapshots/unparser/corpus/literal/block.txt b/test/yarp/snapshots/unparser/corpus/literal/block.txt
index 85f6f8fd..bf3b6806 100644
--- a/test/yarp/snapshots/unparser/corpus/literal/block.txt
+++ b/test/yarp/snapshots/unparser/corpus/literal/block.txt
@@ -1337,25 +1337,39 @@
             ├── closing_loc: ∅
             ├── block:
             │   @ BlockNode (location: (724...737))
-            │   ├── locals: [:_1, :_2]
+            │   ├── locals: []
             │   ├── parameters: ∅
             │   ├── body:
             │   │   @ StatementsNode (location: (728...735))
             │   │   └── body: (length: 1)
             │   │       └── @ CallNode (location: (728...735))
             │   │           ├── receiver:
-            │   │           │   @ LocalVariableReadNode (location: (728...730))
-            │   │           │   ├── name: :_1
-            │   │           │   └── depth: 0
+            │   │           │   @ CallNode (location: (728...730))
+            │   │           │   ├── receiver: ∅
+            │   │           │   ├── call_operator_loc: ∅
+            │   │           │   ├── message_loc: (728...730) = "_1"
+            │   │           │   ├── opening_loc: ∅
+            │   │           │   ├── arguments: ∅
+            │   │           │   ├── closing_loc: ∅
+            │   │           │   ├── block: ∅
+            │   │           │   ├── flags: variable_call
+            │   │           │   └── name: "_1"
             │   │           ├── call_operator_loc: ∅
             │   │           ├── message_loc: (731...732) = "+"
             │   │           ├── opening_loc: ∅
             │   │           ├── arguments:
             │   │           │   @ ArgumentsNode (location: (733...735))
             │   │           │   └── arguments: (length: 1)
-            │   │           │       └── @ LocalVariableReadNode (location: (733...735))
-            │   │           │           ├── name: :_2
-            │   │           │           └── depth: 0
+            │   │           │       └── @ CallNode (location: (733...735))
+            │   │           │           ├── receiver: ∅
+            │   │           │           ├── call_operator_loc: ∅
+            │   │           │           ├── message_loc: (733...735) = "_2"
+            │   │           │           ├── opening_loc: ∅
+            │   │           │           ├── arguments: ∅
+            │   │           │           ├── closing_loc: ∅
+            │   │           │           ├── block: ∅
+            │   │           │           ├── flags: variable_call
+            │   │           │           └── name: "_2"
             │   │           ├── closing_loc: ∅
             │   │           ├── block: ∅
             │   │           ├── flags: ∅
diff --git a/test/yarp/snapshots/unparser/corpus/literal/since/27.txt b/test/yarp/snapshots/unparser/corpus/literal/since/27.txt
index 78ecbff5..05bc6fda 100644
--- a/test/yarp/snapshots/unparser/corpus/literal/since/27.txt
+++ b/test/yarp/snapshots/unparser/corpus/literal/since/27.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (0...22))
     └── body: (length: 2)
         ├── @ LambdaNode (location: (0...16))
-        │   ├── locals: [:_1, :_2]
+        │   ├── locals: []
         │   ├── operator_loc: (0...2) = "->"
         │   ├── opening_loc: (3...4) = "{"
         │   ├── closing_loc: (15...16) = "}"
@@ -14,18 +14,32 @@
         │       └── body: (length: 1)
         │           └── @ CallNode (location: (7...14))
         │               ├── receiver:
-        │               │   @ LocalVariableReadNode (location: (7...9))
-        │               │   ├── name: :_1
-        │               │   └── depth: 0
+        │               │   @ CallNode (location: (7...9))
+        │               │   ├── receiver: ∅
+        │               │   ├── call_operator_loc: ∅
+        │               │   ├── message_loc: (7...9) = "_1"
+        │               │   ├── opening_loc: ∅
+        │               │   ├── arguments: ∅
+        │               │   ├── closing_loc: ∅
+        │               │   ├── block: ∅
+        │               │   ├── flags: variable_call
+        │               │   └── name: "_1"
         │               ├── call_operator_loc: ∅
         │               ├── message_loc: (10...11) = "+"
         │               ├── opening_loc: ∅
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (12...14))
         │               │   └── arguments: (length: 1)
-        │               │       └── @ LocalVariableReadNode (location: (12...14))
-        │               │           ├── name: :_2
-        │               │           └── depth: 0
+        │               │       └── @ CallNode (location: (12...14))
+        │               │           ├── receiver: ∅
+        │               │           ├── call_operator_loc: ∅
+        │               │           ├── message_loc: (12...14) = "_2"
+        │               │           ├── opening_loc: ∅
+        │               │           ├── arguments: ∅
+        │               │           ├── closing_loc: ∅
+        │               │           ├── block: ∅
+        │               │           ├── flags: variable_call
+        │               │           └── name: "_2"
         │               ├── closing_loc: ∅
         │               ├── block: ∅
         │               ├── flags: ∅
diff --git a/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt b/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt
index 9b6d43af..09d4853a 100644
--- a/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt
+++ b/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt
@@ -13,11 +13,7 @@
         │   │       │   ├── value_loc: (1...4) = "BAR"
         │   │       │   ├── closing_loc: (4...5) = ":"
         │   │       │   └── unescaped: "BAR"
-        │   │       ├── value:
-        │   │       │   @ ImplicitNode (location: (1...5))
-        │   │       │   └── value:
-        │   │       │       @ ConstantReadNode (location: (1...5))
-        │   │       │       └── name: :BAR
+        │   │       ├── value: ∅
         │   │       └── operator_loc: ∅
         │   └── closing_loc: (5...6) = "}"
         ├── @ HashNode (location: (8...16))
@@ -30,19 +26,7 @@
         │   │   │   │   ├── value_loc: (9...10) = "a"
         │   │   │   │   ├── closing_loc: (10...11) = ":"
         │   │   │   │   └── unescaped: "a"
-        │   │   │   ├── value:
-        │   │   │   │   @ ImplicitNode (location: (9...11))
-        │   │   │   │   └── value:
-        │   │   │   │       @ CallNode (location: (9...11))
-        │   │   │   │       ├── receiver: ∅
-        │   │   │   │       ├── call_operator_loc: ∅
-        │   │   │   │       ├── message_loc: (9...10) = "a"
-        │   │   │   │       ├── opening_loc: ∅
-        │   │   │   │       ├── arguments: ∅
-        │   │   │   │       ├── closing_loc: ∅
-        │   │   │   │       ├── block: ∅
-        │   │   │   │       ├── flags: ∅
-        │   │   │   │       └── name: "a"
+        │   │   │   ├── value: ∅
         │   │   │   └── operator_loc: ∅
         │   │   └── @ AssocNode (location: (13...15))
         │   │       ├── key:
@@ -51,19 +35,7 @@
         │   │       │   ├── value_loc: (13...14) = "b"
         │   │       │   ├── closing_loc: (14...15) = ":"
         │   │       │   └── unescaped: "b"
-        │   │       ├── value:
-        │   │       │   @ ImplicitNode (location: (13...15))
-        │   │       │   └── value:
-        │   │       │       @ CallNode (location: (13...15))
-        │   │       │       ├── receiver: ∅
-        │   │       │       ├── call_operator_loc: ∅
-        │   │       │       ├── message_loc: (13...14) = "b"
-        │   │       │       ├── opening_loc: ∅
-        │   │       │       ├── arguments: ∅
-        │   │       │       ├── closing_loc: ∅
-        │   │       │       ├── block: ∅
-        │   │       │       ├── flags: ∅
-        │   │       │       └── name: "b"
+        │   │       ├── value: ∅
         │   │       └── operator_loc: ∅
         │   └── closing_loc: (15...16) = "}"
         └── @ HashNode (location: (18...25))
@@ -76,18 +48,6 @@
             │       │   ├── value_loc: (19...23) = "puts"
             │       │   ├── closing_loc: (23...24) = ":"
             │       │   └── unescaped: "puts"
-            │       ├── value:
-            │       │   @ ImplicitNode (location: (19...24))
-            │       │   └── value:
-            │       │       @ CallNode (location: (19...24))
-            │       │       ├── receiver: ∅
-            │       │       ├── call_operator_loc: ∅
-            │       │       ├── message_loc: (19...23) = "puts"
-            │       │       ├── opening_loc: ∅
-            │       │       ├── arguments: ∅
-            │       │       ├── closing_loc: ∅
-            │       │       ├── block: ∅
-            │       │       ├── flags: ∅
-            │       │       └── name: "puts"
+            │       ├── value: ∅
             │       └── operator_loc: ∅
             └── closing_loc: (24...25) = "}"
diff --git a/test/yarp/snapshots/whitequark/keyword_argument_omission.txt b/test/yarp/snapshots/whitequark/keyword_argument_omission.txt
index bbcb0d36..6808bf12 100644
--- a/test/yarp/snapshots/whitequark/keyword_argument_omission.txt
+++ b/test/yarp/snapshots/whitequark/keyword_argument_omission.txt
@@ -20,19 +20,7 @@
             │               │   │   ├── value_loc: (4...5) = "a"
             │               │   │   ├── closing_loc: (5...6) = ":"
             │               │   │   └── unescaped: "a"
-            │               │   ├── value:
-            │               │   │   @ ImplicitNode (location: (4...6))
-            │               │   │   └── value:
-            │               │   │       @ CallNode (location: (4...6))
-            │               │   │       ├── receiver: ∅
-            │               │   │       ├── call_operator_loc: ∅
-            │               │   │       ├── message_loc: (4...5) = "a"
-            │               │   │       ├── opening_loc: ∅
-            │               │   │       ├── arguments: ∅
-            │               │   │       ├── closing_loc: ∅
-            │               │   │       ├── block: ∅
-            │               │   │       ├── flags: ∅
-            │               │   │       └── name: "a"
+            │               │   ├── value: ∅
             │               │   └── operator_loc: ∅
             │               └── @ AssocNode (location: (8...10))
             │                   ├── key:
@@ -41,19 +29,7 @@
             │                   │   ├── value_loc: (8...9) = "b"
             │                   │   ├── closing_loc: (9...10) = ":"
             │                   │   └── unescaped: "b"
-            │                   ├── value:
-            │                   │   @ ImplicitNode (location: (8...10))
-            │                   │   └── value:
-            │                   │       @ CallNode (location: (8...10))
-            │                   │       ├── receiver: ∅
-            │                   │       ├── call_operator_loc: ∅
-            │                   │       ├── message_loc: (8...9) = "b"
-            │                   │       ├── opening_loc: ∅
-            │                   │       ├── arguments: ∅
-            │                   │       ├── closing_loc: ∅
-            │                   │       ├── block: ∅
-            │                   │       ├── flags: ∅
-            │                   │       └── name: "b"
+            │                   ├── value: ∅
             │                   └── operator_loc: ∅
             ├── closing_loc: (10...11) = ")"
             ├── block: ∅
diff --git a/test/yarp/snapshots/whitequark/numbered_args_after_27.txt b/test/yarp/snapshots/whitequark/numbered_args_after_27.txt
index 97e39435..c1cbe3c7 100644
--- a/test/yarp/snapshots/whitequark/numbered_args_after_27.txt
+++ b/test/yarp/snapshots/whitequark/numbered_args_after_27.txt
@@ -4,7 +4,7 @@
     @ StatementsNode (location: (0...65))
     └── body: (length: 4)
         ├── @ LambdaNode (location: (0...17))
-        │   ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+        │   ├── locals: []
         │   ├── operator_loc: (0...2) = "->"
         │   ├── opening_loc: (3...5) = "do"
         │   ├── closing_loc: (14...17) = "end"
@@ -14,24 +14,38 @@
         │       └── body: (length: 1)
         │           └── @ CallNode (location: (6...13))
         │               ├── receiver:
-        │               │   @ LocalVariableReadNode (location: (6...8))
-        │               │   ├── name: :_1
-        │               │   └── depth: 0
+        │               │   @ CallNode (location: (6...8))
+        │               │   ├── receiver: ∅
+        │               │   ├── call_operator_loc: ∅
+        │               │   ├── message_loc: (6...8) = "_1"
+        │               │   ├── opening_loc: ∅
+        │               │   ├── arguments: ∅
+        │               │   ├── closing_loc: ∅
+        │               │   ├── block: ∅
+        │               │   ├── flags: variable_call
+        │               │   └── name: "_1"
         │               ├── call_operator_loc: ∅
         │               ├── message_loc: (9...10) = "+"
         │               ├── opening_loc: ∅
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (11...13))
         │               │   └── arguments: (length: 1)
-        │               │       └── @ LocalVariableReadNode (location: (11...13))
-        │               │           ├── name: :_9
-        │               │           └── depth: 0
+        │               │       └── @ CallNode (location: (11...13))
+        │               │           ├── receiver: ∅
+        │               │           ├── call_operator_loc: ∅
+        │               │           ├── message_loc: (11...13) = "_9"
+        │               │           ├── opening_loc: ∅
+        │               │           ├── arguments: ∅
+        │               │           ├── closing_loc: ∅
+        │               │           ├── block: ∅
+        │               │           ├── flags: variable_call
+        │               │           └── name: "_9"
         │               ├── closing_loc: ∅
         │               ├── block: ∅
         │               ├── flags: ∅
         │               └── name: "+"
         ├── @ LambdaNode (location: (19...32))
-        │   ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+        │   ├── locals: []
         │   ├── operator_loc: (19...21) = "->"
         │   ├── opening_loc: (22...23) = "{"
         │   ├── closing_loc: (31...32) = "}"
@@ -41,18 +55,32 @@
         │       └── body: (length: 1)
         │           └── @ CallNode (location: (24...31))
         │               ├── receiver:
-        │               │   @ LocalVariableReadNode (location: (24...26))
-        │               │   ├── name: :_1
-        │               │   └── depth: 0
+        │               │   @ CallNode (location: (24...26))
+        │               │   ├── receiver: ∅
+        │               │   ├── call_operator_loc: ∅
+        │               │   ├── message_loc: (24...26) = "_1"
+        │               │   ├── opening_loc: ∅
+        │               │   ├── arguments: ∅
+        │               │   ├── closing_loc: ∅
+        │               │   ├── block: ∅
+        │               │   ├── flags: variable_call
+        │               │   └── name: "_1"
         │               ├── call_operator_loc: ∅
         │               ├── message_loc: (27...28) = "+"
         │               ├── opening_loc: ∅
         │               ├── arguments:
         │               │   @ ArgumentsNode (location: (29...31))
         │               │   └── arguments: (length: 1)
-        │               │       └── @ LocalVariableReadNode (location: (29...31))
-        │               │           ├── name: :_9
-        │               │           └── depth: 0
+        │               │       └── @ CallNode (location: (29...31))
+        │               │           ├── receiver: ∅
+        │               │           ├── call_operator_loc: ∅
+        │               │           ├── message_loc: (29...31) = "_9"
+        │               │           ├── opening_loc: ∅
+        │               │           ├── arguments: ∅
+        │               │           ├── closing_loc: ∅
+        │               │           ├── block: ∅
+        │               │           ├── flags: variable_call
+        │               │           └── name: "_9"
         │               ├── closing_loc: ∅
         │               ├── block: ∅
         │               ├── flags: ∅
@@ -66,25 +94,39 @@
         │   ├── closing_loc: ∅
         │   ├── block:
         │   │   @ BlockNode (location: (36...50))
-        │   │   ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+        │   │   ├── locals: []
         │   │   ├── parameters: ∅
         │   │   ├── body:
         │   │   │   @ StatementsNode (location: (39...46))
         │   │   │   └── body: (length: 1)
         │   │   │       └── @ CallNode (location: (39...46))
         │   │   │           ├── receiver:
-        │   │   │           │   @ LocalVariableReadNode (location: (39...41))
-        │   │   │           │   ├── name: :_1
-        │   │   │           │   └── depth: 0
+        │   │   │           │   @ CallNode (location: (39...41))
+        │   │   │           │   ├── receiver: ∅
+        │   │   │           │   ├── call_operator_loc: ∅
+        │   │   │           │   ├── message_loc: (39...41) = "_1"
+        │   │   │           │   ├── opening_loc: ∅
+        │   │   │           │   ├── arguments: ∅
+        │   │   │           │   ├── closing_loc: ∅
+        │   │   │           │   ├── block: ∅
+        │   │   │           │   ├── flags: variable_call
+        │   │   │           │   └── name: "_1"
         │   │   │           ├── call_operator_loc: ∅
         │   │   │           ├── message_loc: (42...43) = "+"
         │   │   │           ├── opening_loc: ∅
         │   │   │           ├── arguments:
         │   │   │           │   @ ArgumentsNode (location: (44...46))
         │   │   │           │   └── arguments: (length: 1)
-        │   │   │           │       └── @ LocalVariableReadNode (location: (44...46))
-        │   │   │           │           ├── name: :_9
-        │   │   │           │           └── depth: 0
+        │   │   │           │       └── @ CallNode (location: (44...46))
+        │   │   │           │           ├── receiver: ∅
+        │   │   │           │           ├── call_operator_loc: ∅
+        │   │   │           │           ├── message_loc: (44...46) = "_9"
+        │   │   │           │           ├── opening_loc: ∅
+        │   │   │           │           ├── arguments: ∅
+        │   │   │           │           ├── closing_loc: ∅
+        │   │   │           │           ├── block: ∅
+        │   │   │           │           ├── flags: variable_call
+        │   │   │           │           └── name: "_9"
         │   │   │           ├── closing_loc: ∅
         │   │   │           ├── block: ∅
         │   │   │           ├── flags: ∅
@@ -102,25 +144,39 @@
             ├── closing_loc: ∅
             ├── block:
             │   @ BlockNode (location: (54...65))
-            │   ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
+            │   ├── locals: []
             │   ├── parameters: ∅
             │   ├── body:
             │   │   @ StatementsNode (location: (56...63))
             │   │   └── body: (length: 1)
             │   │       └── @ CallNode (location: (56...63))
             │   │           ├── receiver:
-            │   │           │   @ LocalVariableReadNode (location: (56...58))
-            │   │           │   ├── name: :_1
-            │   │           │   └── depth: 0
+            │   │           │   @ CallNode (location: (56...58))
+            │   │           │   ├── receiver: ∅
+            │   │           │   ├── call_operator_loc: ∅
+            │   │           │   ├── message_loc: (56...58) = "_1"
+            │   │           │   ├── opening_loc: ∅
+            │   │           │   ├── arguments: ∅
+            │   │           │   ├── closing_loc: ∅
+            │   │           │   ├── block: ∅
+            │   │           │   ├── flags: variable_call
+            │   │           │   └── name: "_1"
             │   │           ├── call_operator_loc: ∅
             │   │           ├── message_loc: (59...60) = "+"
             │   │           ├── opening_loc: ∅
             │   │           ├── arguments:
             │   │           │   @ ArgumentsNode (location: (61...63))
             │   │           │   └── arguments: (length: 1)
-            │   │           │       └── @ LocalVariableReadNode (location: (61...63))
-            │   │           │           ├── name: :_9
-            │   │           │           └── depth: 0
+            │   │           │       └── @ CallNode (location: (61...63))
+            │   │           │           ├── receiver: ∅
+            │   │           │           ├── call_operator_loc: ∅
+            │   │           │           ├── message_loc: (61...63) = "_9"
+            │   │           │           ├── opening_loc: ∅
+            │   │           │           ├── arguments: ∅
+            │   │           │           ├── closing_loc: ∅
+            │   │           │           ├── block: ∅
+            │   │           │           ├── flags: variable_call
+            │   │           │           └── name: "_9"
             │   │           ├── closing_loc: ∅
             │   │           ├── block: ∅
             │   │           ├── flags: ∅
diff --git a/test/yarp/snapshots/whitequark/ruby_bug_15789.txt b/test/yarp/snapshots/whitequark/ruby_bug_15789.txt
index d0069b56..d2ff8984 100644
--- a/test/yarp/snapshots/whitequark/ruby_bug_15789.txt
+++ b/test/yarp/snapshots/whitequark/ruby_bug_15789.txt
@@ -28,7 +28,7 @@
         │   │           │   │   │       ├── operator_loc: (7...8) = "="
         │   │           │   │   │       └── value:
         │   │           │   │   │           @ LambdaNode (location: (9...15))
-        │   │           │   │   │           ├── locals: [:_1]
+        │   │           │   │   │           ├── locals: []
         │   │           │   │   │           ├── operator_loc: (9...11) = "->"
         │   │           │   │   │           ├── opening_loc: (11...12) = "{"
         │   │           │   │   │           ├── closing_loc: (14...15) = "}"
@@ -36,9 +36,16 @@
         │   │           │   │   │           └── body:
         │   │           │   │   │               @ StatementsNode (location: (12...14))
         │   │           │   │   │               └── body: (length: 1)
-        │   │           │   │   │                   └── @ LocalVariableReadNode (location: (12...14))
-        │   │           │   │   │                       ├── name: :_1
-        │   │           │   │   │                       └── depth: 0
+        │   │           │   │   │                   └── @ CallNode (location: (12...14))
+        │   │           │   │   │                       ├── receiver: ∅
+        │   │           │   │   │                       ├── call_operator_loc: ∅
+        │   │           │   │   │                       ├── message_loc: (12...14) = "_1"
+        │   │           │   │   │                       ├── opening_loc: ∅
+        │   │           │   │   │                       ├── arguments: ∅
+        │   │           │   │   │                       ├── closing_loc: ∅
+        │   │           │   │   │                       ├── block: ∅
+        │   │           │   │   │                       ├── flags: variable_call
+        │   │           │   │   │                       └── name: "_1"
         │   │           │   │   ├── rest: ∅
         │   │           │   │   ├── posts: (length: 0)
         │   │           │   │   ├── keywords: (length: 0)
@@ -84,7 +91,7 @@
             │           │   │   │       ├── name_loc: (27...29) = "a:"
             │           │   │   │       └── value:
             │           │   │   │           @ LambdaNode (location: (30...36))
-            │           │   │   │           ├── locals: [:_1]
+            │           │   │   │           ├── locals: []
             │           │   │   │           ├── operator_loc: (30...32) = "->"
             │           │   │   │           ├── opening_loc: (32...33) = "{"
             │           │   │   │           ├── closing_loc: (35...36) = "}"
@@ -92,9 +99,16 @@
             │           │   │   │           └── body:
             │           │   │   │               @ StatementsNode (location: (33...35))
             │           │   │   │               └── body: (length: 1)
-            │           │   │   │                   └── @ LocalVariableReadNode (location: (33...35))
-            │           │   │   │                       ├── name: :_1
-            │           │   │   │                       └── depth: 0
+            │           │   │   │                   └── @ CallNode (location: (33...35))
+            │           │   │   │                       ├── receiver: ∅
+            │           │   │   │                       ├── call_operator_loc: ∅
+            │           │   │   │                       ├── message_loc: (33...35) = "_1"
+            │           │   │   │                       ├── opening_loc: ∅
+            │           │   │   │                       ├── arguments: ∅
+            │           │   │   │                       ├── closing_loc: ∅
+            │           │   │   │                       ├── block: ∅
+            │           │   │   │                       ├── flags: variable_call
+            │           │   │   │                       └── name: "_1"
             │           │   │   ├── keyword_rest: ∅
             │           │   │   └── block: ∅
             │           │   ├── locals: (length: 0)