rbs 0.15.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +8 -4
  3. data/CHANGELOG.md +29 -1
  4. data/Gemfile +1 -0
  5. data/README.md +1 -1
  6. data/Rakefile +19 -1
  7. data/Steepfile +0 -1
  8. data/bin/test_runner.rb +15 -1
  9. data/{stdlib/builtin → core}/array.rbs +1 -1
  10. data/{stdlib/builtin → core}/basic_object.rbs +0 -0
  11. data/{stdlib/builtin → core}/binding.rbs +0 -0
  12. data/{stdlib/builtin → core}/builtin.rbs +2 -2
  13. data/{stdlib/builtin → core}/class.rbs +0 -0
  14. data/{stdlib/builtin → core}/comparable.rbs +0 -0
  15. data/{stdlib/builtin → core}/complex.rbs +0 -0
  16. data/{stdlib/builtin → core}/constants.rbs +0 -0
  17. data/{stdlib/builtin → core}/data.rbs +0 -0
  18. data/{stdlib/builtin → core}/deprecated.rbs +0 -0
  19. data/{stdlib/builtin → core}/dir.rbs +1 -1
  20. data/{stdlib/builtin → core}/encoding.rbs +0 -0
  21. data/{stdlib/builtin → core}/enumerable.rbs +41 -40
  22. data/{stdlib/builtin → core}/enumerator.rbs +5 -5
  23. data/{stdlib/builtin → core}/errno.rbs +0 -0
  24. data/{stdlib/builtin → core}/errors.rbs +0 -0
  25. data/{stdlib/builtin → core}/exception.rbs +0 -0
  26. data/{stdlib/builtin → core}/false_class.rbs +0 -0
  27. data/{stdlib/builtin → core}/fiber.rbs +0 -0
  28. data/{stdlib/builtin → core}/fiber_error.rbs +0 -0
  29. data/{stdlib/builtin → core}/file.rbs +0 -4
  30. data/{stdlib/builtin → core}/file_test.rbs +0 -0
  31. data/{stdlib/builtin → core}/float.rbs +0 -0
  32. data/{stdlib/builtin → core}/gc.rbs +0 -0
  33. data/{stdlib/builtin → core}/hash.rbs +8 -8
  34. data/{stdlib/builtin → core}/integer.rbs +0 -0
  35. data/{stdlib/builtin → core}/io.rbs +1 -1
  36. data/{stdlib/builtin → core}/kernel.rbs +0 -0
  37. data/{stdlib/builtin → core}/marshal.rbs +0 -0
  38. data/{stdlib/builtin → core}/match_data.rbs +0 -0
  39. data/{stdlib/builtin → core}/math.rbs +0 -0
  40. data/{stdlib/builtin → core}/method.rbs +0 -0
  41. data/{stdlib/builtin → core}/module.rbs +0 -0
  42. data/{stdlib/builtin → core}/nil_class.rbs +0 -0
  43. data/{stdlib/builtin → core}/numeric.rbs +0 -0
  44. data/{stdlib/builtin → core}/object.rbs +0 -0
  45. data/core/object_space.rbs +98 -0
  46. data/{stdlib/builtin → core}/proc.rbs +0 -0
  47. data/{stdlib/builtin → core}/process.rbs +0 -0
  48. data/{stdlib/builtin → core}/random.rbs +0 -0
  49. data/{stdlib/builtin → core}/range.rbs +1 -1
  50. data/{stdlib/builtin → core}/rational.rbs +0 -0
  51. data/{stdlib/builtin → core}/rb_config.rbs +0 -0
  52. data/{stdlib/builtin → core}/regexp.rbs +0 -0
  53. data/{stdlib/builtin → core}/ruby_vm.rbs +0 -0
  54. data/{stdlib/builtin → core}/signal.rbs +0 -0
  55. data/{stdlib/builtin → core}/string.rbs +0 -0
  56. data/{stdlib/builtin → core}/string_io.rbs +0 -0
  57. data/{stdlib/builtin → core}/struct.rbs +1 -1
  58. data/{stdlib/builtin → core}/symbol.rbs +0 -0
  59. data/{stdlib/builtin → core}/thread.rbs +0 -0
  60. data/{stdlib/builtin → core}/thread_group.rbs +0 -0
  61. data/{stdlib/builtin → core}/time.rbs +0 -0
  62. data/{stdlib/builtin → core}/trace_point.rbs +0 -0
  63. data/{stdlib/builtin → core}/true_class.rbs +0 -0
  64. data/{stdlib/builtin → core}/unbound_method.rbs +0 -0
  65. data/{stdlib/builtin → core}/warning.rbs +0 -0
  66. data/docs/repo.md +125 -0
  67. data/lib/rbs.rb +1 -0
  68. data/lib/rbs/cli.rb +105 -103
  69. data/lib/rbs/definition.rb +9 -4
  70. data/lib/rbs/definition_builder.rb +48 -15
  71. data/lib/rbs/environment_loader.rb +79 -105
  72. data/lib/rbs/environment_walker.rb +70 -35
  73. data/lib/rbs/parser.rb +306 -301
  74. data/lib/rbs/parser.y +15 -8
  75. data/lib/rbs/prototype/rb.rb +150 -21
  76. data/lib/rbs/prototype/runtime.rb +1 -0
  77. data/lib/rbs/repository.rb +121 -0
  78. data/lib/rbs/test/hook.rb +25 -15
  79. data/lib/rbs/test/setup.rb +5 -3
  80. data/lib/rbs/test/type_check.rb +6 -1
  81. data/lib/rbs/type_name.rb +2 -1
  82. data/lib/rbs/vendorer.rb +38 -16
  83. data/lib/rbs/version.rb +1 -1
  84. data/sig/cli.rbs +58 -0
  85. data/sig/definition.rbs +8 -3
  86. data/sig/definition_builder.rbs +4 -1
  87. data/sig/environment_loader.rbs +92 -46
  88. data/sig/polyfill.rbs +42 -0
  89. data/sig/rbs.rbs +8 -0
  90. data/sig/repository.rbs +79 -0
  91. data/sig/vendorer.rbs +44 -0
  92. data/stdlib/abbrev/{abbrev.rbs → 0/abbrev.rbs} +0 -0
  93. data/stdlib/base64/{base64.rbs → 0/base64.rbs} +0 -0
  94. data/stdlib/benchmark/{benchmark.rbs → 0/benchmark.rbs} +0 -0
  95. data/stdlib/{bigdecimal/math → bigdecimal-math/0}/big_math.rbs +0 -0
  96. data/stdlib/bigdecimal/{big_decimal.rbs → 0/big_decimal.rbs} +0 -0
  97. data/stdlib/coverage/{coverage.rbs → 0/coverage.rbs} +0 -0
  98. data/stdlib/csv/{csv.rbs → 0/csv.rbs} +3 -3
  99. data/stdlib/date/{date.rbs → 0/date.rbs} +0 -0
  100. data/stdlib/date/{date_time.rbs → 0/date_time.rbs} +0 -0
  101. data/stdlib/dbm/0/dbm.rbs +277 -0
  102. data/stdlib/erb/{erb.rbs → 0/erb.rbs} +0 -0
  103. data/stdlib/fiber/{fiber.rbs → 0/fiber.rbs} +0 -0
  104. data/stdlib/find/{find.rbs → 0/find.rbs} +0 -0
  105. data/stdlib/forwardable/{forwardable.rbs → 0/forwardable.rbs} +0 -0
  106. data/stdlib/ipaddr/{ipaddr.rbs → 0/ipaddr.rbs} +0 -0
  107. data/stdlib/json/{json.rbs → 0/json.rbs} +0 -0
  108. data/stdlib/logger/{formatter.rbs → 0/formatter.rbs} +0 -0
  109. data/stdlib/logger/{log_device.rbs → 0/log_device.rbs} +0 -0
  110. data/stdlib/logger/{logger.rbs → 0/logger.rbs} +0 -0
  111. data/stdlib/logger/{period.rbs → 0/period.rbs} +0 -0
  112. data/stdlib/logger/{severity.rbs → 0/severity.rbs} +0 -0
  113. data/stdlib/monitor/0/monitor.rbs +119 -0
  114. data/stdlib/mutex_m/{mutex_m.rbs → 0/mutex_m.rbs} +0 -0
  115. data/stdlib/pathname/{pathname.rbs → 0/pathname.rbs} +0 -0
  116. data/stdlib/prime/{integer-extension.rbs → 0/integer-extension.rbs} +0 -0
  117. data/stdlib/prime/{prime.rbs → 0/prime.rbs} +1 -1
  118. data/stdlib/pstore/{pstore.rbs → 0/pstore.rbs} +0 -0
  119. data/stdlib/pty/{pty.rbs → 0/pty.rbs} +0 -0
  120. data/stdlib/securerandom/{securerandom.rbs → 0/securerandom.rbs} +0 -0
  121. data/stdlib/set/{set.rbs → 0/set.rbs} +10 -10
  122. data/stdlib/singleton/0/singleton.rbs +111 -0
  123. data/stdlib/tmpdir/{tmpdir.rbs → 0/tmpdir.rbs} +0 -0
  124. data/stdlib/tsort/0/cyclic.rbs +4 -0
  125. data/stdlib/tsort/0/interfaces.rbs +19 -0
  126. data/stdlib/tsort/0/tsort.rbs +363 -0
  127. data/stdlib/uri/{file.rbs → 0/file.rbs} +0 -0
  128. data/stdlib/uri/{generic.rbs → 0/generic.rbs} +0 -0
  129. data/stdlib/uri/{http.rbs → 0/http.rbs} +0 -0
  130. data/stdlib/uri/{https.rbs → 0/https.rbs} +0 -0
  131. data/stdlib/uri/{ldap.rbs → 0/ldap.rbs} +0 -0
  132. data/stdlib/uri/{ldaps.rbs → 0/ldaps.rbs} +0 -0
  133. data/stdlib/yaml/0/dbm.rbs +221 -0
  134. data/stdlib/yaml/0/store.rbs +53 -0
  135. data/stdlib/zlib/{zlib.rbs → 0/zlib.rbs} +0 -0
  136. data/steep/Gemfile.lock +9 -9
  137. metadata +108 -94
@@ -1168,14 +1168,21 @@ end
1168
1168
 
1169
1169
  def new_token(type, value = input.matched)
1170
1170
  charpos = charpos(input)
1171
- start_index = charpos - input.matched.size
1172
- end_index = charpos
1171
+ matched = input.matched
1173
1172
 
1174
- location = RBS::Location.new(buffer: buffer,
1175
- start_pos: start_index,
1176
- end_pos: end_index)
1173
+ if matched
1174
+ start_index = charpos - matched.size
1175
+ end_index = charpos
1177
1176
 
1178
- [type, LocatedValue.new(location: location, value: value)]
1177
+ location = RBS::Location.new(buffer: buffer,
1178
+ start_pos: start_index,
1179
+ end_pos: end_index)
1180
+
1181
+ [type, LocatedValue.new(location: location, value: value)]
1182
+ else
1183
+ # scanner hasn't matched yet
1184
+ [false, nil]
1185
+ end
1179
1186
  end
1180
1187
 
1181
1188
  def charpos(scanner)
@@ -1294,10 +1301,10 @@ def next_token
1294
1301
  return [:"type_#{type}", nil]
1295
1302
  end
1296
1303
 
1297
- return if @eof
1304
+ return new_token(false, '') if @eof
1298
1305
 
1299
1306
  while true
1300
- return if input.eos?
1307
+ return new_token(false, '') if input.eos?
1301
1308
 
1302
1309
  case
1303
1310
  when input.scan(/\s+/)
@@ -1,6 +1,22 @@
1
1
  module RBS
2
2
  module Prototype
3
3
  class RB
4
+ Context = Struct.new(:module_function, :singleton, :namespace, keyword_init: true) do
5
+ def self.initial(namespace: Namespace.root)
6
+ self.new(module_function: false, singleton: false, namespace: namespace)
7
+ end
8
+
9
+ def method_kind
10
+ if singleton
11
+ :singleton
12
+ elsif module_function
13
+ :singleton_instance
14
+ else
15
+ :instance
16
+ end
17
+ end
18
+ end
19
+
4
20
  attr_reader :source_decls
5
21
  attr_reader :toplevel_members
6
22
 
@@ -52,10 +68,10 @@ module RBS
52
68
  end
53
69
  end
54
70
 
55
- process RubyVM::AbstractSyntaxTree.parse(string), decls: source_decls, comments: comments, singleton: false
71
+ process RubyVM::AbstractSyntaxTree.parse(string), decls: source_decls, comments: comments, context: Context.initial
56
72
  end
57
73
 
58
- def process(node, decls:, comments:, singleton:)
74
+ def process(node, decls:, comments:, context:)
59
75
  case node.type
60
76
  when :CLASS
61
77
  class_name, super_class, *class_body = node.children
@@ -71,9 +87,11 @@ module RBS
71
87
 
72
88
  decls.push kls
73
89
 
90
+ new_ctx = Context.initial(namespace: context.namespace + kls.name.to_namespace)
74
91
  each_node class_body do |child|
75
- process child, decls: kls.members, comments: comments, singleton: false
92
+ process child, decls: kls.members, comments: comments, context: new_ctx
76
93
  end
94
+ remove_unnecessary_accessibility_methods! kls.members
77
95
 
78
96
  when :MODULE
79
97
  module_name, *module_body = node.children
@@ -90,9 +108,11 @@ module RBS
90
108
 
91
109
  decls.push mod
92
110
 
111
+ new_ctx = Context.initial(namespace: context.namespace + mod.name.to_namespace)
93
112
  each_node module_body do |child|
94
- process child, decls: mod.members, comments: comments, singleton: false
113
+ process child, decls: mod.members, comments: comments, context: new_ctx
95
114
  end
115
+ remove_unnecessary_accessibility_methods! mod.members
96
116
 
97
117
  when :SCLASS
98
118
  this, body = node.children
@@ -101,14 +121,13 @@ module RBS
101
121
  RBS.logger.warn "`class <<` syntax with not-self may be compiled to incorrect code: #{this}"
102
122
  end
103
123
 
104
- each_child(body) do |child|
105
- process child, decls: decls, comments: comments, singleton: true
106
- end
124
+ ctx = Context.initial.tap { |ctx| ctx.singleton = true }
125
+ process_children(body, decls: decls, comments: comments, context: ctx)
107
126
 
108
127
  when :DEFN, :DEFS
109
128
  if node.type == :DEFN
110
129
  def_name, def_body = node.children
111
- kind = singleton ? :singleton : :instance
130
+ kind = context.method_kind
112
131
  else
113
132
  _, def_name, def_body = node.children
114
133
  kind = :singleton
@@ -140,14 +159,14 @@ module RBS
140
159
  member = AST::Members::Alias.new(
141
160
  new_name: new_name,
142
161
  old_name: old_name,
143
- kind: singleton ? :singleton : :instance,
162
+ kind: context.singleton ? :singleton : :instance,
144
163
  annotations: [],
145
164
  location: nil,
146
165
  comment: comments[node.first_lineno - 1],
147
166
  )
148
167
  decls.push member unless decls.include?(member)
149
168
 
150
- when :FCALL
169
+ when :FCALL, :VCALL
151
170
  # Inside method definition cannot reach here.
152
171
  args = node.children[1]&.children || []
153
172
 
@@ -166,7 +185,7 @@ module RBS
166
185
  end
167
186
  when :extend
168
187
  args.each do |arg|
169
- if (name = const_to_name(arg))
188
+ if (name = const_to_name(arg, context: context))
170
189
  decls << AST::Members::Extend.new(
171
190
  name: name,
172
191
  args: [],
@@ -220,16 +239,61 @@ module RBS
220
239
  decls << AST::Members::Alias.new(
221
240
  new_name: new_name,
222
241
  old_name: old_name,
223
- kind: singleton ? :singleton : :instance,
242
+ kind: context.singleton ? :singleton : :instance,
224
243
  annotations: [],
225
244
  location: nil,
226
245
  comment: comments[node.first_lineno - 1],
227
246
  )
228
247
  end
248
+ when :module_function
249
+ if args.empty?
250
+ context.module_function = true
251
+ else
252
+ module_func_context = context.dup.tap { |ctx| ctx.module_function = true }
253
+ args.each do |arg|
254
+ if arg && (name = literal_to_symbol(arg))
255
+ if i = find_def_index_by_name(decls, name)
256
+ decls[i] = decls[i].update(kind: :singleton_instance)
257
+ end
258
+ elsif arg
259
+ process arg, decls: decls, comments: comments, context: module_func_context
260
+ end
261
+ end
262
+ end
263
+ when :public, :private
264
+ accessibility = __send__(node.children[0])
265
+ if args.empty?
266
+ decls << accessibility
267
+ else
268
+ args.each do |arg|
269
+ if arg && (name = literal_to_symbol(arg))
270
+ if i = find_def_index_by_name(decls, name)
271
+ current = current_accessibility(decls, i)
272
+ if current != accessibility
273
+ decls.insert(i + 1, current)
274
+ decls.insert(i, accessibility)
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ # For `private def foo` syntax
281
+ current = current_accessibility(decls)
282
+ decls << accessibility
283
+ process_children(node, decls: decls, comments: comments, context: context)
284
+ decls << current
285
+ end
286
+ else
287
+ process_children(node, decls: decls, comments: comments, context: context)
229
288
  end
230
289
 
231
- each_child node do |child|
232
- process child, decls: decls, comments: comments, singleton: singleton
290
+ when :ITER
291
+ method_name = node.children.first.children.first
292
+ case method_name
293
+ when :refine
294
+ # ignore
295
+ else
296
+ process_children(node, decls: decls, comments: comments, context: context)
233
297
  end
234
298
 
235
299
  when :CDECL
@@ -248,13 +312,17 @@ module RBS
248
312
  )
249
313
 
250
314
  else
251
- each_child node do |child|
252
- process child, decls: decls, comments: comments, singleton: singleton
253
- end
315
+ process_children(node, decls: decls, comments: comments, context: context)
316
+ end
317
+ end
318
+
319
+ def process_children(node, decls:, comments:, context:)
320
+ each_child node do |child|
321
+ process child, decls: decls, comments: comments, context: context
254
322
  end
255
323
  end
256
324
 
257
- def const_to_name(node)
325
+ def const_to_name(node, context: nil)
258
326
  case node&.type
259
327
  when :CONST
260
328
  TypeName.new(name: node.children[0], namespace: Namespace.empty)
@@ -268,6 +336,8 @@ module RBS
268
336
  TypeName.new(name: node.children[1], namespace: namespace)
269
337
  when :COLON3
270
338
  TypeName.new(name: node.children[0], namespace: Namespace.root)
339
+ when :SELF
340
+ context&.then { |c| c.namespace.to_type_name }
271
341
  end
272
342
  end
273
343
 
@@ -411,7 +481,8 @@ module RBS
411
481
  when :HASH
412
482
  list = node.children[0]
413
483
  if list
414
- children = list.children.compact
484
+ children = list.children
485
+ children.pop
415
486
  else
416
487
  children = []
417
488
  end
@@ -419,8 +490,13 @@ module RBS
419
490
  key_types = []
420
491
  value_types = []
421
492
  children.each_slice(2) do |k, v|
422
- key_types << literal_to_type(k)
423
- value_types << literal_to_type(v)
493
+ if k
494
+ key_types << literal_to_type(k)
495
+ value_types << literal_to_type(v)
496
+ else
497
+ key_types << untyped
498
+ value_types << untyped
499
+ end
424
500
  end
425
501
 
426
502
  if !key_types.empty? && key_types.all? { |t| t.is_a?(Types::Literal) }
@@ -580,6 +656,59 @@ module RBS
580
656
  def untyped
581
657
  @untyped ||= Types::Bases::Any.new(location: nil)
582
658
  end
659
+
660
+ def private
661
+ @private ||= AST::Members::Private.new(location: nil)
662
+ end
663
+
664
+ def public
665
+ @public ||= AST::Members::Public.new(location: nil)
666
+ end
667
+
668
+ def current_accessibility(decls, index = decls.size)
669
+ idx = decls.slice(0, index).rindex { |decl| decl == private || decl == public }
670
+ (idx && decls[idx]) || public
671
+ end
672
+
673
+ def remove_unnecessary_accessibility_methods!(decls)
674
+ current = public
675
+ idx = 0
676
+
677
+ loop do
678
+ decl = decls[idx] or break
679
+ if current == decl
680
+ decls.delete_at(idx)
681
+ next
682
+ end
683
+
684
+ if 0 < idx && is_accessibility?(decls[idx - 1]) && is_accessibility?(decl)
685
+ decls.delete_at(idx - 1)
686
+ idx -= 1
687
+ current = current_accessibility(decls, idx)
688
+ next
689
+ end
690
+
691
+ current = decl if is_accessibility?(decl)
692
+ idx += 1
693
+ end
694
+
695
+ decls.pop while decls.last && is_accessibility?(decls.last)
696
+ end
697
+
698
+ def is_accessibility?(decl)
699
+ decl == public || decl == private
700
+ end
701
+
702
+ def find_def_index_by_name(decls, name)
703
+ decls.find_index do |decl|
704
+ case decl
705
+ when AST::Members::MethodDefinition, AST::Members::AttrReader
706
+ decl.name == name
707
+ when AST::Members::AttrWriter
708
+ decl.name == :"#{name}="
709
+ end
710
+ end
711
+ end
583
712
  end
584
713
  end
585
714
  end
@@ -94,6 +94,7 @@ module RBS
94
94
  optional_positionals << Types::Function::Param.new(name: name, type: untyped)
95
95
  when :rest
96
96
  requireds = trailing_positionals
97
+ name = nil if name == :* # For `def f(...) end` syntax
97
98
  rest = Types::Function::Param.new(name: name, type: untyped)
98
99
  when :keyreq
99
100
  required_keywords[name] = Types::Function::Param.new(name: nil, type: untyped)
@@ -0,0 +1,121 @@
1
+ module RBS
2
+ class Repository
3
+ DEFAULT_STDLIB_ROOT = Pathname(_ = __dir__) + "../../stdlib"
4
+
5
+ class GemRBS
6
+ attr_reader :name
7
+ attr_reader :paths
8
+
9
+ def initialize(name:)
10
+ @name = name
11
+ @paths = []
12
+ @versions = nil
13
+ end
14
+
15
+ def versions
16
+ load! unless @versions
17
+ @versions or raise
18
+ end
19
+
20
+ def load!
21
+ @versions = {}
22
+ versions = @versions or raise
23
+
24
+ paths.each do |gem_path|
25
+ gem_path.each_child(false) do |child|
26
+ next unless Gem::Version.correct?(child.to_s)
27
+
28
+ if version = Gem::Version.create(child.to_s)
29
+ unless version.prerelease?
30
+ path = gem_path + child
31
+
32
+ if prev = versions[version]
33
+ RBS.logger.info { "Overwriting gem RBS in repository: gem=#{name}, prev_path=#{prev.path}, new_path=#{path}" }
34
+ end
35
+
36
+ versions[version] = VersionPath.new(gem: self, version: version, path: path)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def version_names
44
+ versions.keys.sort_by(&:version)
45
+ end
46
+
47
+ def oldest_version
48
+ oldest = version_names.first or raise
49
+ versions[oldest] or raise
50
+ end
51
+
52
+ def latest_version
53
+ latest = version_names.last or raise
54
+ versions[latest] or raise
55
+ end
56
+
57
+ def find_best_version(version)
58
+ return latest_version unless version
59
+
60
+ if v = version_names.reverse.bsearch {|v| v <= version ? true : false }
61
+ versions[v]
62
+ else
63
+ oldest_version
64
+ end
65
+ end
66
+
67
+ def empty?
68
+ versions.empty?
69
+ end
70
+ end
71
+
72
+ VersionPath = _ = Struct.new(:gem, :version, :path, keyword_init: true)
73
+
74
+ attr_reader :dirs
75
+ attr_reader :gems
76
+
77
+ def initialize(no_stdlib: false)
78
+ @dirs = []
79
+ @gems = {}
80
+
81
+ unless no_stdlib
82
+ add(DEFAULT_STDLIB_ROOT)
83
+ end
84
+ end
85
+
86
+ def self.default
87
+ new().tap do |repo|
88
+ repo.add(DEFAULT_STDLIB_ROOT)
89
+ end
90
+ end
91
+
92
+ def add(dir)
93
+ dirs << dir
94
+
95
+ dir.each_child(false) do |child|
96
+ gem_name = child.to_s
97
+ gem_rbs = (gems[gem_name] ||= GemRBS.new(name: gem_name))
98
+ gem_rbs.paths << dir + child
99
+ end
100
+ end
101
+
102
+ def lookup(gem, version)
103
+ _, set = lookup_path(gem, version)
104
+ set&.path
105
+ end
106
+
107
+ def lookup_path(gem, version)
108
+ if gem_rbs = gems[gem]
109
+ unless gem_rbs.empty?
110
+ set = if v = Gem::Version.create(version)&.release
111
+ gem_rbs.find_best_version(v)
112
+ else
113
+ gem_rbs.latest_version
114
+ end
115
+
116
+ [gem_rbs, set]
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end