Sync RDoc 6.14.0

This commit is contained in:
Stan Lo 2025-05-22 22:49:04 +01:00 committed by Takashi Kokubun
parent ca1ea95784
commit 03eb777c69
185 changed files with 2008 additions and 1655 deletions

View file

@ -18,7 +18,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
attr_accessor :visibility
attr_reader :container, :singleton
def initialize(top_level, file_name, content, options, stats)
def initialize(top_level, content, options, stats)
super
content = handle_tab_width(content)
@ -31,10 +31,21 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
@track_visibility = :nodoc != @options.visibility
@encoding = @options.encoding
@module_nesting = [top_level]
@module_nesting = [[top_level, false]]
@container = top_level
@visibility = :public
@singleton = false
@in_proc_block = false
end
# Suppress `extend` and `include` within block
# because they might be a metaprogramming block
# example: `Module.new { include M }` `M.module_eval { include N }`
def with_in_proc_block
@in_proc_block = true
yield
@in_proc_block = false
end
# Dive into another container
@ -43,28 +54,30 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
old_container = @container
old_visibility = @visibility
old_singleton = @singleton
old_in_proc_block = @in_proc_block
@visibility = :public
@container = container
@singleton = singleton
@in_proc_block = false
unless singleton
@module_nesting.push container
# Need to update module parent chain to emulate Module.nesting.
# This mechanism is inaccurate and needs to be fixed.
container.parent = old_container
end
@module_nesting.push([container, singleton])
yield container
ensure
@container = old_container
@visibility = old_visibility
@singleton = old_singleton
@module_nesting.pop unless singleton
@in_proc_block = old_in_proc_block
@module_nesting.pop
end
# Records the location of this +container+ in the file for this parser and
# adds it to the list of classes and modules in the file.
def record_location container # :nodoc:
def record_location(container) # :nodoc:
case container
when RDoc::ClassModule then
@top_level.add_to_classes_or_modules container
@ -204,6 +217,10 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
@stats.add_method meth
end
def has_modifier_nodoc?(line_no) # :nodoc:
@modifier_comments[line_no]&.text&.match?(/\A#\s*:nodoc:/)
end
def handle_modifier_directive(code_object, line_no) # :nodoc:
comment = @modifier_comments[line_no]
@preprocess.handle(comment.text, code_object) if comment
@ -272,21 +289,19 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
if attributes
attributes.each do |attr|
a = RDoc::Attr.new(@container, attr, rw, processed_comment)
a = RDoc::Attr.new(@container, attr, rw, processed_comment, singleton: @singleton)
a.store = @store
a.line = line_no
a.singleton = @singleton
record_location(a)
@container.add_attribute(a)
a.visibility = visibility
end
elsif line_no || node
method_name ||= call_node_name_arguments(node).first if is_call_node
meth = RDoc::AnyMethod.new(@container, method_name)
meth.singleton = @singleton || singleton_method
meth = RDoc::AnyMethod.new(@container, method_name, singleton: @singleton || singleton_method)
handle_consecutive_comment_directive(meth, comment)
comment.normalize
comment.extract_call_seq(meth)
meth.call_seq = comment.extract_call_seq
meth.comment = comment
if node
tokens = visible_tokens_from_location(node.location)
@ -299,7 +314,6 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
meth,
line_no: line_no,
visibility: visibility,
singleton: @singleton || singleton_method,
params: '()',
calls_super: false,
block_params: nil,
@ -435,8 +449,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
comment = consecutive_comment(line_no)
handle_consecutive_comment_directive(@container, comment)
visibility = @container.find_method(old_name, @singleton)&.visibility || :public
a = RDoc::Alias.new(nil, old_name, new_name, comment, @singleton)
a.comment = comment
a = RDoc::Alias.new(nil, old_name, new_name, comment, singleton: @singleton)
handle_modifier_directive(a, line_no)
a.store = @store
a.line = line_no
@ -455,10 +468,9 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
return unless @container.document_children
names.each do |symbol|
a = RDoc::Attr.new(nil, symbol.to_s, rw, comment)
a = RDoc::Attr.new(nil, symbol.to_s, rw, comment, singleton: @singleton)
a.store = @store
a.line = line_no
a.singleton = @singleton
record_location(a)
handle_modifier_directive(a, line_no)
@container.add_attribute(a) if should_document?(a)
@ -467,6 +479,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
end
def add_includes_extends(names, rdoc_class, line_no) # :nodoc:
return if @in_proc_block
comment = consecutive_comment(line_no)
handle_consecutive_comment_directive(@container, comment)
names.each do |name|
@ -492,51 +505,53 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
# Adds a method defined by `def` syntax
def add_method(name, receiver_name:, receiver_fallback_type:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:, start_line:, end_line:)
def add_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
meth = RDoc::AnyMethod.new(nil, name)
meth = RDoc::AnyMethod.new(nil, name, singleton: singleton)
if (comment = consecutive_comment(start_line))
handle_consecutive_comment_directive(@container, comment)
handle_consecutive_comment_directive(meth, comment)
comment.normalize
comment.extract_call_seq(meth)
meth.call_seq = comment.extract_call_seq
meth.comment = comment
end
handle_modifier_directive(meth, start_line)
handle_modifier_directive(meth, args_end_line)
handle_modifier_directive(meth, end_line)
return unless should_document?(meth)
if meth.name == 'initialize' && !singleton
if meth.dont_rename_initialize
visibility = :protected
else
meth.name = 'new'
singleton = true
visibility = :public
end
end
internal_add_method(
receiver,
meth,
line_no: start_line,
visibility: visibility,
singleton: singleton,
params: params,
calls_super: calls_super,
block_params: block_params,
tokens: tokens
)
# Rename after add_method to register duplicated 'new' and 'initialize'
# defined in c and ruby just like the old parser did.
if meth.name == 'initialize' && !singleton
if meth.dont_rename_initialize
meth.visibility = :protected
else
meth.name = 'new'
meth.singleton = true
meth.visibility = :public
end
end
end
private def internal_add_method(container, meth, line_no:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:) # :nodoc:
private def internal_add_method(container, meth, line_no:, visibility:, params:, calls_super:, block_params:, tokens:) # :nodoc:
meth.name ||= meth.call_seq[/\A[^()\s]+/] if meth.call_seq
meth.name ||= 'unknown'
meth.store = @store
meth.line = line_no
meth.singleton = singleton
container.add_method(meth) # should add after setting singleton and before setting visibility
meth.visibility = visibility
meth.params ||= params
@ -565,12 +580,17 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
if root_name.empty?
mod = @top_level
else
@module_nesting.reverse_each do |nesting|
@module_nesting.reverse_each do |nesting, singleton|
next if singleton
mod = nesting.find_module_named(root_name)
break if mod
# If a constant is found and it is not a module or class, RDoc can't document about it.
# Return an anonymous module to avoid wrong document creation.
return RDoc::NormalModule.new(nil) if nesting.find_constant_named(root_name)
end
return mod || add_module.call(@top_level, root_name, create_mode) unless name
mod ||= add_module.call(@top_level, root_name, :module)
last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
return mod || add_module.call(last_nesting, root_name, create_mode) unless name
mod ||= add_module.call(last_nesting, root_name, :module)
end
path.each do |name|
mod = mod.find_module_named(name) || add_module.call(mod, name, :module)
@ -584,7 +604,8 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
owner_name, path = constant_path.split('::', 2)
return constant_path if owner_name.empty? # ::Foo, ::Foo::Bar
mod = nil
@module_nesting.reverse_each do |nesting|
@module_nesting.reverse_each do |nesting, singleton|
next if singleton
mod = nesting.find_module_named(owner_name)
break if mod
end
@ -598,7 +619,10 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
def find_or_create_constant_owner_name(constant_path)
const_path, colon, name = constant_path.rpartition('::')
if colon.empty? # class Foo
[@container, name]
# Within `class C` or `module C`, owner is C(== current container)
# Within `class <<C`, owner is C.singleton_class
# but RDoc don't track constants of a singleton class of module
[(@singleton ? nil : @container), name]
elsif const_path.empty? # class ::Foo
[@top_level, name]
else # `class Foo::Bar` or `class ::Foo::Bar`
@ -612,6 +636,8 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
comment = consecutive_comment(start_line)
handle_consecutive_comment_directive(@container, comment)
owner, name = find_or_create_constant_owner_name(constant_name)
return unless owner
constant = RDoc::Constant.new(name, rhs_name, comment)
constant.store = @store
constant.line = start_line
@ -635,24 +661,29 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
# Adds module or class
def add_module_or_class(module_name, start_line, end_line, is_class: false, superclass_name: nil)
def add_module_or_class(module_name, start_line, end_line, is_class: false, superclass_name: nil, superclass_expr: nil)
comment = consecutive_comment(start_line)
handle_consecutive_comment_directive(@container, comment)
return unless @container.document_children
owner, name = find_or_create_constant_owner_name(module_name)
if is_class
mod = owner.classes_hash[name] || owner.add_class(RDoc::NormalClass, name, superclass_name || '::Object')
return unless owner
if is_class
# RDoc::NormalClass resolves superclass name despite of the lack of module nesting information.
# We need to fix it when RDoc::NormalClass resolved to a wrong constant name
if superclass_name
superclass_full_path = resolve_constant_path(superclass_name)
superclass = @store.find_class_or_module(superclass_full_path) if superclass_full_path
superclass_full_path ||= superclass_name
superclass_full_path = superclass_full_path.sub(/^::/, '')
end
# add_class should be done after resolving superclass
mod = owner.classes_hash[name] || owner.add_class(RDoc::NormalClass, name, superclass_name || superclass_expr || '::Object')
if superclass_name
if superclass
mod.superclass = superclass
elsif mod.superclass.is_a?(String) && mod.superclass != superclass_full_path
elsif (mod.superclass.is_a?(String) || mod.superclass.name == 'Object') && mod.superclass != superclass_full_path
mod.superclass = superclass_full_path
end
end
@ -676,6 +707,20 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
@store = store
end
def visit_if_node(node)
if node.end_keyword
super
else
# Visit with the order in text representation to handle this method comment
# # comment
# def f
# end if call_node
node.statements.accept(self)
node.predicate.accept(self)
end
end
alias visit_unless_node visit_if_node
def visit_call_node(node)
@scanner.process_comments_until(node.location.start_line - 1)
if node.receiver.nil?
@ -713,6 +758,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
when :private_class_method
_visit_call_public_private_class_method(node, :private) { super }
else
node.arguments&.accept(self)
super
end
else
@ -720,6 +766,13 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
end
end
def visit_block_node(node)
@scanner.with_in_proc_block do
# include, extend and method definition inside block are not documentable
super
end
end
def visit_alias_method_node(node)
@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)
@ -727,12 +780,13 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
end
def visit_module_node(node)
node.constant_path.accept(self)
@scanner.process_comments_until(node.location.start_line - 1)
module_name = constant_path_string(node.constant_path)
mod = @scanner.add_module_or_class(module_name, node.location.start_line, node.location.end_line) if module_name
if mod
@scanner.with_container(mod) do
super
node.body&.accept(self)
@scanner.process_comments_until(node.location.end_line)
end
else
@ -741,13 +795,16 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
end
def visit_class_node(node)
node.constant_path.accept(self)
node.superclass&.accept(self)
@scanner.process_comments_until(node.location.start_line - 1)
superclass_name = constant_path_string(node.superclass) if node.superclass
superclass_expr = node.superclass.slice if node.superclass && !superclass_name
class_name = constant_path_string(node.constant_path)
klass = @scanner.add_module_or_class(class_name, node.location.start_line, node.location.end_line, is_class: true, superclass_name: superclass_name) if class_name
klass = @scanner.add_module_or_class(class_name, node.location.start_line, node.location.end_line, is_class: true, superclass_name: superclass_name, superclass_expr: superclass_expr) if class_name
if klass
@scanner.with_container(klass) do
super
node.body&.accept(self)
@scanner.process_comments_until(node.location.end_line)
end
else
@ -758,6 +815,12 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
def visit_singleton_class_node(node)
@scanner.process_comments_until(node.location.start_line - 1)
if @scanner.has_modifier_nodoc?(node.location.start_line)
# Skip visiting inside the singleton class. Also skips creation of node.expression as a module
@scanner.skip_comments_until(node.location.end_line)
return
end
expression = node.expression
expression = expression.body.body.first if expression.is_a?(Prism::ParenthesesNode) && expression.body&.body&.size == 1
@ -772,9 +835,10 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
when Prism::SelfNode
mod = @scanner.container if @scanner.container != @top_level
end
expression.accept(self)
if mod
@scanner.with_container(mod, singleton: true) do
super
node.body&.accept(self)
@scanner.process_comments_until(node.location.end_line)
end
else
@ -784,6 +848,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
def visit_def_node(node)
start_line = node.location.start_line
args_end_line = node.parameters&.location&.end_line || start_line
end_line = node.location.end_line
@scanner.process_comments_until(start_line - 1)
@ -834,6 +899,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
calls_super: calls_super,
tokens: tokens,
start_line: start_line,
args_end_line: args_end_line,
end_line: end_line
)
ensure
@ -942,7 +1008,7 @@ class RDoc::Parser::PrismRuby < RDoc::Parser
@scanner.visibility = visibility
else # `public :foo, :bar`, `private def foo; end`
yield
names = visibility_method_arguments(call_node, singleton: @scanner.singleton)
names = visibility_method_arguments(call_node, singleton: false)
@scanner.change_method_visibility(names, visibility) if names
end
end