mirror of
https://github.com/ruby/ruby.git
synced 2026-11-08 01:20:26 +08:00
Load the error decoration gems by firing their autoloads
The lazy loading of error_highlight, did_you_mean, and syntax_suggest
claimed the load by an atomic exchange before the load started. So a
caller in another Ractor or thread during the load returned the
undecorated message without waiting for the load to complete.
rs = 2.times.map do
Ractor.new do
nil.upcase
rescue => e
e.detailed_message(highlight: false)
end
end
rs.each { puts it.value, "" }
# One of them lacks the snippet by error_highlight
Load the gems with rb_autoload_load instead of rb_require_string, as a
constant reference would. The autoload's own synchronization makes
concurrent callers wait until the load is done. The flag to load the
gems only once is no longer needed: rb_autoload_load does nothing once
the gems are loaded, and whether to re-dispatch is now decided by whether
the load has just put a decorator above Exception#detailed_message.
This also makes the gems loaded through RubyGems' Kernel#require, so a
newer version of the gems installed by the user is activated, as it was
before they were lazily loaded.
[Bug #22442]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ce3a306211
commit
d193fd3217
Notes:
git
2026-10-15 04:51:10 +00:00
35
error.c
35
error.c
@ -1787,22 +1787,15 @@ exc_message(VALUE exc)
|
||||
return rb_funcallv(exc, idTo_s, 0, 0);
|
||||
}
|
||||
|
||||
// Whether error_highlight, did_you_mean, and syntax_suggest have already
|
||||
// been loaded (lazily on the first error, or eagerly via Process.warmup).
|
||||
// Ractors run in parallel, so the load is claimed by an atomic exchange.
|
||||
static rb_atomic_t decoration_gems_loaded = 0;
|
||||
|
||||
static VALUE
|
||||
load_decoration_gem(VALUE feature)
|
||||
load_decoration_gem(VALUE name)
|
||||
{
|
||||
// The C-level require bypasses Kernel#require monkeypatches;
|
||||
// displaying an error must not invoke or depend on them.
|
||||
return rb_require_string(feature);
|
||||
// Fire the autoload, which waits for a concurrent load to complete.
|
||||
return rb_autoload_load(rb_cObject, (ID)name);
|
||||
}
|
||||
|
||||
// Require error_highlight, did_you_mean, and syntax_suggest.
|
||||
// rb_define_gem_modules registers an autoload entry for each enabled gem;
|
||||
// disabled gems have no entry and are skipped.
|
||||
// rb_define_gem_modules registers an autoload entry for each enabled gem.
|
||||
static void
|
||||
require_decoration_gems(void)
|
||||
{
|
||||
@ -1813,32 +1806,26 @@ require_decoration_gems(void)
|
||||
VALUE saved_errinfo = rb_errinfo();
|
||||
static const char *const gems[] = {"ErrorHighlight", "DidYouMean", "SyntaxSuggest"};
|
||||
for (size_t i = 0; i < numberof(gems); i++) {
|
||||
VALUE feature = rb_autoload_p(rb_cObject, rb_intern(gems[i]));
|
||||
if (NIL_P(feature)) continue;
|
||||
int state;
|
||||
rb_protect(load_decoration_gem, feature, &state);
|
||||
rb_protect(load_decoration_gem, (VALUE)rb_intern(gems[i]), &state);
|
||||
(void)state;
|
||||
}
|
||||
rb_set_errinfo(saved_errinfo);
|
||||
}
|
||||
|
||||
// Load the decoration gems on the first error display instead of at boot.
|
||||
// In non-main Ractors rb_require_string delegates the require to the main
|
||||
// In non-main Ractors rb_autoload_load delegates the load to the main
|
||||
// Ractor, so this works from any Ractor.
|
||||
// Returns whether the caller should re-dispatch to pick up the
|
||||
// detailed_message decorators the gems prepend.
|
||||
static bool
|
||||
lazy_load_decoration_gems(VALUE exc)
|
||||
{
|
||||
if (ATOMIC_EXCHANGE(decoration_gems_loaded, 1)) return false;
|
||||
|
||||
// When entered through super from a decorator already sitting above
|
||||
// this method, the caller decorates the result; re-dispatching would
|
||||
// decorate it twice.
|
||||
bool redispatch = rb_method_basic_definition_p(CLASS_OF(exc), id_detailed_message);
|
||||
|
||||
// Re-dispatch only if the load has just prepended a decorator.
|
||||
bool before = rb_method_basic_definition_p(CLASS_OF(exc), id_detailed_message);
|
||||
require_decoration_gems();
|
||||
return redispatch;
|
||||
bool after = rb_method_basic_definition_p(CLASS_OF(exc), id_detailed_message);
|
||||
return before && !after;
|
||||
}
|
||||
|
||||
// Load the decoration gems eagerly, e.g. from Process.warmup before a
|
||||
@ -1847,8 +1834,6 @@ lazy_load_decoration_gems(VALUE exc)
|
||||
void
|
||||
rb_eager_load_detailed_message_extension(void)
|
||||
{
|
||||
if (ATOMIC_EXCHANGE(decoration_gems_loaded, 1)) return;
|
||||
|
||||
require_decoration_gems();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user