From 35d33520a63b0f572ae8f6c599cd1b5ace6e098a Mon Sep 17 00:00:00 2001 From: tompng Date: Tue, 9 Dec 2025 02:55:39 +0900 Subject: [PATCH] Fix call-seq deduplicate for name overlap in aliases Fix call-seq deduplicate of `Hash#value?` and `Hash#has_value?`. When alias names have overlap, deduplicate didn't work correctly. --- lib/rdoc/code_object/any_method.rb | 22 +++++++++---- test/rdoc/parser/c_test.rb | 51 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/lib/rdoc/code_object/any_method.rb b/lib/rdoc/code_object/any_method.rb index b319f0d0dd..f56110ea11 100644 --- a/lib/rdoc/code_object/any_method.rb +++ b/lib/rdoc/code_object/any_method.rb @@ -351,7 +351,6 @@ def deduplicate_call_seq(call_seq) return call_seq unless is_alias_for || !aliases.empty? method_name = self.name - method_name = method_name[0, 1] if method_name =~ /\A\[/ entries = call_seq.split "\n" @@ -360,15 +359,24 @@ def deduplicate_call_seq(call_seq) ignore << is_alias_for.name ignore.concat is_alias_for.aliases.map(&:name) end - ignore.map! { |n| n =~ /\A\[/ ? /\[.*\]/ : n} + ignore.delete(method_name) - ignore = Regexp.union(ignore) + ignore_bracket_methods, ignore_other_methods = ignore.partition {|i| i.start_with?('[') } - matching = entries.reject do |entry| - entry =~ /^\w*\.?#{ignore}[$\(\s]/ or - entry =~ /\s#{ignore}\s/ + if ignore_other_methods.any? + ignore_union = Regexp.union(ignore_other_methods) + entries.reject! do |entry| + /\A(?:\w*\.)?#{ignore_union}(?:[(\s]|\z)/.match?(entry) || + /\s#{ignore_union}\s/.match?(entry) + end + end + if ignore_bracket_methods.any? + entries.reject! do |entry| + # Ignore `receiver[arg] -> return_type` `[](arg)` `[]` + /\A\w*\[.*?\](?:[(\s]|\z)/.match?(entry) + end end - matching.empty? ? nil : matching.join("\n") + entries.empty? ? nil : entries.join("\n") end end diff --git a/test/rdoc/parser/c_test.rb b/test/rdoc/parser/c_test.rb index 6794677008..03157119c9 100644 --- a/test/rdoc/parser/c_test.rb +++ b/test/rdoc/parser/c_test.rb @@ -2107,6 +2107,57 @@ def test_scan_method_copy assert_match 'str === obj', equals3.call_seq end + def test_scan_method_copy_bracket_name + parser = util_parser <<~C + /* + * call-seq: + * obj[index] -> nil + * obj.aref(index) -> nil + */ + static VALUE + rb_obj_aref(VALUE obj, VALUE index) { return Qnil; } + + Init_Blah(void) + { + rb_define_method(rb_cObject, "[]", rb_obj_aref, 1); + rb_define_method(rb_cObject, "aref", rb_obj_aref, 1); + } + C + + parser.scan + + object = @store.classes_hash['Object'] + bracket_method = object.method_list.find { |m| m.name == '[]' } + non_bracket_method = object.method_list.find { |m| m.name == 'aref' } + assert_equal 'obj[index] -> nil', bracket_method.call_seq + assert_equal 'obj.aref(index) -> nil', non_bracket_method.call_seq + end + + def test_scan_method_copy_name_overlap + parser = util_parser <<~C + /* + * call-seq: + * value?(value) -> true or false + * has_value?(value) -> true or false + */ + static VALUE + rb_hash_has_value(VALUE hash, VALUE val) { return Qtrue; } + Init_Hash(void) + { + rb_define_method(rb_cHash, "has_value?", rb_hash_has_value, 1); + rb_define_method(rb_cHash, "value?", rb_hash_has_value, 1); + } + C + + parser.scan + + hash = @store.classes_hash['Hash'] + value_method = hash.method_list.find { |m| m.name == 'value?' } + has_value_method = hash.method_list.find { |m| m.name == 'has_value?' } + assert_equal 'value?(value) -> true or false', value_method.call_seq + assert_equal 'has_value?(value) -> true or false', has_value_method.call_seq + end + def test_scan_order_dependent parser = util_parser <<-C void a(void) {