From 761346a9604ca2c79777d1d67fb5dcc3c30dbf69 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 Jul 2019 16:54:34 +0900 Subject: [PATCH] Show the previous definition location, when reopened class/module redefinition mismatched the previous definition. [Feature #11460] --- test/ruby/test_class.rb | 10 ++++++---- test/ruby/test_module.rb | 10 ++++++---- vm_insnhelper.c | 28 ++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index f07a1cdc40..6960ab5971 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -591,15 +591,17 @@ class TestClass < Test::Unit::TestCase def test_redefinition_mismatch m = Module.new - m.module_eval "A = 1" - assert_raise_with_message(TypeError, /is not a class/) { + m.module_eval "A = 1", __FILE__, line = __LINE__ + e = assert_raise_with_message(TypeError, /is not a class/) { m.module_eval "class A; end" } + assert_include(e.message, "#{__FILE__}:#{line}: previous definition") n = "M\u{1f5ff}" - m.module_eval "#{n} = 42" - assert_raise_with_message(TypeError, "#{n} is not a class") { + m.module_eval "#{n} = 42", __FILE__, line = __LINE__ + e = assert_raise_with_message(TypeError, /#{n} is not a class/) { m.module_eval "class #{n}; end" } + assert_include(e.message, "#{__FILE__}:#{line}: previous definition") assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}") begin; diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index d56e63e4be..7bb2261ffb 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -2383,15 +2383,17 @@ class TestModule < Test::Unit::TestCase def test_redefinition_mismatch m = Module.new - m.module_eval "A = 1" - assert_raise_with_message(TypeError, /is not a module/) { + m.module_eval "A = 1", __FILE__, line = __LINE__ + e = assert_raise_with_message(TypeError, /is not a module/) { m.module_eval "module A; end" } + assert_include(e.message, "#{__FILE__}:#{line}: previous definition") n = "M\u{1f5ff}" - m.module_eval "#{n} = 42" - assert_raise_with_message(TypeError, "#{n} is not a module") { + m.module_eval "#{n} = 42", __FILE__, line = __LINE__ + e = assert_raise_with_message(TypeError, /#{n} is not a module/) { m.module_eval "module #{n}; end" } + assert_include(e.message, "#{__FILE__}:#{line}: previous definition") assert_separately([], <<-"end;") Etc = (class C\u{1f5ff}; self; end).new diff --git a/vm_insnhelper.c b/vm_insnhelper.c index e80a036e06..925d465669 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3389,7 +3389,7 @@ static VALUE vm_check_if_class(ID id, rb_num_t flags, VALUE super, VALUE klass) { if (!RB_TYPE_P(klass, T_CLASS)) { - rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a class", rb_id2str(id)); + return 0; } else if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) { VALUE tmp = rb_class_real(RCLASS_SUPER(klass)); @@ -3412,7 +3412,7 @@ static VALUE vm_check_if_module(ID id, VALUE mod) { if (!RB_TYPE_P(mod, T_MODULE)) { - rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a module", rb_id2str(id)); + return 0; } else { return mod; @@ -3442,6 +3442,22 @@ vm_declare_module(ID id, VALUE cbase) return mod; } +NORETURN(static void unmatched_redefinition(const char *type, VALUE cbase, ID id, VALUE old)); +static void +unmatched_redefinition(const char *type, VALUE cbase, ID id, VALUE old) +{ + VALUE name = rb_id2str(id); + VALUE message = rb_sprintf("%"PRIsVALUE" is not a %s", + name, type); + VALUE location = rb_const_source_location_at(cbase, id); + if (!NIL_P(location)) { + rb_str_catf(message, "\n%"PRIsVALUE":%"PRIsVALUE":" + " previous definition of %"PRIsVALUE" was here", + rb_ary_entry(location, 0), rb_ary_entry(location, 1), name); + } + rb_exc_raise(rb_exc_new_str(rb_eTypeError, message)); +} + static VALUE vm_define_class(ID id, rb_num_t flags, VALUE cbase, VALUE super) { @@ -3458,7 +3474,9 @@ vm_define_class(ID id, rb_num_t flags, VALUE cbase, VALUE super) /* find klass */ rb_autoload_load(cbase, id); if ((klass = vm_const_get_under(id, flags, cbase)) != 0) { - return vm_check_if_class(id, flags, super, klass); + if (!vm_check_if_class(id, flags, super, klass)) + unmatched_redefinition("class", cbase, id, klass); + return klass; } else { return vm_declare_class(id, flags, cbase, super); @@ -3472,7 +3490,9 @@ vm_define_module(ID id, rb_num_t flags, VALUE cbase) vm_check_if_namespace(cbase); if ((mod = vm_const_get_under(id, flags, cbase)) != 0) { - return vm_check_if_module(id, mod); + if (!vm_check_if_module(id, mod)) + unmatched_redefinition("module", cbase, id, mod); + return mod; } else { return vm_declare_module(id, cbase);