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 711

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 1023

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 1058

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 1041

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 1029

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 998

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 1052

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 1046

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 1005

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 1012

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: false)
    @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 991

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 945

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 981

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 951

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 957

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 783

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_block_node(node)

[ GitHub ]

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

def visit_block_node(node)
  @scanner.with_in_proc_block do
    # include, extend and method definition inside block are not documentable
    super
  end
end

#visit_call_node(node)

[ GitHub ]

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

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
      node.arguments&.accept(self)
      super
    end
  else
    super
  end
end

#visit_class_node(node)

[ GitHub ]

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

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, superclass_expr: superclass_expr) if class_name
  if klass
    @scanner.with_container(klass) do
      node.body&.accept(self)
      @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 916

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 931

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 856

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)

  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,
    args_end_line: args_end_line,
    end_line: end_line
  )
ensure
  @scanner.skip_comments_until(end_line)
end

#visit_if_node(node) Also known as: #visit_unless_node

[ GitHub ]

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

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

#visit_module_node(node)

[ GitHub ]

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

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
      node.body&.accept(self)
      @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 822

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

  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
  expression.accept(self)
  if mod
    @scanner.with_container(mod, singleton: true) do
      node.body&.accept(self)
      @scanner.process_comments_until(node.location.end_line)
    end
  else
    @scanner.skip_comments_until(node.location.end_line)
  end
end

#visit_unless_node(node)

[ GitHub ]

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

alias visit_unless_node visit_if_node