Skip to content
Open
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
31 changes: 30 additions & 1 deletion lib/rdoc/parser/c.rb
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ def handle_attr(var_name, attr_name, read, write)
# named +class_name+ in +parent+ which was assigned to the C +var_name+.

def handle_class_module(var_name, type, class_name, parent, in_module)
parent_name = @known_classes[parent] || parent
parent_name = @known_classes[parent] || const_get_name(parent) || parent

if in_module
enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
Expand Down Expand Up @@ -917,6 +917,35 @@ def handle_class_module(var_name, type, class_name, parent, in_module)
@store.add_c_enclosure var_name, cm
end

##
# Returns the name of the class or module the C variable +var_name+ was
# assigned from <tt>rb_const_get(under, rb_intern("Name"))</tt>, or +nil+
# if there is no such assignment, +under+ is unknown, or +var_name+ is
# assigned different constants in different places.
#
#: (String?) -> String?
def const_get_name(var_name)
return unless var_name

names = @content.scan(%r%
\b#{Regexp.escape var_name}\s*=
\s*rb_const_get(?:_at)?\s*\(
\s*(\w+)\s*,
\s*rb_intern(?:_const)?\s*\(\s*"(\w+)"\s*\)
\s*\)
%x).uniq

return unless names.length == 1

under, name = names.first
under_name = @known_classes[under]

return unless under_name
return name if under_name == 'Object'

"#{under_name}::#{name}"
end

##
# Adds constants. By providing some_value: at the start of the comment you
# can override the C value of the comment to give a friendly definition.
Expand Down
3 changes: 2 additions & 1 deletion lib/rdoc/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ def cleanup_stale_contributions

def resolve_c_superclasses
@classes_hash.each_value do |klass|
if klass.superclass.is_a?(String) && (candidate = find_c_enclosure(klass.superclass))
if klass.superclass.is_a?(String) && (candidate = find_c_enclosure(klass.superclass)) &&
!candidate.equal?(klass)
klass.superclass = candidate
end
end
Expand Down
41 changes: 41 additions & 0 deletions test/rdoc/parser/c_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,47 @@ def test_do_classes_class_under_rb_path2class
assert_equal 'this is Kernel::Foo < A::B', klass.comment.text
end

def test_do_classes_class_under_rb_const_get
content = <<~C
VALUE mExample = rb_define_module("Example");
VALUE cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
cSocket = rb_define_class_under(mExample, "Socket", cSocket);
C

klass = util_get_class content, 'cSocket'

assert_equal 'Example::Socket', klass.full_name
assert_equal 'Socket', klass.superclass
end

def test_do_classes_class_rb_const_get_under_module
content = <<~C
VALUE mNet = rb_define_module("Net");
VALUE cProtocol = rb_const_get(mNet, rb_intern("Protocol"));
VALUE cFoo = rb_define_class("Foo", cProtocol);
C

klass = util_get_class content, 'cFoo'

assert_equal 'Foo', klass.full_name
assert_equal 'Net::Protocol', klass.superclass
end

def test_do_classes_class_rb_const_get_reassigned
content = <<~C
VALUE mA = rb_define_module("A");
VALUE klass = rb_const_get(rb_cObject, rb_intern("IO"));
VALUE cX = rb_define_class_under(mA, "X", klass);
klass = rb_const_get(rb_cObject, rb_intern("String"));
VALUE cY = rb_define_class_under(mA, "Y", klass);
C

klass = util_get_class content, 'cY'

assert_equal 'A::Y', klass.full_name
assert_equal 'klass', klass.superclass
end

def test_do_classes_singleton
content = <<~C
VALUE cFoo = rb_define_class("Foo", rb_cObject);
Expand Down
11 changes: 11 additions & 0 deletions test/rdoc/rdoc_store_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ def test_resolve_c_superclasses
assert_equal(parent, @s.classes_hash['Child'].superclass)
end

def test_resolve_c_superclasses_self
c_file = @s.add_file 'ext.c'
klass = c_file.add_class RDoc::NormalClass, 'Socket', 'cSocket'

@s.add_c_enclosure('cSocket', klass)

@s.resolve_c_superclasses

assert_equal 'cSocket', @s.classes_hash['Socket'].superclass
end

def test_find_class_named
assert_equal @c1, @store.find_class_named('C1')

Expand Down
Loading