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.



267
268
269
# File 'lib/solargraph/complex_type.rb', line 267

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:



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
388
389
390
391
# File 'lib/solargraph/complex_type.rb', line 311

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:



395
396
397
398
399
400
# File 'lib/solargraph/complex_type.rb', line 395

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)


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

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

#all_paramsArray<ComplexType>

Returns:



241
242
243
# File 'lib/solargraph/complex_type.rb', line 241

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)


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

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

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

Yield Parameters:

Yield Returns:

  • (Boolean)

Returns:

  • (Boolean)


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

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)


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

def desc
  rooted_tags
end

#downcast_to_literal_if_possibleComplexType

Returns:



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

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)


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

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

#generic?Boolean

Returns:

  • (Boolean)


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

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)


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

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)


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

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:



246
247
248
249
250
251
252
253
# File 'lib/solargraph/complex_type.rb', line 246

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:



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

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)


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

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

#rooted_tagsString

Returns:

  • (String)


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

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)


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

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

#simple_tagsString

Returns:

  • (String)


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

def simple_tags
  simplify_literals.tags
end

#simplify_literalsself

Returns:

  • (self)


208
209
210
# File 'lib/solargraph/complex_type.rb', line 208

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

#tagsString

Returns:

  • (String)


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

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:



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

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