rigortype 0.4.6 → 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 (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  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 +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../typing_state"
4
+ require_relative "../memo_audit"
5
+
3
6
  module Rigor
4
7
  # {Reflection}'s constant-ancestor walk ([#354](https://github.com/rigortype/rigor/issues/354)), split out for
5
8
  # the reader rather than for the loader: the whole-spelling ladder's step 2 (`reflection.rb`) and the
@@ -7,13 +10,23 @@ module Rigor
7
10
  # name's owner inherits constants from, and this file answers that once for both. Reopening the module
8
11
  # keeps the walk behind the facade its callers already read.
9
12
  module Reflection
10
- # #354 — thread-local slot for the per-run ancestor-scope memo. See {.ancestor_constant_scopes}.
11
- ANCESTOR_SCOPES_KEY = :__rigor_ancestor_constant_scopes__
12
- private_constant :ANCESTOR_SCOPES_KEY
13
-
14
- # The recording run's slot. See {.recorded_ancestor_constant_scopes}.
15
- RECORDED_ANCESTOR_SCOPES_KEY = :__rigor_recorded_ancestor_constant_scopes__
16
- private_constant :RECORDED_ANCESTOR_SCOPES_KEY
13
+ # #354 — the per-run ancestor-scope memo. See {.ancestor_constant_scopes}. ADR-129 WD1 — a run-scoped Memo.
14
+ ANCESTOR_SCOPES = TypingState.memo(
15
+ :ancestor_constant_scopes,
16
+ run_scoped: true, audit: "ancestor constant scopes",
17
+ reason: "one [discovery, scopes by class name] slot keyed on the discovery index's identity, as the " \
18
+ "resolution chain the scopes are read from is (#2848)"
19
+ )
20
+ private_constant :ANCESTOR_SCOPES
21
+
22
+ # The recording run's slot. See {.recorded_ancestor_constant_scopes}. ADR-129 WD1 — a run-scoped Memo.
23
+ RECORDED_ANCESTOR_SCOPES = TypingState.memo(
24
+ :recorded_ancestor_constant_scopes,
25
+ run_scoped: true, audit: "recorded ancestor constant scopes",
26
+ reason: "one [discovery, scopes by class name] slot keyed on the discovery index's identity; a hit replays " \
27
+ "the chain's edges, so the key is exact"
28
+ )
29
+ private_constant :RECORDED_ANCESTOR_SCOPES
17
30
 
18
31
  module_function
19
32
 
@@ -35,11 +48,13 @@ module Rigor
35
48
  # constant it owns resolves only if the bare name reaches it at step 3: widening to the superclass's
36
49
  # RBS graph is a separate question with its own FP surface. See {.external_constant_owner}.
37
50
  #
38
- # Memoised per run because step 2 runs on every constant reference whose lexical candidates all
39
- # miss — which is the common case for a core-class reference (`String` inside `class Foo`). The
40
- # bucket keys on the identity of the runner-seeded run-generation token (ADR-84 WD2), falling
41
- # back to the per-file discovery table for runner-less scopes, so a re-run in one process (LSP,
42
- # ADR-62 warm loop) cannot hit stale entries.
51
+ # Memoised because step 2 runs on every constant reference whose lexical candidates all miss — which
52
+ # is the common case for a core-class reference (`String` inside `class Foo`). The bucket keys on the
53
+ # identity of the discovery index, as the `Scope::ResolutionChain` memo it reads does: the chain's
54
+ # order depends on the analysing file's own view of the include order (a class declared in two files
55
+ # orders its `include`s from the reader's file first), so a run-generation key served one file's
56
+ # order to another (#2848). A re-run in one process (LSP, ADR-62 warm loop) builds a new index, so it
57
+ # cannot hit stale entries either.
43
58
  def ancestor_constant_scopes(class_name, scope)
44
59
  ancestor_constant_worlds(class_name, scope).first
45
60
  end
@@ -85,32 +100,76 @@ module Rigor
85
100
  # every call.
86
101
  return recorded_ancestor_constant_scopes(class_name, scope) if Analysis::DependencyRecorder.active?
87
102
 
88
- generation = scope.run_generation || scope.discovered_superclasses
89
- slot = Thread.current[ANCESTOR_SCOPES_KEY]
90
- unless slot && slot[0].equal?(generation)
91
- slot = [generation, {}]
92
- Thread.current[ANCESTOR_SCOPES_KEY] = slot
103
+ # ADR-129 G2 — an audit recomputation bypasses the memo (neither read nor written).
104
+ if Inference::MemoAudit.enabled? && Inference::MemoAudit.suspended?
105
+ return compute_ancestor_constant_scopes(class_name, scope)
106
+ end
107
+
108
+ discovery = scope.discovery
109
+ state = TypingState.current
110
+ slot = state[ANCESTOR_SCOPES]
111
+ unless slot && slot[0].equal?(discovery)
112
+ slot = [discovery, {}]
113
+ state[ANCESTOR_SCOPES] = slot
93
114
  end
94
115
  bucket = slot[1]
95
- bucket.fetch(class_name) { bucket[class_name] = compute_ancestor_constant_scopes(class_name, scope) }
116
+ worlds = bucket[class_name]
117
+ if worlds.nil?
118
+ return bucket[class_name] = Inference::MemoAudit.key_audit("ancestor constant scopes") do
119
+ compute_ancestor_constant_scopes(class_name, scope)
120
+ end
121
+ end
122
+ return worlds unless Inference::MemoAudit.enabled?
123
+
124
+ Inference::MemoAudit.check("ancestor constant scopes", audit_view(worlds), class_name) do
125
+ audit_view(compute_ancestor_constant_scopes(class_name, scope))
126
+ end
127
+ worlds
96
128
  end
97
129
  private_class_method :ancestor_constant_worlds
98
130
 
131
+ # `[scopes, chain]` compared by content: a recomputation builds a new chain object, equal to the stored one
132
+ # entry for entry when the key holds every input.
133
+ def audit_view(worlds) = [worlds[0], worlds[1].audit_signature]
134
+ private_class_method :audit_view
135
+
136
+ # A recomputation for the audit, its dependency reads withheld: the hit it checks files its own edges, so the
137
+ # recorded set stays what an unaudited run records.
138
+ def fresh_ancestor_constant_scopes(class_name, scope)
139
+ Analysis::DependencyRecorder.withhold { compute_ancestor_constant_scopes(class_name, scope) }.first
140
+ end
141
+ private_class_method :fresh_ancestor_constant_scopes
142
+
99
143
  # The recording run's memo. The answer is every project entry of the chain, so its edges are the
100
144
  # root's and every entry's — what `Scope::ResolutionChain#record` files, which a computation does and a
101
145
  # hit replays, in the same order. One slot keyed on the discovery index: the answer and the edges are
102
146
  # both read from it, so the key is exact, and the slot never pins more than the index it serves.
103
147
  def recorded_ancestor_constant_scopes(class_name, scope)
148
+ # ADR-129 G2 — an audit recomputation bypasses the memo (neither read nor written).
149
+ if Inference::MemoAudit.enabled? && Inference::MemoAudit.suspended?
150
+ return compute_ancestor_constant_scopes(class_name, scope)
151
+ end
152
+
104
153
  discovery = scope.discovery
105
- slot = Thread.current[RECORDED_ANCESTOR_SCOPES_KEY]
154
+ state = TypingState.current
155
+ slot = state[RECORDED_ANCESTOR_SCOPES]
106
156
  unless slot && slot[0].equal?(discovery)
107
157
  slot = [discovery, {}]
108
- Thread.current[RECORDED_ANCESTOR_SCOPES_KEY] = slot
158
+ state[RECORDED_ANCESTOR_SCOPES] = slot
109
159
  end
110
160
  bucket = slot[1]
111
161
  worlds = bucket[class_name]
112
- return bucket[class_name] = compute_ancestor_constant_scopes(class_name, scope) if worlds.nil?
162
+ if worlds.nil?
163
+ return bucket[class_name] = Inference::MemoAudit.key_audit("recorded ancestor constant scopes") do
164
+ compute_ancestor_constant_scopes(class_name, scope)
165
+ end
166
+ end
113
167
 
168
+ if Inference::MemoAudit.enabled?
169
+ Inference::MemoAudit.check("recorded ancestor constant scopes", audit_view(worlds), class_name) do
170
+ audit_view(fresh_ancestor_constant_scopes(class_name, scope))
171
+ end
172
+ end
114
173
  chain = Scope::ResolutionChain.for(scope, class_name.to_s, :instance, :constants)
115
174
  chain.record(scope)
116
175
  record_external_owner_edges(chain)
@@ -228,7 +287,7 @@ module Rigor
228
287
  end
229
288
  private_class_method :ancestry_unknown_before?
230
289
 
231
- # Ruby places these after every entry of a chain, so a constant they own is the top level's (step 4).
290
+ # Ruby places these after every entry of a chain, so a constant they own is the top level's.
232
291
  IMPLICIT_ANCESTORS = %w[Object Kernel BasicObject].freeze
233
292
  private_constant :IMPLICIT_ANCESTORS
234
293
 
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ # Not required on its own: `rigor/inference/declaration_walk` loads it once the walk exists, and
6
+ # {Reflection.class_declarations_in} loads that entry point on first use. Keeping the walk out of `Reflection`'s own
7
+ # load keeps the engine-free paths engine-free (ADR-104).
8
+ module Rigor
9
+ module Reflection
10
+ # The walk's collector behind {.class_declarations_in}; the plugin never sees it.
11
+ class DeclarationCollector
12
+ include Inference::DeclarationWalk::Collector
13
+
14
+ EMPTY_NESTING = [].freeze
15
+
16
+ attr_reader :classes
17
+
18
+ def initialize
19
+ @classes = []
20
+ end
21
+
22
+ def on_declaration(node, context, body_context)
23
+ if node.is_a?(Prism::ClassNode)
24
+ name = body_context.prefix.join("::")
25
+ @classes << ClassDeclaration.new(name: name, nesting: frozen_nesting(context), node: node) unless name.empty?
26
+ end
27
+ Inference::DeclarationWalk::DESCEND
28
+ end
29
+
30
+ private
31
+
32
+ # The walk's chain, frozen: the root's `[]` is one array shared by every top-level declaration.
33
+ def frozen_nesting(context)
34
+ nesting = context.nesting || EMPTY_NESTING
35
+ nesting.frozen? ? nesting : nesting.dup.freeze
36
+ end
37
+ end
38
+
39
+ # {DeclarationCollector} that also forwards every call. A subclass, because the walk dispatches only the events a
40
+ # collector's class overrides, so a classes-only run pays for no call event.
41
+ class DeclarationCallCollector < DeclarationCollector
42
+ def initialize(on_call)
43
+ super()
44
+ @on_call = on_call
45
+ end
46
+
47
+ def on_call(node, context)
48
+ owner = context.prefix.join("::")
49
+ @on_call.call(DeclaredCall.new(node: node, owner: owner.empty? ? nil : owner))
50
+ Inference::DeclarationWalk::DESCEND
51
+ end
52
+ end
53
+ private_constant :DeclarationCollector, :DeclarationCallCollector
54
+ end
55
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Rigor
6
+ # {Reflection}'s per-file declaration walk for plugins (ADR-128, #2708): {.class_declarations_in} answers which
7
+ # classes a parsed file declares under their `Module.nesting`-correct names, and hands each call the walk visits
8
+ # to a plugin together with the class or module its position reaches. It replaces a plugin's own
9
+ # `DeclarationWalk::Collector`, so the walk's context, its control constants and its collector protocol stay
10
+ # internal.
11
+ module Reflection
12
+ # One `class` header of a file, as {.class_declarations_in} reports it.
13
+ #
14
+ # - `name` — the qualified name the header's body is keyed under (`"Admin::User"` for `class User` in
15
+ # `module Admin`, and for `class Admin::User`); never empty.
16
+ # - `nesting` — `Module.nesting` where the header is written, innermost first, not counting the class itself
17
+ # (`["Admin"]` for `class User` in `module Admin`; `[]` at the top level). Frozen.
18
+ # - `node` — the `Prism::ClassNode`, for its `superclass` and `body`.
19
+ ClassDeclaration = Data.define(:name, :nesting, :node)
20
+
21
+ # One call the walk visited, as {.class_declarations_in} yields it.
22
+ #
23
+ # - `node` — the `Prism::CallNode`.
24
+ # - `owner` — the qualified name of the innermost `class` / `module` the call is lexically inside, or nil where
25
+ # none is (the top level, or below a bare header in a `class << self` body).
26
+ DeclaredCall = Data.define(:node, :owner)
27
+
28
+ module_function
29
+
30
+ # ADR-128, #2708 — the classes a parsed file declares, in the order their headers are written. A class
31
+ # reopened in the file appears once per header. A `module`, a `class << self` and a class no name reaches
32
+ # (`Class.new { … }`, a header below `class << self`) are not reported.
33
+ #
34
+ # With a block, the walk also yields a {DeclaredCall} for the calls the engine's declaration walk visits, in
35
+ # source order, outer calls before the calls inside them. Those are the calls in statements and in the bodies of
36
+ # `class` / `module` / `class << self` / blocks, at any depth. The owner is lexical, not `self`: a call in
37
+ # `class << self` or in a `Class.new { … }` block reports the class around it.
38
+ #
39
+ # The block does not see every call node of the file. The walk does not enter, so the block never sees:
40
+ #
41
+ # - a class or module header's own expressions: the superclass (`class Foo < Struct.new(:a)`) and the constant
42
+ # path (`class foo::Bar`);
43
+ # - the factory call a constant write binds (`K = Class.new { … }`), whose receiver, arguments and block body the
44
+ # walk does enter;
45
+ # - the tail of such a write (`K = Class.new { … }.freeze` yields what is inside the block, not `freeze`), and
46
+ # the constant-path parent of a write target (`P` in `P::K = …`).
47
+ #
48
+ # `root` is a `Prism::Node`, typically `Prism.parse(source).value`.
49
+ def class_declarations_in(root, &on_call)
50
+ # Loaded on first use, not with `Reflection`: the walk heads the analysis engine, which the engine-free
51
+ # paths (a warm `rigor effects`) load `Reflection` without. The walk's entry point loads the collectors.
52
+ require_relative "../inference/declaration_walk"
53
+ collector = on_call ? DeclarationCallCollector.new(on_call) : DeclarationCollector.new
54
+ Inference::DeclarationWalk.run(root, [collector], Inference::DeclarationWalk::Context.root(nesting: []))
55
+ collector.classes
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ # {Reflection}'s ancestor facade for plugins (ADR-128 WD1), split out for the reader rather than for the
5
+ # loader: {.project_method_defined} turns the two `Scope` ancestor walks into one `Trinary` answer. Reopening
6
+ # the module keeps it behind the facade its callers already read.
7
+ module Reflection
8
+ module_function
9
+
10
+ PROJECT_METHOD_KINDS = %i[instance singleton].freeze
11
+ PROJECT_METHOD_SOURCES = %i[def discovered either].freeze
12
+ private_constant :PROJECT_METHOD_KINDS, :PROJECT_METHOD_SOURCES
13
+
14
+ # ADR-128 WD1 — does the project define `method_name` on `class_name` or on an ancestor the project itself
15
+ # declares? Answers a {Trinary}: `yes` where the walk found a definer, `no` where it found none and its
16
+ # `ResolutionChain` was not truncated, and `maybe` where the chain was truncated at its budget before it found one
17
+ # (ADR-41 WD4), so a definer past the cut cannot be ruled out. It replaces a plugin's direct call to the
18
+ # two `Scope` walks, whose budget answers differ and are not a promise to plugin authors.
19
+ #
20
+ # - `kind:` is `:instance` or `:singleton`; each walks its own chain.
21
+ # - `source:` names the walk outright, with that walk's order and settling:
22
+ # - `:def` — the first project `def` (`Scope#user_def_through_ancestors`, or `#singleton_def_through_ancestors`
23
+ # for `kind: :singleton`). It does not count `attr_*` or `define_method`.
24
+ # - `:discovered` — the discovery table (`Scope#discovered_method_through_ancestors?`), which also counts
25
+ # `attr_*` and `define_method`.
26
+ # - `:either` — their union as a three-valued OR: `yes` wins, then `maybe`, then `no`.
27
+ #
28
+ # A site that tested the `:def` walk's old answer tests `.yes?` (that walk read a cut as absent); a site that
29
+ # tested the `:discovered` walk's tests `!no?` (that walk read a cut as present).
30
+ def project_method_defined(class_name, method_name, kind:, source:, scope:)
31
+ raise ArgumentError, "unknown kind #{kind.inspect}" unless PROJECT_METHOD_KINDS.include?(kind)
32
+ raise ArgumentError, "unknown source #{source.inspect}" unless PROJECT_METHOD_SOURCES.include?(source)
33
+
34
+ case source
35
+ when :def then project_def_defined(class_name, method_name, kind, scope)
36
+ when :discovered then project_discovered_defined(class_name, method_name, kind, scope)
37
+ else project_either_defined(class_name, method_name, kind, scope)
38
+ end
39
+ end
40
+
41
+ # Does RBS declare `method_name` on `class_name` or on an ancestor the project's own ancestry reaches? A
42
+ # project class (`class ArticlesController < ActionController::Base`) is rarely in RBS itself; its RBS-known
43
+ # ancestors are the EXTERNAL entries of its `ResolutionChain`, which the RBS-only reads
44
+ # ({.instance_method_definition}) cannot reach from the project class's name. This walks the chain and asks RBS
45
+ # of each entry (every candidate spelling of an external one), on the side the entry sits.
46
+ #
47
+ # Answers a {Trinary}: `yes` when an entry's RBS declares the method, `no` when none does and the chain was
48
+ # not truncated, `maybe` when the chain was cut at its budget (ADR-41 WD4) before one did. `kind:` is
49
+ # `:instance` or `:singleton`; a `nil` `class_name` answers `no`.
50
+ def rbs_method_defined(class_name, method_name, kind:, scope:)
51
+ chain = ancestry_chain(class_name, kind, scope)
52
+ return Trinary.no if chain.nil?
53
+
54
+ found = chain.search(scope) do |entry|
55
+ chain_entry_names(entry).any? { |name| !rbs_method_on_side(name, method_name, entry.side, scope).nil? }
56
+ end
57
+ chain_answer(found, chain)
58
+ end
59
+
60
+ # Does the project ancestry of `class_name` reach any of `ancestor_names` (qualified names, a leading `::`
61
+ # ignored)? An external ancestor counts by any candidate spelling, so a superclass the project names but
62
+ # does not declare (`ActionController::Base`, with no RBS loaded) is found. Same `Trinary` and `kind:` as
63
+ # {.rbs_method_defined}.
64
+ def ancestry_reaches(class_name, ancestor_names, kind:, scope:)
65
+ chain = ancestry_chain(class_name, kind, scope)
66
+ return Trinary.no if chain.nil?
67
+
68
+ wanted = ancestor_names.map { |name| name.to_s.delete_prefix("::") }
69
+ found = chain.search(scope) do |entry|
70
+ chain_entry_names(entry).any? { |name| wanted.include?(name.to_s.delete_prefix("::")) }
71
+ end
72
+ chain_answer(found, chain)
73
+ end
74
+
75
+ def ancestry_chain(class_name, kind, scope)
76
+ raise ArgumentError, "unknown kind #{kind.inspect}" unless PROJECT_METHOD_KINDS.include?(kind)
77
+ return nil if class_name.nil?
78
+
79
+ Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
80
+ end
81
+ private_class_method :ancestry_chain
82
+
83
+ def chain_answer(found, chain)
84
+ return Trinary.yes if found
85
+
86
+ chain.truncated? ? Trinary.maybe : Trinary.no
87
+ end
88
+ private_class_method :chain_answer
89
+
90
+ def chain_entry_names(entry)
91
+ entry.external? ? entry.candidates : [entry.name]
92
+ end
93
+ private_class_method :chain_entry_names
94
+
95
+ def rbs_method_on_side(class_name, method_name, side, scope)
96
+ if side == :singleton
97
+ singleton_method_definition(class_name, method_name, scope: scope)
98
+ else
99
+ instance_method_definition(class_name, method_name, scope: scope)
100
+ end
101
+ end
102
+ private_class_method :rbs_method_on_side
103
+
104
+ # `yes` wins, then `maybe`, then `no`. The early return is a short-circuit and carries no answer of its own:
105
+ # `yes.or(x)` is `yes`, so removing it changes only which walks run, and so which dependencies they read.
106
+ def project_either_defined(class_name, method_name, kind, scope)
107
+ by_def = project_def_defined(class_name, method_name, kind, scope)
108
+ return by_def if by_def.yes?
109
+
110
+ by_def.or(project_discovered_defined(class_name, method_name, kind, scope))
111
+ end
112
+ private_class_method :project_either_defined
113
+
114
+ # The def walk answers `[node, owner]` or `[nil, nil]`, and `[nil, nil]` both when the chain holds no definer
115
+ # and when it was cut; the chain (memoised, so this is the one the walk read) tells the two apart.
116
+ def project_def_defined(class_name, method_name, kind, scope)
117
+ found = if kind == :singleton
118
+ scope.singleton_def_through_ancestors(class_name, method_name)
119
+ else
120
+ scope.user_def_through_ancestors(class_name, method_name)
121
+ end
122
+ return Trinary.yes if found.first
123
+
124
+ project_chain_truncated?(class_name, kind, scope) ? Trinary.maybe : Trinary.no
125
+ end
126
+ private_class_method :project_def_defined
127
+
128
+ # The discovery walk answers `true` both for a definer and for a cut chain, so a `true` on a cut chain is
129
+ # settled by searching the chain's entries for a definer, as the walk does.
130
+ #
131
+ # Issue #2968 — where the walk finds none, a class on the chain whose side is open (an open-names mark: a
132
+ # splatted `Struct.new(*FIELDS)`, `attr_accessor(*NAMES)`, a `define_method` under a computed name) answers
133
+ # `maybe`: it defines methods under names no table holds. Issue #2994 — so does an instance side whose chain mixes
134
+ # in a module no table names (`include(*MODS)`, `ResolutionChain#unnamed_mixin?`); the #618 top-level-`def` veto
135
+ # (`ExpressionTyper#instance_self_answers?`) reads the same mark, so a plugin that stands down here does not hand
136
+ # the call to a top-level `def`.
137
+ def project_discovered_defined(class_name, method_name, kind, scope)
138
+ unless scope.discovered_method_through_ancestors?(class_name, method_name, kind)
139
+ return open_side?(class_name, kind, scope) ? Trinary.maybe : Trinary.no
140
+ end
141
+ # A shortcut, not a decision: on an untruncated chain the search below finds the same definer.
142
+ return Trinary.yes unless project_chain_truncated?(class_name, kind, scope)
143
+
144
+ chain = Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
145
+ found = chain.search(scope) do |entry|
146
+ # A guard, not a decision: it mirrors `Scope#discovered_method_through_ancestors?`, whose search skips
147
+ # an external entry (no project name to look up). `discovered_method?(nil, ...)` is false anyway.
148
+ next false if entry.external?
149
+
150
+ scope.discovered_method?(entry.name, method_name, discovered_entry_kind(entry, kind))
151
+ end
152
+ found ? Trinary.yes : Trinary.maybe
153
+ end
154
+ private_class_method :project_discovered_defined
155
+
156
+ def open_side?(class_name, kind, scope)
157
+ return true if Scope::DiscoveryIndex.open_names_on_chain?(scope, class_name, kind)
158
+
159
+ kind == :instance && Scope::DiscoveryIndex.unnamed_mixin_on_chain?(scope, class_name)
160
+ end
161
+ private_class_method :open_side?
162
+
163
+ # A singleton-side entry reads the singleton table; a module on the singleton chain answers with its
164
+ # INSTANCE methods; on the instance chain the caller's kind stands (`Scope#kind_for`).
165
+ def discovered_entry_kind(entry, kind)
166
+ return :singleton if entry.side == :singleton
167
+
168
+ kind == :singleton ? :instance : kind
169
+ end
170
+ private_class_method :discovered_entry_kind
171
+
172
+ def project_chain_truncated?(class_name, kind, scope)
173
+ Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods).truncated?
174
+ end
175
+ private_class_method :project_chain_truncated?
176
+ end
177
+ end
@@ -511,3 +511,5 @@ end
511
511
  # private candidate lookups, so the facade has to exist first.
512
512
  require_relative "reflection/constant_ancestors"
513
513
  require_relative "reflection/constant_path"
514
+ require_relative "reflection/project_method"
515
+ require_relative "reflection/declarations"
@@ -20,7 +20,7 @@ module Rigor
20
20
 
21
21
  module_function
22
22
 
23
- # @param root — a directory path (String or Pathname), taken literally
23
+ # @param root — a directory path, taken literally
24
24
  # @param pattern — a `Dir.glob` pattern relative to `root`
25
25
  # @return the matches spelled under `root`, in `Dir.glob` order; empty when `root` is not a directory
26
26
  def glob(root, pattern)
@@ -32,5 +32,36 @@ module Rigor
32
32
  def literal(name)
33
33
  name.to_s.gsub(METACHARACTERS, "\\\\\\0")
34
34
  end
35
+
36
+ # A configured file path (#2222), absolute or relative to the working directory, as the `[root, pattern]` pair
37
+ # that `glob(root, pattern)` reads as exactly that file. A relative path stays under the working directory, as
38
+ # it always did. An absolute one is split into its own directory and a literal basename: `File.join(".", "/abs")`
39
+ # reads `./abs`, so an absolute path joined under `"."` matched nothing.
40
+ #
41
+ # A `~`-prefixed path is expanded before the split (#2586): the readers apply `File.expand_path`, so `~/x.yml`
42
+ # reads `$HOME/x.yml` and the watch must be split from that spelling — `[".", "~/x.yml"]` would watch
43
+ # `./~/x.yml` and never fire. Only the `~` form is expanded; expanding every path would change how relative
44
+ # entries are watched.
45
+ #
46
+ # @param path — a file path, taken literally
47
+ # @return `[root, pattern]`: `"."` and the literal path when relative, else the directory and the literal basename
48
+ def split_file(path)
49
+ path = expand_home(path.to_s)
50
+ return [".", literal(path)] unless File.absolute_path?(path)
51
+
52
+ [File.dirname(path), literal(File.basename(path))]
53
+ end
54
+
55
+ # `File.expand_path`'s `~` handling, kept literal on failure: `~nouser/x` (or an unset HOME) names no home
56
+ # directory and raises `ArgumentError`; the readers fail on the same path, so the unexpanded spelling —
57
+ # watched under `"."` like any other relative entry — is the consistent answer.
58
+ def expand_home(path)
59
+ return path unless path.start_with?("~")
60
+
61
+ File.expand_path(path)
62
+ rescue ArgumentError
63
+ path
64
+ end
65
+ private_class_method :expand_home
35
66
  end
36
67
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+ require_relative "typing_state"
5
+ require_relative "memo_audit"
6
+
7
+ module Rigor
8
+ # Issues #2412 and #2399 — the run-end hook for per-thread state whose contents belong to one analysis run. An
9
+ # owner declares such a slot in {TypingState} with `run_scoped: true` where it names it, and each entry point that
10
+ # runs an analysis calls {.release} in its `ensure`, so a finished run, or one that raised, leaves none of its
11
+ # scopes, discovery tables or environment behind on the thread.
12
+ #
13
+ # A slot that holds its run until the next run overwrites it retains one run per thread. A slot whose value
14
+ # reaches its own key retains every run, as `LoopValueRecord`'s `ObjectSpace::WeakKeyMap` did: the map holds
15
+ # its values strongly, and a recorded scope reaches the parse tree that holds the key node. Releasing at run end
16
+ # bounds both. Inside a run nothing changes: every memo keeps its key and its hit rate, and a slot read outside
17
+ # a run (a language-server hover on the main thread) refills on its first miss.
18
+ #
19
+ # Only Tables, Memos and Resources are run-scoped. A Frame a `with_*` block installs and restores is already empty
20
+ # when the frame returns, and clearing it from inside a frame would break the frame.
21
+ #
22
+ # ADR-129 — run-scoped state is a `run_scoped: true` {TypingState} slot, and nothing else registers: this module
23
+ # keeps no key list of its own.
24
+ module RunScopedSlots
25
+ # Empties every run-scoped {TypingState} slot on the calling thread.
26
+ #
27
+ # ADR-129 WD5 (#2785) — with the memo audit on, a mismatch no file drained (one raised by typing outside the
28
+ # per-file loop: return summaries, return re-evaluation, sig-gen) would be released here unreported, so it is
29
+ # raised after the release, unless an exception is already leaving the run.
30
+ def self.release
31
+ undrained = Inference::MemoAudit.enabled? ? Inference::MemoAudit.drain : nil
32
+ TypingState.release_run_scoped
33
+ raise undrained.first if undrained && !undrained.empty? && $ERROR_INFO.nil?
34
+
35
+ nil
36
+ end
37
+ end
38
+ end
@@ -37,7 +37,7 @@ module Rigor
37
37
  # *remaining* deadline rather than restarting the window; see its own
38
38
  # comment for why that distinction is worth the bookkeeping.
39
39
  module Jit
40
- # Opt-out switch: `RIGOR_DISABLE_YJIT=1` disables both entry points.
40
+ # Opt-out switch: `RIGOR_DISABLE_YJIT=1` disables every entry point.
41
41
  DISABLE_ENV = "RIGOR_DISABLE_YJIT"
42
42
 
43
43
  # Advanced / calibration override of the {enable_after} deadline, in