Method: Parlour::TypeParser#parse_method_into_methods

Defined in:
lib/parlour/type_parser.rb

#parse_method_into_methods(path, is_within_eigenclass: false) ⇒ <RbiGenerator::Method>

Given a path to a method in the AST, finds the associated definition and parses them into methods. Usually this will return one method; the only exception currently is for attributes, where multiple can be declared in one call, e.g. attr_reader :x, :y, :z.

Parameters:

  • path (NodePath)

    The sig to parse.

  • is_within_eigenclass (Boolean) (defaults to: false)

    Whether the method definition this sig is associated with appears inside an eigenclass definition. If true, the returned method is made a class method. If the method definition is already a class method, an exception is thrown as the method will be a class method of the eigenclass, which Parlour can’t represent.

Returns:



688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'lib/parlour/type_parser.rb', line 688

def parse_method_into_methods(path, is_within_eigenclass: false)
  # A :def node represents a definition like "def x; end"
  # A :defs node represents a definition like "def self.x; end"
  def_node = path.traverse(ast)
  case def_node.type
  when :def
    class_method = false
    def_names = [def_node.to_a[0].to_s]
    def_params = def_node.to_a[1].to_a
    kind = :def
  when :defs
    parse_err 'targeted definitions on a non-self target are not supported', def_node \
      unless def_node.to_a[0].type == :self
    class_method = true
    def_names = [def_node.to_a[1].to_s]
    def_params = def_node.to_a[2].to_a
    kind = :def
  when :send
    target, method_name, *parameters = *def_node

    parse_err 'node after a sig must be a method definition', def_node \
      unless [:attr_reader, :attr_writer, :attr_accessor].include?(method_name) \
        || target != nil

    parse_err 'typed attribute should have at least one name', def_node if parameters&.length == 0

    kind = :attr
    attr_direction = method_name.to_s.gsub('attr_', '').to_sym
    def_names = T.must(parameters).map { |param| param.to_a[0].to_s }
    class_method = false
  else
    parse_err 'node after a sig must be a method definition', def_node
  end

  if is_within_eigenclass
    parse_err 'cannot represent multiple levels of eigenclassing', def_node if class_method
    class_method = true
  end

  return_type = unless def_names == ["initialize"]
    "T.untyped"
  end

  if kind == :def
    parameters = def_params.map do |def_param|
      arg_name = def_param.to_a[0]

      # TODO: anonymous restarg
      full_name = arg_name.to_s
      full_name = "*#{arg_name}"  if def_param.type == :restarg
      full_name = "**#{arg_name}" if def_param.type == :kwrestarg
      full_name = "#{arg_name}:"  if def_param.type == :kwarg || def_param.type == :kwoptarg
      full_name = "&#{arg_name}"  if def_param.type == :blockarg

      default = def_param.to_a[1] ? node_to_s(def_param.to_a[1]) : nil
      type = nil

      RbiGenerator::Parameter.new(full_name, type: type, default: default)
    end

    # There should only be one ever here, but future-proofing anyway
    def_names.map do |def_name|
      RbiGenerator::Method.new(
        generator,
        def_name,
        parameters,
        return_type,
        class_method: class_method
      )
    end
  elsif kind == :attr
    case attr_direction
    when :reader, :accessor, :writer
      attr_type = return_type || "T.untyped"
    else
      raise "unknown attribute direction #{attr_direction}"
    end

    def_names.map do |def_name|
      RbiGenerator::Attribute.new(
        generator,
        def_name,
        attr_direction,
        attr_type,
        class_attribute: class_method
      )
    end
  else
    raise "unknown definition kind #{kind}"
  end
end