diff --git a/lib/rdoc/parser/prism_ruby.rb b/lib/rdoc/parser/prism_ruby.rb index b0d446748a..1ae1fa199a 100644 --- a/lib/rdoc/parser/prism_ruby.rb +++ b/lib/rdoc/parser/prism_ruby.rb @@ -16,7 +16,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser parse_files_matching(/\.rbw?$/) if ENV['RDOC_USE_PRISM_PARSER'] attr_accessor :visibility - attr_reader :container, :singleton + attr_reader :container, :singleton, :in_proc_block def initialize(top_level, content, options, stats) super @@ -43,9 +43,10 @@ def initialize(top_level, content, options, stats) # example: `Module.new { include M }` `M.module_eval { include N }` def with_in_proc_block + in_proc_block = @in_proc_block @in_proc_block = true yield - @in_proc_block = false + @in_proc_block = in_proc_block end # Dive into another container @@ -480,7 +481,6 @@ def add_attributes(names, rw, line_no) # Adds includes/extends. Module name is resolved to full before adding. def add_includes_extends(names, rdoc_class, line_no) # :nodoc: - return if @in_proc_block comment, directives = consecutive_comment(line_no) handle_code_object_directives(@container, directives) if directives names.each do |name| @@ -508,8 +508,6 @@ def add_extends(names, line_no) # :nodoc: # Adds a method defined by `def` syntax def add_method(method_name, receiver_name:, receiver_fallback_type:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:, start_line:, args_end_line:, end_line:) - return if @in_proc_block - receiver = receiver_name ? find_or_create_module_path(receiver_name, receiver_fallback_type) : @container comment, directives = consecutive_comment(start_line) handle_code_object_directives(@container, directives) if directives @@ -745,11 +743,14 @@ def visit_call_node(node) when :extend _visit_call_extend(node) when :public - _visit_call_public_private_protected(node, :public) { super } + super + _visit_call_public_private_protected(node, :public) when :private - _visit_call_public_private_protected(node, :private) { super } + super + _visit_call_public_private_protected(node, :private) when :protected - _visit_call_public_private_protected(node, :protected) { super } + super + _visit_call_public_private_protected(node, :protected) when :private_constant _visit_call_private_constant(node) when :public_constant @@ -759,11 +760,14 @@ def visit_call_node(node) when :alias_method _visit_call_alias_method(node) when :module_function - _visit_call_module_function(node) { super } + super + _visit_call_module_function(node) when :public_class_method - _visit_call_public_private_class_method(node, :public) { super } + super + _visit_call_public_private_class_method(node, :public) when :private_class_method - _visit_call_public_private_class_method(node, :private) { super } + super + _visit_call_public_private_class_method(node, :private) else super end @@ -774,12 +778,14 @@ def visit_call_node(node) def visit_block_node(node) @scanner.with_in_proc_block do - # include, extend and method definition inside block are not documentable + # include, extend and method definition inside block are not documentable. + # visibility methods and attribute definition methods should be ignored inside block. super end end def visit_alias_method_node(node) + return if @scanner.in_proc_block @scanner.process_comments_until(node.location.start_line - 1) return unless node.old_name.is_a?(Prism::SymbolNode) && node.new_name.is_a?(Prism::SymbolNode) @scanner.add_alias_method(node.old_name.value.to_s, node.new_name.value.to_s, node.location.start_line) @@ -858,6 +864,8 @@ def visit_def_node(node) end_line = node.location.end_line @scanner.process_comments_until(start_line - 1) + return if @scanner.in_proc_block + case node.receiver when Prism::NilNode, Prism::TrueNode, Prism::FalseNode visibility = :public @@ -995,37 +1003,39 @@ def _visit_call_require(call_node) end def _visit_call_module_function(call_node) - yield - return if @scanner.singleton + return if @scanner.in_proc_block || @scanner.singleton names = visibility_method_arguments(call_node, singleton: false)&.map(&:to_s) @scanner.change_method_to_module_function(names) if names end def _visit_call_public_private_class_method(call_node, visibility) - yield - return if @scanner.singleton + return if @scanner.in_proc_block || @scanner.singleton names = visibility_method_arguments(call_node, singleton: true) @scanner.change_method_visibility(names, visibility, singleton: true) if names end def _visit_call_public_private_protected(call_node, visibility) + return if @scanner.in_proc_block arguments_node = call_node.arguments if arguments_node.nil? # `public` `private` @scanner.visibility = visibility else # `public :foo, :bar`, `private def foo; end` - yield names = visibility_method_arguments(call_node, singleton: false) @scanner.change_method_visibility(names, visibility) if names end end def _visit_call_alias_method(call_node) + return if @scanner.in_proc_block + new_name, old_name, *rest = symbol_arguments(call_node) return unless old_name && new_name && rest.empty? @scanner.add_alias_method(old_name.to_s, new_name.to_s, call_node.location.start_line) end def _visit_call_include(call_node) + return if @scanner.in_proc_block + names = constant_arguments_names(call_node) line_no = call_node.location.start_line return unless names @@ -1038,26 +1048,30 @@ def _visit_call_include(call_node) end def _visit_call_extend(call_node) + return if @scanner.in_proc_block + names = constant_arguments_names(call_node) @scanner.add_extends(names, call_node.location.start_line) if names && !@scanner.singleton end def _visit_call_public_constant(call_node) - return if @scanner.singleton + return if @scanner.in_proc_block || @scanner.singleton names = symbol_arguments(call_node) @scanner.container.set_constant_visibility_for(names.map(&:to_s), :public) if names end def _visit_call_private_constant(call_node) - return if @scanner.singleton + return if @scanner.in_proc_block || @scanner.singleton names = symbol_arguments(call_node) @scanner.container.set_constant_visibility_for(names.map(&:to_s), :private) if names end def _visit_call_attr_reader_writer_accessor(call_node, rw) + return if @scanner.in_proc_block names = symbol_arguments(call_node) @scanner.add_attributes(names.map(&:to_s), rw, call_node.location.start_line) if names end + class MethodSignatureVisitor < Prism::Visitor # :nodoc: class << self def scan_signature(def_node) diff --git a/test/rdoc/parser/prism_ruby_test.rb b/test/rdoc/parser/prism_ruby_test.rb index fe21533191..d8e6cb0314 100644 --- a/test/rdoc/parser/prism_ruby_test.rb +++ b/test/rdoc/parser/prism_ruby_test.rb @@ -2020,9 +2020,10 @@ def test_include_extend_suppressed_within_block util_parser <<~RUBY module M; end module N; end - module O: end + module O; end class A metaprogramming do + tap do end include M extend N class B @@ -2045,6 +2046,98 @@ class B assert_equal ['N'], b.extends.map(&:name) end + def test_visibility_methods_suppressed_within_block + util_parser <<~RUBY + class A + def pub1; end + X = 1 + Y = 1 + def self.s_pub; end + private_class_method def self.s_pri; end + private_constant :Y + Module.new do + tap do end + private_method :pub1 + private_constant :X + public_constant :Y + private + private_class_method :s_pub + public_class_method :s_pri + end + def pub2; end + private + Module.new do + public + end + def pri; end + end + RUBY + klass = @store.find_class_named 'A' + + assert_equal :public, klass.find_constant_named('X').visibility + assert_equal :private, klass.find_constant_named('Y').visibility + assert_equal :public, klass.find_method_named('pub1').visibility + assert_equal :private, klass.find_method_named('pri').visibility + assert_equal :public, klass.find_method_named('pub2').visibility + assert_equal :public, klass.find_class_method_named('s_pub').visibility + assert_equal :private, klass.find_class_method_named('s_pri').visibility unless accept_legacy_bug? + end + + def test_alias_method_suppressed_within_block + omit if accept_legacy_bug? + + util_parser <<~RUBY + class A + def foo; end + Module.new do + tap do end + def foo; end + alias_method :bar2, :foo + alias bar3 foo + end + alias_method :foo2, :foo + alias foo3 foo + end + RUBY + klass = @store.find_class_named 'A' + assert_equal ['foo', 'foo2', 'foo3'], klass.method_list.map(&:name) + end + + def test_attr_method_suppressed_within_block + util_parser <<~RUBY + class A + attr_reader :r + attr_writer :w + attr_accessor :rw + Module.new do + tap do end + attr_reader :r2 + attr_writer :w2 + attr_accessor :rw2 + end + end + RUBY + klass = @store.find_class_named 'A' + assert_equal ['r', 'w', 'rw'], klass.attributes.map(&:name) + end + + def test_module_function_suppressed_within_block + util_parser <<~RUBY + module M + def foo; end + Module.new do + tap do end + def foo; end + module_function :foo + end + def bar; end + module_function :bar + end + RUBY + mod = @store.find_module_named 'M' + assert_equal ['bar'], mod.class_method_list.map(&:name) + end + def test_multibyte_method_name content = <<~RUBY class Foo