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, #tag

Instance Method Summary collapse

Methods included from TypeMethods

#==, #defined?, #duck_type?, #each_unique_type, #erase_generics, #fixed_parameters?, #hash_parameters?, #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

#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

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


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

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)


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/solargraph/complex_type/unique_type.rb', line 188

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:



158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/solargraph/complex_type/unique_type.rb', line 158

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

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:



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

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

#self_to(dst) ⇒ UniqueType

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

Parameters:

  • dst (String)

Returns:



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

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)


244
245
246
# File 'lib/solargraph/complex_type/unique_type.rb', line 244

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

#to_aArray<UniqueType>

Returns:



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

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)


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

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