rbs 4.1.0.pre.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +37 -0
  3. data/.github/workflows/bundle-update.yml +1 -1
  4. data/.github/workflows/c-check.yml +14 -6
  5. data/.github/workflows/comments.yml +2 -2
  6. data/.github/workflows/dependabot.yml +2 -2
  7. data/.github/workflows/jruby.yml +79 -0
  8. data/.github/workflows/milestone.yml +13 -2
  9. data/.github/workflows/ruby.yml +16 -4
  10. data/.github/workflows/rust.yml +13 -8
  11. data/.github/workflows/truffleruby.yml +54 -0
  12. data/.github/workflows/typecheck.yml +5 -2
  13. data/.github/workflows/wasm.yml +55 -0
  14. data/.github/workflows/windows.yml +8 -2
  15. data/.gitignore +8 -0
  16. data/CHANGELOG.md +88 -0
  17. data/Dockerfile.jruby +53 -0
  18. data/README.md +3 -3
  19. data/Rakefile +103 -1
  20. data/Steepfile +9 -0
  21. data/core/array.rbs +243 -180
  22. data/core/builtin.rbs +6 -6
  23. data/core/enumerable.rbs +109 -109
  24. data/core/enumerator/product.rbs +5 -5
  25. data/core/enumerator.rbs +28 -28
  26. data/core/file.rbs +24 -1018
  27. data/core/file_constants.rbs +463 -0
  28. data/core/file_stat.rbs +534 -0
  29. data/core/float.rbs +0 -24
  30. data/core/hash.rbs +117 -101
  31. data/core/integer.rbs +21 -58
  32. data/core/io.rbs +25 -7
  33. data/core/match_data.rbs +1 -1
  34. data/core/module.rbs +88 -74
  35. data/core/numeric.rbs +3 -0
  36. data/core/object_space/weak_key_map.rbs +7 -7
  37. data/core/pathname.rbs +0 -10
  38. data/core/ractor.rbs +0 -10
  39. data/core/range.rbs +23 -23
  40. data/core/rbs/ops.rbs +154 -0
  41. data/core/rbs/unnamed/argf.rbs +3 -3
  42. data/core/rubygems/errors.rbs +4 -1
  43. data/core/rubygems/requirement.rbs +0 -10
  44. data/core/rubygems/rubygems.rbs +4 -1
  45. data/core/rubygems/specification.rbs +8 -0
  46. data/core/rubygems/version.rbs +0 -160
  47. data/core/set.rbs +3 -3
  48. data/core/struct.rbs +16 -16
  49. data/core/thread.rbs +9 -14
  50. data/docs/CONTRIBUTING.md +2 -1
  51. data/docs/inline.md +36 -6
  52. data/docs/rbs_by_example.md +20 -20
  53. data/docs/release.md +69 -0
  54. data/docs/syntax.md +2 -2
  55. data/docs/wasm_serialization.md +80 -0
  56. data/ext/rbs_extension/ast_translation.c +1294 -973
  57. data/ext/rbs_extension/ast_translation.h +4 -0
  58. data/ext/rbs_extension/legacy_location.c +11 -6
  59. data/ext/rbs_extension/main.c +139 -4
  60. data/include/rbs/ast.h +9 -1
  61. data/include/rbs/serialize.h +39 -0
  62. data/lib/rbs/ast/ruby/comment_block.rb +6 -4
  63. data/lib/rbs/ast/ruby/helpers/location_helper.rb +1 -1
  64. data/lib/rbs/ast/ruby/members.rb +12 -1
  65. data/lib/rbs/buffer.rb +48 -11
  66. data/lib/rbs/collection/sources/git.rb +6 -0
  67. data/lib/rbs/definition_builder/method_builder.rb +12 -6
  68. data/lib/rbs/environment.rb +4 -3
  69. data/lib/rbs/inline_parser.rb +5 -3
  70. data/lib/rbs/namespace.rb +47 -11
  71. data/lib/rbs/parser_aux.rb +4 -2
  72. data/lib/rbs/prototype/rbi.rb +193 -25
  73. data/lib/rbs/prototype/runtime.rb +2 -0
  74. data/lib/rbs/resolver/type_name_resolver.rb +12 -14
  75. data/lib/rbs/type_name.rb +33 -13
  76. data/lib/rbs/unit_test/type_assertions.rb +9 -0
  77. data/lib/rbs/version.rb +1 -1
  78. data/lib/rbs/wasm/deserializer.rb +213 -0
  79. data/lib/rbs/wasm/location.rb +61 -0
  80. data/lib/rbs/wasm/parser.rb +137 -0
  81. data/lib/rbs/wasm/runtime.rb +196 -0
  82. data/lib/rbs/wasm/serialization_schema.rb +110 -0
  83. data/lib/rbs.rb +13 -2
  84. data/lib/rbs_jars.rb +39 -0
  85. data/lib/rdoc_plugin/parser.rb +5 -0
  86. data/rbs.gemspec +32 -2
  87. data/sig/ast/ruby/members.rbs +6 -1
  88. data/sig/buffer.rbs +19 -1
  89. data/sig/namespace.rbs +20 -0
  90. data/sig/parser.rbs +10 -0
  91. data/sig/prototype/rbi.rbs +33 -4
  92. data/sig/resolver/type_name_resolver.rbs +2 -4
  93. data/sig/typename.rbs +15 -0
  94. data/sig/unit_test/type_assertions.rbs +4 -0
  95. data/sig/wasm/deserializer.rbs +66 -0
  96. data/sig/wasm/serialization_schema.rbs +13 -0
  97. data/src/ast.c +78 -78
  98. data/src/lexer.c +97 -93
  99. data/src/lexer.re +1 -1
  100. data/src/lexstate.c +9 -1
  101. data/src/serialize.c +958 -0
  102. data/src/util/rbs_allocator.c +14 -8
  103. data/stdlib/abbrev/0/array.rbs +1 -1
  104. data/stdlib/csv/0/csv.rbs +5 -5
  105. data/stdlib/delegate/0/delegator.rbs +2 -1
  106. data/stdlib/digest/0/digest.rbs +11 -5
  107. data/stdlib/erb/0/erb.rbs +1 -1
  108. data/stdlib/etc/0/etc.rbs +18 -4
  109. data/stdlib/fileutils/0/fileutils.rbs +21 -21
  110. data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
  111. data/stdlib/json/0/json.rbs +6 -6
  112. data/stdlib/monitor/0/monitor.rbs +2 -2
  113. data/stdlib/openssl/0/openssl.rbs +44 -38
  114. data/stdlib/resolv/0/resolv.rbs +1 -1
  115. data/stdlib/shellwords/0/shellwords.rbs +1 -1
  116. data/stdlib/stringio/0/stringio.rbs +32 -10
  117. data/stdlib/strscan/0/string_scanner.rbs +74 -55
  118. data/stdlib/tempfile/0/manifest.yaml +3 -0
  119. data/stdlib/timeout/0/timeout.rbs +0 -5
  120. data/stdlib/tsort/0/cyclic.rbs +1 -1
  121. data/stdlib/tsort/0/interfaces.rbs +8 -8
  122. data/stdlib/tsort/0/tsort.rbs +9 -9
  123. data/stdlib/uri/0/generic.rbs +0 -5
  124. data/stdlib/zlib/0/gzip_reader.rbs +2 -2
  125. data/stdlib/zlib/0/zstream.rbs +0 -1
  126. data/wasm/README.md +60 -0
  127. data/wasm/rbs_wasm.c +423 -0
  128. metadata +26 -5
  129. data/.vscode/extensions.json +0 -5
  130. data/.vscode/settings.json +0 -19
data/lib/rbs/namespace.rb CHANGED
@@ -9,30 +9,65 @@ module RBS
9
9
  @absolute = absolute ? true : false
10
10
  end
11
11
 
12
+ # Process-wide flyweight cache. Two tries (one per `absolute` flag)
13
+ # keyed on path Symbols, with the cached Namespace stored under the
14
+ # `INTERN_LEAF` sentinel at each path's terminal node.
15
+ @intern_mutex = Mutex.new
16
+ @intern_trie_absolute = {}
17
+ @intern_trie_relative = {}
18
+ INTERN_LEAF = Module.new
19
+
20
+ # Returns a canonical `Namespace` instance for the given `path` /
21
+ # `absolute` pair. Repeated calls with structurally equal arguments
22
+ # return the same object, so callers can rely on `equal?` for fast
23
+ # equality. The path Array is duplicated and frozen on insert.
24
+ def self.[](path, absolute)
25
+ absolute = absolute ? true : false
26
+
27
+ # Lock-free fast path.
28
+ node = absolute ? @intern_trie_absolute : @intern_trie_relative
29
+ path.each do |sym|
30
+ node = node[sym]
31
+ break unless node
32
+ end
33
+ if node && (cached = node[INTERN_LEAF])
34
+ return cached
35
+ end
36
+
37
+ @intern_mutex.synchronize do
38
+ node = absolute ? @intern_trie_absolute : @intern_trie_relative
39
+ path.each { |sym| node = (node[sym] ||= {}) }
40
+ node[INTERN_LEAF] ||= begin
41
+ frozen_path = path.frozen? ? path : path.dup.freeze
42
+ new(path: frozen_path, absolute: absolute)
43
+ end
44
+ end
45
+ end
46
+
12
47
  def self.empty
13
- @empty ||= new(path: [], absolute: false)
48
+ @empty ||= self[[], false]
14
49
  end
15
50
 
16
51
  def self.root
17
- @root ||= new(path: [], absolute: true)
52
+ @root ||= self[[], true]
18
53
  end
19
54
 
20
55
  def +(other)
21
56
  if other.absolute?
22
57
  other
23
58
  else
24
- self.class.new(path: path + other.path, absolute: absolute?)
59
+ Namespace[path + other.path, absolute?]
25
60
  end
26
61
  end
27
62
 
28
63
  def append(component)
29
- self.class.new(path: path + [component], absolute: absolute?)
64
+ Namespace[path + [component], absolute?]
30
65
  end
31
66
 
32
67
  def parent
33
68
  @parent ||= begin
34
69
  raise "Parent with empty namespace" if empty?
35
- self.class.new(path: path.take(path.size - 1), absolute: absolute?)
70
+ Namespace[path.take(path.size - 1), absolute?]
36
71
  end
37
72
  end
38
73
 
@@ -45,11 +80,11 @@ module RBS
45
80
  end
46
81
 
47
82
  def absolute!
48
- self.class.new(path: path, absolute: true)
83
+ Namespace[path, true]
49
84
  end
50
85
 
51
86
  def relative!
52
- self.class.new(path: path, absolute: false)
87
+ Namespace[path, false]
53
88
  end
54
89
 
55
90
  def empty?
@@ -57,13 +92,14 @@ module RBS
57
92
  end
58
93
 
59
94
  def ==(other)
95
+ return true if equal?(other)
60
96
  other.is_a?(Namespace) && other.path == path && other.absolute? == absolute?
61
97
  end
62
98
 
63
99
  alias eql? ==
64
100
 
65
101
  def hash
66
- path.hash ^ absolute?.hash
102
+ @hash ||= path.hash ^ absolute?.hash
67
103
  end
68
104
 
69
105
  def split
@@ -87,14 +123,14 @@ module RBS
87
123
  raise unless name
88
124
  raise unless parent
89
125
 
90
- TypeName.new(name: name, namespace: parent)
126
+ TypeName[parent, name]
91
127
  end
92
128
 
93
129
  def self.parse(string)
94
130
  if string.start_with?("::")
95
- new(path: string.split("::").drop(1).map(&:to_sym), absolute: true)
131
+ self[string.split("::").drop(1).map(&:to_sym), true]
96
132
  else
97
- new(path: string.split("::").map(&:to_sym), absolute: false)
133
+ self[string.split("::").map(&:to_sym), false]
98
134
  end
99
135
  end
100
136
 
@@ -121,12 +121,14 @@ module RBS
121
121
 
122
122
  def self.parse_inline_leading_annotation(source, range, variables: [])
123
123
  buf = buffer(source)
124
- _parse_inline_leading_annotation(buf, range.begin || 0, range.end || buf.last_position, variables)
124
+ byte_range = byte_range(range, buf.content)
125
+ _parse_inline_leading_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
125
126
  end
126
127
 
127
128
  def self.parse_inline_trailing_annotation(source, range, variables: [])
128
129
  buf = buffer(source)
129
- _parse_inline_trailing_annotation(buf, range.begin || 0, range.end || buf.last_position, variables)
130
+ byte_range = byte_range(range, buf.content)
131
+ _parse_inline_trailing_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
130
132
  end
131
133
 
132
134
  def self.byte_range(char_range, content)
@@ -9,10 +9,22 @@ module RBS
9
9
  attr_reader :modules
10
10
  attr_reader :last_sig
11
11
 
12
+ class Context
13
+ attr_accessor :singleton
14
+ attr_accessor :visibility
15
+
16
+ def initialize(singleton:, visibility:)
17
+ @singleton = singleton
18
+ @visibility = visibility
19
+ end
20
+ end
21
+
12
22
  def initialize
13
23
  @decls = []
14
24
 
15
25
  @modules = []
26
+ @contexts = []
27
+ @emitted_visibility = {}
16
28
  end
17
29
 
18
30
  def parse(string)
@@ -20,19 +32,17 @@ module RBS
20
32
  process RubyVM::AbstractSyntaxTree.parse(string), comments: comments
21
33
  end
22
34
 
23
- def nested_name(name)
24
- (current_namespace + const_to_name(name).to_namespace).to_type_name.relative!
25
- end
26
-
27
- def current_namespace
28
- modules.inject(Namespace.empty) do |parent, mod|
29
- parent + mod.name.to_namespace
35
+ def append_decl(decl)
36
+ if mod = current_module
37
+ mod.members << decl
38
+ else
39
+ decls << decl
30
40
  end
31
41
  end
32
42
 
33
43
  def push_class(name, super_class, comment:)
34
44
  class_decl = AST::Declarations::Class.new(
35
- name: nested_name(name),
45
+ name: const_to_name(name),
36
46
  super_class: super_class && AST::Declarations::Class::Super.new(name: const_to_name(super_class), args: [], location: nil),
37
47
  type_params: [],
38
48
  members: [],
@@ -41,17 +51,20 @@ module RBS
41
51
  comment: comment
42
52
  )
43
53
 
54
+ append_decl class_decl
44
55
  modules << class_decl
45
- decls << class_decl
56
+ @contexts << Context.new(singleton: false, visibility: :public)
57
+ @emitted_visibility[class_decl.object_id] = :public
46
58
 
47
59
  yield
48
60
  ensure
61
+ @contexts.pop
49
62
  modules.pop
50
63
  end
51
64
 
52
65
  def push_module(name, comment:)
53
66
  module_decl = AST::Declarations::Module.new(
54
- name: nested_name(name),
67
+ name: const_to_name(name),
55
68
  type_params: [],
56
69
  members: [],
57
70
  annotations: [],
@@ -60,11 +73,14 @@ module RBS
60
73
  comment: comment
61
74
  )
62
75
 
76
+ append_decl module_decl
63
77
  modules << module_decl
64
- decls << module_decl
78
+ @contexts << Context.new(singleton: false, visibility: :public)
79
+ @emitted_visibility[module_decl.object_id] = :public
65
80
 
66
81
  yield
67
82
  ensure
83
+ @contexts.pop
68
84
  modules.pop
69
85
  end
70
86
 
@@ -76,6 +92,47 @@ module RBS
76
92
  current_module or raise
77
93
  end
78
94
 
95
+ def current_context
96
+ @contexts.last
97
+ end
98
+
99
+ def current_context!
100
+ current_context or raise
101
+ end
102
+
103
+ # Visibility of a member, given as `private def ...` in RBS
104
+ #
105
+ # Returns `nil` for members in a visibility _section_, which `sync_visibility` emits instead.
106
+ def member_visibility(context)
107
+ # RBS visibility sections don't apply to singleton members, so they need their own visibility.
108
+ if context.singleton && context.visibility != :public
109
+ :private
110
+ end
111
+ end
112
+
113
+ def sync_visibility(visibility)
114
+ # Visibility sections don't apply to singleton members in RBS.
115
+ return if current_context!.singleton
116
+
117
+ # RBS has no protected visibility. Private is the conservative fallback.
118
+ visibility = :private if visibility == :protected
119
+
120
+ mod = current_module!
121
+ return if @emitted_visibility[mod.object_id] == visibility
122
+
123
+ member = case visibility
124
+ when :public
125
+ AST::Members::Public.new(location: nil)
126
+ when :private
127
+ AST::Members::Private.new(location: nil)
128
+ else
129
+ raise "Unexpected visibility: #{visibility}"
130
+ end
131
+
132
+ mod.members << member
133
+ @emitted_visibility[mod.object_id] = visibility
134
+ end
135
+
79
136
  def push_sig(node)
80
137
  if last_sig = @last_sig
81
138
  last_sig << node
@@ -107,6 +164,15 @@ module RBS
107
164
  push_module node.children[0], comment: comment do
108
165
  process node.children[1], outer: outer + [node], comments: comments
109
166
  end
167
+ when :SCLASS
168
+ if node.children[0].type == :SELF
169
+ @contexts << Context.new(singleton: true, visibility: :public)
170
+ begin
171
+ process node.children[1], outer: outer + [node], comments: comments
172
+ ensure
173
+ @contexts.pop
174
+ end
175
+ end
110
176
  when :FCALL
111
177
  case node.children[0]
112
178
  when :include
@@ -125,9 +191,9 @@ module RBS
125
191
  end
126
192
  when :extend
127
193
  each_arg node.children[1] do |arg|
128
- if arg.type == :CONST || arg.type == :COLON2
194
+ if arg.type == :CONST || arg.type == :COLON2 || arg.type == :COLON3
129
195
  name = const_to_name(arg)
130
- unless name.to_s == "T::Generic" || name.to_s == "T::Sig"
196
+ unless ["T::Generic", "T::Helpers", "T::Sig"].include?(name.to_s.delete_prefix("::"))
131
197
  member = AST::Members::Extend.new(
132
198
  name: name,
133
199
  args: [],
@@ -142,6 +208,10 @@ module RBS
142
208
  when :sig
143
209
  out = outer.last or raise
144
210
  push_sig out.children.last.children.last
211
+ when :attr_reader, :attr_writer, :attr_accessor
212
+ process_attribute node, comments: comments
213
+ when :private, :protected, :public
214
+ process_visibility node, outer: outer, comments: comments
145
215
  when :alias_method
146
216
  new, old = each_arg(node.children[1]).map {|x| x.children[0] }
147
217
  current_module!.members << AST::Members::Alias.new(
@@ -149,10 +219,15 @@ module RBS
149
219
  old_name: old,
150
220
  location: nil,
151
221
  annotations: [],
152
- kind: :instance,
222
+ kind: current_context!.singleton ? :singleton : :instance,
153
223
  comment: nil
154
224
  )
155
225
  end
226
+ when :VCALL
227
+ case node.children[0]
228
+ when :private, :protected, :public
229
+ current_context!.visibility = node.children[0]
230
+ end
156
231
  when :DEFS
157
232
  sigs = pop_sig
158
233
 
@@ -178,6 +253,8 @@ module RBS
178
253
  sigs = pop_sig
179
254
 
180
255
  if sigs
256
+ context = current_context!
257
+ sync_visibility(context.visibility)
181
258
  comment = join_comments(sigs, comments)
182
259
 
183
260
  args = node.children[1]
@@ -188,10 +265,10 @@ module RBS
188
265
  location: nil,
189
266
  annotations: [],
190
267
  overloads: types.map {|type| AST::Members::MethodDefinition::Overload.new(annotations: [], method_type: type) },
191
- kind: :instance,
268
+ kind: context.singleton ? :singleton : :instance,
192
269
  comment: comment,
193
270
  overloading: false,
194
- visibility: nil
271
+ visibility: member_visibility(context)
195
272
  )
196
273
  end
197
274
 
@@ -222,11 +299,7 @@ module RBS
222
299
  end
223
300
  else
224
301
  name = node.children[0].yield_self do |n|
225
- if n.is_a?(Symbol)
226
- TypeName.new(namespace: current_namespace, name: n)
227
- else
228
- const_to_name(n)
229
- end
302
+ n.is_a?(Symbol) ? TypeName.new(namespace: Namespace.empty, name: n) : const_to_name(n)
230
303
  end
231
304
  value_node = node.children.last
232
305
  type = if value_node && value_node.type == :CALL && value_node.children[1] == :let
@@ -235,7 +308,7 @@ module RBS
235
308
  else
236
309
  Types::Bases::Any.new(location: nil)
237
310
  end
238
- decls << AST::Declarations::Constant.new(
311
+ append_decl AST::Declarations::Constant.new(
239
312
  name: name,
240
313
  type: type,
241
314
  location: nil,
@@ -244,12 +317,13 @@ module RBS
244
317
  )
245
318
  end
246
319
  when :ALIAS
320
+ sync_visibility(current_context!.visibility)
247
321
  current_module!.members << AST::Members::Alias.new(
248
322
  new_name: node.children[0].children[0],
249
323
  old_name: node.children[1].children[0],
250
324
  location: nil,
251
325
  annotations: [],
252
- kind: :instance,
326
+ kind: current_context!.singleton ? :singleton : :instance,
253
327
  comment: nil
254
328
  )
255
329
  else
@@ -259,6 +333,93 @@ module RBS
259
333
  end
260
334
  end
261
335
 
336
+ def process_visibility(node, outer:, comments:)
337
+ visibility = node.children[0]
338
+ args = each_arg(node.children[1]).to_a
339
+ context = current_context!
340
+
341
+ if args.empty?
342
+ context.visibility = visibility
343
+ else
344
+ previous_visibility = context.visibility
345
+ context.visibility = visibility
346
+
347
+ begin
348
+ args.each do |arg|
349
+ if arg.type == :DEFN || arg.type == :DEFS
350
+ process arg, outer: outer + [node], comments: comments
351
+ end
352
+ end
353
+ ensure
354
+ context.visibility = previous_visibility
355
+ end
356
+ end
357
+ end
358
+
359
+ def process_attribute(node, comments:)
360
+ sigs = pop_sig
361
+ kind = node.children[0]
362
+ context = current_context!
363
+ sync_visibility(context.visibility)
364
+
365
+ type = attribute_type(kind, sigs)
366
+ comment = join_comments(sigs, comments) if sigs
367
+ member_class = case kind
368
+ when :attr_reader
369
+ AST::Members::AttrReader
370
+ when :attr_writer
371
+ AST::Members::AttrWriter
372
+ when :attr_accessor
373
+ AST::Members::AttrAccessor
374
+ else
375
+ raise "Unexpected attribute kind: #{kind}"
376
+ end
377
+
378
+ each_arg node.children[1] do |arg|
379
+ if name = symbol_literal_node?(arg)
380
+ current_module!.members << member_class.new(
381
+ name: name,
382
+ type: type,
383
+ ivar_name: nil,
384
+ kind: context.singleton ? :singleton : :instance,
385
+ annotations: [],
386
+ location: nil,
387
+ comment: comment,
388
+ visibility: member_visibility(context)
389
+ )
390
+ end
391
+ end
392
+ end
393
+
394
+ def attribute_type(kind, sigs)
395
+ any = Types::Bases::Any.new(location: nil)
396
+ return any unless sigs
397
+
398
+ method_types = sigs.filter_map do |sig|
399
+ method_type(nil, sig, variables: current_module!.type_params, overloads: sigs.size)
400
+ end
401
+ function = method_types.last&.type
402
+ return any unless function.is_a?(Types::Function)
403
+
404
+ parameter_type = function.required_positionals.first&.type
405
+ return_type = function.return_type
406
+
407
+ case kind
408
+ when :attr_reader
409
+ return_type
410
+ when :attr_writer
411
+ parameter_type || return_type
412
+ when :attr_accessor
413
+ if return_type.is_a?(Types::Bases::Any) || return_type.is_a?(Types::Bases::Void)
414
+ parameter_type || any
415
+ else
416
+ return_type
417
+ end
418
+ else
419
+ any
420
+ end
421
+ end
422
+
262
423
  def method_type(args_node, type_node, variables:, overloads:)
263
424
  if type_node
264
425
  if type_node.type == :CALL
@@ -463,8 +624,10 @@ module RBS
463
624
  case
464
625
  when type.is_a?(Types::ClassInstance) && type.name.name == BuiltinNames::BasicObject.name.name
465
626
  Types::Bases::Any.new(location: nil)
466
- when type.is_a?(Types::ClassInstance) && type.name.to_s == "T::Boolean"
627
+ when type.is_a?(Types::ClassInstance) && type.name.to_s.delete_prefix("::") == "T::Boolean"
467
628
  Types::Bases::Bool.new(location: nil)
629
+ when type.is_a?(Types::ClassInstance) && type.name.to_s.delete_prefix("::") == "T::Class"
630
+ Types::Bases::Any.new(location: nil)
468
631
  else
469
632
  type
470
633
  end
@@ -482,6 +645,11 @@ module RBS
482
645
  Types::ClassInstance.new(name: const_to_name(type_node), args: [], location: nil)
483
646
  when call_node?(type_node, name: :[], receiver: -> (_) { true })
484
647
  # The type_node represents a type application
648
+ receiver = type_node.children[0]
649
+ if [:CONST, :COLON2, :COLON3].include?(receiver.type) && const_to_name(receiver).to_s.delete_prefix("::") == "T::Class"
650
+ return Types::Bases::Any.new(location: nil)
651
+ end
652
+
485
653
  type = type_of(type_node.children[0], variables: variables)
486
654
  type.is_a?(Types::ClassInstance) or raise
487
655
 
@@ -559,7 +727,7 @@ module RBS
559
727
 
560
728
  type_name = TypeName.new(name: node.children[1], namespace: namespace)
561
729
 
562
- case type_name.to_s
730
+ case type_name.to_s.delete_prefix("::")
563
731
  when "T::Array"
564
732
  BuiltinNames::Array.name
565
733
  when "T::Hash"
@@ -77,6 +77,8 @@ module RBS
77
77
  attr_accessor :outline
78
78
 
79
79
  def initialize(patterns:, env:, merge:, todo: false, owners_included: [])
80
+ Kernel.warn("RBS::Prototype::Runtime is deprecated. See https://github.com/ruby/rbs/issues/2841")
81
+
80
82
  @patterns = patterns
81
83
  @decls = nil
82
84
  @modules = {}
@@ -29,11 +29,9 @@ module RBS
29
29
  new(all_names, aliases)
30
30
  end
31
31
 
32
- def try_cache(query)
33
- cache.fetch(query) do
34
- result = yield
35
- cache[query] = result
36
- end
32
+ def try_cache(type_name, context)
33
+ inner = cache[context] ||= {}
34
+ inner.fetch(type_name) { inner[type_name] = yield }
37
35
  end
38
36
 
39
37
  def resolve(type_name, context:)
@@ -41,7 +39,7 @@ module RBS
41
39
  return type_name
42
40
  end
43
41
 
44
- try_cache([type_name, context]) do
42
+ try_cache(type_name, context) do
45
43
  if type_name.class?
46
44
  resolve_namespace0(type_name, context, Set.new) || nil
47
45
  else
@@ -51,7 +49,7 @@ module RBS
51
49
  resolve_type_name(type_name.name, context)
52
50
  else
53
51
  if namespace = resolve_namespace0(namespace.to_type_name, context, Set.new)
54
- type_name = TypeName.new(name: type_name.name, namespace: namespace.to_namespace)
52
+ type_name = TypeName[namespace.to_namespace, type_name.name]
55
53
  has_type_name?(type_name)
56
54
  end
57
55
  end
@@ -68,7 +66,7 @@ module RBS
68
66
  raise "Type name must be a class name: #{type_name}"
69
67
  end
70
68
 
71
- try_cache([type_name, context]) do
69
+ try_cache(type_name, context) do
72
70
  ns = resolve_namespace0(type_name, context, Set.new) or return ns
73
71
  end
74
72
  end
@@ -93,10 +91,10 @@ module RBS
93
91
  resolve_type_name(type_name, outer)
94
92
  else
95
93
  has_type_name?(inner) or raise "Context must be normalized: #{inner.inspect}"
96
- has_type_name?(TypeName.new(name: type_name, namespace: inner.to_namespace)) || resolve_type_name(type_name, outer)
94
+ has_type_name?(TypeName[inner.to_namespace, type_name]) || resolve_type_name(type_name, outer)
97
95
  end
98
96
  else
99
- type_name = TypeName.new(name: type_name, namespace: Namespace.root)
97
+ type_name = TypeName[Namespace.root, type_name]
100
98
  has_type_name?(type_name)
101
99
  end
102
100
  end
@@ -109,11 +107,11 @@ module RBS
109
107
  resolve_head_namespace(head, outer)
110
108
  when TypeName
111
109
  has_type_name?(inner) or raise "Context must be normalized: #{inner.inspect}"
112
- type_name = TypeName.new(name: head, namespace: inner.to_namespace)
110
+ type_name = TypeName[inner.to_namespace, head]
113
111
  has_type_name?(type_name) || aliased_name?(type_name) || resolve_head_namespace(head, outer)
114
112
  end
115
113
  else
116
- type_name = TypeName.new(name: head, namespace: Namespace.root)
114
+ type_name = TypeName[Namespace.root, head]
117
115
  has_type_name?(type_name) || aliased_name?(type_name)
118
116
  end
119
117
  end
@@ -140,7 +138,7 @@ module RBS
140
138
 
141
139
  head =
142
140
  if type_name.absolute?
143
- root_name = TypeName.new(name: head, namespace: Namespace.root)
141
+ root_name = TypeName[Namespace.root, head]
144
142
  has_type_name?(root_name) || aliased_name?(root_name)
145
143
  else
146
144
  resolve_head_namespace(head, context)
@@ -152,7 +150,7 @@ module RBS
152
150
  end
153
151
 
154
152
  tail.inject(head) do |namespace, name|
155
- type_name = TypeName.new(name: name, namespace: namespace.to_namespace)
153
+ type_name = TypeName[namespace.to_namespace, name]
156
154
  case
157
155
  when has_type_name?(type_name)
158
156
  type_name
data/lib/rbs/type_name.rb CHANGED
@@ -22,14 +22,40 @@ module RBS
22
22
  end
23
23
  end
24
24
 
25
+ # Process-wide flyweight cache. Two-level Hash keyed by canonical
26
+ # Namespace identity (outer uses `compare_by_identity`) and name
27
+ # Symbol.
28
+ @intern_mutex = Mutex.new
29
+ @intern_cache = {} #: Hash[Namespace, Hash[Symbol, TypeName]]
30
+ @intern_cache.compare_by_identity
31
+
32
+ # Returns a canonical `TypeName` instance for the given `namespace` /
33
+ # `name` pair. The namespace is canonicalized through `Namespace.[]`
34
+ # so identity-based lookup works regardless of the caller passing a
35
+ # fresh `Namespace.new` or an already-interned instance.
36
+ def self.[](namespace, name)
37
+ ns = Namespace[namespace.path, namespace.absolute?]
38
+
39
+ inner = @intern_cache[ns]
40
+ if inner && (cached = inner[name])
41
+ return cached
42
+ end
43
+
44
+ @intern_mutex.synchronize do
45
+ inner = (@intern_cache[ns] ||= {})
46
+ inner[name] ||= new(namespace: ns, name: name)
47
+ end
48
+ end
49
+
25
50
  def ==(other)
51
+ return true if equal?(other)
26
52
  other.is_a?(self.class) && other.namespace == namespace && other.name == name
27
53
  end
28
54
 
29
55
  alias eql? ==
30
56
 
31
57
  def hash
32
- namespace.hash ^ name.hash
58
+ @hash ||= namespace.hash ^ name.hash
33
59
  end
34
60
 
35
61
  def to_s
@@ -53,7 +79,7 @@ module RBS
53
79
  end
54
80
 
55
81
  def absolute!
56
- self.class.new(namespace: namespace.absolute!, name: name)
82
+ TypeName[namespace.absolute!, name]
57
83
  end
58
84
 
59
85
  def absolute?
@@ -61,7 +87,7 @@ module RBS
61
87
  end
62
88
 
63
89
  def relative!
64
- self.class.new(namespace: namespace.relative!, name: name)
90
+ TypeName[namespace.relative!, name]
65
91
  end
66
92
 
67
93
  def interface?
@@ -69,7 +95,7 @@ module RBS
69
95
  end
70
96
 
71
97
  def with_prefix(namespace)
72
- self.class.new(namespace: namespace + self.namespace, name: name)
98
+ TypeName[namespace + self.namespace, name]
73
99
  end
74
100
 
75
101
  def split
@@ -80,23 +106,17 @@ module RBS
80
106
  if other.absolute?
81
107
  other
82
108
  else
83
- TypeName.new(
84
- namespace: self.to_namespace + other.namespace,
85
- name: other.name
86
- )
109
+ TypeName[self.to_namespace + other.namespace, other.name]
87
110
  end
88
111
  end
89
-
112
+
90
113
  def self.parse(string)
91
114
  absolute = string.start_with?("::")
92
115
 
93
116
  *path, name = string.delete_prefix("::").split("::").map(&:to_sym)
94
117
  raise unless name
95
118
 
96
- TypeName.new(
97
- name: name,
98
- namespace: RBS::Namespace.new(path: path, absolute: absolute)
99
- )
119
+ TypeName[Namespace[path, absolute], name]
100
120
  end
101
121
  end
102
122
  end