rigortype 0.4.7 → 0.4.8

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +29 -0
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/docs/handbook/01-getting-started.md +1 -1
  8. data/docs/handbook/11-sig-gen.md +35 -0
  9. data/docs/install.md +20 -0
  10. data/docs/manual/01-installation.md +67 -26
  11. data/docs/manual/02-cli-reference.md +307 -91
  12. data/docs/manual/03-configuration.md +41 -3
  13. data/docs/manual/04-diagnostics.md +5 -4
  14. data/docs/manual/08-skills.md +11 -0
  15. data/docs/manual/09-editor-integration.md +8 -0
  16. data/docs/manual/14-rails-quickstart.md +1 -1
  17. data/docs/manual/19-effect-labels.md +3 -4
  18. data/docs/manual/plugins/rigor-actionpack.md +15 -6
  19. data/docs/manual/plugins/rigor-activesupport-core-ext.md +3 -0
  20. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  21. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  22. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  23. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +8 -0
  24. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +13 -2
  25. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -0
  26. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  27. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +40 -11
  28. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  29. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  30. data/lib/rigor/analysis/check_rules/rule_ids.rb +8 -1
  31. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
  32. data/lib/rigor/analysis/check_rules.rb +598 -99
  33. data/lib/rigor/analysis/crash_signature.rb +43 -1
  34. data/lib/rigor/analysis/dependency_recorder.rb +233 -76
  35. data/lib/rigor/analysis/incremental.rb +11 -1
  36. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -1
  37. data/lib/rigor/analysis/incremental_session.rb +279 -37
  38. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  39. data/lib/rigor/analysis/rule_catalog.rb +37 -1
  40. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  41. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  42. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +4 -0
  43. data/lib/rigor/analysis/runner/declaration_position.rb +7 -7
  44. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +72 -20
  45. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +3 -8
  46. data/lib/rigor/analysis/runner/pool_coordinator.rb +26 -5
  47. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  48. data/lib/rigor/analysis/runner.rb +222 -39
  49. data/lib/rigor/analysis/self_call_resolution_recorder.rb +61 -22
  50. data/lib/rigor/analysis/worker_session.rb +42 -41
  51. data/lib/rigor/bleeding_edge.rb +19 -4
  52. data/lib/rigor/cache/file_digest.rb +39 -31
  53. data/lib/rigor/cache/incremental_snapshot.rb +66 -7
  54. data/lib/rigor/cache/rbs_descriptor.rb +37 -2
  55. data/lib/rigor/cache/rbs_environment.rb +5 -1
  56. data/lib/rigor/cli/baseline_command.rb +15 -2
  57. data/lib/rigor/cli/check_command.rb +4 -2
  58. data/lib/rigor/cli/effects_snapshot_command.rb +8 -1
  59. data/lib/rigor/cli/lens_command.rb +17 -2
  60. data/lib/rigor/cli/sig_gen_command.rb +8 -0
  61. data/lib/rigor/cli/skill_describe.rb +1 -0
  62. data/lib/rigor/cli.rb +5 -2
  63. data/lib/rigor/config_audit.rb +32 -18
  64. data/lib/rigor/configuration/severity_profile.rb +9 -0
  65. data/lib/rigor/configuration.rb +32 -5
  66. data/lib/rigor/effects/collector.rb +53 -21
  67. data/lib/rigor/environment/bundle_sig_discovery.rb +63 -28
  68. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  69. data/lib/rigor/environment/default_libraries.rb +17 -0
  70. data/lib/rigor/environment/deferred_signature_plan.rb +104 -6
  71. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  72. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  73. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  74. data/lib/rigor/environment/rbs_hierarchy.rb +6 -0
  75. data/lib/rigor/environment/rbs_loader.rb +312 -63
  76. data/lib/rigor/environment.rb +131 -35
  77. data/lib/rigor/inference/acceptance.rb +50 -15
  78. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  79. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  80. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  81. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  82. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  83. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  84. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  85. data/lib/rigor/inference/block_run_count.rb +9 -2
  86. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  87. data/lib/rigor/inference/callee_mutation_chain.rb +1 -1
  88. data/lib/rigor/inference/captured_locals.rb +2 -0
  89. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  90. data/lib/rigor/inference/clause_widening.rb +1 -1
  91. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  92. data/lib/rigor/inference/construction_escape.rb +56 -15
  93. data/lib/rigor/inference/declaration_walk.rb +4 -0
  94. data/lib/rigor/inference/def_node_resolver.rb +32 -14
  95. data/lib/rigor/inference/def_return_typer.rb +1 -1
  96. data/lib/rigor/inference/definer_resolution.rb +15 -7
  97. data/lib/rigor/inference/definers_index.rb +72 -16
  98. data/lib/rigor/inference/error_info.rb +16 -0
  99. data/lib/rigor/inference/expression_typer.rb +1459 -287
  100. data/lib/rigor/inference/flow_tracer.rb +31 -14
  101. data/lib/rigor/inference/fork_map.rb +3 -0
  102. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  103. data/lib/rigor/inference/global_write_census.rb +8 -6
  104. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  105. data/lib/rigor/inference/indexed_narrowing.rb +57 -8
  106. data/lib/rigor/inference/ivar_writer_census.rb +106 -11
  107. data/lib/rigor/inference/key_presence_guard.rb +51 -25
  108. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  109. data/lib/rigor/inference/last_line.rb +5 -2
  110. data/lib/rigor/inference/loop_value_record.rb +63 -15
  111. data/lib/rigor/inference/match_rebinding/frame.rb +11 -0
  112. data/lib/rigor/inference/match_rebinding/yielding_match.rb +74 -18
  113. data/lib/rigor/inference/match_rebinding.rb +21 -8
  114. data/lib/rigor/inference/method_dispatcher/block_folding.rb +43 -29
  115. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  116. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  117. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  118. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  119. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  120. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  121. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +12 -0
  122. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +409 -67
  123. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +1 -1
  124. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +384 -64
  125. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  126. data/lib/rigor/inference/method_dispatcher.rb +66 -18
  127. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  128. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  129. data/lib/rigor/inference/module_function_state.rb +13 -2
  130. data/lib/rigor/inference/multi_target_binder.rb +1 -1
  131. data/lib/rigor/inference/mutation_widening.rb +44 -4
  132. data/lib/rigor/inference/narrowing.rb +201 -27
  133. data/lib/rigor/inference/operand_effects.rb +43 -2
  134. data/lib/rigor/inference/optimistic_origin.rb +133 -8
  135. data/lib/rigor/inference/origin_lookup.rb +1 -1
  136. data/lib/rigor/inference/precision_scanner.rb +1 -1
  137. data/lib/rigor/inference/project_method_ownership.rb +28 -10
  138. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  139. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  140. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  141. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  142. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  143. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  144. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +56 -18
  145. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -17
  146. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  147. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  148. data/lib/rigor/inference/remover_holder_scan.rb +76 -22
  149. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  150. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +12 -11
  151. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +188 -28
  152. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  153. data/lib/rigor/inference/scope_indexer.rb +2079 -183
  154. data/lib/rigor/inference/self_call_ivar_writes.rb +26 -7
  155. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  156. data/lib/rigor/inference/singleton_hook_decline.rb +41 -6
  157. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  158. data/lib/rigor/inference/statement_evaluator.rb +970 -250
  159. data/lib/rigor/inference/string_mutation.rb +14 -0
  160. data/lib/rigor/inference/synthetic_method_scanner.rb +35 -11
  161. data/lib/rigor/inference/toplevel_def_census.rb +14 -6
  162. data/lib/rigor/inference/unknown_store_widening.rb +1 -1
  163. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  164. data/lib/rigor/inference/void_tail_summary.rb +29 -10
  165. data/lib/rigor/lens/claims/comparison.rb +78 -0
  166. data/lib/rigor/lens/claims/request.rb +131 -0
  167. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  168. data/lib/rigor/lens/claims.rb +208 -0
  169. data/lib/rigor/lens/contract_reader.rb +5 -0
  170. data/lib/rigor/lens/document.rb +1 -1
  171. data/lib/rigor/lens/file_reader.rb +21 -8
  172. data/lib/rigor/lens/run.rb +8 -0
  173. data/lib/rigor/lens.rb +1 -1
  174. data/lib/rigor/library_probe.rb +68 -0
  175. data/lib/rigor/memo_audit.rb +282 -0
  176. data/lib/rigor/plugin/base.rb +15 -2
  177. data/lib/rigor/plugin/io_boundary.rb +25 -3
  178. data/lib/rigor/plugin/manifest.rb +7 -3
  179. data/lib/rigor/plugin/registry.rb +13 -2
  180. data/lib/rigor/plugin/trust_policy.rb +46 -0
  181. data/lib/rigor/protection/mutation_cache.rb +4 -1
  182. data/lib/rigor/reflection/constant_ancestors.rb +79 -22
  183. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  184. data/lib/rigor/reflection/declarations.rb +58 -0
  185. data/lib/rigor/reflection/project_method.rb +177 -0
  186. data/lib/rigor/reflection.rb +2 -0
  187. data/lib/rigor/rooted_glob.rb +18 -1
  188. data/lib/rigor/run_scoped_slots.rb +20 -20
  189. data/lib/rigor/scope/discovery_index.rb +149 -2
  190. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  191. data/lib/rigor/scope/resolution_chain.rb +900 -55
  192. data/lib/rigor/scope.rb +307 -34
  193. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  194. data/lib/rigor/sig_gen/classification.rb +14 -1
  195. data/lib/rigor/sig_gen/generator.rb +222 -13
  196. data/lib/rigor/sig_gen/method_candidate.rb +14 -0
  197. data/lib/rigor/sig_gen/observation_collector.rb +11 -2
  198. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  199. data/lib/rigor/sig_gen/skip_reason_catalog.rb +47 -0
  200. data/lib/rigor/sig_gen/writer.rb +21 -0
  201. data/lib/rigor/sig_gen.rb +1 -0
  202. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  203. data/lib/rigor/source/node_children.rb +13 -0
  204. data/lib/rigor/type/combinator.rb +164 -8
  205. data/lib/rigor/type/interface.rb +60 -0
  206. data/lib/rigor/type.rb +1 -0
  207. data/lib/rigor/type_of_memo/serving.rb +99 -0
  208. data/lib/rigor/type_of_memo/stats.rb +43 -0
  209. data/lib/rigor/type_of_memo.rb +337 -0
  210. data/lib/rigor/typing_state.rb +1094 -0
  211. data/lib/rigor/version.rb +1 -1
  212. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +2 -2
  213. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +57 -25
  214. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  215. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  216. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  217. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  218. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  219. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +130 -30
  220. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -5
  221. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  222. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +49 -13
  223. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  224. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +4 -2
  225. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +13 -5
  226. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +29 -0
  227. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  228. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  229. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -2
  230. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  231. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +6 -0
  232. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +199 -1
  233. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +3 -2
  234. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +4 -4
  235. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +209 -2
  236. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  237. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  238. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +80 -3
  239. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  240. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +1 -6
  241. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  242. data/sig/rigor/analysis/fact_store.rbs +2 -3
  243. data/sig/rigor/cache/descriptor.rbs +14 -0
  244. data/sig/rigor/environment.rbs +4 -1
  245. data/sig/rigor/plugin/base.rbs +53 -19
  246. data/sig/rigor/plugin/interfaces.rbs +51 -0
  247. data/sig/rigor/plugin/io_boundary.rbs +2 -3
  248. data/sig/rigor/plugin/node_context.rbs +21 -0
  249. data/sig/rigor/reflection.rbs +37 -0
  250. data/sig/rigor/scope.rbs +22 -3
  251. data/sig/rigor/trinary.rbs +1 -0
  252. data/sig/rigor/type.rbs +9 -3
  253. data/sig/rigor.rbs +3 -2
  254. data/skills/rigor-ask/SKILL.md +1 -0
  255. data/skills/rigor-project-init/references/01-detect.md +1 -0
  256. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +5 -1
  257. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  258. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  259. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  260. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  261. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  262. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  263. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  264. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  265. data/skills/rigor-type-oracle/SKILL.md +26 -1
  266. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  267. metadata +48 -2
  268. data/lib/rigor/inference/memo_audit.rb +0 -55
@@ -82,11 +82,16 @@ module Rigor
82
82
  # the entry's answer, as `[owner_name, value]` (the owner is part of the answer so that two worlds naming
83
83
  # different parents disagree). It is the override lints' parent read: the nearest project ancestor after the
84
84
  # class whose block answers.
85
+ #
86
+ # Issue #3156 — a class or module the project declares but whose Ruby body records no method, superclass or
87
+ # include (`module PMod; end`, `class AMid` / `end`) is an external entry on the chain, which only its RBS
88
+ # declaration speaks for. The override block answers from RBS, so such an entry is asked too, under the
89
+ # project name it resolves to (`ResolutionChain::Entry#declared_name`); an undeclared external is not.
85
90
  def override_answer(scope)
86
91
  lambda do |chain, from|
87
- first_hit(chain, scope, from) do |entry|
88
- value = yield(entry.name)
89
- [entry.name, value] unless value.nil?
92
+ first_hit(chain, scope, from, askable_external: true) do |name|
93
+ value = yield(name)
94
+ [name, value] unless value.nil?
90
95
  end
91
96
  end
92
97
  end
@@ -331,17 +336,20 @@ module Rigor
331
336
  kind == :singleton ? [:singleton, method_name.to_sym] : method_name.to_sym
332
337
  end
333
338
 
334
- def first_hit(chain, scope, from)
339
+ # The first entry at or after `from` whose block answers. The default answer functions are handed the project
340
+ # entry; the override one (`askable_external:`) is handed a name, and also asks an external entry the project
341
+ # declares.
342
+ def first_hit(chain, scope, from, askable_external: false)
335
343
  index = from - 1
336
344
  found = nil
337
345
  chain.search(scope, from) do |entry|
338
346
  index += 1
339
- next if entry.external?
347
+ next unless (name = entry.external? ? askable_external && entry.declared_name(scope) : entry.name)
340
348
 
341
- answer = yield(entry)
349
+ answer = yield(askable_external ? name : entry)
342
350
  next if answer.nil?
343
351
 
344
- found = Hit.new(answer, entry.name, index, entry.side)
352
+ found = Hit.new(answer, name, index, entry.side)
345
353
  end
346
354
  found
347
355
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../run_scoped_slots"
3
+ require_relative "../typing_state"
4
+ require_relative "../memo_audit"
4
5
 
5
6
  module Rigor
6
7
  module Inference
@@ -21,12 +22,32 @@ module Rigor
21
22
  module DefinersIndex
22
23
  # These thread slots are bounded, like `ExpressionTyper#method_definers_index`'s two ways. Each is a fixed
23
24
  # number of entries, replaced rather than accumulated: two `[merged, seed]` pairs (the last file's two def
24
- # tables) and two seed indexes. The run that filled them releases them when it ends ({RunScopedSlots}, #2399),
25
+ # tables) and two seed indexes. The run that filled them releases them when it ends (run-scoped, #2399),
25
26
  # so a finished run keeps none of its seed tables or their indexes alive.
26
- LAYERS_KEY = RunScopedSlots.register(:__rigor_definers_index_layers__)
27
- SEED_INDEX_KEY = RunScopedSlots.register(:__rigor_definers_index_seed__)
28
- SEED_INDEX_ALT_KEY = RunScopedSlots.register(:__rigor_definers_index_seed_alt__)
29
- private_constant :LAYERS_KEY, :SEED_INDEX_KEY, :SEED_INDEX_ALT_KEY
27
+ #
28
+ # ADR-129 WD1 — all three are run-scoped Memos keyed on table identity. The layers say only which seed a merged
29
+ # table was built over, a shortcut: {.for} answers the same index owners, `Layered` or whole, so the answer is
30
+ # a function of the table alone. Each seed index is a pure function of its seed table.
31
+ #
32
+ # Each hit goes through `MemoAudit.check`: a `Layered` answer is compared, in {.canonical} form, with the table
33
+ # inverted whole, and a seed index with the seed inverted again. While an audit recomputation runs
34
+ # ({MemoAudit.suspended?}) the three are neither read nor written, so it inverts every table whole.
35
+ LAYERS = TypingState.memo(
36
+ :definers_index_layers,
37
+ run_scoped: true, audit: "definers index layers",
38
+ reason: "the seed each of the last two merged tables was built over; keyed on table identity"
39
+ )
40
+ SEED_INDEX = TypingState.memo(
41
+ :definers_index_seed,
42
+ run_scoped: true, audit: "definers index seed",
43
+ reason: "pure function of the seed table it keys on"
44
+ )
45
+ SEED_INDEX_ALT = TypingState.memo(
46
+ :definers_index_seed_alt,
47
+ run_scoped: true, audit: "definers index seed alt",
48
+ reason: "pure function of the seed table it keys on"
49
+ )
50
+ private_constant :LAYERS, :SEED_INDEX, :SEED_INDEX_ALT
30
51
 
31
52
  # A name's owners: the seed's, less the owners the file replaced, plus the replaced owners' own.
32
53
  class Layered
@@ -46,6 +67,9 @@ module Rigor
46
67
  owners = from_file.nil? ? from_seed : (from_seed || []) + from_file
47
68
  @answers[method_name] = owners.nil? || owners.empty? ? nil : owners
48
69
  end
70
+
71
+ # Every name some owner may define, for {DefinersIndex.canonical}.
72
+ def names = @seed_index.keys | @replaced_index.keys
49
73
  end
50
74
 
51
75
  module_function
@@ -53,13 +77,18 @@ module Rigor
53
77
  # Records that the final `merged` table was built over `seed`. The last two are kept (a file merges an
54
78
  # instance and a singleton table), replaced rather than accumulated.
55
79
  def record_layers(merged, seed)
56
- previous = Thread.current[LAYERS_KEY]
57
- Thread.current[LAYERS_KEY] = [[merged, seed], previous&.first].compact
80
+ return if MemoAudit.enabled? && MemoAudit.suspended?
81
+
82
+ state = TypingState.current
83
+ previous = state[LAYERS]
84
+ state[LAYERS] = [[merged, seed], previous&.first].compact
58
85
  end
59
86
 
60
87
  # The definers index over `table`.
61
88
  def for(table)
62
- seed = Thread.current[LAYERS_KEY]&.find { |entry| entry[0].equal?(table) }&.last
89
+ return build(table) if MemoAudit.suspended?
90
+
91
+ seed = TypingState.current[LAYERS]&.find { |entry| entry[0].equal?(table) }&.last
63
92
  return build(table) if seed.nil? || !deeply_frozen?(seed)
64
93
 
65
94
  replaced = {}
@@ -73,7 +102,25 @@ module Rigor
73
102
  end
74
103
  return build(table) unless kept + replaced.count { |owner, _| seed.key?(owner) } == seed.size
75
104
 
76
- Layered.new(seed_index(seed), replaced, build(replaced))
105
+ audit_layered(Layered.new(seed_index(seed), replaced, build(replaced)), table)
106
+ end
107
+
108
+ # `index`, after checking it against `table` inverted whole when the audit is on.
109
+ def audit_layered(index, table)
110
+ return index unless MemoAudit.enabled?
111
+
112
+ MemoAudit.check("definers index layers", canonical(index), table.size) { canonical(build(table)) }
113
+ index
114
+ end
115
+
116
+ # `index` as a plain `name → sorted owners` Hash, so a `Layered` index and a whole one compare equal when they
117
+ # hold the same owners. Audit-only: it reads every name.
118
+ def canonical(index)
119
+ names = index.is_a?(Layered) ? index.names : index.keys
120
+ names.each_with_object({}) do |name, out|
121
+ owners = index[name]
122
+ out[name] = owners.sort if owners
123
+ end
77
124
  end
78
125
 
79
126
  def deeply_frozen?(seed)
@@ -82,13 +129,22 @@ module Rigor
82
129
 
83
130
  # The seed's index, built once per seed table. Two ways, an instance and a singleton seed table.
84
131
  def seed_index(seed)
85
- [SEED_INDEX_KEY, SEED_INDEX_ALT_KEY].each do |key|
86
- slot = Thread.current[key]
87
- return slot[1] if slot && slot[0].equal?(seed)
132
+ state = TypingState.current
133
+ slot = state[SEED_INDEX]
134
+ if slot && slot[0].equal?(seed)
135
+ return MemoAudit.check("definers index seed", slot[1], seed.size) do
136
+ build(seed)
137
+ end
88
138
  end
89
- index = build(seed)
90
- Thread.current[SEED_INDEX_ALT_KEY] = Thread.current[SEED_INDEX_KEY]
91
- Thread.current[SEED_INDEX_KEY] = [seed, index]
139
+
140
+ slot = state[SEED_INDEX_ALT]
141
+ if slot && slot[0].equal?(seed)
142
+ return MemoAudit.check("definers index seed alt", slot[1], seed.size) { build(seed) }
143
+ end
144
+
145
+ index = MemoAudit.key_audit("definers index seed") { build(seed) }
146
+ state[SEED_INDEX_ALT] = state[SEED_INDEX]
147
+ state[SEED_INDEX] = [seed, index]
92
148
  index
93
149
  end
94
150
 
@@ -37,6 +37,22 @@ module Rigor
37
37
 
38
38
  module_function
39
39
 
40
+ # The exception instance type a `RescueNode` binds its reference to: `StandardError` for a bare `rescue`, else
41
+ # the union over the named exceptions, each the instance of the class the block types it as (`yield`ed the
42
+ # exception node) or `Dynamic[top]` when that is not a `Singleton` (a splat list, an unresolved constant).
43
+ # Shared by `StatementEvaluator#rescue_exception_type` and the class-ivar census (issue #1217), so the two
44
+ # agree.
45
+ def rescue_exception_type(rescue_node)
46
+ exceptions = rescue_node.exceptions
47
+ return Type::Combinator.nominal_of("StandardError") if exceptions.empty?
48
+
49
+ types = exceptions.map do |exception_node|
50
+ type = yield(exception_node)
51
+ type.is_a?(Type::Singleton) ? Type::Combinator.nominal_of(type.class_name) : Type::Combinator.untyped
52
+ end
53
+ Type::Combinator.union(*types)
54
+ end
55
+
40
56
  # The scope a `rescue` clause, or a rescue modifier's fallback, runs from: `$!` bound to the exception it rescued
41
57
  # and `$@` to that exception's backtrace, with `$?` unbound. `exception_type` is what `rescue … => e` binds `e` to
42
58
  # (`StatementEvaluator#rescue_exception_type`): the union of the named classes' instances, `StandardError` for a