module SorbetRails::SorbetUtils

Public Class Methods

extract_default_value_for_params!(parsed_params, method_def) click to toggle source
# File lib/sorbet-rails/sorbet_utils.rb, line 129
def self.extract_default_value_for_params!(parsed_params, method_def)
  source = method_def.source
  parsed_ast = Parser::CurrentRuby.parse(source)
  if parsed_ast.type != :def
    # could be a method added at runtime? ignore it
    puts "Warning: unable to parse the source of #{method_def.name}"
    return
  end

  args = parsed_ast.children[1]
  if args.type != :args
    puts "Warning: unable to parse the source of #{method_def.name}"
    return
  end

  parsed_params_map = Hash[parsed_params.map {|p| [p.name, p]}]
  args.children.each do |arg|
    arg_name = arg.children[0]
    default = arg.children[1] ? node_to_s(arg.children[1]) : nil

    next if arg_name.blank?

    param_def = parsed_params_map[arg_name]

    raise UnexpectedParam.new(
      "Unexpected param #{arg_name} when parsing #{method_def.name}"
    ) unless param_def.present?

    param_def.default = default
  end
end
get_ordered_parameters_with_type(signature) click to toggle source
# File lib/sorbet-rails/sorbet_utils.rb, line 75
def self.get_ordered_parameters_with_type(signature)
  # extract original method param from signature
  # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L5-L8
  params = T.let([], T::Array[ParsedParamDef])
  signature.arg_types.each do |arg_type|
    # could be :opt, but doesn't matter
    params << ParsedParamDef.new(
      name: arg_type[0],
      kind: :req,
      type_str: arg_type[1].to_s,
    )
  end
  signature.kwarg_types.each do |kwarg_name, kwarg_type|
    # could be :key, but doesn't matter
    params << ParsedParamDef.new(
      name: kwarg_name,
      kind: :keyreq,
      type_str: kwarg_type.to_s,
    )
  end
  if signature.has_rest
    params << ParsedParamDef.new(
      name: signature.rest_name,
      kind: :rest,
      type_str: signature.rest_type.to_s,
    )
  end
  if signature.has_keyrest
    params << ParsedParamDef.new(
      name: signature.keyrest_name,
      kind: :keyrest,
      type_str: signature.keyrest_type.to_s,
    )
  end
  if !signature.block_name.nil?
    # special case `.void` in a proc
    # see https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/types/proc.rb#L10
    block_param_type = signature.block_type.to_s
    block_param_type = block_param_type.gsub('returns(<VOID>)', 'void')
    params << ParsedParamDef.new(
      name: signature.block_name,
      kind: :block,
      type_str: block_param_type,
    )
  end
  params
end
node_to_s(node) click to toggle source
# File lib/sorbet-rails/sorbet_utils.rb, line 165
def self.node_to_s(node)
  return nil unless node

  exp = node.loc.expression
  exp.source_buffer.source[exp.begin_pos...exp.end_pos]
end
parameters_from_method_def(method_def) click to toggle source
# File lib/sorbet-rails/sorbet_utils.rb, line 23
def self.parameters_from_method_def(method_def)
  signature = T::Private::Methods.signature_for_method(method_def)
  method_def = signature.nil? ? method_def : signature.method

  parameters_with_type = signature.nil? ?
    method_def.parameters.map { |p|
      ParsedParamDef.new(
        name: p.size == 1 ? :_ : p[1],  # give param without name default name _
        kind: p[0], # append untyped as type of each param
        type_str: 'T.untyped',
      )
    } :
    get_ordered_parameters_with_type(signature)

  # add prefix & suffix
  parameters_with_type.each do |param_def|
    param_def.prefix =
      case param_def.kind
      when :rest; '*'
      when :keyrest; '**'
      when :block; '&'
      # being comprehensive
      when :req, :opt; ''
      when :key, :keyreq; ''
      else nil
      end

    param_def.suffix =
      case param_def.kind
      when :key, :keyreq; ':'
      else nil
      end
  end

  extract_default_value_for_params!(
    parameters_with_type,
    method_def,
  )

  parameters_with_type.map do |param_def|
    ::Parlour::RbiGenerator::Parameter.new(
      "#{param_def.prefix}#{param_def.name}#{param_def.suffix}",
      type: param_def.type_str,
      default: param_def.default,
    )
  end
end