rigortype 0.0.8 → 0.0.9

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +195 -21
  3. data/data/builtins/ruby_core/encoding.yml +210 -0
  4. data/data/builtins/ruby_core/exception.yml +641 -0
  5. data/data/builtins/ruby_core/numeric.yml +3 -2
  6. data/data/builtins/ruby_core/proc.yml +731 -0
  7. data/data/builtins/ruby_core/random.yml +166 -0
  8. data/data/builtins/ruby_core/re.yml +689 -0
  9. data/data/builtins/ruby_core/struct.yml +449 -0
  10. data/lib/rigor/analysis/runner.rb +19 -3
  11. data/lib/rigor/builtins/imported_refinements.rb +6 -1
  12. data/lib/rigor/cache/rbs_class_ancestor_table.rb +63 -0
  13. data/lib/rigor/cache/rbs_class_type_param_names.rb +60 -0
  14. data/lib/rigor/cache/rbs_constant_table.rb +15 -51
  15. data/lib/rigor/cache/rbs_descriptor.rb +53 -0
  16. data/lib/rigor/cache/rbs_environment.rb +52 -0
  17. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +40 -0
  18. data/lib/rigor/cache/rbs_known_class_names.rb +43 -0
  19. data/lib/rigor/cache/store.rb +79 -15
  20. data/lib/rigor/cli.rb +36 -4
  21. data/lib/rigor/environment/rbs_hierarchy.rb +18 -5
  22. data/lib/rigor/environment/rbs_loader.rb +137 -25
  23. data/lib/rigor/environment.rb +11 -2
  24. data/lib/rigor/flow_contribution.rb +128 -0
  25. data/lib/rigor/inference/builtins/encoding_catalog.rb +67 -0
  26. data/lib/rigor/inference/builtins/exception_catalog.rb +92 -0
  27. data/lib/rigor/inference/builtins/proc_catalog.rb +122 -0
  28. data/lib/rigor/inference/builtins/random_catalog.rb +58 -0
  29. data/lib/rigor/inference/builtins/re_catalog.rb +81 -0
  30. data/lib/rigor/inference/builtins/struct_catalog.rb +55 -0
  31. data/lib/rigor/inference/expression_typer.rb +26 -1
  32. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +16 -1
  33. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +87 -0
  34. data/lib/rigor/inference/method_dispatcher.rb +2 -0
  35. data/lib/rigor/inference/narrowing.rb +29 -14
  36. data/lib/rigor/rbs_extended.rb +55 -0
  37. data/lib/rigor/type/combinator.rb +72 -0
  38. data/lib/rigor/type/refined.rb +50 -2
  39. data/lib/rigor/version.rb +1 -1
  40. data/lib/rigor.rb +6 -0
  41. data/sig/rigor.rbs +3 -1
  42. metadata +21 -1
@@ -0,0 +1,449 @@
1
+ # DO NOT EDIT — generated by tool/extract_builtin_catalog.rb
2
+ ---
3
+ schema_version: 1
4
+ generated_from:
5
+ ruby_init_c: references/ruby/struct.c
6
+ ruby_prelude:
7
+ rbs:
8
+ - references/rbs/core/struct.rbs
9
+ purity_levels:
10
+ leaf: Prelude :leaf marker (VM-enforced) or C body uses no dispatch/yield/mutation.
11
+ trivial: Prelude method body is a literal return (self/true/false/nil/Integer).
12
+ leaf_when_numeric: C body falls through to rb_num_coerce_* only when an operand
13
+ is non-numeric; safe to fold when every argument is a concrete numeric.
14
+ inline_block: Prelude method carries :inline_block or :use_block; block-dependent.
15
+ block_dependent: C body yields or checks rb_block_given_p.
16
+ mutates_self: C body checks rb_check_frozen — typically a prelude to mutation.
17
+ dispatch: C body calls user-redefinable methods (rb_funcall*, rb_equal, rb_Float,
18
+ num_funcall*, etc).
19
+ unknown: C body not located in indexed C files.
20
+ classes:
21
+ Struct:
22
+ parent: Object
23
+ defined_at: references/ruby/struct.c:2245
24
+ includes:
25
+ - module: Enumerable
26
+ defined_at: references/ruby/struct.c:2246
27
+ constants: {}
28
+ aliases:
29
+ to_s:
30
+ old: inspect
31
+ source: c
32
+ defined_at: references/ruby/struct.c:2263
33
+ instance_methods:
34
+ initialize:
35
+ source: c
36
+ cfunc: rb_struct_initialize_m
37
+ arity: -1
38
+ defined_at: references/ruby/struct.c:2255
39
+ c_body_at: references/ruby/struct.c:752
40
+ c_effects:
41
+ - mutate
42
+ - raises
43
+ purity: mutates_self
44
+ initialize_copy:
45
+ source: c
46
+ cfunc: rb_struct_init_copy
47
+ arity: 1
48
+ defined_at: references/ruby/struct.c:2256
49
+ c_body_at: references/ruby/struct.c:1163
50
+ c_effects:
51
+ - raises
52
+ purity: leaf
53
+ "==":
54
+ source: c
55
+ cfunc: rb_struct_equal
56
+ arity: 1
57
+ defined_at: references/ruby/struct.c:2258
58
+ c_body_at: references/ruby/struct.c:1440
59
+ c_effects:
60
+ - dispatch
61
+ - raises
62
+ purity: dispatch
63
+ rbs:
64
+ - "(untyped other) -> bool"
65
+ rbs_at: references/rbs/core/struct.rbs:289
66
+ eql?:
67
+ source: c
68
+ cfunc: rb_struct_eql
69
+ arity: 1
70
+ defined_at: references/ruby/struct.c:2259
71
+ c_body_at: references/ruby/struct.c:1521
72
+ c_effects:
73
+ - dispatch
74
+ - raises
75
+ purity: dispatch
76
+ rbs:
77
+ - "(untyped other) -> bool"
78
+ rbs_at: references/rbs/core/struct.rbs:310
79
+ hash:
80
+ source: c
81
+ cfunc: rb_struct_hash
82
+ arity: 0
83
+ defined_at: references/ruby/struct.c:2260
84
+ c_body_at: references/ruby/struct.c:1472
85
+ c_effects: []
86
+ purity: leaf
87
+ rbs:
88
+ - "() -> Integer"
89
+ rbs_at: references/rbs/core/struct.rbs:330
90
+ inspect:
91
+ source: c
92
+ cfunc: rb_struct_inspect
93
+ arity: 0
94
+ defined_at: references/ruby/struct.c:2262
95
+ c_body_at: references/ruby/struct.c:1043
96
+ c_effects:
97
+ - dispatch
98
+ purity: dispatch
99
+ rbs:
100
+ - "() -> String"
101
+ rbs_at: references/rbs/core/struct.rbs:342
102
+ to_a:
103
+ source: c
104
+ cfunc: rb_struct_to_a
105
+ arity: 0
106
+ defined_at: references/ruby/struct.c:2264
107
+ c_body_at: references/ruby/struct.c:1062
108
+ c_effects: []
109
+ purity: leaf
110
+ rbs:
111
+ - "() -> Array[Elem]"
112
+ rbs_at: references/rbs/core/struct.rbs:365
113
+ to_h:
114
+ source: c
115
+ cfunc: rb_struct_to_h
116
+ arity: 0
117
+ defined_at: references/ruby/struct.c:2265
118
+ c_body_at: references/ruby/struct.c:1091
119
+ c_effects:
120
+ - block
121
+ - mutate
122
+ purity: block_dependent
123
+ rbs:
124
+ - "() -> Hash[Symbol, Elem]"
125
+ - "[K, V] () { (Symbol key, Elem value) -> [ K, V ] } -> Hash[K, V]"
126
+ rbs_at: references/rbs/core/struct.rbs:388
127
+ values:
128
+ source: c
129
+ cfunc: rb_struct_to_a
130
+ arity: 0
131
+ defined_at: references/ruby/struct.c:2266
132
+ c_body_at: references/ruby/struct.c:1062
133
+ c_effects: []
134
+ purity: leaf
135
+ size:
136
+ source: c
137
+ cfunc: rb_struct_size
138
+ arity: 0
139
+ defined_at: references/ruby/struct.c:2267
140
+ c_body_at: references/ruby/struct.c:1546
141
+ c_effects: []
142
+ purity: leaf
143
+ rbs:
144
+ - "() -> Integer"
145
+ rbs_at: references/rbs/core/struct.rbs:412
146
+ length:
147
+ source: c
148
+ cfunc: rb_struct_size
149
+ arity: 0
150
+ defined_at: references/ruby/struct.c:2268
151
+ c_body_at: references/ruby/struct.c:1546
152
+ c_effects: []
153
+ purity: leaf
154
+ each:
155
+ source: c
156
+ cfunc: rb_struct_each
157
+ arity: 0
158
+ defined_at: references/ruby/struct.c:2270
159
+ c_body_at: references/ruby/struct.c:926
160
+ c_effects:
161
+ - block
162
+ purity: block_dependent
163
+ rbs:
164
+ - "() -> Enumerator[Elem, self]"
165
+ - "() { (Elem value) -> void } -> self"
166
+ rbs_at: references/rbs/core/struct.rbs:444
167
+ each_pair:
168
+ source: c
169
+ cfunc: rb_struct_each_pair
170
+ arity: 0
171
+ defined_at: references/ruby/struct.c:2271
172
+ c_body_at: references/ruby/struct.c:961
173
+ c_effects:
174
+ - block
175
+ purity: block_dependent
176
+ rbs:
177
+ - "() -> Enumerator[[ Symbol, Elem ], self]"
178
+ - "() { ([ Symbol, Elem ] key_value) -> void } -> self"
179
+ rbs_at: references/rbs/core/struct.rbs:468
180
+ "[]":
181
+ source: c
182
+ cfunc: rb_struct_aref
183
+ arity: 1
184
+ defined_at: references/ruby/struct.c:2272
185
+ c_body_at: references/ruby/struct.c:1258
186
+ c_effects: []
187
+ purity: leaf
188
+ rbs:
189
+ - "(index name_or_position) -> Elem"
190
+ rbs_at: references/rbs/core/struct.rbs:495
191
+ "[]=":
192
+ source: c
193
+ cfunc: rb_struct_aset
194
+ arity: 2
195
+ defined_at: references/ruby/struct.c:2273
196
+ c_body_at: references/ruby/struct.c:1296
197
+ c_effects:
198
+ - mutate
199
+ purity: mutates_self
200
+ rbs:
201
+ - "(index name_or_position, Elem value) -> Elem"
202
+ rbs_at: references/rbs/core/struct.rbs:524
203
+ select:
204
+ source: c
205
+ cfunc: rb_struct_select
206
+ arity: -1
207
+ defined_at: references/ruby/struct.c:2274
208
+ c_body_at: references/ruby/struct.c:1388
209
+ c_effects:
210
+ - block
211
+ - mutate
212
+ purity: block_dependent
213
+ rbs:
214
+ - "() -> Enumerator[Elem, Array[Elem]]"
215
+ - "() { (Elem value) -> boolish } -> Array[Elem]"
216
+ rbs_at: references/rbs/core/struct.rbs:543
217
+ filter:
218
+ source: c
219
+ cfunc: rb_struct_select
220
+ arity: -1
221
+ defined_at: references/ruby/struct.c:2275
222
+ c_body_at: references/ruby/struct.c:1388
223
+ c_effects:
224
+ - block
225
+ - mutate
226
+ purity: block_dependent
227
+ values_at:
228
+ source: c
229
+ cfunc: rb_struct_values_at
230
+ arity: -1
231
+ defined_at: references/ruby/struct.c:2276
232
+ c_body_at: references/ruby/struct.c:1364
233
+ c_effects: []
234
+ purity: leaf
235
+ rbs:
236
+ - "(*int | range[int?] positions) -> Array[Elem]"
237
+ rbs_at: references/rbs/core/struct.rbs:594
238
+ members:
239
+ source: c
240
+ cfunc: rb_struct_members_m
241
+ arity: 0
242
+ defined_at: references/ruby/struct.c:2278
243
+ c_body_at: references/ruby/struct.c:227
244
+ c_effects: []
245
+ purity: leaf
246
+ rbs:
247
+ - "() -> Array[Symbol]"
248
+ rbs_at: references/rbs/core/struct.rbs:607
249
+ dig:
250
+ source: c
251
+ cfunc: rb_struct_dig
252
+ arity: -1
253
+ defined_at: references/ruby/struct.c:2279
254
+ c_body_at: references/ruby/struct.c:1584
255
+ c_effects: []
256
+ purity: leaf
257
+ rbs:
258
+ - "(index name_or_position) -> Elem"
259
+ - "(index name_or_position, untyped, *untyped) -> untyped"
260
+ rbs_at: references/rbs/core/struct.rbs:637
261
+ deconstruct:
262
+ source: c
263
+ cfunc: rb_struct_to_a
264
+ arity: 0
265
+ defined_at: references/ruby/struct.c:2281
266
+ c_body_at: references/ruby/struct.c:1062
267
+ c_effects: []
268
+ purity: leaf
269
+ deconstruct_keys:
270
+ source: c
271
+ cfunc: rb_struct_deconstruct_keys
272
+ arity: 1
273
+ defined_at: references/ruby/struct.c:2282
274
+ c_body_at: references/ruby/struct.c:1126
275
+ c_effects: []
276
+ purity: leaf
277
+ rbs:
278
+ - "(Array[index & Hash::_Key]? indices) -> Hash[index & Hash::_Key, Elem]"
279
+ rbs_at: references/rbs/core/struct.rbs:667
280
+ singleton_methods:
281
+ new:
282
+ source: c
283
+ cfunc: rb_struct_s_def
284
+ arity: -1
285
+ defined_at: references/ruby/struct.c:2249
286
+ c_body_at: references/ruby/struct.c:643
287
+ c_effects:
288
+ - block
289
+ - mutate
290
+ - raises
291
+ purity: block_dependent
292
+ rbs:
293
+ - "(string? classname, *interned fields, ?keyword_init: boolish?) ?{ (singleton(Struct))
294
+ [self: singleton(Struct)] -> void } -> untyped"
295
+ - "(Symbol field1, *interned fields, ?keyword_init: boolish?) ?{ (singleton(Struct))
296
+ [self: singleton(Struct)] -> void } -> untyped"
297
+ rbs_at: references/rbs/core/struct.rbs:238
298
+ keyword_init?:
299
+ source: c
300
+ cfunc: rb_struct_s_keyword_init_p
301
+ arity: 0
302
+ defined_at: references/ruby/struct.c:2251
303
+ c_body_at: references/ruby/struct.c:62
304
+ c_effects: []
305
+ purity: leaf
306
+ rbs:
307
+ - "() -> bool?"
308
+ rbs_at: references/rbs/core/struct.rbs:267
309
+ members:
310
+ source: c
311
+ cfunc: rb_struct_s_members_m
312
+ arity: 0
313
+ defined_at: references/ruby/struct.c:2252
314
+ c_body_at: references/ruby/struct.c:207
315
+ c_effects: []
316
+ purity: leaf
317
+ rbs:
318
+ - "() -> Array[Symbol]"
319
+ rbs_at: references/rbs/core/struct.rbs:250
320
+ undefined: []
321
+ Data:
322
+ parent: Object
323
+ defined_at: references/ruby/struct.c:2284
324
+ includes: []
325
+ constants: {}
326
+ aliases:
327
+ to_s:
328
+ old: inspect
329
+ source: c
330
+ defined_at: references/ruby/struct.c:2302
331
+ instance_methods:
332
+ initialize:
333
+ source: c
334
+ cfunc: rb_data_initialize_m
335
+ arity: -1
336
+ defined_at: references/ruby/struct.c:2294
337
+ c_body_at: references/ruby/struct.c:1833
338
+ c_effects:
339
+ - mutate
340
+ purity: mutates_self
341
+ initialize_copy:
342
+ source: c
343
+ cfunc: rb_data_init_copy
344
+ arity: 1
345
+ defined_at: references/ruby/struct.c:2295
346
+ c_body_at: references/ruby/struct.c:1881
347
+ c_effects: []
348
+ purity: leaf
349
+ "==":
350
+ source: c
351
+ cfunc: rb_data_equal
352
+ arity: 1
353
+ defined_at: references/ruby/struct.c:2297
354
+ c_body_at: references/ruby/struct.c:1440
355
+ c_effects:
356
+ - dispatch
357
+ - raises
358
+ purity: dispatch
359
+ eql?:
360
+ source: c
361
+ cfunc: rb_data_eql
362
+ arity: 1
363
+ defined_at: references/ruby/struct.c:2298
364
+ c_body_at: references/ruby/struct.c:1521
365
+ c_effects:
366
+ - dispatch
367
+ - raises
368
+ purity: dispatch
369
+ hash:
370
+ source: c
371
+ cfunc: rb_data_hash
372
+ arity: 0
373
+ defined_at: references/ruby/struct.c:2299
374
+ c_body_at: references/ruby/struct.c:1472
375
+ c_effects: []
376
+ purity: leaf
377
+ inspect:
378
+ source: c
379
+ cfunc: rb_data_inspect
380
+ arity: 0
381
+ defined_at: references/ruby/struct.c:2301
382
+ c_body_at: references/ruby/struct.c:1952
383
+ c_effects:
384
+ - dispatch
385
+ purity: dispatch
386
+ to_h:
387
+ source: c
388
+ cfunc: rb_data_to_h
389
+ arity: 0
390
+ defined_at: references/ruby/struct.c:2303
391
+ c_body_at: references/ruby/struct.c:1091
392
+ c_effects:
393
+ - block
394
+ - mutate
395
+ purity: block_dependent
396
+ members:
397
+ source: c
398
+ cfunc: rb_data_members_m
399
+ arity: 0
400
+ defined_at: references/ruby/struct.c:2305
401
+ c_body_at: references/ruby/struct.c:227
402
+ c_effects: []
403
+ purity: leaf
404
+ deconstruct:
405
+ source: c
406
+ cfunc: rb_data_deconstruct
407
+ arity: 0
408
+ defined_at: references/ruby/struct.c:2307
409
+ c_body_at: references/ruby/struct.c:1062
410
+ c_effects: []
411
+ purity: leaf
412
+ deconstruct_keys:
413
+ source: c
414
+ cfunc: rb_data_deconstruct_keys
415
+ arity: 1
416
+ defined_at: references/ruby/struct.c:2308
417
+ c_body_at: references/ruby/struct.c:2131
418
+ c_effects: []
419
+ purity: leaf
420
+ with:
421
+ source: c
422
+ cfunc: rb_data_with
423
+ arity: -1
424
+ defined_at: references/ruby/struct.c:2310
425
+ c_body_at: references/ruby/struct.c:1919
426
+ c_effects: []
427
+ purity: leaf
428
+ singleton_methods:
429
+ define:
430
+ source: c
431
+ cfunc: rb_data_s_def
432
+ arity: -1
433
+ defined_at: references/ruby/struct.c:2288
434
+ c_body_at: references/ruby/struct.c:1709
435
+ c_effects:
436
+ - block
437
+ - mutate
438
+ - raises
439
+ purity: block_dependent
440
+ members:
441
+ source: c
442
+ cfunc: rb_data_s_members_m
443
+ arity: 0
444
+ defined_at: references/ruby/struct.c:2291
445
+ c_body_at: references/ruby/struct.c:207
446
+ c_effects: []
447
+ purity: leaf
448
+ undefined:
449
+ - new
@@ -4,6 +4,7 @@ require "prism"
4
4
 
5
5
  require_relative "../environment"
6
6
  require_relative "../scope"
7
+ require_relative "../cache/store"
7
8
  require_relative "../inference/coverage_scanner"
8
9
  require_relative "../inference/scope_indexer"
9
10
  require_relative "../inference/method_dispatcher/file_folding"
@@ -15,10 +16,24 @@ module Rigor
15
16
  module Analysis
16
17
  class Runner # rubocop:disable Metrics/ClassLength
17
18
  RUBY_GLOB = "**/*.rb"
18
-
19
- def initialize(configuration:, explain: false)
19
+ DEFAULT_CACHE_ROOT = ".rigor/cache"
20
+
21
+ attr_reader :cache_store
22
+
23
+ # @param configuration [Rigor::Configuration]
24
+ # @param explain [Boolean] surface fail-soft fallback events
25
+ # as `:info` diagnostics.
26
+ # @param cache_store [Rigor::Cache::Store, nil] the persistent
27
+ # cache the runner exposes to producers (`RbsConstantTable`
28
+ # and successors). Pass `nil` to disable caching for this
29
+ # run; the CLI's `--no-cache` flag wires `nil` through.
30
+ # v0.0.9 group A slice 1 introduces the surface; later
31
+ # slices route real producers through it.
32
+ def initialize(configuration:, explain: false,
33
+ cache_store: Cache::Store.new(root: DEFAULT_CACHE_ROOT))
20
34
  @configuration = configuration
21
35
  @explain = explain
36
+ @cache_store = cache_store
22
37
  end
23
38
 
24
39
  # Walks every Ruby file under `paths`, parses it, builds a
@@ -35,7 +50,8 @@ module Rigor
35
50
 
36
51
  environment = Environment.for_project(
37
52
  libraries: @configuration.libraries,
38
- signature_paths: @configuration.signature_paths
53
+ signature_paths: @configuration.signature_paths,
54
+ cache_store: @cache_store
39
55
  )
40
56
  expansion = expand_paths(paths)
41
57
 
@@ -54,13 +54,18 @@ module Rigor
54
54
  "negative-int" => -> { Type::Combinator.negative_int },
55
55
  "non-positive-int" => -> { Type::Combinator.non_positive_int },
56
56
  "lowercase-string" => -> { Type::Combinator.lowercase_string },
57
+ "non-lowercase-string" => -> { Type::Combinator.non_lowercase_string },
57
58
  "uppercase-string" => -> { Type::Combinator.uppercase_string },
59
+ "non-uppercase-string" => -> { Type::Combinator.non_uppercase_string },
58
60
  "numeric-string" => -> { Type::Combinator.numeric_string },
61
+ "non-numeric-string" => -> { Type::Combinator.non_numeric_string },
59
62
  "decimal-int-string" => -> { Type::Combinator.decimal_int_string },
60
63
  "octal-int-string" => -> { Type::Combinator.octal_int_string },
61
64
  "hex-int-string" => -> { Type::Combinator.hex_int_string },
62
65
  "non-empty-lowercase-string" => -> { Type::Combinator.non_empty_lowercase_string },
63
- "non-empty-uppercase-string" => -> { Type::Combinator.non_empty_uppercase_string }
66
+ "non-empty-uppercase-string" => -> { Type::Combinator.non_empty_uppercase_string },
67
+ "literal-string" => -> { Type::Combinator.literal_string },
68
+ "non-empty-literal-string" => -> { Type::Combinator.non_empty_literal_string }
64
69
  }.freeze
65
70
  private_constant :REGISTRY
66
71
 
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rbs_descriptor"
4
+
5
+ module Rigor
6
+ module Cache
7
+ # Cache producer that materialises the RBS-declared ancestor
8
+ # chain of every loaded class / module into a Marshal-clean
9
+ # `Hash<String, Array<String>>`. Ancestor names are top-level-
10
+ # stripped (e.g. `"Integer"` not `"::Integer"`) to match
11
+ # `Environment::RbsHierarchy#normalize_name`.
12
+ #
13
+ # The hierarchy is the substrate behind every `class_ordering`
14
+ # query, which is itself a hot path on the dispatcher (overload
15
+ # selection, narrowing, etc.). Building one ancestor chain
16
+ # requires a full `RBS::DefinitionBuilder#build_instance` over
17
+ # that class — a cold-cost dominated by RBS's own resolution
18
+ # work. Caching the table lets a warm process skip the build
19
+ # entirely and pay only a `Marshal.load` of the resulting
20
+ # hash.
21
+ #
22
+ # Cache descriptor shape is shared with every other cache
23
+ # producer that depends on the RBS environment — see
24
+ # {RbsDescriptor.build}.
25
+ class RbsClassAncestorTable
26
+ PRODUCER_ID = "rbs.class_ancestor_table"
27
+
28
+ # @param loader [Rigor::Environment::RbsLoader]
29
+ # @param store [Rigor::Cache::Store]
30
+ # @return [Hash{String => Array<String>}]
31
+ def self.fetch(loader:, store:)
32
+ descriptor = RbsDescriptor.build(loader)
33
+ store.fetch_or_compute(producer_id: PRODUCER_ID, params: {}, descriptor: descriptor) do
34
+ compute(loader)
35
+ end
36
+ end
37
+
38
+ def self.compute(loader)
39
+ table = {}
40
+ loader.each_known_class_name do |name|
41
+ key = name.delete_prefix("::")
42
+ ancestors = ancestors_for(loader, key)
43
+ table[key] = ancestors unless ancestors.nil?
44
+ end
45
+ table
46
+ end
47
+
48
+ def self.ancestors_for(loader, class_name)
49
+ definition = loader.instance_definition(class_name)
50
+ return nil if definition.nil?
51
+
52
+ definition.ancestors.ancestors
53
+ .map { |ancestor| ancestor.name.to_s.delete_prefix("::") }
54
+ .uniq
55
+ .freeze
56
+ rescue StandardError
57
+ nil
58
+ end
59
+
60
+ private_class_method :compute, :ancestors_for
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rbs_descriptor"
4
+
5
+ module Rigor
6
+ module Cache
7
+ # Cache producer that materialises every loaded class's
8
+ # RBS-declared type-parameter names as a Marshal-clean
9
+ # `Hash<String, Array<Symbol>>` keyed by top-level-stripped
10
+ # class name (e.g. `"Array"` → `[:Elem]`, `"Hash"` →
11
+ # `[:K, :V]`). Producer id `"rbs.class_type_param_names"`.
12
+ #
13
+ # The dispatcher reads type-parameter names every time it
14
+ # builds a substitution map from a receiver's `type_args`
15
+ # into a method's return type — it is one of the hottest
16
+ # reflection lookups during analysis. Building one entry
17
+ # requires a full `RBS::DefinitionBuilder#build_instance`
18
+ # over that class, the same expensive operation
19
+ # {RbsClassAncestorTable} caches; the two producers share
20
+ # the build cost when populated together.
21
+ #
22
+ # Cache descriptor shape is shared with every other cache
23
+ # producer that depends on the RBS environment — see
24
+ # {RbsDescriptor.build}.
25
+ class RbsClassTypeParamNames
26
+ PRODUCER_ID = "rbs.class_type_param_names"
27
+
28
+ # @param loader [Rigor::Environment::RbsLoader]
29
+ # @param store [Rigor::Cache::Store]
30
+ # @return [Hash{String => Array<Symbol>}]
31
+ def self.fetch(loader:, store:)
32
+ descriptor = RbsDescriptor.build(loader)
33
+ store.fetch_or_compute(producer_id: PRODUCER_ID, params: {}, descriptor: descriptor) do
34
+ compute(loader)
35
+ end
36
+ end
37
+
38
+ def self.compute(loader)
39
+ table = {}
40
+ loader.each_known_class_name do |name|
41
+ key = name.delete_prefix("::")
42
+ params = type_params_for(loader, key)
43
+ table[key] = params unless params.nil?
44
+ end
45
+ table
46
+ end
47
+
48
+ def self.type_params_for(loader, class_name)
49
+ definition = loader.instance_definition(class_name)
50
+ return nil if definition.nil?
51
+
52
+ definition.type_params.dup.freeze
53
+ rescue StandardError
54
+ nil
55
+ end
56
+
57
+ private_class_method :compute, :type_params_for
58
+ end
59
+ end
60
+ end