Module: Sord::TypeConverter

Defined in:
lib/sord/type_converter.rb

Overview

Contains methods to convert YARD types to Sorbet types.

Constant Summary collapse

SIMPLE_TYPE_REGEX =

A regular expression which matches Ruby namespaces and identifiers. “Foo”, “Foo::Bar”, and “::Foo::Bar” are all matches, whereas “Foo.Bar” or “Foo#bar” are not.

/(?:\:\:)?[a-zA-Z_][a-zA-Z_0-9]*(?:\:\:[a-zA-Z_][a-zA-Z_0-9]*)*/
GENERIC_TYPE_REGEX =

A regular expression which matches a Ruby namespace immediately followed by another Ruby namespace in angle brackets. This is the format usually used in YARD to model generic types, such as “Array<String>”.

/(#{SIMPLE_TYPE_REGEX})\s*<\s*(.*)\s*>/
SORBET_SUPPORTED_GENERIC_TYPES =

An array of built-in generic types supported by Sorbet.

%w{Array Set Enumerable Enumerator Range Hash}

Class Method Summary collapse

Class Method Details

.split_type_parameters(params) ⇒ Array<String>

Given a string of YARD type parameters (without angle brackets), splits the string into an array of each type parameter.

Parameters:

  • params (String)

    The type parameters.

Returns:

  • (Array<String>)

    The split type parameters.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sord/type_converter.rb', line 25

def self.split_type_parameters(params)
  result = []
  buffer = ""
  current_bracketing_level = 0
  character_pointer = 0
  
  while character_pointer < params.length
    should_buffer = true

    current_bracketing_level += 1 if params[character_pointer] == ?<
    current_bracketing_level -= 1 if params[character_pointer] == ?>

    if params[character_pointer] == ?,
      if current_bracketing_level == 0
        result << buffer.strip
        buffer = ""
        should_buffer = false
      end
    end

    buffer += params[character_pointer] if should_buffer
    character_pointer += 1
  end

  result << buffer.strip

  result
end

.yard_to_sorbet(yard, item = nil) ⇒ Object

Converts a YARD type into a Sorbet type.

Parameters:

  • yard (Boolean, Array, String)

    The YARD type.

  • item (YARD::CodeObjects::Base) (defaults to: nil)

    The CodeObject which the YARD type is associated with. This is used for logging and can be nil, but this will lead to less informative log messages.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/sord/type_converter.rb', line 59

def self.yard_to_sorbet(yard, item=nil)
  case yard
  when nil # Type not specified
    "T.untyped"
  when  "bool", "Bool", "boolean", "Boolean", "true", "false"
    "T::Boolean"
  when 'self'
    item.parent.path
  when Array
    # If there's only one element, unwrap it, otherwise allow for a
    # selection of any of the types
    types = yard
      .reject { |x| x == 'nil' }
      .map { |x| yard_to_sorbet(x, item) }
      .uniq
    result = types.length == 1 ? types.first : "T.any(#{types.join(', ')})"
    result = "T.nilable(#{result})" if yard.include?('nil')
    result
  when /^#{SIMPLE_TYPE_REGEX}$/
    # If this doesn't begin with an uppercase letter, warn
    if /^[_a-z]/ === yard
      Logging.warn("#{yard} is probably not a type, but using anyway", item)
    end
    yard
  when /^#{GENERIC_TYPE_REGEX}$/
    generic_type = $1
    type_parameters = $2

    if SORBET_SUPPORTED_GENERIC_TYPES.include?(generic_type)
      "T::#{generic_type}[#{
        split_type_parameters(type_parameters).map { |x| yard_to_sorbet(x, item) }.join(', ')}]"
    else
      Logging.warn("unsupported generic type #{generic_type.inspect} in #{yard.inspect}", item)
      "SORD_ERROR_#{generic_type.gsub(/[^0-9A-Za-z_]/i, '')}"
    end
  else
    Logging.warn("#{yard.inspect} does not appear to be a type", item)
    "SORD_ERROR_#{yard.gsub(/[^0-9A-Za-z_]/i, '')}"
  end
end