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)


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

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.



283
284
285
# File 'lib/solargraph/complex_type.rb', line 283

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>]

Examples:

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

Parameters:

  • strings (Array<String>)

    The type definitions to parse

Returns:



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
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/solargraph/complex_type.rb', line 327

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
    # @param char [String]

    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:



412
413
414
415
416
417
# File 'lib/solargraph/complex_type.rb', line 412

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:



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

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

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

Yield Parameters:

Returns:

  • (Boolean)


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

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

#all_paramsArray<ComplexType>

Returns:



256
257
258
# File 'lib/solargraph/complex_type.rb', line 256

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)


273
274
275
# File 'lib/solargraph/complex_type.rb', line 273

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

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

Yield Parameters:

Yield Returns:

  • (Boolean)

Returns:

  • (Boolean)


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

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

#descString

Returns:

  • (String)


193
194
195
# File 'lib/solargraph/complex_type.rb', line 193

def desc
  rooted_tags
end

#downcast_to_literal_if_possibleComplexType

Returns:



188
189
190
# File 'lib/solargraph/complex_type.rb', line 188

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)


237
238
239
240
241
# File 'lib/solargraph/complex_type.rb', line 237

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

#generic?Boolean

Returns:

  • (Boolean)


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

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

#lengthInteger

Returns:

  • (Integer)


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

def length
  @items.length
end

#literal?Boolean

Returns:

  • (Boolean)


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

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)


145
146
147
148
# File 'lib/solargraph/complex_type.rb', line 145

def namespace
  # cache this attr for high frequency call

  @namespace ||= method_missing(:namespace).to_s
end

#namespacesArray<String>

Returns:

  • (Array<String>)


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

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

#nullable?Boolean

Returns:

  • (Boolean)


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

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

#qualify(api_map, *gates) ⇒ ComplexType

Parameters:

  • api_map (ApiMap)
  • context (String)

Returns:



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

def qualify api_map, *gates
  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, *gates
  end
  ComplexType.new(types).reduce_object
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<ComplexType>, nil) (defaults to: nil)
  • rooted (Boolean, nil)
  • new_subtypes (Array<ComplexType>, nil) (defaults to: nil)

Returns:

  • (self)


114
115
116
117
118
119
120
121
# File 'lib/solargraph/complex_type.rb', line 114

def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
  ComplexType.new(map do |ut|
                    ut.recreate(new_name: new_name,
                                make_rooted: make_rooted,
                                new_key_types: new_key_types,
                                new_subtypes: new_subtypes)
                  end)
end

#reduce_class_typeComplexType

Returns:



261
262
263
264
265
266
267
268
269
# File 'lib/solargraph/complex_type.rb', line 261

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

    type.all_params
  end
  ComplexType.new(new_items)
end

#resolve_generics(definitions, context_type) ⇒ ComplexType

Parameters:

Returns:



246
247
248
249
# File 'lib/solargraph/complex_type.rb', line 246

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)


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

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)


279
280
281
# File 'lib/solargraph/complex_type.rb', line 279

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

#rooted_tagsString

Returns:

  • (String)


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

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

#select(&block) ⇒ Array<UniqueType>

Returns:



140
141
142
# File 'lib/solargraph/complex_type.rb', line 140

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)


214
215
216
# File 'lib/solargraph/complex_type.rb', line 214

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

#simple_tagsString

Returns:

  • (String)


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

def simple_tags
  simplify_literals.tags
end

#simplify_literalsself

Returns:

  • (self)


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

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

#tagsString

Returns:

  • (String)


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

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

#to_aArray<UniqueType>

Returns:



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

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



169
170
171
# File 'lib/solargraph/complex_type.rb', line 169

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:



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

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