зеркало из https://github.com/github/ruby.git
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:
Родитель
0adeb1c8ab
Коммит
5ad56e7c0b
8
error.c
8
error.c
|
@ -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
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,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче