load.c: backtrace of circular require

* load.c (load_lock): print backtrace of circular require via
  `Warning.warn` [ruby-core:80850] [Bug #13505]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58471 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2017-04-25 08:17:24 +00:00
Родитель 0adeb1c8ab
Коммит 5ad56e7c0b
5 изменённых файлов: 53 добавлений и 12 удалений

Просмотреть файл

@ -145,10 +145,16 @@ rb_warning_s_warn(VALUE mod, VALUE str)
return Qnil; return Qnil;
} }
VALUE
rb_warning_warn(VALUE mod, VALUE str)
{
return rb_funcallv(mod, id_warn, 1, &str);
}
static void static void
rb_write_warning_str(VALUE str) rb_write_warning_str(VALUE str)
{ {
rb_funcall(rb_mWarning, id_warn, 1, str); rb_warning_warn(rb_mWarning, str);
} }
static VALUE static VALUE

Просмотреть файл

@ -1139,6 +1139,7 @@ VALUE rb_name_err_new(VALUE mesg, VALUE recv, VALUE method);
NORETURN(void ruby_deprecated_internal_feature(const char *)); NORETURN(void ruby_deprecated_internal_feature(const char *));
#define DEPRECATED_INTERNAL_FEATURE(func) \ #define DEPRECATED_INTERNAL_FEATURE(func) \
(ruby_deprecated_internal_feature(func), UNREACHABLE) (ruby_deprecated_internal_feature(func), UNREACHABLE)
VALUE rb_warning_warn(VALUE mod, VALUE str);
/* eval.c */ /* eval.c */
VALUE rb_refinement_module_get_refined_class(VALUE module); VALUE rb_refinement_module_get_refined_class(VALUE module);
@ -1749,7 +1750,7 @@ void rb_backtrace_print_as_bugreport(void);
int rb_backtrace_p(VALUE obj); int rb_backtrace_p(VALUE obj);
VALUE rb_backtrace_to_str_ary(VALUE obj); VALUE rb_backtrace_to_str_ary(VALUE obj);
VALUE rb_backtrace_to_location_ary(VALUE obj); VALUE rb_backtrace_to_location_ary(VALUE obj);
void rb_backtrace_print_to(VALUE output); void rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output);
RUBY_SYMBOL_EXPORT_BEGIN RUBY_SYMBOL_EXPORT_BEGIN
const char *rb_objspace_data_type_name(VALUE obj); const char *rb_objspace_data_type_name(VALUE obj);

4
load.c
Просмотреть файл

@ -719,6 +719,8 @@ rb_f_load(int argc, VALUE *argv)
return Qtrue; return Qtrue;
} }
extern VALUE rb_mWarning;
static char * static char *
load_lock(const char *ftptr) load_lock(const char *ftptr)
{ {
@ -742,7 +744,7 @@ load_lock(const char *ftptr)
} }
if (RTEST(ruby_verbose)) { if (RTEST(ruby_verbose)) {
rb_warning("loading in progress, circular require considered harmful - %s", ftptr); rb_warning("loading in progress, circular require considered harmful - %s", ftptr);
rb_backtrace_print_to(rb_stderr); rb_backtrace_each(rb_warning_warn, rb_mWarning);
} }
switch (rb_thread_shield_wait((VALUE)data)) { switch (rb_thread_shield_wait((VALUE)data)) {
case Qfalse: case Qfalse:

Просмотреть файл

@ -943,23 +943,23 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end end
end end
def test_warning_warn def capture_warning_warn
verbose = $VERBOSE verbose = $VERBOSE
warning = nil warning = []
::Warning.class_eval do ::Warning.class_eval do
alias_method :warn2, :warn alias_method :warn2, :warn
remove_method :warn remove_method :warn
define_method(:warn) do |str| define_method(:warn) do |str|
warning = str warning << str
end end
end end
$VERBOSE = true $VERBOSE = true
a = @a yield
assert_match(/instance variable @a not initialized/, warning) return warning
ensure ensure
$VERBOSE = verbose $VERBOSE = verbose
@ -970,6 +970,11 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end end
end end
def test_warning_warn
warning = capture_warning_warn {@a}
assert_match(/instance variable @a not initialized/, warning[0])
end
def test_warning_warn_invalid_argument def test_warning_warn_invalid_argument
assert_raise(TypeError) do assert_raise(TypeError) do
::Warning.warn nil ::Warning.warn nil
@ -982,6 +987,25 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end end
end end
def test_warning_warn_circular_require_backtrace
warning = nil
path = nil
Tempfile.create(%w[circular .rb]) do |t|
path = t.path
basename = File.basename(path)
t.puts "require '#{basename}'"
t.close
$LOAD_PATH.push(File.dirname(t))
warning = capture_warning_warn {require basename}
ensure
$LOAD_PATH.pop
$LOADED_FEATURES.delete(t)
end
assert_match(/circular require/, warning.first)
warning.pop while %r[lib/rubygems/core_ext/kernel_require.rb:] =~ warning.last
assert_operator(warning.last, :start_with?, "\tfrom #{path}:1:")
end
def test_undefined_backtrace def test_undefined_backtrace
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}") assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin; begin;

Просмотреть файл

@ -772,10 +772,15 @@ rb_backtrace(void)
vm_backtrace_print(stderr); vm_backtrace_print(stderr);
} }
struct print_to_arg {
VALUE (*iter)(VALUE recv, VALUE str);
VALUE output;
};
static void static void
oldbt_print_to(void *data, VALUE file, int lineno, VALUE name) oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
{ {
VALUE output = (VALUE)data; const struct print_to_arg *arg = data;
VALUE str = rb_sprintf("\tfrom %"PRIsVALUE":%d:in ", file, lineno); VALUE str = rb_sprintf("\tfrom %"PRIsVALUE":%d:in ", file, lineno);
if (NIL_P(name)) { if (NIL_P(name)) {
@ -784,16 +789,19 @@ oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
else { else {
rb_str_catf(str, " `%"PRIsVALUE"'\n", name); rb_str_catf(str, " `%"PRIsVALUE"'\n", name);
} }
rb_io_write(output, str); (*arg->iter)(arg->output, str);
} }
void void
rb_backtrace_print_to(VALUE output) rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output)
{ {
struct oldbt_arg arg; struct oldbt_arg arg;
struct print_to_arg parg;
parg.iter = iter;
parg.output = output;
arg.func = oldbt_print_to; arg.func = oldbt_print_to;
arg.data = (void *)output; arg.data = &parg;
backtrace_each(GET_THREAD(), backtrace_each(GET_THREAD(),
oldbt_init, oldbt_init,
oldbt_iter_iseq, oldbt_iter_iseq,