Class: Solargraph::ComplexType

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

Overview

A container for type data based on YARD type tags.

Defined Under Namespace

Modules: TypeMethods Classes: UniqueType

Constant Summary collapse

GENERIC_TAG_NAME =
'generic'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Equality

#==, #eql?, #freeze, #hash

Constructor Details

#initialize(types = [UniqueType::UNDEFINED]) ⇒ ComplexType

Returns a new instance of ComplexType.

Parameters:



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/solargraph/complex_type.rb', line 16

def initialize types = [UniqueType::UNDEFINED]
  # @todo @items here should not need an annotation
  # @type [Array<UniqueType>]
  items = types.flat_map(&:items).uniq(&:to_s)
  if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
    items.delete_if { |i| i.name == 'false' || i.name == 'true' }
    items.unshift(ComplexType::BOOLEAN)
  end
  items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
  @items = items
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object?

Parameters:

  • name (Symbol)

Returns:

  • (Object, nil)


142
143
144
145
146
# File 'lib/solargraph/complex_type.rb', line 142

def method_missing name, *args, &block
  return if @items.first.nil?
  return @items.first.send(name, *args, &block) if respond_to_missing?(name)
  super
end

Instance Attribute Details

#itemsObject (readonly)

Returns the value of attribute items.



262
263
264
# File 'lib/solargraph/complex_type.rb', line 262

def items
  @items
end

Class Method Details

.parse(*strings, partial: false) ⇒ ComplexType

TODO:

To be able to select the right signature above, Chain::Call needs to know the decl type (:arg, :optarg, :kwarg, etc) of the arguments given, instead of just having an array of Chains as the arguments.

Note:

The ‘partial` parameter is used to indicate that the method is receiving a string that will be used inside another ComplexType. It returns arrays of ComplexTypes instead of a single cohesive one. Consumers should not need to use this parameter; it should only be used internally.

Parse type strings into a ComplexType.

# @overload parse(*strings, partial: false) # @todo Need ability to use a literal true as a type below # @param partial [Boolean] True if the string is part of a another type # @return [Array<UniqueType>] @sg-ignore

Examples:

ComplexType.parse 'String', 'Foo', 'nil' #=> [String, Foo, nil]

Parameters:

  • strings (Array<String>)

    The type definitions to parse

Returns:



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/solargraph/complex_type.rb', line 307

def parse *strings, partial: false
  # @type [Hash{Array<String> => ComplexType}]
  @cache ||= {}
  unless partial
    cached = @cache[strings]
    return cached unless cached.nil?
  end
  types = []
  key_types = nil
  strings.each do |type_string|
    point_stack = 0
    curly_stack = 0
    paren_stack = 0
    base = String.new
    subtype_string = String.new
    type_string&.each_char do |char|
      if char == '='
        #raise ComplexTypeError, "Invalid = in type #{type_string}" unless curly_stack > 0
      elsif char == '<'
        point_stack += 1
      elsif char == '>'
        if subtype_string.end_with?('=') && curly_stack > 0
          subtype_string += char
        elsif base.end_with?('=')
          raise ComplexTypeError, "Invalid hash thing" unless key_types.nil?
          # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
          types.push UniqueType.parse(base[0..-2].strip, subtype_string)
          # @todo this should either expand key_type's type
          #   automatically or complain about not being
          #   compatible with key_type's type in type checking
          key_types = types
          types = []
          base.clear
          subtype_string.clear
          next
        else
          raise ComplexTypeError, "Invalid close in type #{type_string}" if point_stack == 0
          point_stack -= 1
          subtype_string += char
        end
        next
      elsif char == '{'
        curly_stack += 1
      elsif char == '}'
        curly_stack -= 1
        subtype_string += char
        raise ComplexTypeError, "Invalid close in type #{type_string}" if curly_stack < 0
        next
      elsif char == '('
        paren_stack += 1
      elsif char == ')'
        paren_stack -= 1
        subtype_string += char
        raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack < 0
        next
      elsif char == ',' && point_stack == 0 && curly_stack == 0 && paren_stack == 0
        # types.push ComplexType.new([UniqueType.new(base.strip, subtype_string.strip)])
        types.push UniqueType.parse(base.strip, subtype_string.strip)
        base.clear
        subtype_string.clear
        next
      end
      if point_stack == 0 && curly_stack == 0 && paren_stack == 0
        base.concat char
      else
        subtype_string.concat char
      end
    end
    raise ComplexTypeError, "Unclosed subtype in #{type_string}" if point_stack != 0 || curly_stack != 0 || paren_stack != 0
    # types.push ComplexType.new([UniqueType.new(base, subtype_string)])
    types.push UniqueType.parse(base.strip, subtype_string.strip)
  end
  unless key_types.nil?
    raise ComplexTypeError, "Invalid use of key/value parameters" unless partial
    return key_types if types.empty?
    return [key_types, types]
  end
  result = partial ? types : ComplexType.new(types)
  @cache[strings] = result unless partial
  result
end

.try_parse(*strings) ⇒ ComplexType

Parameters:

  • strings (Array<String>)

Returns:



391
392
393
394
395
396
# File 'lib/solargraph/complex_type.rb', line 391

def try_parse *strings
  parse *strings
rescue ComplexTypeError => e
  Solargraph.logger.info "Error parsing complex type `#{strings.join(', ')}`: #{e.message}"
  ComplexType::UNDEFINED
end

Instance Method Details

#[](index) ⇒ UniqueType

Parameters:

  • index (Integer)

Returns:



120
121
122
# File 'lib/solargraph/complex_type.rb', line 120

def [](index)
  @items[index]
end

#all? {|| ... } ⇒ Boolean

Yield Parameters:

Returns:

  • (Boolean)


184
185
186
# File 'lib/solargraph/complex_type.rb', line 184

def all? &block
  @items.all? &block
end

#all_paramsArray<ComplexType>

Returns:



236
237
238
# File 'lib/solargraph/complex_type.rb', line 236

def all_params
  @items.first.all_params || []
end

#all_rooted?Boolean

every type and subtype in this union have been resolved to be fully qualified

Returns:

  • (Boolean)


252
253
254
# File 'lib/solargraph/complex_type.rb', line 252

def all_rooted?
  all?(&:all_rooted?)
end

#any? {|| ... } ⇒ Boolean

Yield Parameters:

Yield Returns:

  • (Boolean)

Returns:

  • (Boolean)


191
192
193
# File 'lib/solargraph/complex_type.rb', line 191

def any? &block
  @items.compact.any? &block
end

#can_assign?(api_map, atype) ⇒ Boolean

Parameters:

  • atype (ComplexType)

    type which may be assigned to this type

  • api_map (ApiMap)

    The ApiMap that performs qualification

Returns:

  • (Boolean)


104
105
106
# File 'lib/solargraph/complex_type.rb', line 104

def can_assign?(api_map, atype)
  any? { |ut| ut.can_assign?(api_map, atype) }
end

#descObject



175
176
177
# File 'lib/solargraph/complex_type.rb', line 175

def desc
  rooted_tags
end

#downcast_to_literal_if_possibleComplexType

Returns:



171
172
173
# File 'lib/solargraph/complex_type.rb', line 171

def downcast_to_literal_if_possible
  ComplexType.new(items.map(&:downcast_to_literal_if_possible))
end

#each {|| ... } ⇒ Enumerable<UniqueType>

Yield Parameters:

Returns:



86
87
88
# File 'lib/solargraph/complex_type.rb', line 86

def each &block
  @items.each &block
end

#each_unique_typeEnumerator<UniqueType>

This method returns an undefined value.

Returns:

Yield Parameters:



94
95
96
97
98
99
100
# File 'lib/solargraph/complex_type.rb', line 94

def each_unique_type &block
  return enum_for(__method__) unless block_given?

  @items.each do |item|
    item.each_unique_type &block
  end
end

#firstUniqueType

Returns:



57
58
59
# File 'lib/solargraph/complex_type.rb', line 57

def first
  @items.first
end

#force_rootedself

Returns:

  • (self)


217
218
219
220
221
# File 'lib/solargraph/complex_type.rb', line 217

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

#generic?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/solargraph/complex_type.rb', line 199

def generic?
  any?(&:generic?)
end

#lengthInteger

Returns:

  • (Integer)


109
110
111
# File 'lib/solargraph/complex_type.rb', line 109

def length
  @items.length
end

#literal?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/solargraph/complex_type.rb', line 166

def literal?
  @items.any?(&:literal?)
end

#map {|| ... } ⇒ Array<UniqueType>

Yield Parameters:

Returns:



80
81
82
# File 'lib/solargraph/complex_type.rb', line 80

def map &block
  @items.map &block
end

#namespaceString

Returns:

  • (String)


130
131
132
133
# File 'lib/solargraph/complex_type.rb', line 130

def namespace
  # cache this attr for high frequency call
  @namespace ||= method_missing(:namespace).to_s
end

#namespacesArray<String>

Returns:

  • (Array<String>)


136
137
138
# File 'lib/solargraph/complex_type.rb', line 136

def namespaces
  @items.map(&:namespace)
end

#nullable?Boolean

Returns:

  • (Boolean)


231
232
233
# File 'lib/solargraph/complex_type.rb', line 231

def nullable?
  @items.any?(&:nil_type?)
end

#qualify(api_map, context = '') ⇒ ComplexType

Parameters:

  • api_map (ApiMap)
  • context (String) (defaults to: '')

Returns:



36
37
38
39
40
41
42
43
44
# File 'lib/solargraph/complex_type.rb', line 36

def qualify api_map, context = ''
  red = reduce_object
  types = red.items.map do |t|
    next t if ['nil', 'void', 'undefined'].include?(t.name)
    next t if ['::Boolean'].include?(t.rooted_name)
    t.qualify api_map, context
  end
  ComplexType.new(types).reduce_object
end

#reduce_class_typeComplexType

Returns:



241
242
243
244
245
246
247
248
# File 'lib/solargraph/complex_type.rb', line 241

def reduce_class_type
  new_items = items.flat_map do |type|
    next type unless ['Module', 'Class'].include?(type.name)

    type.all_params
  end
  ComplexType.new(new_items)
end

#resolve_generics(definitions, context_type) ⇒ ComplexType

Parameters:

Returns:



226
227
228
229
# File 'lib/solargraph/complex_type.rb', line 226

def resolve_generics definitions, context_type
  result = @items.map { |i| i.resolve_generics(definitions, context_type) }
  ComplexType.new(result)
end

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

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:

  • (self)


50
51
52
53
54
# File 'lib/solargraph/complex_type.rb', line 50

def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
  return self unless generic?

  ComplexType.new(@items.map { |i| i.resolve_generics_from_context(generics_to_resolve, context_type, resolved_generic_values: resolved_generic_values) })
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Parameters:

  • name (Symbol)
  • include_private (Boolean) (defaults to: false)

Returns:

  • (Boolean)


150
151
152
# File 'lib/solargraph/complex_type.rb', line 150

def respond_to_missing?(name, include_private = false)
  TypeMethods.public_instance_methods.include?(name) || super
end

#rooted?Boolean

every top-level type has resolved to be fully qualified; see #all_rooted? to check their subtypes as well

Returns:

  • (Boolean)


258
259
260
# File 'lib/solargraph/complex_type.rb', line 258

def rooted?
  all?(&:rooted?)
end

#rooted_tagsObject



179
180
181
# File 'lib/solargraph/complex_type.rb', line 179

def rooted_tags
  map(&:rooted_tag).join(', ')
end

#select(&block) ⇒ Array<UniqueType>

Returns:



125
126
127
# File 'lib/solargraph/complex_type.rb', line 125

def select &block
  @items.select &block
end

#self_to_type(dst) ⇒ ComplexType

Parameters:

Returns:



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

def self_to_type dst
  object_type_dst = dst.reduce_class_type
  transform do |t|
    next t if t.name != 'self'
    object_type_dst
  end
end

#selfy?Boolean

Returns:

  • (Boolean)


195
196
197
# File 'lib/solargraph/complex_type.rb', line 195

def selfy?
  @items.any?(&:selfy?)
end

#simple_tagsObject



162
163
164
# File 'lib/solargraph/complex_type.rb', line 162

def simple_tags
  simplify_literals.tags
end

#simplify_literalsObject



203
204
205
# File 'lib/solargraph/complex_type.rb', line 203

def simplify_literals
  ComplexType.new(map(&:simplify_literals))
end

#tagsObject



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

def tags
  map(&:tag).join(', ')
end

#to_aArray<UniqueType>

Returns:



114
115
116
# File 'lib/solargraph/complex_type.rb', line 114

def to_a
  @items
end

#to_rbsString

Returns:

  • (String)


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

def to_rbs
  ((@items.length > 1 ? '(' : '') +
   @items.map(&:to_rbs).join(' | ') +
   (@items.length > 1 ? ')' : ''))
end

#to_sObject



154
155
156
# File 'lib/solargraph/complex_type.rb', line 154

def to_s
  map(&:tag).join(', ')
end

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

Parameters:

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

Yield Parameters:

Yield Returns:

Returns:



211
212
213
214
# File 'lib/solargraph/complex_type.rb', line 211

def transform(new_name = nil, &transform_type)
  raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
  ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) })
end