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', rooted: false)
BOOLEAN =
UniqueType.new('Boolean', rooted: true)

Constants included from TypeMethods

TypeMethods::PARAMETERS_TYPE_BY_STARTING_TAG

Instance Attribute Summary collapse

Attributes included from TypeMethods

#name, #parameters_type

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TypeMethods

#==, #defined?, #duck_type?, #each_unique_type, #erase_generics, #fixed_parameters?, #generate_substring_from, #hash_parameters?, #list_parameters?, #namespace, #nil_type?, #qualify, #rooted?, #rooted_name, #rooted_namespace, #rooted_substring, #rooted_tag, #scope, #substring, #tag, #tuple?, #undefined?, #value_types, #void?

Constructor Details

#initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil) ⇒ UniqueType

Returns a new instance of UniqueType.

Parameters:

  • name (String)
  • key_types (Array<ComplexType>) (defaults to: [])
  • subtypes (Array<ComplexType>) (defaults to: [])
  • rooted (Boolean)
  • parameters_type (Symbol, nil) (defaults to: nil)


62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/solargraph/complex_type/unique_type.rb', line 62

def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil)
  if parameters_type.nil?
    raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
  end
  raise "Please remove leading :: and set rooted instead - #{name}" if name.start_with?('::')
  @name = name
  @key_types = key_types
  @subtypes = subtypes
  @rooted = rooted
  @all_params = []
  @all_params.concat key_types
  @all_params.concat subtypes
  @parameters_type = parameters_type
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

#key_typesObject (readonly)

Returns the value of attribute key_types.



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

def key_types
  @key_types
end

#subtypesObject (readonly)

Returns the value of attribute subtypes.



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

def subtypes
  @subtypes
end

Class Method Details

.parse(name, substring = '', make_rooted: nil) ⇒ 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

  • make_rooted (Boolean, nil) (defaults to: nil)

Returns:



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
# File 'lib/solargraph/complex_type/unique_type.rb', line 22

def self.parse name, substring = '', make_rooted: nil
  if name.start_with?(':::')
    raise "Illegal prefix: #{name}"
  end
  if name.start_with?('::')
    name = name[2..-1]
    rooted = true
  else
    rooted = false
  end
  rooted = make_rooted unless make_rooted.nil?

  # @type [Array<ComplexType>]
  key_types = []
  # @type [Array<ComplexType>]
  subtypes = []
  parameters_type = nil
  unless substring.empty?
    subs = ComplexType.parse(substring[1..-2], partial: true)
    parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
    if parameters_type == :hash
      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
  end
  new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
end

Instance Method Details

#force_rootedself

Returns:

  • (self)


259
260
261
262
263
# File 'lib/solargraph/complex_type/unique_type.rb', line 259

def force_rooted
  transform do |t|
    t.recreate(make_rooted: true)
  end
end

#generic?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/solargraph/complex_type/unique_type.rb', line 148

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

#itemsArray<UniqueType>

Returns:



82
83
84
# File 'lib/solargraph/complex_type/unique_type.rb', line 82

def items
  [self]
end

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

Yield Parameters:

  • t (self)

Yield Returns:

  • (self)

Returns:

  • (Array<self>)


224
225
226
# File 'lib/solargraph/complex_type/unique_type.rb', line 224

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

#parameters?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/solargraph/complex_type/unique_type.rb', line 122

def parameters?
  !all_params.empty?
end

#parameters_as_rbsString

Returns:

  • (String)


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

def parameters_as_rbs
  return '' unless parameters?

  return "[#{all_params.map(&:to_rbs).join(', ')}]" if key_types.empty?

  # handle, e.g., Hash[K, V] case
  key_types_str = rbs_union(key_types)
  subtypes_str = rbs_union(subtypes)
  "[#{key_types_str}, #{subtypes_str}]"
end

#rbs_nameString

Returns:

  • (String)


87
88
89
90
91
92
93
# File 'lib/solargraph/complex_type/unique_type.rb', line 87

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

#rbs_union(types) ⇒ String

Parameters:

Returns:

  • (String)


128
129
130
131
132
133
134
# File 'lib/solargraph/complex_type/unique_type.rb', line 128

def rbs_union(types)
  if types.length == 1
    types.first.to_rbs
  else
    "(#{types.map(&:to_rbs).join(' | ')})"
  end
end

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

Parameters:

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

Returns:

  • (self)


239
240
241
242
243
244
245
246
# File 'lib/solargraph/complex_type/unique_type.rb', line 239

def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
  raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
  new_name ||= name
  new_key_types ||= @key_types
  new_subtypes ||= @subtypes
  make_rooted = @rooted if make_rooted.nil?
  UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
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:



207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/solargraph/complex_type/unique_type.rb', line 207

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:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/solargraph/complex_type/unique_type.rb', line 156

def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
  if name == ComplexType::GENERIC_TAG_NAME
    type_param = subtypes.first&.name
    return self 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
    return resolved_generic_values[type_param] || self
  end

  # @todo typechecking should complain when the method being called has no @yieldparam tag
  new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:key_types)
  new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:subtypes)
  recreate(new_key_types: new_key_types, new_subtypes: new_subtypes)
end

#resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values) ⇒ Array<ComplexType>

Parameters:

  • generics_to_resolve (Enumerable<String>)
  • context_type (UniqueType)
  • resolved_generic_values (Hash{String => ComplexType})

Yield Returns:

Returns:



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/solargraph/complex_type/unique_type.rb', line 183

def resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values)
  types = yield self
  types.each_with_index.flat_map do |ct, i|
    ct.items.flat_map do |ut|
      context_params = yield context_type if context_type
      if context_params && context_params[i]
        type_arg = context_params[i]
        type_arg.map do |new_unique_context_type|
          ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, resolved_generic_values: resolved_generic_values
        end
      else
        ut.resolve_generics_from_context generics_to_resolve, nil, resolved_generic_values: resolved_generic_values
      end
    end
  end
end

#rooted_tagsString

Returns:

  • (String)


249
250
251
# File 'lib/solargraph/complex_type/unique_type.rb', line 249

def rooted_tags
  rooted_tag
end

#self_to(dst) ⇒ UniqueType

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

Parameters:

  • dst (String)

Returns:



288
289
290
291
292
293
# File 'lib/solargraph/complex_type/unique_type.rb', line 288

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)


295
296
297
# File 'lib/solargraph/complex_type/unique_type.rb', line 295

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

#tagsString

Returns:

  • (String)


254
255
256
# File 'lib/solargraph/complex_type/unique_type.rb', line 254

def tags
  tag
end

#to_aArray<UniqueType>

Returns:



229
230
231
# File 'lib/solargraph/complex_type/unique_type.rb', line 229

def to_a
  [self]
end

#to_rbsString

Returns:

  • (String)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/solargraph/complex_type/unique_type.rb', line 96

def to_rbs
  if duck_type?
    'untyped'
  elsif name == 'Boolean'
    'bool'
  elsif name.downcase == 'nil'
    'nil'
  elsif name == GENERIC_TAG_NAME
    all_params.first.name
  elsif ['Class', 'Module'].include?(name)
    rbs_name
  elsif ['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



77
78
79
# File 'lib/solargraph/complex_type/unique_type.rb', line 77

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)


271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/solargraph/complex_type/unique_type.rb', line 271

def transform(new_name = nil, &transform_type)
  raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
  if name == ComplexType::GENERIC_TAG_NAME
    # doesn't make sense to manipulate the name of the generic
    new_key_types = @key_types
    new_subtypes = @subtypes
  else
    new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
    new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
  end
  new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes)
  yield new_type
end