123456789_123456789_123456789_123456789_123456789_

Class: RDoc::Parser::PrismRuby::RDocVisitor

Do not use. This class is for internal use only.
Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Prism::Visitor
Instance Chain:
self, Prism::Visitor
Inherits: Prism::Visitor
  • Object
Defined in: lib/rdoc/parser/prism_ruby.rb

Class Method Summary

Instance Method Summary

Constructor Details

.new(scanner, top_level, store) ⇒ RDocVisitor

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 673

def initialize(scanner, top_level, store)
  @scanner = scanner
  @top_level = top_level
  @store = store
end

Instance Method Details

#_visit_call_alias_method(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 950

def _visit_call_alias_method(call_node)
  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

#_visit_call_attr_reader_writer_accessor(call_node, rw) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 985

def _visit_call_attr_reader_writer_accessor(call_node, rw)
  names = symbol_arguments(call_node)
  @scanner.add_attributes(names.map(&:to_s), rw, call_node.location.start_line) if names
end

#_visit_call_extend(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 968

def _visit_call_extend(call_node)
  names = constant_arguments_names(call_node)
  @scanner.add_extends(names, call_node.location.start_line) if names && !@scanner.singleton
end

#_visit_call_include(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 956

def _visit_call_include(call_node)
  names = constant_arguments_names(call_node)
  line_no = call_node.location.start_line
  return unless names

  if @scanner.singleton
    @scanner.add_extends(names, line_no)
  else
    @scanner.add_includes(names, line_no)
  end
end

#_visit_call_module_function(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 925

def _visit_call_module_function(call_node)
  yield
  return if @scanner.singleton
  names = visibility_method_arguments(call_node, singleton: false)&.map(&:to_s)
  @scanner.change_method_to_module_function(names) if names
end

#_visit_call_private_constant(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 979

def _visit_call_private_constant(call_node)
  return if @scanner.singleton
  names = symbol_arguments(call_node)
  @scanner.container.set_constant_visibility_for(names.map(&:to_s), :private) if names
end

#_visit_call_public_constant(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 973

def _visit_call_public_constant(call_node)
  return if @scanner.singleton
  names = symbol_arguments(call_node)
  @scanner.container.set_constant_visibility_for(names.map(&:to_s), :public) if names
end

#_visit_call_public_private_class_method(call_node, visibility) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 932

def _visit_call_public_private_class_method(call_node, visibility)
  yield
  return if @scanner.singleton
  names = visibility_method_arguments(call_node, singleton: true)
  @scanner.change_method_visibility(names, visibility, singleton: true) if names
end

#_visit_call_public_private_protected(call_node, visibility) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 939

def _visit_call_public_private_protected(call_node, visibility)
  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: @scanner.singleton)
    @scanner.change_method_visibility(names, visibility) if names
  end
end

#_visit_call_require(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 918

def _visit_call_require(call_node)
  return unless call_node.arguments&.arguments&.size == 1
  arg = call_node.arguments.arguments.first
  return unless arg.is_a?(Prism::StringNode)
  @scanner.container.add_require(RDoc::Require.new(arg.unescaped, nil))
end

#constant_arguments_names(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 872

def constant_arguments_names(call_node)
  return unless call_node.arguments
  names = call_node.arguments.arguments.map { |arg| constant_path_string(arg) }
  names.all? ? names : nil
end

#constant_path_string(node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 908

def constant_path_string(node)
  case node
  when Prism::ConstantReadNode
    node.name.to_s
  when Prism::ConstantPathNode
    parent_name = node.parent ? constant_path_string(node.parent) : ''
    "#{parent_name}::#{node.name}" if parent_name
  end
end

#symbol_arguments(call_node) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 878

def symbol_arguments(call_node)
  arguments_node = call_node.arguments
  return unless arguments_node && arguments_node.arguments.all? { |arg| arg.is_a?(Prism::SymbolNode)}
  arguments_node.arguments.map { |arg| arg.value.to_sym }
end

#visibility_method_arguments(call_node, singleton:) (private)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 884

def visibility_method_arguments(call_node, singleton:)
  arguments_node = call_node.arguments
  return unless arguments_node
  symbols = symbol_arguments(call_node)
  if symbols
    # module_function :foo, :bar
    return symbols.map(&:to_s)
  else
    return unless arguments_node.arguments.size == 1
    arg = arguments_node.arguments.first
    return unless arg.is_a?(Prism::DefNode)

    if singleton
      # `private_class_method def foo; end` `private_class_method def not_self.foo; end` should be ignored
      return unless arg.receiver.is_a?(Prism::SelfNode)
    else
      # `module_function def something.foo` should be ignored
      return if arg.receiver
    end
    # `module_function def foo; end` or `private_class_method def self.foo; end`
    [arg.name.to_s]
  end
end

#visit_alias_method_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 723

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)
  @scanner.add_alias_method(node.old_name.value.to_s, node.new_name.value.to_s, node.location.start_line)
end

#visit_call_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 679

def visit_call_node(node)
  @scanner.process_comments_until(node.location.start_line - 1)
  if node.receiver.nil?
    case node.name
    when :attr
      _visit_call_attr_reader_writer_accessor(node, 'R')
    when :attr_reader
      _visit_call_attr_reader_writer_accessor(node, 'R')
    when :attr_writer
      _visit_call_attr_reader_writer_accessor(node, 'W')
    when :attr_accessor
      _visit_call_attr_reader_writer_accessor(node, 'RW')
    when :include
      _visit_call_include(node)
    when :extend
      _visit_call_extend(node)
    when :public
      _visit_call_public_private_protected(node, :public) { super }
    when :private
      _visit_call_public_private_protected(node, :private) { super }
    when :protected
      _visit_call_public_private_protected(node, :protected) { super }
    when :private_constant
      _visit_call_private_constant(node)
    when :public_constant
      _visit_call_public_constant(node)
    when :require
      _visit_call_require(node)
    when :alias_method
      _visit_call_alias_method(node)
    when :module_function
      _visit_call_module_function(node) { super }
    when :public_class_method
      _visit_call_public_private_class_method(node, :public) { super }
    when :private_class_method
      _visit_call_public_private_class_method(node, :private) { super }
    else
      super
    end
  else
    super
  end
end

#visit_class_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 743

def visit_class_node(node)
  @scanner.process_comments_until(node.location.start_line - 1)
  superclass_name = constant_path_string(node.superclass) if node.superclass
  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
  if klass
    @scanner.with_container(klass) do
      super
      @scanner.process_comments_until(node.location.end_line)
    end
  else
    @scanner.skip_comments_until(node.location.end_line)
  end
end

#visit_constant_path_write_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 843

def visit_constant_path_write_node(node)
  @scanner.process_comments_until(node.location.start_line - 1)
  path = constant_path_string(node.target)
  return unless path

  @scanner.add_constant(
    path,
    constant_path_string(node.value) || node.value.slice,
    node.location.start_line,
    node.location.end_line
  )
  @scanner.skip_comments_until(node.location.end_line)
  # Do not traverse rhs not to document `A::B = Struct.new{def undocumentable_method; end}`
end

#visit_constant_write_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 858

def visit_constant_write_node(node)
  @scanner.process_comments_until(node.location.start_line - 1)
  @scanner.add_constant(
    node.name.to_s,
    constant_path_string(node.value) || node.value.slice,
    node.location.start_line,
    node.location.end_line
  )
  @scanner.skip_comments_until(node.location.end_line)
  # Do not traverse rhs not to document `A = Struct.new{def undocumentable_method; end}`
end

#visit_def_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 785

def visit_def_node(node)
  start_line = node.location.start_line
  end_line = node.location.end_line
  @scanner.process_comments_until(start_line - 1)

  case node.receiver
  when Prism::NilNode, Prism::TrueNode, Prism::FalseNode
    visibility = :public
    singleton = false
    receiver_name =
      case node.receiver
      when Prism::NilNode
        'NilClass'
      when Prism::TrueNode
        'TrueClass'
      when Prism::FalseNode
        'FalseClass'
      end
    receiver_fallback_type = :class
  when Prism::SelfNode
    # singleton method of a singleton class is not documentable
    return if @scanner.singleton
    visibility = :public
    singleton = true
  when Prism::ConstantReadNode, Prism::ConstantPathNode
    visibility = :public
    singleton = true
    receiver_name = constant_path_string(node.receiver)
    receiver_fallback_type = :module
    return unless receiver_name
  when nil
    visibility = @scanner.visibility
    singleton = @scanner.singleton
  else
    # `def (unknown expression).method_name` is not documentable
    return
  end
  name = node.name.to_s
  params, block_params, calls_super = MethodSignatureVisitor.scan_signature(node)
  tokens = @scanner.visible_tokens_from_location(node.location)

  @scanner.add_method(
    name,
    receiver_name: receiver_name,
    receiver_fallback_type: receiver_fallback_type,
    visibility: visibility,
    singleton: singleton,
    params: params,
    block_params: block_params,
    calls_super: calls_super,
    tokens: tokens,
    start_line: start_line,
    end_line: end_line
  )
ensure
  @scanner.skip_comments_until(end_line)
end

#visit_module_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 729

def visit_module_node(node)
  @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
      @scanner.process_comments_until(node.location.end_line)
    end
  else
    @scanner.skip_comments_until(node.location.end_line)
  end
end

#visit_singleton_class_node(node)

[ GitHub ]

  
# File 'lib/rdoc/parser/prism_ruby.rb', line 758

def visit_singleton_class_node(node)
  @scanner.process_comments_until(node.location.start_line - 1)

  expression = node.expression
  expression = expression.body.body.first if expression.is_a?(Prism::ParenthesesNode) && expression.body&.body&.size == 1

  case expression
  when Prism::ConstantWriteNode
    # Accept `class << (NameErrorCheckers = Object.new)` as a module which is not actually a module
    mod = @scanner.container.add_module(RDoc::NormalModule, expression.name.to_s)
  when Prism::ConstantPathNode, Prism::ConstantReadNode
    expression_name = constant_path_string(expression)
    # If a constant_path does not exist, RDoc creates a module
    mod = @scanner.find_or_create_module_path(expression_name, :module) if expression_name
  when Prism::SelfNode
    mod = @scanner.container if @scanner.container != @top_level
  end
  if mod
    @scanner.with_container(mod, singleton: true) do
      super
      @scanner.process_comments_until(node.location.end_line)
    end
  else
    @scanner.skip_comments_until(node.location.end_line)
  end
end