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
52 changes: 33 additions & 19 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 All @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tap do
  include A # in_proc_block = true
  tap do end # Exiting this block was making `@in_proc_block = false
  include B # in_proc_block was false, fixed to true
end

end

# Dive into another container
Expand Down Expand Up @@ -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|
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the same check be added to visit_alias_method_node too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Fixed


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 @@ -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)
Expand Down
95 changes: 94 additions & 1 deletion test/rdoc/parser/prism_ruby_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down