From 576acb950232a8d979b94117050f577505d8a167 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sat, 6 Dec 2025 16:02:49 +0900 Subject: [PATCH 1/9] Remove `FORWARD_ARGS_WITH_RUBY2_KEYWORDS` check Because `FORWARD_ARGS_WITH_RUBY2_KEYWORDS` definition was removed by 4f77d8d3289ece0e3537d9273a5c745120bff59a. --- parse.y | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/parse.y b/parse.y index 32431434de9734..b700302e7b615f 100644 --- a/parse.y +++ b/parse.y @@ -6385,11 +6385,7 @@ f_args : f_arg ',' f_opt_arg(arg_value) ',' f_rest_arg opt_args_tail(args_tail) args_forward : tBDOT3 { -#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS - $$ = 0; -#else $$ = idFWD_KWREST; -#endif /*% ripper: args_forward! %*/ } ; @@ -14434,11 +14430,7 @@ new_args(struct parser_params *p, rb_node_args_aux_t *pre_args, rb_node_opt_arg_ args->opt_args = opt_args; -#ifdef FORWARD_ARGS_WITH_RUBY2_KEYWORDS - args->ruby2_keywords = args->forwarding; -#else args->ruby2_keywords = 0; -#endif nd_set_loc(RNODE(tail), loc); @@ -15049,9 +15041,7 @@ static void add_forwarding_args(struct parser_params *p) { arg_var(p, idFWD_REST); -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS arg_var(p, idFWD_KWREST); -#endif arg_var(p, idFWD_BLOCK); arg_var(p, idFWD_ALL); } @@ -15094,15 +15084,11 @@ static NODE * new_args_forward_call(struct parser_params *p, NODE *leading, const YYLTYPE *loc, const YYLTYPE *argsloc) { NODE *rest = NEW_LVAR(idFWD_REST, loc); -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS NODE *kwrest = list_append(p, NEW_LIST(0, loc), NEW_LVAR(idFWD_KWREST, loc)); -#endif rb_node_block_pass_t *block = NEW_BLOCK_PASS(NEW_LVAR(idFWD_BLOCK, loc), argsloc, &NULL_LOC); NODE *args = leading ? rest_arg_append(p, leading, rest, argsloc) : NEW_SPLAT(rest, loc, &NULL_LOC); block->forwarding = TRUE; -#ifndef FORWARD_ARGS_WITH_RUBY2_KEYWORDS args = arg_append(p, args, new_hash(p, kwrest, loc), argsloc); -#endif return arg_blk_pass(args, block); } From 056997cbcdd38b062518fe54e311c964f8bfd98f Mon Sep 17 00:00:00 2001 From: yui-knk Date: Tue, 9 Dec 2025 09:05:45 +0900 Subject: [PATCH 2/9] Remove needless `ruby2_keywords` field from `struct rb_args_info` `ruby2_keywords` is set only to be `0` in parse.y. However `args->ruby2_keywords` is initialized with `0` by `MEMZERO` in `rb_node_args_new` function and `body->param.flags.ruby2_keywords` is initialized with `0` by `ZALLOC` in `rb_iseq_constant_body_alloc` function, so `args->ruby2_keywords` does nothing for `body->param.flags.ruby2_keywords`. --- compile.c | 1 - parse.y | 2 -- rubyparser.h | 1 - 3 files changed, 4 deletions(-) diff --git a/compile.c b/compile.c index e6665c7e1d966f..a5d821eb810cf1 100644 --- a/compile.c +++ b/compile.c @@ -2106,7 +2106,6 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG); - body->param.flags.ruby2_keywords = args->ruby2_keywords; body->param.lead_num = arg_size = (int)args->pre_args_num; if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE; debugs(" - argc: %d\n", body->param.lead_num); diff --git a/parse.y b/parse.y index b700302e7b615f..cb73ea2ef026bb 100644 --- a/parse.y +++ b/parse.y @@ -14430,8 +14430,6 @@ new_args(struct parser_params *p, rb_node_args_aux_t *pre_args, rb_node_opt_arg_ args->opt_args = opt_args; - args->ruby2_keywords = 0; - nd_set_loc(RNODE(tail), loc); return tail; diff --git a/rubyparser.h b/rubyparser.h index ee4fe2e44da03d..5af8f6db62c308 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -782,7 +782,6 @@ struct rb_args_info { struct RNode_OPT_ARG *opt_args; unsigned int no_kwarg: 1; - unsigned int ruby2_keywords: 1; unsigned int forwarding: 1; }; From fab94ecd344b5804f9a1e6b38082049f37bacbd3 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 13:04:34 +0100 Subject: [PATCH 3/9] [ruby/rubygems] Fix the config suggestion in the warning for `$ bundle` * `install_or_cli_help` does not exist for older Bundler like Bundler 2 and so results in a confusing error on Bundler 2: ``` $ bundle Could not find command "". ``` * See https://github.com/ruby/rubygems/pull/9136/files#r2592366837 * Merge the behavior of `install_or_cli_help` in `install`. https://github.com/ruby/rubygems/commit/9b4819ae18 --- lib/bundler/cli.rb | 17 +++++++++++------ spec/bundler/bundler/cli_spec.rb | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 36ce04eb2a5d05..1321c56ed740c1 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -120,12 +120,14 @@ def cli_help self.class.send(:class_options_help, shell) end - desc "install_or_cli_help", "Tries to run bundle install but prints a summary of bundler commands if there is no Gemfile", hide: true + desc "install_or_cli_help", "Deprecated alias of install", hide: true def install_or_cli_help + Bundler.ui.warn <<~MSG + `bundle install_or_cli_help` is a deprecated alias of `bundle install`. + It might be called due to the 'default_cli_command' being set to 'install_or_cli_help', + if so fix that by running `bundle config set default_cli_command install --global`. + MSG invoke_other_command("install") - rescue GemfileNotFound => error - Bundler.ui.error error.message, wrap: true - invoke_other_command("cli_help") end def self.default_command(meth = nil) @@ -136,12 +138,12 @@ def self.default_command(meth = nil) In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, - or you can continue to use the current behavior with `bundle config set default_cli_command install_or_cli_help --global`. + or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. MSG end - Bundler.settings[:default_cli_command] || "install_or_cli_help" + Bundler.settings[:default_cli_command] || "install" end class_option "no-color", type: :boolean, desc: "Disable colorization in output" @@ -287,6 +289,9 @@ def install Bundler.settings.temporary(no_install: false) do Install.new(options).run end + rescue GemfileNotFound => error + invoke_other_command("cli_help") + raise error # re-raise to show the error and get a failing exit status end map aliases_for("install") diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index 4503cea6a0058c..7bc8fd3d36adaf 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -100,10 +100,11 @@ def out_with_macos_man_workaround end it "runs bundle install when default_cli_command set to install" do - bundle "config set default_cli_command install_or_cli_help" + bundle "config set default_cli_command install" bundle "", raise_on_error: false expect(out).to_not include("In a future version of Bundler") expect(err).to include("Could not locate Gemfile") + expect(exitstatus).to_not be_zero end end From 19172d64ebe909f185e28b1d8368a8a920f94a8b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 12:24:03 +0100 Subject: [PATCH 4/9] [ruby/rubygems] Fix indentation of the info message for default_cli_command * It looked like: In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. Bundler version 4.0.0 (2025-12-08 commit https://github.com/ruby/rubygems/commit/9b4819ae18) * And now looks like: In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. Bundler version 4.0.0 (2025-12-08 commit https://github.com/ruby/rubygems/commit/9b4819ae18) https://github.com/ruby/rubygems/commit/979dada8f3 --- lib/bundler/cli.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 1321c56ed740c1..17d8c42e6ecdc5 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -134,12 +134,13 @@ def self.default_command(meth = nil) return super if meth unless Bundler.settings[:default_cli_command] - Bundler.ui.info <<-MSG + Bundler.ui.info <<~MSG In a future version of Bundler, running `bundle` without argument will no longer run `bundle install`. Instead, the `cli_help` command will be displayed. Please use `bundle install` explicitly for scripts like CI/CD. You can use the future behavior now with `bundle config set default_cli_command cli_help --global`, or you can continue to use the current behavior with `bundle config set default_cli_command install --global`. This message will be removed after a default_cli_command value is set. + MSG end From 12c16f9dedb38af8111809229495e28e8d37b569 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Mon, 8 Dec 2025 17:39:34 +0100 Subject: [PATCH 5/9] [ruby/rubygems] Fix Bundler removing executables after creating them When running a fresh `bundle install` with gems that contains executables, Bundler will generate binstubs but soon after will remove them. This is a regression introduced in https://github.com/ruby/rubygems/commit/573ffad3ea4a. This results in doing `bundle install && bundle exec foo` to raise an error saying `foo` couldn't be found. This issue only appears if `BUNDLE_CLEAN` is set. At the end of the installation process, when Bundler has installed gems and generated binstubs, it runs the cleanup. 1. It [detects](https://github.com/ruby/rubygems/blob/4f8aa3b40cded3465bb2cd761e9ce7f8673b7fcb/bundler/lib/bundler/runtime.rb#L182) the executable for the current specs 2. Any existing executables not detected is then removed https://github.com/ruby/rubygems/blob/4f8aa3b40cded3465bb2cd761e9ce7f8673b7fcb/bundler/lib/bundler/runtime.rb#L194. The issue being that 1. now returns an empty array where as it should return the executables of the gems from the current bundle. The problem is in https://github.com/ruby/rubygems/commit/573ffad3ea4a where we removed the `executables` method from the `EndpointSpecification`. When Bundler reads the lockfile, it creates a `EndpointSpecification` object for each spec. At this point, the EndpointSpecification doesn't know about the `executables` of a gem. Once Bundler fetches the `gemspec` from the remote, it swaps the the "spec" with the real one and from here knows what executables the gem has. Reintroduce the `executables` method and the `bindir` in the EndpointSpecification class. From what I'm seeing, the removal of those wasn't needed to resolve the issue where Bundler remembers CLI flags. This is probably an oversight. https://github.com/ruby/rubygems/commit/b47f6b0247 --- lib/bundler/endpoint_specification.rb | 22 ++++++++++++++++++++++ spec/bundler/commands/install_spec.rb | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index 95c6deea26bf4e..c06684657d598b 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -60,6 +60,28 @@ def load_paths end end + # needed for binstubs + def executables + if @remote_specification + @remote_specification.executables + elsif _local_specification + _local_specification.executables + else + super + end + end + + # needed for bundle clean + def bindir + if @remote_specification + @remote_specification.bindir + elsif _local_specification + _local_specification.bindir + else + super + end + end + # needed for post_install_messages during install def post_install_message if @remote_specification diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 3dc8aa0dc01757..41b3a865cd9dfd 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -1885,6 +1885,25 @@ def run expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables) end + it "prevents removing binstubs when BUNDLE_CLEAN is set" do + build_repo4 do + build_gem "kamal", "4.0.6" do |s| + s.executables = ["kamal"] + end + end + + gemfile = <<~G + source "https://gem.repo4" + gem "kamal" + G + + install_gemfile(gemfile, env: { "BUNDLE_CLEAN" => "true", "BUNDLE_PATH" => "vendor/bundle" }) + + expected_executables = [vendored_gems("bin/kamal").to_s] + expected_executables << vendored_gems("bin/kamal.bat").to_s if Gem.win_platform? + expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables) + end + it "preserves lockfile versions conservatively" do build_repo4 do build_gem "mypsych", "4.0.6" do |s| From 71354a9879c5e68b215469ef1b17eeb1ba308ada Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 08:26:28 -0500 Subject: [PATCH 6/9] [ruby/prism] Fix hash pattern location when missing nodes https://github.com/ruby/prism/commit/0ad30561e2 --- prism/prism.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 93392da3499fbe..0cb8c9057054d1 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -4096,8 +4096,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme if (elements->size > 0) { if (rest) { - start = elements->nodes[0]->location.start; - end = rest->location.end; + start = MIN(rest->location.start, elements->nodes[0]->location.start); + end = MAX(rest->location.end, elements->nodes[elements->size - 1]->location.end); } else { start = elements->nodes[0]->location.start; end = elements->nodes[elements->size - 1]->location.end; @@ -4117,11 +4117,7 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme .closing_loc = { 0 } }; - pm_node_t *element; - PM_NODE_LIST_FOREACH(elements, index, element) { - pm_node_list_append(&node->elements, element); - } - + pm_node_list_concat(&node->elements, elements); return node; } From cbf39c3be5b71b7321cfba768417427e7c3bf4ad Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 08:37:52 -0500 Subject: [PATCH 7/9] [ruby/prism] Fix up call target node when invalid When there is an invalid syntax tree, we need to make sure to fill in the required call operator location. https://github.com/ruby/prism/commit/937313d7f0 --- prism/prism.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/prism/prism.c b/prism/prism.c index 0cb8c9057054d1..7f1d782f35cfa6 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -3077,6 +3077,17 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { .message_loc = target->message_loc }; + /* It is possible to get here where we have parsed an invalid syntax tree + * where the call operator was not present. In that case we will have a + * problem because it is a required location. In this case we need to fill + * it in with a fake location so that the syntax tree remains valid. */ + if (node->call_operator_loc.start == NULL) { + node->call_operator_loc = (pm_location_t) { + .start = target->base.location.start, + .end = target->base.location.start + }; + } + // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. From 268cbb29c7f69ea5a6d45973091a6e5f5aa89403 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 10:07:39 -0500 Subject: [PATCH 8/9] [ruby/prism] Fully handle unreferencing a block exit If a block exit has a further block exit in its subtree, we need to keep recursing. https://github.com/ruby/prism/commit/855d81a4a8 --- prism/prism.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index 7f1d782f35cfa6..ace4b0b1abcf2c 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12540,7 +12540,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) { ); } parser->current_block_exits->size--; - return false; + + /* Note returning true here because these nodes could have + * arguments that are themselves block exits. */ + return true; } index++; From 59314911a13d10eab2afbca62081311d702a1373 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 8 Dec 2025 10:18:42 -0500 Subject: [PATCH 9/9] [ruby/prism] Nested heredoc with newline terminator When you have a heredoc interpolated into another heredoc where the inner heredoc is terminated by a newline, you need to avoid adding the newline character a second time. https://github.com/ruby/prism/commit/8eeb5f358b --- prism/prism.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prism/prism.c b/prism/prism.c index ace4b0b1abcf2c..20037b5b9f6b6c 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12031,7 +12031,10 @@ parser_lex(pm_parser_t *parser) { // string content. if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) { const uint8_t *end = parser->current.end; - pm_newline_list_append(&parser->newline_list, end); + + if (parser->heredoc_end == NULL) { + pm_newline_list_append(&parser->newline_list, end); + } // Here we want the buffer to only // include up to the backslash.