Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,14 @@ node_locations(VALUE ast_value, const NODE *node)
return rb_ary_new_from_args(2,
location_new(nd_code_loc(node)),
location_new(&RNODE_RETURN(node)->keyword_loc));

case NODE_SCLASS:
return rb_ary_new_from_args(4,
location_new(nd_code_loc(node)),
location_new(&RNODE_SCLASS(node)->class_keyword_loc),
location_new(&RNODE_SCLASS(node)->operator_loc),
location_new(&RNODE_SCLASS(node)->end_keyword_loc));

case NODE_SPLAT:
return rb_ary_new_from_args(2,
location_new(nd_code_loc(node)),
Expand Down
5 changes: 4 additions & 1 deletion node_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1023,8 +1023,11 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("format: class << [nd_recv]; [nd_body]; end");
ANN("example: class << obj; ..; end");
F_NODE(nd_recv, RNODE_SCLASS, "receiver");
LAST_NODE;
F_NODE(nd_body, RNODE_SCLASS, "singleton class definition");
F_LOC(class_keyword_loc, RNODE_SCLASS);
F_LOC(operator_loc, RNODE_SCLASS);
LAST_NODE;
F_LOC(end_keyword_loc, RNODE_SCLASS);
return;

case NODE_COLON2:
Expand Down
3 changes: 1 addition & 2 deletions object.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ init_copy(VALUE dest, VALUE obj)
break;
case T_CLASS:
case T_MODULE:
// noop: handled in class.c: rb_mod_init_copy
rb_mod_init_copy(dest, obj);
break;
case T_OBJECT:
rb_obj_copy_ivar(dest, obj);
Expand Down Expand Up @@ -4571,7 +4571,6 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "<=", rb_class_inherited_p, 1);
rb_define_method(rb_cModule, ">", rb_mod_gt, 1);
rb_define_method(rb_cModule, ">=", rb_mod_ge, 1);
rb_define_method(rb_cModule, "initialize_copy", rb_mod_init_copy, 1); /* in class.c */
rb_define_method(rb_cModule, "to_s", rb_mod_to_s, 0);
rb_define_alias(rb_cModule, "inspect", "to_s");
rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); /* in class.c */
Expand Down
11 changes: 7 additions & 4 deletions parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ static rb_node_valias_t *rb_node_valias_new(struct parser_params *p, ID nd_alias
static rb_node_undef_t *rb_node_undef_new(struct parser_params *p, NODE *nd_undef, const YYLTYPE *loc);
static rb_node_class_t *rb_node_class_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, NODE *nd_super, const YYLTYPE *loc, const YYLTYPE *class_keyword_loc, const YYLTYPE *inheritance_operator_loc, const YYLTYPE *end_keyword_loc);
static rb_node_module_t *rb_node_module_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *module_keyword_loc, const YYLTYPE *end_keyword_loc);
static rb_node_sclass_t *rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc);
static rb_node_sclass_t *rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *class_keyword_loc, const YYLTYPE *operator_loc, const YYLTYPE *end_keyword_loc);
static rb_node_colon2_t *rb_node_colon2_new(struct parser_params *p, NODE *nd_head, ID nd_mid, const YYLTYPE *loc, const YYLTYPE *delimiter_loc, const YYLTYPE *name_loc);
static rb_node_colon3_t *rb_node_colon3_new(struct parser_params *p, ID nd_mid, const YYLTYPE *loc, const YYLTYPE *delimiter_loc, const YYLTYPE *name_loc);
static rb_node_dot2_t *rb_node_dot2_new(struct parser_params *p, NODE *nd_beg, NODE *nd_end, const YYLTYPE *loc, const YYLTYPE *operator_loc);
Expand Down Expand Up @@ -1254,7 +1254,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#define NEW_UNDEF(i,loc) (NODE *)rb_node_undef_new(p,i,loc)
#define NEW_CLASS(n,b,s,loc,ck_loc,io_loc,ek_loc) (NODE *)rb_node_class_new(p,n,b,s,loc,ck_loc,io_loc,ek_loc)
#define NEW_MODULE(n,b,loc,mk_loc,ek_loc) (NODE *)rb_node_module_new(p,n,b,loc,mk_loc,ek_loc)
#define NEW_SCLASS(r,b,loc) (NODE *)rb_node_sclass_new(p,r,b,loc)
#define NEW_SCLASS(r,b,loc,ck_loc,op_loc,ek_loc) (NODE *)rb_node_sclass_new(p,r,b,loc,ck_loc,op_loc,ek_loc)
#define NEW_COLON2(c,i,loc,d_loc,n_loc) (NODE *)rb_node_colon2_new(p,c,i,loc,d_loc,n_loc)
#define NEW_COLON3(i,loc,d_loc,n_loc) (NODE *)rb_node_colon3_new(p,i,loc,d_loc,n_loc)
#define NEW_DOT2(b,e,loc,op_loc) (NODE *)rb_node_dot2_new(p,b,e,loc,op_loc)
Expand Down Expand Up @@ -4605,7 +4605,7 @@ primary : inline_primary
bodystmt
k_end
{
$$ = NEW_SCLASS($expr_value, $bodystmt, &@$);
$$ = NEW_SCLASS($expr_value, $bodystmt, &@$, &@k_class, &@tLSHFT, &@k_end);
nd_set_line(RNODE_SCLASS($$)->nd_body, @k_end.end_pos.lineno);
set_line_body($bodystmt, nd_line($expr_value));
fixpos($$, $expr_value);
Expand Down Expand Up @@ -11431,14 +11431,17 @@ rb_node_class_new(struct parser_params *p, NODE *nd_cpath, NODE *nd_body, NODE *
}

static rb_node_sclass_t *
rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc)
rb_node_sclass_new(struct parser_params *p, NODE *nd_recv, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *class_keyword_loc, const YYLTYPE *operator_loc, const YYLTYPE *end_keyword_loc)
{
/* Keep the order of node creation */
NODE *scope = NEW_SCOPE(0, nd_body, NULL, loc);
rb_node_sclass_t *n = NODE_NEWNODE(NODE_SCLASS, rb_node_sclass_t, loc);
RNODE_SCOPE(scope)->nd_parent = &n->node;
n->nd_recv = nd_recv;
n->nd_body = scope;
n->class_keyword_loc = *class_keyword_loc;
n->operator_loc = *operator_loc;
n->end_keyword_loc = *end_keyword_loc;

return n;
}
Expand Down
3 changes: 3 additions & 0 deletions rubyparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,9 @@ typedef struct RNode_SCLASS {

struct RNode *nd_recv;
struct RNode *nd_body;
rb_code_location_t class_keyword_loc;
rb_code_location_t operator_loc;
rb_code_location_t end_keyword_loc;
} rb_node_sclass_t;

typedef struct RNode_COLON2 {
Expand Down
8 changes: 8 additions & 0 deletions test/ruby/test_ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,14 @@ def test_return_locations
assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 0, 1, 6]])
end

def test_sclass_locations
node = ast_parse("class << self; end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 18], [1, 0, 1, 5], [1, 6, 1, 8], [1, 15, 1, 18]])

node = ast_parse("class << obj; foo; end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 22], [1, 0, 1, 5], [1, 6, 1, 8], [1, 19, 1, 22]])
end

def test_splat_locations
node = ast_parse("a = *1")
assert_locations(node.children[-1].children[1].locations, [[1, 4, 1, 6], [1, 4, 1, 5]])
Expand Down
40 changes: 40 additions & 0 deletions test/ruby/test_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,46 @@ def test_initialize_copy
assert_raise(TypeError) { BasicObject.dup }
end

def test_class_hierarchy_inside_initialize_dup_bug_21538
ancestors = sc_ancestors = nil
b = Class.new
b.define_singleton_method(:initialize_dup) do |x|
ancestors = self.ancestors
sc_ancestors = singleton_class.ancestors
super(x)
end

a = Class.new(b)

c = a.dup

expected_ancestors = [c, b, *Object.ancestors]
expected_sc_ancestors = [c.singleton_class, b.singleton_class, *Object.singleton_class.ancestors]
assert_equal expected_ancestors, ancestors
assert_equal expected_sc_ancestors, sc_ancestors
assert_equal expected_ancestors, c.ancestors
assert_equal expected_sc_ancestors, c.singleton_class.ancestors
end

def test_class_hierarchy_inside_initialize_clone_bug_21538
ancestors = sc_ancestors = nil
a = Class.new
a.define_singleton_method(:initialize_clone) do |x|
ancestors = self.ancestors
sc_ancestors = singleton_class.ancestors
super(x)
end

c = a.clone

expected_ancestors = [c, *Object.ancestors]
expected_sc_ancestors = [c.singleton_class, *Object.singleton_class.ancestors]
assert_equal expected_ancestors, ancestors
assert_equal expected_sc_ancestors, sc_ancestors
assert_equal expected_ancestors, c.ancestors
assert_equal expected_sc_ancestors, c.singleton_class.ancestors
end

def test_singleton_class
assert_raise(TypeError) { 1.extend(Module.new) }
assert_raise(TypeError) { 1.0.extend(Module.new) }
Expand Down
8 changes: 0 additions & 8 deletions test/ruby/test_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,6 @@ def test_initialize_copy
instance = klass.new
assert_equal(:first, instance.foo)
new_mod = Module.new { define_method(:foo) { :second } }
assert_raise(TypeError) do
mod.send(:initialize_copy, new_mod)
end
4.times { GC.start }
assert_equal(:first, instance.foo) # [BUG] unreachable
end
Expand All @@ -435,11 +432,6 @@ def x
assert_equal([:x], m.instance_methods)
assert_equal([:@x], m.instance_variables)
assert_equal([:X], m.constants)
assert_raise(TypeError) do
m.module_eval do
initialize_copy(Module.new)
end
end

m = Class.new(Module) do
def initialize_copy(other)
Expand Down