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
27 changes: 18 additions & 9 deletions lib/rdoc/parser/prism_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -480,7 +480,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|
Expand Down Expand Up @@ -508,8 +507,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
Expand Down Expand Up @@ -775,7 +772,8 @@ 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
Expand Down Expand Up @@ -859,6 +857,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
Expand Down Expand Up @@ -997,19 +997,20 @@ def _visit_call_require(call_node)

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
Expand All @@ -1021,12 +1022,16 @@ def _visit_call_public_private_protected(call_node, visibility)
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
Expand All @@ -1039,26 +1044,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)
Expand Down
87 changes: 87 additions & 0 deletions test/rdoc/parser/prism_ruby_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,93 @@ 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
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
def bar; end
alias_method :bar2, :bar
end
alias_method :foo3, :foo
end
RUBY
klass = @store.find_class_named 'A'
assert_equal ['foo', '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
attr_reader :r2
attr_writer :w2
attr_accessor :rw2
end
alias_method :foo3, :foo
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
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
Expand Down