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
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+ require_relative "../source/node_walker"
5
+
6
+ module Rigor
7
+ module Inference
8
+ # Which of a def's parameters its own recursion rebinds (issue #3115). The bot-collapse retry of
9
+ # `ExpressionTyper#fixpoint_user_method_return` types the body once for every call the recursion makes, and a
10
+ # recursive call binds a parameter to whatever it passes: `tog(n: n - 1, f: !f)` gives `f` a value the
11
+ # outer call never held. A parameter is KEPT only when every call to the def's own name inside the body
12
+ # passes it the same parameter read unchanged (`cfg(n - 1, mode)` keeps `mode`), and the body never
13
+ # assigns it; every other parameter is rebound, and the retry binds it `Dynamic[top]`.
14
+ #
15
+ # The test is syntactic and reads only the def. A call counts by its method name alone, whatever its
16
+ # receiver (`child.depth(n + 1)` re-enters the same recursion guard), so a same-named method of another
17
+ # class is read as a recursive call too, which only rebinds more. The caller supplies the def's other
18
+ # names (`alias`), since the def does not carry them. A call to ANOTHER method that recurses back (mutual
19
+ # recursion) is not followed: its parameters are judged by the def's own calls alone.
20
+ #
21
+ # Two shapes the test cannot read rebind more instead. A body that calls a method by a computed name
22
+ # ({REFLECTIVE_CALLS}) may recurse with any arguments, so every parameter is rebound ({ALL}). A name a
23
+ # block or lambda in the body declares (a parameter, a block-local, `it`) is rebound, because a read of
24
+ # it inside that block is the block's variable, not the parameter.
25
+ module RecursionRebinding
26
+ module_function
27
+
28
+ LOCAL_WRITE_NODES = Set[
29
+ Prism::LocalVariableWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode,
30
+ Prism::LocalVariableAndWriteNode, Prism::LocalVariableTargetNode
31
+ ].freeze
32
+ private_constant :LOCAL_WRITE_NODES
33
+
34
+ # The calls that reach a method by a name the scan cannot read.
35
+ REFLECTIVE_CALLS = Set[:send, :__send__, :public_send, :method, :instance_method, :define_method].freeze
36
+ private_constant :REFLECTIVE_CALLS
37
+
38
+ # {.rebound_parameters}'s answer when every parameter is rebound.
39
+ ALL = :all
40
+
41
+ # The names the body's recursive calls, its blocks or its own assignments rebind, as a Set, or {ALL}. The
42
+ # Set may hold a name that is no parameter (any assigned local); the caller looks up parameters only.
43
+ # `names` is every name the def answers to: its own and its aliases.
44
+ def rebound_parameters(def_node, names = Set[def_node.name])
45
+ params = def_node.parameters
46
+ return Set.new unless params.is_a?(Prism::ParametersNode)
47
+
48
+ rebound = Set.new
49
+ catch(ALL) { collect(def_node.body, names, params, rebound) } == ALL ? ALL : rebound
50
+ end
51
+
52
+ # Every method name the body calls, for the caller to ask which of them are this def's aliases.
53
+ def called_names(node, names = Set.new)
54
+ return names if node.nil?
55
+
56
+ names << node.name if node.is_a?(Prism::CallNode)
57
+ node.rigor_each_child { |child| called_names(child, names) }
58
+ names
59
+ end
60
+
61
+ def collect(node, names, params, rebound)
62
+ return if node.nil?
63
+
64
+ case node
65
+ when *LOCAL_WRITE_NODES then rebound << node.name
66
+ when Prism::BlockNode, Prism::LambdaNode then note_block(node, rebound)
67
+ when Prism::CallNode
68
+ throw ALL, ALL if REFLECTIVE_CALLS.include?(node.name)
69
+
70
+ note_call(node, params, rebound) if names.include?(node.name)
71
+ end
72
+ node.rigor_each_child { |child| collect(child, names, params, rebound) }
73
+ end
74
+
75
+ # A block's `locals` are its parameters, its block-locals and the locals it first assigns; `it` is not
76
+ # among them.
77
+ def note_block(block, rebound)
78
+ rebound.merge(block.locals)
79
+ rebound << :it if block.parameters.is_a?(Prism::ItParametersNode)
80
+ end
81
+
82
+ def note_call(call, params, rebound)
83
+ args = call.arguments&.arguments || []
84
+ keywords = args.last if args.last.is_a?(Prism::KeywordHashNode)
85
+ positional = keywords ? args[0...-1] : args
86
+ forwarding = args.any?(Prism::ForwardingArgumentsNode)
87
+ note_positionals(params, positional, rebound)
88
+ note_keywords(params, keywords, forwarding, rebound)
89
+ end
90
+
91
+ # Positionals correspond by index up to the first `*splat` (or `...`), which may reach every slot from
92
+ # its own on. The rest parameter is kept only by `*rest` in its own place as the last positional.
93
+ def note_positionals(params, positional, rebound)
94
+ slots = params.requireds + params.optionals
95
+ cut = positional.index { |arg| arg.is_a?(Prism::SplatNode) || arg.is_a?(Prism::ForwardingArgumentsNode) }
96
+ cut ||= positional.size
97
+ slots.each_with_index do |param, index|
98
+ next if param.is_a?(Prism::MultiTargetNode)
99
+
100
+ rebound << param.name unless index < cut && reads?(positional[index], param.name)
101
+ end
102
+ note_rest(params.rest, positional[slots.size..] || [], cut >= slots.size, rebound)
103
+ end
104
+
105
+ def note_rest(rest, tail, aligned, rebound)
106
+ return unless rest.is_a?(Prism::RestParameterNode) && rest.name
107
+
108
+ splat = tail.first
109
+ kept = aligned && tail.size == 1 && splat.is_a?(Prism::SplatNode) && reads?(splat.expression, rest.name)
110
+ rebound << rest.name unless kept
111
+ end
112
+
113
+ # Keywords correspond by name. A keyword the call omits falls back to its default, so it is rebound. A
114
+ # `**splat` other than the def's own `**rest`, a non-Symbol key or `...` may carry any keyword, so each of
115
+ # them rebinds every keyword. `**rest` is kept by `**rest` alone, with no key beside it that it would collect.
116
+ def note_keywords(params, keywords, forwarding, rebound)
117
+ names = params.keywords.map { |param| param.name.to_s.delete_suffix(":").to_sym }
118
+ rest_name = keyword_rest_name(params)
119
+ passed, splats, opaque = keyword_entries(keywords)
120
+ opaque ||= forwarding || splats.any? { |value| !reads?(value, rest_name) }
121
+ return rebound.merge(names, [rest_name].compact) if opaque
122
+
123
+ names.each { |name| rebound << name unless reads?(passed[name], name) }
124
+ rebound << rest_name if rest_name && !(splats.size == 1 && (passed.keys - names).empty?)
125
+ end
126
+
127
+ def keyword_rest_name(params)
128
+ rest = params.keyword_rest
129
+ rest.name if rest.is_a?(Prism::KeywordRestParameterNode)
130
+ end
131
+
132
+ # The call's keyword hash as `[{ name => value node }, [**splat value nodes], opaque]`, `opaque` when
133
+ # a key is not a plain Symbol.
134
+ def keyword_entries(keywords)
135
+ passed = {}
136
+ splats = []
137
+ opaque = false
138
+ (keywords&.elements || []).each do |element|
139
+ if element.is_a?(Prism::AssocSplatNode)
140
+ splats << element.value
141
+ elsif element.key.is_a?(Prism::SymbolNode) && element.key.unescaped
142
+ passed[element.key.unescaped.to_sym] = element.value
143
+ else
144
+ opaque = true
145
+ end
146
+ end
147
+ [passed, splats, opaque]
148
+ end
149
+
150
+ # Whether `node` is a bare read of the local `name` (`n`, or the `n:` shorthand's implicit read).
151
+ def reads?(node, name)
152
+ node = node.value if node.is_a?(Prism::ImplicitNode)
153
+ !name.nil? && node.is_a?(Prism::LocalVariableReadNode) && node.name == name
154
+ end
155
+ end
156
+ end
157
+ end
@@ -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
  module Inference
5
8
  module RefinedDispatch
@@ -16,9 +19,16 @@ module Rigor
16
19
  # the name itself, and no constant write of it. Any other shape answers false, as does a class a
17
20
  # `K = Class.new(...)` write names.
18
21
  module ConstructedClass
19
- MEMO_KEY = :__rigor_constructed_class__
22
+ # ADR-129 WD1 — a run-scoped Memo keyed on the run-generation token and the environment, whose tables every
23
+ # answer reads and which are fixed for a run.
24
+ MEMO = TypingState.memo(
25
+ :constructed_class,
26
+ run_scoped: true,
27
+ audit: ["constructed class verdict", "constructed class segments", "constructed class names"],
28
+ reason: "verdicts keyed on the run-generation token and environment identities"
29
+ )
20
30
  EMPTY = [].freeze
21
- private_constant :MEMO_KEY, :EMPTY
31
+ private_constant :MEMO, :EMPTY
22
32
 
23
33
  module_function
24
34
 
@@ -34,9 +44,19 @@ module Rigor
34
44
  return false if named.empty?
35
45
 
36
46
  by_class = (table[:verdicts][class_name] ||= {})
37
- return by_class[named] if by_class.key?(named)
47
+ if by_class.key?(named)
48
+ return MemoAudit.check("constructed class verdict", by_class[named], class_name, named) do
49
+ apart_verdict(scope, named, class_name, memo(scope))
50
+ end
51
+ end
52
+
53
+ by_class[named] = MemoAudit.key_audit("constructed class verdict") do
54
+ apart_verdict(scope, named, class_name, table)
55
+ end
56
+ end
38
57
 
39
- by_class[named] = named.all? { |constant| core_class_apart?(scope, constant, class_name.to_s, table) }
58
+ def apart_verdict(scope, named, class_name, table)
59
+ named.all? { |constant| core_class_apart?(scope, constant, class_name.to_s, table) }
40
60
  end
41
61
 
42
62
  def core_class_apart?(scope, constant, class_name, table)
@@ -66,7 +86,18 @@ module Rigor
66
86
 
67
87
  # The last segment of every class or module the project declares, reopenings included.
68
88
  def declared_segments(scope, table)
69
- table[:segments] ||= declared_names(scope).each_key.to_set { |key| key.to_s.split("::").last }.freeze
89
+ segments = table[:segments]
90
+ if segments.nil?
91
+ return table[:segments] = MemoAudit.key_audit("constructed class segments") do
92
+ compute_declared_segments(scope)
93
+ end
94
+ end
95
+
96
+ MemoAudit.check("constructed class segments", segments, :segments) { compute_declared_segments(scope) }
97
+ end
98
+
99
+ def compute_declared_segments(scope)
100
+ declared_names(scope).each_key.to_set { |key| key.to_s.split("::").last }.freeze
70
101
  end
71
102
 
72
103
  # The constants `name`'s superclass expression names, or an empty Array; memoised per envelope bucket,
@@ -76,15 +107,18 @@ module Rigor
76
107
  return EMPTY if bucket.nil?
77
108
 
78
109
  names = table[:names]
79
- return names[bucket] if names.key?(bucket)
80
-
81
- names[bucket] =
82
- if bucket.key?(Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK)
83
- EMPTY
84
- else
85
- kind = Scope::DiscoveryIndex::ENVELOPE_CONSTRUCTED_KIND
86
- bucket.each_key.filter_map { |key| key[1] if key.is_a?(Array) && key[0] == kind }.freeze
87
- end
110
+ if names.key?(bucket)
111
+ return MemoAudit.check("constructed class names", names[bucket], name) { bucket_constructed_names(bucket) }
112
+ end
113
+
114
+ names[bucket] = MemoAudit.key_audit("constructed class names") { bucket_constructed_names(bucket) }
115
+ end
116
+
117
+ def bucket_constructed_names(bucket)
118
+ return EMPTY if bucket.key?(Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK)
119
+
120
+ kind = Scope::DiscoveryIndex::ENVELOPE_CONSTRUCTED_KIND
121
+ bucket.each_key.filter_map { |key| key[1] if key.is_a?(Array) && key[0] == kind }.freeze
88
122
  end
89
123
 
90
124
  # The run-wide table of declared classes: the seeded `discovered_class_sources`, or a single-file run's
@@ -98,15 +132,21 @@ module Rigor
98
132
  # `discovered_def_nodes`, as `ExpressionTyper` falls back) and the environment: every table read here is
99
133
  # fixed for a run, so each answer is computed once a run, and a new run replaces the slot.
100
134
  def memo(scope)
101
- slot = Thread.current[MEMO_KEY]
135
+ return new_table if MemoAudit.enabled? && MemoAudit.suspended?
136
+
137
+ state = TypingState.current
138
+ slot = state[MEMO]
102
139
  generation = scope.run_generation || scope.discovered_def_nodes
103
140
  environment = scope.environment
104
141
  unless slot && slot[0].equal?(generation) && slot[1].equal?(environment)
105
- slot = [generation, environment, { names: {}.compare_by_identity, verdicts: {} }]
106
- Thread.current[MEMO_KEY] = slot
142
+ slot = [generation, environment, new_table]
143
+ state[MEMO] = slot
107
144
  end
108
145
  slot[2]
109
146
  end
147
+
148
+ # Empty tables: a new slot's, and a throwaway set while an audit recomputation bypasses the memo.
149
+ def new_table = { names: {}.compare_by_identity, verdicts: {} }
110
150
  end
111
151
  end
112
152
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative "../builtins/cruby_definers"
4
4
  require_relative "constructed_class"
5
+ require_relative "../../typing_state"
6
+ require_relative "../../memo_audit"
5
7
 
6
8
  module Rigor
7
9
  module Inference
@@ -306,30 +308,54 @@ module Rigor
306
308
 
307
309
  def unknown_superclass_classes(scope, class_name)
308
310
  Analysis::DependencyRecorder.read_name(:subclass_of, "*") if Analysis::DependencyRecorder.active?
309
- table = subclass_memo(scope)
310
- all = table[:unknown] ||= begin
311
- mark = Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK
312
- computed = scope.discovered_superclasses.filter_map { |child, parent| child if parent.nil? }
313
- meta = scope.discovered_parameter_envelopes.filter_map { |name, bucket| name if bucket.key?(mark) }
314
- (computed + meta).uniq.freeze
315
- end
311
+ all = all_unknown_superclass_classes(scope)
316
312
  name = class_name.to_s
317
313
  all.include?(name) ? all - [name] : all
318
314
  end
319
315
 
320
- SUBCLASS_MEMO_KEY = :__rigor_own_method_subclasses__
321
- private_constant :SUBCLASS_MEMO_KEY
316
+ def all_unknown_superclass_classes(scope)
317
+ table = subclass_memo(scope)
318
+ stored = table[:unknown]
319
+ if stored.nil?
320
+ return table[:unknown] = MemoAudit.key_audit("own method unknown superclasses") do
321
+ compute_unknown_superclass_classes(scope)
322
+ end
323
+ end
324
+
325
+ MemoAudit.check("own method unknown superclasses", stored, :unknown) do
326
+ compute_unknown_superclass_classes(scope)
327
+ end
328
+ end
329
+
330
+ def compute_unknown_superclass_classes(scope)
331
+ mark = Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK
332
+ computed = scope.discovered_superclasses.filter_map { |child, parent| child if parent.nil? }
333
+ meta = scope.discovered_parameter_envelopes.filter_map { |name, bucket| name if bucket.key?(mark) }
334
+ (computed + meta).uniq.freeze
335
+ end
336
+
337
+ # ADR-129 WD1 — a run-scoped Memo keyed on the identities of the discovery index and environment, the only
338
+ # inputs of the buckets it holds.
339
+ SUBCLASS_MEMO = TypingState.memo(
340
+ :own_method_subclasses,
341
+ run_scoped: true, audit: ["own method unknown superclasses", "own method children"],
342
+ reason: "subclass buckets keyed on the discovery index and environment identities"
343
+ )
344
+ private_constant :SUBCLASS_MEMO
322
345
 
323
346
  # One thread-local slot keyed on the scope's discovery index (a frozen `Data`) and environment, the only
324
347
  # inputs `children_by_parent` and the unknown-superclass list read; neither files a dependency edge
325
348
  # (`each_subclass` files its own `subclass_of:` edges on every call), so a hit drops none.
326
349
  def subclass_memo(scope)
327
- slot = Thread.current[SUBCLASS_MEMO_KEY]
350
+ return {} if MemoAudit.enabled? && MemoAudit.suspended?
351
+
352
+ state = TypingState.current
353
+ slot = state[SUBCLASS_MEMO]
328
354
  discovery = scope.discovery
329
355
  environment = scope.environment
330
356
  unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
331
357
  slot = [discovery, environment, {}]
332
- Thread.current[SUBCLASS_MEMO_KEY] = slot
358
+ state[SUBCLASS_MEMO] = slot
333
359
  end
334
360
  slot[2]
335
361
  end
@@ -341,11 +367,20 @@ module Rigor
341
367
  end
342
368
 
343
369
  def children_by_parent(scope)
344
- subclass_memo(scope)[:children] ||=
345
- scope.discovered_superclasses.each_with_object({}) do |(child, raw_parent), children|
346
- parent = scope.ancestor_name_candidates(child, raw_parent).find { |name| known_class?(scope, name) }
347
- (children[parent] ||= []) << child if parent
348
- end.each_value(&:freeze).freeze
370
+ table = subclass_memo(scope)
371
+ stored = table[:children]
372
+ if stored.nil?
373
+ return table[:children] = MemoAudit.key_audit("own method children") { compute_children_by_parent(scope) }
374
+ end
375
+
376
+ MemoAudit.check("own method children", stored, :children) { compute_children_by_parent(scope) }
377
+ end
378
+
379
+ def compute_children_by_parent(scope)
380
+ scope.discovered_superclasses.each_with_object({}) do |(child, raw_parent), children|
381
+ parent = scope.ancestor_name_candidates(child, raw_parent).find { |name| known_class?(scope, name) }
382
+ (children[parent] ||= []) << child if parent
383
+ end.each_value(&:freeze).freeze
349
384
  end
350
385
 
351
386
  def known_class?(scope, name)
@@ -126,6 +126,19 @@ module Rigor
126
126
  out
127
127
  end
128
128
 
129
+ # ADR-121 WD7 — a names-wildcard row counts as a refinement of every name. True when a row `list` puts in
130
+ # effect names `method_name` (or the wildcard) for ANY refined class — which class the row refines is not
131
+ # decided here — or `list` carries {InEffectRefinements::UNKNOWN} and any row names it. {ExpressionTyper}
132
+ # asks it for a `&:name` block, whose receiver is not known; {SelfPreservingBlock} for the yielding method
133
+ # (issue #2909).
134
+ def refined_anywhere?(scope, method_name, list)
135
+ refinements = scope.discovered_refinements
136
+ return true if list.include?(InEffectRefinements::UNKNOWN) &&
137
+ refinements.any? { |_refined, methods| methods.key?(method_name) || methods.key?(wildcard) }
138
+
139
+ !targets(refinements, method_name, list).nil? || !targets(refinements, wildcard, list).nil?
140
+ end
141
+
129
142
  # The refinement table records every name a `refine` argument's spelling can denote (`refine String` inside
130
143
  # `module M` records `M::String` and `String`), which only withholds a check. To type, a key must be the class
131
144
  # Ruby's lexical lookup resolves: one the project or RBS knows, with no longer spelling of the same last segment
@@ -3,6 +3,7 @@
3
3
  require "prism"
4
4
 
5
5
  require_relative "../type"
6
+ require_relative "../typing_state"
6
7
 
7
8
  module Rigor
8
9
  module Inference
@@ -26,8 +27,13 @@ module Rigor
26
27
  IVAR_NAME = /\A@[A-Za-z_]\w*\z/
27
28
  private_constant :IVAR_NAME
28
29
 
29
- REPEATING_KEY = :__rigor_reflective_ivar_write_repeating__
30
- private_constant :REPEATING_KEY
30
+ # ADR-129 WD1 — a Frame: the depth is installed for a region and restored on the way out.
31
+ REPEATING = TypingState.frame(
32
+ :reflective_ivar_write_repeating,
33
+ reason: "the repeat depth of `for` bodies and loop predicates, installed per region",
34
+ witness: "spec/fixtures/typing_state_witnesses/reflective_ivar_write_repeating.rb"
35
+ )
36
+ private_constant :REPEATING
31
37
 
32
38
  module_function
33
39
 
@@ -69,18 +75,20 @@ module Rigor
69
75
  # rebind fixpoint, and a predicate runs before every iteration but is walked once, so a literal-name write there
70
76
  # must not replace the binding with one pass's value ({.apply}). A `while` / `until` body does not enter it: its
71
77
  # fixpoint widens the ivar (issue #1930).
72
- def in_repeating_body
73
- depth = Thread.current[REPEATING_KEY] || 0
74
- Thread.current[REPEATING_KEY] = depth + 1
75
- begin
76
- yield
77
- ensure
78
- Thread.current[REPEATING_KEY] = depth
79
- end
78
+ def in_repeating_body(&)
79
+ state = TypingState.current
80
+ state.with(REPEATING, (state[REPEATING] || 0) + 1, &)
81
+ end
82
+
83
+ # Runs the block at repeat depth zero, restoring the depth on exit. A user-method body re-typed for its return
84
+ # starts from its own scope and reads its own write, so a caller's `for` body does not repeat it
85
+ # ({ExpressionTyper.with_call_site_frame}).
86
+ def outside_repeating_body(&)
87
+ TypingState.current.with(REPEATING, 0, &)
80
88
  end
81
89
 
82
90
  def repeating_body?
83
- (Thread.current[REPEATING_KEY] || 0).positive?
91
+ (TypingState.current[REPEATING] || 0).positive?
84
92
  end
85
93
 
86
94
  # `scope` after `node` runs. A literal name binds that ivar to the value's type (yielded), as `@n = v` does.