irb 1.8.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,426 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'methods'
4
+
5
+ module IRB
6
+ module TypeCompletion
7
+ module Types
8
+ OBJECT_TO_TYPE_SAMPLE_SIZE = 50
9
+
10
+ singleton_class.attr_reader :rbs_builder, :rbs_load_error
11
+
12
+ def self.preload_in_thread
13
+ return if @preload_started
14
+
15
+ @preload_started = true
16
+ Thread.new do
17
+ load_rbs_builder
18
+ end
19
+ end
20
+
21
+ def self.load_rbs_builder
22
+ require 'rbs'
23
+ require 'rbs/cli'
24
+ loader = RBS::CLI::LibraryOptions.new.loader
25
+ loader.add path: Pathname('sig')
26
+ @rbs_builder = RBS::DefinitionBuilder.new env: RBS::Environment.from_loader(loader).resolve_type_names
27
+ rescue LoadError, StandardError => e
28
+ @rbs_load_error = e
29
+ nil
30
+ end
31
+
32
+ def self.class_name_of(klass)
33
+ klass = klass.superclass if klass.singleton_class?
34
+ Methods::MODULE_NAME_METHOD.bind_call klass
35
+ end
36
+
37
+ def self.rbs_search_method(klass, method_name, singleton)
38
+ klass.ancestors.each do |ancestor|
39
+ name = class_name_of ancestor
40
+ next unless name && rbs_builder
41
+ type_name = RBS::TypeName(name).absolute!
42
+ definition = (singleton ? rbs_builder.build_singleton(type_name) : rbs_builder.build_instance(type_name)) rescue nil
43
+ method = definition.methods[method_name] if definition
44
+ return method if method
45
+ end
46
+ nil
47
+ end
48
+
49
+ def self.method_return_type(type, method_name)
50
+ receivers = type.types.map do |t|
51
+ case t
52
+ in SingletonType
53
+ [t, t.module_or_class, true]
54
+ in InstanceType
55
+ [t, t.klass, false]
56
+ end
57
+ end
58
+ types = receivers.flat_map do |receiver_type, klass, singleton|
59
+ method = rbs_search_method klass, method_name, singleton
60
+ next [] unless method
61
+ method.method_types.map do |method|
62
+ from_rbs_type(method.type.return_type, receiver_type, {})
63
+ end
64
+ end
65
+ UnionType[*types]
66
+ end
67
+
68
+ def self.rbs_methods(type, method_name, args_types, kwargs_type, has_block)
69
+ return [] unless rbs_builder
70
+
71
+ receivers = type.types.map do |t|
72
+ case t
73
+ in SingletonType
74
+ [t, t.module_or_class, true]
75
+ in InstanceType
76
+ [t, t.klass, false]
77
+ end
78
+ end
79
+ has_splat = args_types.include?(nil)
80
+ methods_with_score = receivers.flat_map do |receiver_type, klass, singleton|
81
+ method = rbs_search_method klass, method_name, singleton
82
+ next [] unless method
83
+ method.method_types.map do |method_type|
84
+ score = 0
85
+ score += 2 if !!method_type.block == has_block
86
+ reqs = method_type.type.required_positionals
87
+ opts = method_type.type.optional_positionals
88
+ rest = method_type.type.rest_positionals
89
+ trailings = method_type.type.trailing_positionals
90
+ keyreqs = method_type.type.required_keywords
91
+ keyopts = method_type.type.optional_keywords
92
+ keyrest = method_type.type.rest_keywords
93
+ args = args_types
94
+ if kwargs_type&.any? && keyreqs.empty? && keyopts.empty? && keyrest.nil?
95
+ kw_value_type = UnionType[*kwargs_type.values]
96
+ args += [InstanceType.new(Hash, K: SYMBOL, V: kw_value_type)]
97
+ end
98
+ if has_splat
99
+ score += 1 if args.count(&:itself) <= reqs.size + opts.size + trailings.size
100
+ elsif reqs.size + trailings.size <= args.size && (rest || args.size <= reqs.size + opts.size + trailings.size)
101
+ score += 2
102
+ centers = args[reqs.size...-trailings.size]
103
+ given = args.first(reqs.size) + centers.take(opts.size) + args.last(trailings.size)
104
+ expected = (reqs + opts.take(centers.size) + trailings).map(&:type)
105
+ if rest
106
+ given << UnionType[*centers.drop(opts.size)]
107
+ expected << rest.type
108
+ end
109
+ if given.any?
110
+ score += given.zip(expected).count do |t, e|
111
+ e = from_rbs_type e, receiver_type
112
+ intersect?(t, e) || (intersect?(STRING, e) && t.methods.include?(:to_str)) || (intersect?(INTEGER, e) && t.methods.include?(:to_int)) || (intersect?(ARRAY, e) && t.methods.include?(:to_ary))
113
+ end.fdiv(given.size)
114
+ end
115
+ end
116
+ [[method_type, given || [], expected || []], score]
117
+ end
118
+ end
119
+ max_score = methods_with_score.map(&:last).max
120
+ methods_with_score.select { _2 == max_score }.map(&:first)
121
+ end
122
+
123
+ def self.intersect?(a, b)
124
+ atypes = a.types.group_by(&:class)
125
+ btypes = b.types.group_by(&:class)
126
+ if atypes[SingletonType] && btypes[SingletonType]
127
+ aa, bb = [atypes, btypes].map {|types| types[SingletonType].map(&:module_or_class) }
128
+ return true if (aa & bb).any?
129
+ end
130
+
131
+ aa, bb = [atypes, btypes].map {|types| (types[InstanceType] || []).map(&:klass) }
132
+ (aa.flat_map(&:ancestors) & bb).any?
133
+ end
134
+
135
+ def self.type_from_object(object)
136
+ case object
137
+ when Array
138
+ InstanceType.new Array, { Elem: union_type_from_objects(object) }
139
+ when Hash
140
+ InstanceType.new Hash, { K: union_type_from_objects(object.keys), V: union_type_from_objects(object.values) }
141
+ when Module
142
+ SingletonType.new object
143
+ else
144
+ klass = Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(object) rescue Methods::OBJECT_CLASS_METHOD.bind_call(object)
145
+ InstanceType.new klass
146
+ end
147
+ end
148
+
149
+ def self.union_type_from_objects(objects)
150
+ values = objects.size <= OBJECT_TO_TYPE_SAMPLE_SIZE ? objects : objects.sample(OBJECT_TO_TYPE_SAMPLE_SIZE)
151
+ klasses = values.map { Methods::OBJECT_CLASS_METHOD.bind_call(_1) }
152
+ UnionType[*klasses.uniq.map { InstanceType.new _1 }]
153
+ end
154
+
155
+ class SingletonType
156
+ attr_reader :module_or_class
157
+ def initialize(module_or_class)
158
+ @module_or_class = module_or_class
159
+ end
160
+ def transform() = yield(self)
161
+ def methods() = @module_or_class.methods
162
+ def all_methods() = methods | Kernel.methods
163
+ def constants() = @module_or_class.constants
164
+ def types() = [self]
165
+ def nillable?() = false
166
+ def nonnillable() = self
167
+ def inspect
168
+ "#{module_or_class}.itself"
169
+ end
170
+ end
171
+
172
+ class InstanceType
173
+ attr_reader :klass, :params
174
+ def initialize(klass, params = {})
175
+ @klass = klass
176
+ @params = params
177
+ end
178
+ def transform() = yield(self)
179
+ def methods() = rbs_methods.select { _2.public? }.keys | @klass.instance_methods
180
+ def all_methods() = rbs_methods.keys | @klass.instance_methods | @klass.private_instance_methods
181
+ def constants() = []
182
+ def types() = [self]
183
+ def nillable?() = (@klass == NilClass)
184
+ def nonnillable() = self
185
+ def rbs_methods
186
+ name = Types.class_name_of(@klass)
187
+ return {} unless name && Types.rbs_builder
188
+
189
+ type_name = RBS::TypeName(name).absolute!
190
+ Types.rbs_builder.build_instance(type_name).methods rescue {}
191
+ end
192
+ def inspect
193
+ if params.empty?
194
+ inspect_without_params
195
+ else
196
+ params_string = "[#{params.map { "#{_1}: #{_2.inspect}" }.join(', ')}]"
197
+ "#{inspect_without_params}#{params_string}"
198
+ end
199
+ end
200
+ def inspect_without_params
201
+ if klass == NilClass
202
+ 'nil'
203
+ elsif klass == TrueClass
204
+ 'true'
205
+ elsif klass == FalseClass
206
+ 'false'
207
+ else
208
+ klass.singleton_class? ? klass.superclass.to_s : klass.to_s
209
+ end
210
+ end
211
+ end
212
+
213
+ NIL = InstanceType.new NilClass
214
+ OBJECT = InstanceType.new Object
215
+ TRUE = InstanceType.new TrueClass
216
+ FALSE = InstanceType.new FalseClass
217
+ SYMBOL = InstanceType.new Symbol
218
+ STRING = InstanceType.new String
219
+ INTEGER = InstanceType.new Integer
220
+ RANGE = InstanceType.new Range
221
+ REGEXP = InstanceType.new Regexp
222
+ FLOAT = InstanceType.new Float
223
+ RATIONAL = InstanceType.new Rational
224
+ COMPLEX = InstanceType.new Complex
225
+ ARRAY = InstanceType.new Array
226
+ HASH = InstanceType.new Hash
227
+ CLASS = InstanceType.new Class
228
+ MODULE = InstanceType.new Module
229
+ PROC = InstanceType.new Proc
230
+
231
+ class UnionType
232
+ attr_reader :types
233
+
234
+ def initialize(*types)
235
+ @types = []
236
+ singletons = []
237
+ instances = {}
238
+ collect = -> type do
239
+ case type
240
+ in UnionType
241
+ type.types.each(&collect)
242
+ in InstanceType
243
+ params = (instances[type.klass] ||= {})
244
+ type.params.each do |k, v|
245
+ (params[k] ||= []) << v
246
+ end
247
+ in SingletonType
248
+ singletons << type
249
+ end
250
+ end
251
+ types.each(&collect)
252
+ @types = singletons.uniq + instances.map do |klass, params|
253
+ InstanceType.new(klass, params.transform_values { |v| UnionType[*v] })
254
+ end
255
+ end
256
+
257
+ def transform(&block)
258
+ UnionType[*types.map(&block)]
259
+ end
260
+
261
+ def nillable?
262
+ types.any?(&:nillable?)
263
+ end
264
+
265
+ def nonnillable
266
+ UnionType[*types.reject { _1.is_a?(InstanceType) && _1.klass == NilClass }]
267
+ end
268
+
269
+ def self.[](*types)
270
+ type = new(*types)
271
+ if type.types.empty?
272
+ OBJECT
273
+ elsif type.types.size == 1
274
+ type.types.first
275
+ else
276
+ type
277
+ end
278
+ end
279
+
280
+ def methods() = @types.flat_map(&:methods).uniq
281
+ def all_methods() = @types.flat_map(&:all_methods).uniq
282
+ def constants() = @types.flat_map(&:constants).uniq
283
+ def inspect() = @types.map(&:inspect).join(' | ')
284
+ end
285
+
286
+ BOOLEAN = UnionType[TRUE, FALSE]
287
+
288
+ def self.array_of(*types)
289
+ type = types.size >= 2 ? UnionType[*types] : types.first || OBJECT
290
+ InstanceType.new Array, Elem: type
291
+ end
292
+
293
+ def self.from_rbs_type(return_type, self_type, extra_vars = {})
294
+ case return_type
295
+ when RBS::Types::Bases::Self
296
+ self_type
297
+ when RBS::Types::Bases::Bottom, RBS::Types::Bases::Nil
298
+ NIL
299
+ when RBS::Types::Bases::Any, RBS::Types::Bases::Void
300
+ OBJECT
301
+ when RBS::Types::Bases::Class
302
+ self_type.transform do |type|
303
+ case type
304
+ in SingletonType
305
+ InstanceType.new(self_type.module_or_class.is_a?(Class) ? Class : Module)
306
+ in InstanceType
307
+ SingletonType.new type.klass
308
+ end
309
+ end
310
+ UnionType[*types]
311
+ when RBS::Types::Bases::Bool
312
+ BOOLEAN
313
+ when RBS::Types::Bases::Instance
314
+ self_type.transform do |type|
315
+ if type.is_a?(SingletonType) && type.module_or_class.is_a?(Class)
316
+ InstanceType.new type.module_or_class
317
+ else
318
+ OBJECT
319
+ end
320
+ end
321
+ when RBS::Types::Union
322
+ UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
323
+ when RBS::Types::Proc
324
+ PROC
325
+ when RBS::Types::Tuple
326
+ elem = UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
327
+ InstanceType.new Array, Elem: elem
328
+ when RBS::Types::Record
329
+ InstanceType.new Hash, K: SYMBOL, V: OBJECT
330
+ when RBS::Types::Literal
331
+ InstanceType.new return_type.literal.class
332
+ when RBS::Types::Variable
333
+ if extra_vars.key? return_type.name
334
+ extra_vars[return_type.name]
335
+ elsif self_type.is_a? InstanceType
336
+ self_type.params[return_type.name] || OBJECT
337
+ elsif self_type.is_a? UnionType
338
+ types = self_type.types.filter_map do |t|
339
+ t.params[return_type.name] if t.is_a? InstanceType
340
+ end
341
+ UnionType[*types]
342
+ else
343
+ OBJECT
344
+ end
345
+ when RBS::Types::Optional
346
+ UnionType[from_rbs_type(return_type.type, self_type, extra_vars), NIL]
347
+ when RBS::Types::Alias
348
+ case return_type.name.name
349
+ when :int
350
+ INTEGER
351
+ when :boolish
352
+ BOOLEAN
353
+ when :string
354
+ STRING
355
+ else
356
+ # TODO: ???
357
+ OBJECT
358
+ end
359
+ when RBS::Types::Interface
360
+ # unimplemented
361
+ OBJECT
362
+ when RBS::Types::ClassInstance
363
+ klass = return_type.name.to_namespace.path.reduce(Object) { _1.const_get _2 }
364
+ if return_type.args
365
+ args = return_type.args.map { from_rbs_type _1, self_type, extra_vars }
366
+ names = rbs_builder.build_singleton(return_type.name).type_params
367
+ params = names.map.with_index { [_1, args[_2] || OBJECT] }.to_h
368
+ end
369
+ InstanceType.new klass, params || {}
370
+ end
371
+ end
372
+
373
+ def self.method_return_bottom?(method)
374
+ method.type.return_type.is_a? RBS::Types::Bases::Bottom
375
+ end
376
+
377
+ def self.match_free_variables(vars, types, values)
378
+ accumulator = {}
379
+ types.zip values do |t, v|
380
+ _match_free_variable(vars, t, v, accumulator) if v
381
+ end
382
+ accumulator.transform_values { UnionType[*_1] }
383
+ end
384
+
385
+ def self._match_free_variable(vars, rbs_type, value, accumulator)
386
+ case [rbs_type, value]
387
+ in [RBS::Types::Variable,]
388
+ (accumulator[rbs_type.name] ||= []) << value if vars.include? rbs_type.name
389
+ in [RBS::Types::ClassInstance, InstanceType]
390
+ names = rbs_builder.build_singleton(rbs_type.name).type_params
391
+ names.zip(rbs_type.args).each do |name, arg|
392
+ v = value.params[name]
393
+ _match_free_variable vars, arg, v, accumulator if v
394
+ end
395
+ in [RBS::Types::Tuple, InstanceType] if value.klass == Array
396
+ v = value.params[:Elem]
397
+ rbs_type.types.each do |t|
398
+ _match_free_variable vars, t, v, accumulator
399
+ end
400
+ in [RBS::Types::Record, InstanceType] if value.klass == Hash
401
+ # TODO
402
+ in [RBS::Types::Interface,]
403
+ definition = rbs_builder.build_interface rbs_type.name
404
+ convert = {}
405
+ definition.type_params.zip(rbs_type.args).each do |from, arg|
406
+ convert[from] = arg.name if arg.is_a? RBS::Types::Variable
407
+ end
408
+ return if convert.empty?
409
+ ac = {}
410
+ definition.methods.each do |method_name, method|
411
+ return_type = method_return_type value, method_name
412
+ method.defs.each do |method_def|
413
+ interface_return_type = method_def.type.type.return_type
414
+ _match_free_variable convert, interface_return_type, return_type, ac
415
+ end
416
+ end
417
+ convert.each do |from, to|
418
+ values = ac[from]
419
+ (accumulator[to] ||= []).concat values if values
420
+ end
421
+ else
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end
data/lib/irb/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
- VERSION = "1.8.1"
8
+ VERSION = "1.9.0"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2023-09-04"
10
+ @LAST_UPDATE_DATE = "2023-11-11"
11
11
  end