Class: Solargraph::ComplexType::UniqueType

Inherits:
Object
  • Object
show all
Includes:
TypeMethods
Defined in:
lib/solargraph/complex_type/unique_type.rb

Overview

An individual type signature. A complex type can consist of multiple unique types.

Constant Summary collapse

UNDEFINED =
UniqueType.new('undefined')
BOOLEAN =
UniqueType.new('Boolean')

Instance Attribute Summary collapse

Attributes included from TypeMethods

#name, #substring, #subtypes, #tag

Instance Method Summary collapse

Methods included from TypeMethods

#==, #defined?, #duck_type?, #each_unique_type, #erase_generics, #fixed_parameters?, #hash_parameters?, #key_types, #list_parameters?, #namespace, #nil_type?, #parameters?, #qualify, #rooted?, #rooted_name, #rooted_namespace, #scope, #undefined?, #value_types, #void?

Constructor Details

#initialize(name, substring = '') ⇒ UniqueType

Create a UniqueType with the specified name and an optional substring. The substring is the parameter section of a parametrized type, e.g., for the type ‘Array<String>`, the name is `Array` and the substring is `<String>`.

Parameters:

  • name (String)

    The name of the type

  • substring (String) (defaults to: '')

    The substring of the type



20
21
22
23
24
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
53
54
55
56
57
# File 'lib/solargraph/complex_type/unique_type.rb', line 20

def initialize name, substring = ''
  if name.start_with?('::')
    @name = name[2..-1]
    @rooted = true
  else
    @name = name
    @rooted = false
  end
  @substring = substring
  @tag = @name + substring
  # @type [Array<ComplexType>]
  @key_types = []
  # @type [Array<ComplexType>]
  @subtypes = []
  # @type [Array<ComplexType>]
  @all_params = []
  return unless parameters?
  # @todo we should be able to probe the type of 'subs' without
  #   hoisting the definition outside of the if statement
  subs = if @substring.start_with?('<(') && @substring.end_with?(')>')
           ComplexType.parse(substring[2..-3], partial: true)
         else
           ComplexType.parse(substring[1..-2], partial: true)
         end
  if hash_parameters?
    raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
    # @todo should be able to resolve map; both types have it
    #   with same return type
    # @sg-ignore
    @key_types.concat subs[0].map { |u| ComplexType.new([u]) }
    # @sg-ignore
    @subtypes.concat subs[1].map { |u| ComplexType.new([u]) }
  else
    @subtypes.concat subs
  end
  @all_params.concat @key_types
  @all_params.concat @subtypes
end

Instance Attribute Details

#all_paramsObject (readonly)

Returns the value of attribute all_params.



11
12
13
# File 'lib/solargraph/complex_type/unique_type.rb', line 11

def all_params
  @all_params
end

Instance Method Details

#generic?Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/solargraph/complex_type/unique_type.rb', line 98

def generic?
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
end

#itemsArray<UniqueType>

Returns:



64
65
66
# File 'lib/solargraph/complex_type/unique_type.rb', line 64

def items
  [self]
end

#map {|t| ... } ⇒ Array<self>

Yield Parameters:

  • t (self)

Yield Returns:

  • (self)

Returns:

  • (Array<self>)


152
153
154
# File 'lib/solargraph/complex_type/unique_type.rb', line 152

def map &block
  [block.yield(self)]
end

#parameters_as_rbsString

Returns:

  • (String)


94
95
96
# File 'lib/solargraph/complex_type/unique_type.rb', line 94

def parameters_as_rbs
  parameters? ? "[#{all_params.map { |s| s.to_rbs }.join(', ')}]" : ''
end

#rbs_nameString

Returns:

  • (String)


69
70
71
72
73
74
75
# File 'lib/solargraph/complex_type/unique_type.rb', line 69

def rbs_name
  if name == 'undefined'
    'untyped'
  else
    rooted_name
  end
end

#recreate(new_name: nil, new_key_types: nil, new_subtypes: nil) ⇒ self

Parameters:

  • new_name (String, nil) (defaults to: nil)
  • new_key_types (Array<UniqueType>, nil) (defaults to: nil)
  • new_subtypes (Array<UniqueType>, nil) (defaults to: nil)

Returns:

  • (self)


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/solargraph/complex_type/unique_type.rb', line 165

def recreate(new_name: nil, new_key_types: nil, new_subtypes: nil)
  new_name ||= name
  new_key_types ||= @key_types
  new_subtypes ||= @subtypes
  if new_key_types.none?(&:defined?) && new_subtypes.none?(&:defined?)
    # if all subtypes are undefined, erase down to the non-parametric type
    UniqueType.new(new_name)
  elsif new_key_types.empty? && new_subtypes.empty?
    UniqueType.new(new_name)
  elsif hash_parameters?
    UniqueType.new(new_name, "{#{new_key_types.join(', ')} => #{new_subtypes.join(', ')}}")
  elsif @substring.start_with?('<(')
    # @todo This clause is probably wrong, and if so, fixing it
    #    will be some level of breaking change.  Probably best
    #    handled before real tuple support is rolled out and
    #    folks start relying on it more.
    #
    #   (String) is a one element tuple in https://yardoc.org/types
    #   <String> is an array of zero or more Strings in https://yardoc.org/types
    #   Array<(String)> could be an Array of one-element tuples or a
    #     one element tuple.  https://yardoc.org/types treats it
    #     as the former.
    #   Array<(String), Integer> is not ambiguous if we accept
    #     (String) as a tuple type, but not currently understood
    #     by Solargraph.
    UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
  elsif fixed_parameters?
    UniqueType.new(new_name, "(#{new_subtypes.join(', ')})")
  else
    UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
  end
end

#resolve_generics(definitions, context_type) ⇒ UniqueType, ComplexType

Probe the concrete type for each of the generic type parameters used in this type, and return a new type if possible.

Parameters:

Returns:



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/solargraph/complex_type/unique_type.rb', line 135

def resolve_generics definitions, context_type
  return self if definitions.nil? || definitions.generics.empty?

  transform(name) do |t|
    if t.name == GENERIC_TAG_NAME
      idx = definitions.generics.index(t.subtypes.first&.name)
      next t if idx.nil?
      context_type.all_params[idx] || ComplexType::UNDEFINED
    else
      t
    end
  end
end

#resolve_generics_from_context(generics_to_resolve, context_type, resolved_generic_values: {}) ⇒ UniqueType, ComplexType

Parameters:

  • generics_to_resolve (Enumerable<String>)
  • context_type (UniqueType, nil)
  • resolved_generic_values (Hash{String => ComplexType}) (defaults to: {})

    Added to as types are encountered or resolved

Returns:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/solargraph/complex_type/unique_type.rb', line 107

def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
  transform(name) do |t|
    next t unless t.name == ComplexType::GENERIC_TAG_NAME

    new_binding = false

    type_param = t.subtypes.first&.name
    next t unless generics_to_resolve.include? type_param
    unless context_type.nil? || !resolved_generic_values[type_param].nil?
      new_binding = true
      resolved_generic_values[type_param] = context_type
    end
    if new_binding
      resolved_generic_values.transform_values! do |complex_type|
        complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
      end
    end
    resolved_generic_values[type_param] || t
  end
end

#self_to(dst) ⇒ UniqueType

Transform references to the ‘self’ type to the specified concrete namespace

Parameters:

  • dst (String)

Returns:



214
215
216
217
218
219
# File 'lib/solargraph/complex_type/unique_type.rb', line 214

def self_to dst
  transform do |t|
    next t if t.name != 'self'
    t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
  end
end

#selfy?Boolean

Returns:

  • (Boolean)


221
222
223
# File 'lib/solargraph/complex_type/unique_type.rb', line 221

def selfy?
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
end

#to_aArray<UniqueType>

Returns:



157
158
159
# File 'lib/solargraph/complex_type/unique_type.rb', line 157

def to_a
  [self]
end

#to_rbsString

Returns:

  • (String)


78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/solargraph/complex_type/unique_type.rb', line 78

def to_rbs
  if ['Tuple', 'Array'].include?(name) && fixed_parameters?
    # tuples don't have a name; they're just [foo, bar, baz].
    if substring == '()'
      # but there are no zero element tuples, so we go with an array
      'Array[]'
    else
      # already generated surrounded by []
      parameters_as_rbs
    end
  else
    "#{rbs_name}#{parameters_as_rbs}"
  end
end

#to_sObject



59
60
61
# File 'lib/solargraph/complex_type/unique_type.rb', line 59

def to_s
  tag
end

#transform(new_name = nil) {|t| ... } ⇒ self

Apply the given transformation to each subtype and then finally to this type

Parameters:

  • new_name (String, nil) (defaults to: nil)

Yield Parameters:

Yield Returns:

  • (self)

Returns:

  • (self)


204
205
206
207
208
209
# File 'lib/solargraph/complex_type/unique_type.rb', line 204

def transform(new_name = nil, &transform_type)
  new_key_types = @key_types.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
  new_subtypes = @subtypes.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
  new_type = recreate(new_name: new_name || rooted_name, new_key_types: new_key_types, new_subtypes: new_subtypes)
  yield new_type
end