rigortype 0.2.7 → 0.2.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 (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -4,17 +4,15 @@ require_relative "../type"
4
4
 
5
5
  module Rigor
6
6
  module Inference
7
- # Widens a local- or instance-variable binding after a call
8
- # whose receiver is that variable AND whose method is a known
9
- # in-place mutator.
7
+ # Widens a local- or instance-variable binding after a call whose receiver is that variable
8
+ # AND whose method is a known in-place mutator.
10
9
  #
11
10
  # Closes the **G1 / G2** flow-folding gaps documented at
12
- # `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md`
13
- # and queued in [`docs/CURRENT_WORK.md`](../../../docs/CURRENT_WORK.md)
14
- # § "Flow-folding". The user-visible symptom they shared was a
15
- # spurious `flow.always-truthy-condition` on a `arr.size == N`
16
- # / `arr.empty?` / `@arr.empty?` check that follows a loop
17
- # body or sibling method that mutates `arr` / `@arr` in place.
11
+ # `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md` and queued in
12
+ # [`docs/CURRENT_WORK.md`](../../../docs/CURRENT_WORK.md) § "Flow-folding". The user-visible
13
+ # symptom they shared was a spurious `flow.always-truthy-condition` on a `arr.size == N` /
14
+ # `arr.empty?` / `@arr.empty?` check that follows a loop body or sibling method that mutates
15
+ # `arr` / `@arr` in place.
18
16
  #
19
17
  # **The mechanism.** When source like
20
18
  #
@@ -24,30 +22,23 @@ module Rigor
24
22
  # end
25
23
  # return arms.first if arms.size == 1
26
24
  #
27
- # runs through inference today, the literal `[first]` writes
28
- # `arms` as `Tuple[T]`. The shape carrier's `size` folds to
29
- # `Constant[1]`. The body's `arms << next_arm` returns a type
30
- # for the call expression but does NOT rebind `arms`, so after
31
- # the loop `arms` still carries the `Tuple[T]` binding —
32
- # `arms.size == 1` constant-folds to `true` and the user sees
33
- # a false `flow.always-truthy-condition`.
25
+ # runs through inference today, the literal `[first]` writes `arms` as `Tuple[T]`. The shape
26
+ # carrier's `size` folds to `Constant[1]`. The body's `arms << next_arm` returns a type for
27
+ # the call expression but does NOT rebind `arms`, so after the loop `arms` still carries the
28
+ # `Tuple[T]` binding `arms.size == 1` constant-folds to `true` and the user sees a false
29
+ # `flow.always-truthy-condition`.
34
30
  #
35
- # The narrowest correct fix is to **widen the receiver binding
36
- # at the mutator call site**: replace `arms`'s binding with
37
- # `Nominal[Array, [union(elements)]]` so the carrier no longer
38
- # carries the literal arity. Inside a loop body, the post-call
39
- # body scope then joins with the pre-loop scope through
40
- # `join_with_nil_injection` `Scope#join` (which unions per
41
- # name); the resulting union loses size precision, so the
42
- # `arms.size` fold returns `Integer` (not `Constant[1]`) and
43
- # the diagnostic correctly stays silent.
31
+ # The narrowest correct fix is to **widen the receiver binding at the mutator call site**:
32
+ # replace `arms`'s binding with `Nominal[Array, [union(elements)]]` so the carrier no longer
33
+ # carries the literal arity. Inside a loop body, the post-call body scope then joins with the
34
+ # pre-loop scope through `join_with_nil_injection` `Scope#join` (which unions per name); the
35
+ # resulting union loses size precision, so the `arms.size` fold returns `Integer` (not
36
+ # `Constant[1]`) and the diagnostic correctly stays silent.
44
37
  #
45
- # The widening is **always type-safe**: it never introduces a
46
- # new fact, only forgets a literal-shape fact that is no longer
47
- # justified once mutation occurred. It costs only the precise
48
- # arity / pair-set the shape carrier was tracking; the underlying
49
- # nominal stays exact (`Array` / `Hash`) and element types
50
- # stay as a union of what was there.
38
+ # The widening is **always type-safe**: it never introduces a new fact, only forgets a
39
+ # literal-shape fact that is no longer justified once mutation occurred. It costs only the
40
+ # precise arity / pair-set the shape carrier was tracking; the underlying nominal stays exact
41
+ # (`Array` / `Hash`) and element types stay as a union of what was there.
51
42
  #
52
43
  # **Scope.** This slice addresses:
53
44
  #
@@ -56,32 +47,25 @@ module Rigor
56
47
  #
57
48
  # Out of scope (left for a separate cycle):
58
49
  #
59
- # - **`retry` flow edge** (e.g. `tries += 1; retry`). The
60
- # `tries` rebind across `retry` is a flow-edge issue, not a
61
- # call-site mutation issue.
62
- # - **Intervening method call invalidates the ivar binding**
63
- # (e.g. `if @performed; perform!; if @performed`). The
64
- # intra-procedural call effect on ivars is a separate
50
+ # - **`retry` flow edge** (e.g. `tries += 1; retry`). The `tries` rebind across `retry` is a
51
+ # flow-edge issue, not a call-site mutation issue.
52
+ # - **Intervening method call invalidates the ivar binding** (e.g. `if @performed;
53
+ # perform!; if @performed`). The intra-procedural call effect on ivars is a separate
65
54
  # mutation-effect feature.
66
- # - **Read-before-write nil** (e.g. `unless @warning_issued;
67
- # ...; @warning_issued = true`). Requires tracking the
68
- # first-write position; flow-sensitive but orthogonal.
69
- # - **Local-variable mutation inside a block body** (e.g.
70
- # `arr = []; xs.each { |x| arr << x }`) landed as
71
- # ADR-56 slice A (`widen_after_block`). **Ivar mutations
72
- # inside a block ARE also handled** (ivars live in the
73
- # method-body scope, not the block-local scope).
55
+ # - **Read-before-write nil** (e.g. `unless @warning_issued; ...; @warning_issued = true`).
56
+ # Requires tracking the first-write position; flow-sensitive but orthogonal.
57
+ # - **Local-variable mutation inside a block body** (e.g. `arr = []; xs.each { |x| arr <<
58
+ # x }`) — landed as ADR-56 slice A (`widen_after_block`). **Ivar mutations inside a block
59
+ # ARE also handled** (ivars live in the method-body scope, not the block-local scope).
74
60
  #
75
61
  # The remaining three items above are demand-gated; see ADR-56.
76
62
  module MutationWidening
77
- # Array mutators that change either the size or the element
78
- # set of a literal-shape carrier (Tuple). Receiver-mutating
79
- # methods only — non-mutating siblings (`map` vs `map!`,
63
+ # Array mutators that change either the size or the element set of a literal-shape carrier
64
+ # (Tuple). Receiver-mutating methods only — non-mutating siblings (`map` vs `map!`,
80
65
  # `select` vs `select!`) stay precise.
81
66
  #
82
- # `<<` and `[]=` are the dominant survey cases; the bang
83
- # variants and the size-mutators cover the rest of the
84
- # Mastodon cluster-4 G1 catalogue.
67
+ # `<<` and `[]=` are the dominant survey cases; the bang variants and the size-mutators
68
+ # cover the rest of the Mastodon cluster-4 G1 catalogue.
85
69
  ARRAY_MUTATORS = %i[
86
70
  << push append prepend unshift concat insert
87
71
  pop shift
@@ -92,9 +76,8 @@ module Rigor
92
76
  flatten! sort! sort_by! reverse! rotate! shuffle! slice!
93
77
  ].to_set.freeze
94
78
 
95
- # Hash mutators that invalidate a `HashShape` carrier. Same
96
- # principle as `ARRAY_MUTATORS`: only the receiver-mutating
97
- # methods are listed.
79
+ # Hash mutators that invalidate a `HashShape` carrier. Same principle as `ARRAY_MUTATORS`:
80
+ # only the receiver-mutating methods are listed.
98
81
  HASH_MUTATORS = %i[
99
82
  []= store
100
83
  delete delete_if reject! select! filter! keep_if
@@ -102,12 +85,10 @@ module Rigor
102
85
  replace
103
86
  ].to_set.freeze
104
87
 
105
- # Methods that return the receiver (or a shallow copy) and
106
- # cannot mutate it. They must not trigger widening or any
107
- # other receiver-fact invalidation. The list is intentionally
108
- # narrow only methods whose purity is unconditional and
109
- # whose return value is the receiver itself (or a copy that
110
- # leaves the original untouched).
88
+ # Methods that return the receiver (or a shallow copy) and cannot mutate it. They must not
89
+ # trigger widening or any other receiver-fact invalidation. The list is intentionally
90
+ # narrow only methods whose purity is unconditional and whose return value is the
91
+ # receiver itself (or a copy that leaves the original untouched).
111
92
  PURE_SELF_RETURNERS = %i[freeze dup clone itself].freeze
112
93
 
113
94
  module_function
@@ -118,11 +99,10 @@ module Rigor
118
99
  PURE_SELF_RETURNERS.include?(method_name)
119
100
  end
120
101
 
121
- # Returns a scope with the call's receiver widened, when the
122
- # receiver is a local-/instance-variable read whose current
123
- # binding is a literal-shape carrier (`Tuple` / `HashShape`)
124
- # AND the call name is a known in-place mutator for that
125
- # shape. Returns `current_scope` unchanged otherwise.
102
+ # Returns a scope with the call's receiver widened, when the receiver is a
103
+ # local-/instance-variable read whose current binding is a literal-shape carrier
104
+ # (`Tuple` / `HashShape`) AND the call name is a known in-place mutator for that shape.
105
+ # Returns `current_scope` unchanged otherwise.
126
106
  #
127
107
  # @param call_node [Prism::CallNode]
128
108
  # @param current_scope [Rigor::Scope]
@@ -143,29 +123,22 @@ module Rigor
143
123
  end
144
124
  end
145
125
 
146
- # Propagate block-body mutations of outer-scope variables
147
- # back into `outer_scope`. Block bodies live in a child
148
- # scope; mutations the block body performs on captured
149
- # outer LOCALS are otherwise invisible to the post-call
150
- # outer scope (ivars are handled correctly already because
151
- # they live in the method-body scope, not the block-local
152
- # scope).
126
+ # Propagate block-body mutations of outer-scope variables back into `outer_scope`. Block
127
+ # bodies live in a child scope; mutations the block body performs on captured outer
128
+ # LOCALS are otherwise invisible to the post-call outer scope (ivars are handled correctly
129
+ # already because they live in the method-body scope, not the block-local scope).
153
130
  #
154
- # Walks the block AST for `<receiver>.<method>(...)` calls
155
- # whose receiver is either a `LocalVariableReadNode` with
156
- # `depth > 0` (a captured outer local Prism's `depth`
157
- # counts scope hops outward; `depth == 0` means a
158
- # block-local) or an `InstanceVariableReadNode` (always
159
- # method-scope), and applies `widen_after_call` for each
160
- # one against the outer scope. The widening is always safe
161
- # — it can only LOSE precision — so blindly propagating is
162
- # sound regardless of whether the block actually runs.
131
+ # Walks the block AST for `<receiver>.<method>(...)` calls whose receiver is either a
132
+ # `LocalVariableReadNode` with `depth > 0` (a captured outer local — Prism's `depth`
133
+ # counts scope hops outward; `depth == 0` means a block-local) or an
134
+ # `InstanceVariableReadNode` (always method-scope), and applies `widen_after_call` for
135
+ # each one against the outer scope. The widening is always safe — it can only LOSE
136
+ # precision so blindly propagating is sound regardless of whether the block actually
137
+ # runs.
163
138
  #
164
- # Recurses into nested expression nodes so chained / nested
165
- # forms (`arr << f(x); arr << g(y)`, `arr.push(x) if cond`)
166
- # are all caught. Does NOT recurse into nested
167
- # `Prism::BlockNode`s — each block is processed by its own
168
- # `eval_call`.
139
+ # Recurses into nested expression nodes so chained / nested forms (`arr << f(x); arr <<
140
+ # g(y)`, `arr.push(x) if cond`) are all caught. Does NOT recurse into nested
141
+ # `Prism::BlockNode`s each block is processed by its own `eval_call`.
169
142
  def widen_after_block(call_node:, outer_scope:)
170
143
  block = call_node.block
171
144
  return outer_scope unless block.is_a?(Prism::BlockNode)
@@ -181,15 +154,12 @@ module Rigor
181
154
 
182
155
  scope = widen_for_outer_receiver(node, scope) if node.is_a?(Prism::CallNode)
183
156
 
184
- # Descend into every child, including nested blocks. The
185
- # `LocalVariableReadNode#depth` check inside
186
- # `widen_for_outer_receiver` keeps nested-block-locals
187
- # from being widened in the outer scope only references
188
- # with `depth >= 1` (true captures of the outer scope's
189
- # locals) trigger widening, so descending into nested
190
- # blocks is safe and necessary for the hkt_registry-shape
191
- # case (an outer collection mutated inside an iterator
192
- # block whose body is itself inside another block).
157
+ # Descend into every child, including nested blocks. The `LocalVariableReadNode#depth`
158
+ # check inside `widen_for_outer_receiver` keeps nested-block-locals from being widened
159
+ # in the outer scope — only references with `depth >= 1` (true captures of the outer
160
+ # scope's locals) trigger widening, so descending into nested blocks is safe and
161
+ # necessary for the hkt_registry-shape case (an outer collection mutated inside an
162
+ # iterator block whose body is itself inside another block).
193
163
  node.compact_child_nodes.each do |child|
194
164
  scope = walk_for_outer_mutations(child, scope)
195
165
  end
@@ -230,11 +200,10 @@ module Rigor
230
200
  current_scope.with_ivar(var_name, widened)
231
201
  end
232
202
 
233
- # Returns the widened type for a binding whose receiver is
234
- # about to be mutated by `method_name`, or `nil` when no
235
- # widening applies (binding is not a literal-shape carrier,
236
- # OR the method is not a mutator for that shape, OR the
237
- # binding is already a nominal — no precision to lose).
203
+ # Returns the widened type for a binding whose receiver is about to be mutated by
204
+ # `method_name`, or `nil` when no widening applies (binding is not a literal-shape
205
+ # carrier, OR the method is not a mutator for that shape, OR the binding is already a
206
+ # nominal no precision to lose).
238
207
  def widen_for_mutator(type, method_name)
239
208
  return nil if type.nil?
240
209
 
@@ -250,9 +219,8 @@ module Rigor
250
219
  end
251
220
  end
252
221
 
253
- # `Tuple[A, B, C]` → `Nominal[Array, [union(A, B, C)]]`.
254
- # An empty tuple has no element evidence, so the widened
255
- # form carries `untyped` element bound — matches the
222
+ # `Tuple[A, B, C]` → `Nominal[Array, [union(A, B, C)]]`. An empty tuple has no element
223
+ # evidence, so the widened form carries `untyped` element bound matches the
256
224
  # `tuple_to_array` widening already used by `BlockFolding`.
257
225
  def widen_tuple(tuple)
258
226
  element_type =
@@ -266,9 +234,8 @@ module Rigor
266
234
  Type::Combinator.nominal_of("Array", type_args: [element_type])
267
235
  end
268
236
 
269
- # `HashShape` (closed or open) → `Nominal[Hash, [Kunion,
270
- # Vunion]]`. Empty / extra-keys-only shapes degrade to a
271
- # fully-untyped Hash.
237
+ # `HashShape` (closed or open) → `Nominal[Hash, [Kunion, Vunion]]`. Empty / extra-keys-only
238
+ # shapes degrade to a fully-untyped Hash.
272
239
  def widen_hash_shape(shape)
273
240
  if shape.pairs.empty?
274
241
  return Type::Combinator.nominal_of("Hash",
@@ -281,12 +248,10 @@ module Rigor
281
248
  Type::Combinator.nominal_of("Hash", type_args: [key_type, value_type])
282
249
  end
283
250
 
284
- # Maps the literal Ruby key set (`Symbol` / `String`) to a
285
- # union of the corresponding type carriers. We deliberately
286
- # do NOT fold to a `Constant<:k1> | Constant<:k2>` union
287
- # that would be a precision improvement that complicates the
288
- # widening contract; the goal here is to LOSE precision, not
289
- # to record a new fact set.
251
+ # Maps the literal Ruby key set (`Symbol` / `String`) to a union of the corresponding type
252
+ # carriers. We deliberately do NOT fold to a `Constant<:k1> | Constant<:k2>` union — that
253
+ # would be a precision improvement that complicates the widening contract; the goal here
254
+ # is to LOSE precision, not to record a new fact set.
290
255
  def key_union_for(keys)
291
256
  kinds = keys.map { |k| k.is_a?(Symbol) ? "Symbol" : "String" }.uniq
292
257
  carriers = kinds.map { |name| Type::Combinator.nominal_of(name) }
@@ -296,45 +261,40 @@ module Rigor
296
261
  # ----------------------------------------------------------------
297
262
  # ADR-56 slice C — receiver-content element-type JOIN.
298
263
  #
299
- # `widen_after_block` above forgets a literal-shape carrier's arity
300
- # when a captured local is content-mutated inside a block, but it
301
- # keeps only the SEED's element types an unsound under-
302
- # approximation for a non-empty seed (`out = [0]; arr.each { |x|
303
- # out << x }` types `Array[0]` while the runtime array is
304
- # `[0, 1, 2, 3]`). Slice C joins the appended/stored element (and
305
- # key/value) types INTO the continuation collection's parameter, so
306
- # the result is `Array[0 | Integer]` rather than `Array[0]`.
264
+ # `widen_after_block` above forgets a literal-shape carrier's arity when a captured local
265
+ # is content-mutated inside a block, but it keeps only the SEED's element types — an
266
+ # unsound under-approximation for a non-empty seed (`out = [0]; arr.each { |x| out << x
267
+ # }` types `Array[0]` while the runtime array is `[0, 1, 2, 3]`). Slice C joins the
268
+ # appended/stored element (and key/value) types INTO the continuation collection's
269
+ # parameter, so the result is `Array[0 | Integer]` rather than `Array[0]`.
307
270
  #
308
- # Array content-mutators that append/store ELEMENTS. The appended
309
- # element type is the call's argument type(s); `[]=`'s value is its
310
- # LAST argument (the keys precede it). Subset of `ARRAY_MUTATORS`:
311
- # only the element-INTRODUCING methods (removers / reorderers add no
312
- # new element evidence and are already covered by the arity-forget).
271
+ # Array content-mutators that append/store ELEMENTS. The appended element type is the
272
+ # call's argument type(s); `[]=`'s value is its LAST argument (the keys precede it).
273
+ # Subset of `ARRAY_MUTATORS`: only the element-INTRODUCING methods (removers / reorderers
274
+ # add no new element evidence and are already covered by the arity-forget).
313
275
  ARRAY_CONTENT_ADDERS = %i[
314
276
  << push append prepend unshift concat insert []= fill replace
315
277
  ].to_set.freeze
316
278
 
317
- # Hash content-mutators that store a key→value pair. For `[]=` /
318
- # `store` the key is the first argument and the value the last.
279
+ # Hash content-mutators that store a key→value pair. For `[]=` / `store` the key is the
280
+ # first argument and the value the last.
319
281
  HASH_CONTENT_ADDERS = %i[[]= store].to_set.freeze
320
282
 
321
- # String content-mutators that append to the buffer. String carries
322
- # no element parameter, so these contribute nothing to a join — they
323
- # are listed so the orchestrator recognises them as content mutators
324
- # (the binding already widens to `String` via normal typing); the
325
- # join helpers below short-circuit on a non-collection pre-state.
283
+ # String content-mutators that append to the buffer. String carries no element parameter,
284
+ # so these contribute nothing to a join — they are listed so the orchestrator recognises
285
+ # them as content mutators (the binding already widens to `String` via normal typing);
286
+ # the join helpers below short-circuit on a non-collection pre-state.
326
287
  STRING_CONTENT_ADDERS = %i[<< concat prepend insert replace].to_set.freeze
327
288
 
328
- # Every method name that mutates a captured local's CONTENT — the
329
- # union the orchestrator scans the block body for.
289
+ # Every method name that mutates a captured local's CONTENT — the union the orchestrator
290
+ # scans the block body for.
330
291
  CONTENT_ADDERS = (ARRAY_CONTENT_ADDERS | HASH_CONTENT_ADDERS | STRING_CONTENT_ADDERS).freeze
331
292
 
332
- # The element types a single content-mutator call introduces into an
333
- # Array, given the per-argument types (already typed in the block
334
- # body scope). `concat`/`replace` take collection arguments, so their
335
- # element evidence is the arguments' OWN element types unioned; the
336
- # rest append the argument values directly. Returns `[]` when no
337
- # element evidence (e.g. a `<<` with no resolvable arg).
293
+ # The element types a single content-mutator call introduces into an Array, given the
294
+ # per-argument types (already typed in the block body scope). `concat`/`replace` take
295
+ # collection arguments, so their element evidence is the arguments' OWN element types
296
+ # unioned; the rest append the argument values directly. Returns `[]` when no element
297
+ # evidence (e.g. a `<<` with no resolvable arg).
338
298
  def array_added_elements(method_name, arg_types)
339
299
  return [] if arg_types.empty?
340
300
 
@@ -357,17 +317,15 @@ module Rigor
357
317
  end
358
318
  end
359
319
 
360
- # Builds the continuation Array type from the pre-state binding and
361
- # the appended element types. The floor is `Array[Dynamic[top]]`
362
- # (the sound empty-seed behaviour) when there is no element evidence
363
- # at all.
320
+ # Builds the continuation Array type from the pre-state binding and the appended element
321
+ # types. The floor is `Array[Dynamic[top]]` (the sound empty-seed behaviour) when there is
322
+ # no element evidence at all.
364
323
  def join_array_content(pre_state, added_elements)
365
324
  seed_elements = collection_element_types(pre_state)
366
325
  added = added_elements.compact
367
- # The empty-seed floor element is `Dynamic[top]` (no element
368
- # evidence). When real appended evidence exists that floor carries
369
- # nothing, so drop it — an empty accumulator built by `out << x*2`
370
- # reads `Array[Integer]`, not `Array[Integer | Dynamic[top]]`.
326
+ # The empty-seed floor element is `Dynamic[top]` (no element evidence). When real
327
+ # appended evidence exists that floor carries nothing, so drop it — an empty accumulator
328
+ # built by `out << x*2` reads `Array[Integer]`, not `Array[Integer | Dynamic[top]]`.
371
329
  seed_elements = drop_dynamic(seed_elements) unless added.empty?
372
330
  elements = seed_elements + added
373
331
  return Type::Combinator.nominal_of("Array", type_args: [Type::Combinator.untyped]) if elements.empty?
@@ -375,8 +333,8 @@ module Rigor
375
333
  Type::Combinator.nominal_of("Array", type_args: [Type::Combinator.union(*elements)])
376
334
  end
377
335
 
378
- # Builds the continuation Hash type from the pre-state binding and a
379
- # list of `[key_type, value_type]` pairs stored by `[]=` / `store`.
336
+ # Builds the continuation Hash type from the pre-state binding and a list of `[key_type,
337
+ # value_type]` pairs stored by `[]=` / `store`.
380
338
  def join_hash_content(pre_state, added_pairs)
381
339
  seed_keys, seed_values = hash_shape_key_values(pre_state)
382
340
  added_keys = added_pairs.map(&:first).compact
@@ -395,9 +353,9 @@ module Rigor
395
353
  types.grep_v(Type::Dynamic)
396
354
  end
397
355
 
398
- # Element types carried by a collection binding, regardless of which
399
- # carrier holds them: a `Tuple` lists them, a `Nominal[Array, [E]]`
400
- # has one element param, a bare `Array` / anything else yields none.
356
+ # Element types carried by a collection binding, regardless of which carrier holds them: a
357
+ # `Tuple` lists them, a `Nominal[Array, [E]]` has one element param, a bare `Array` /
358
+ # anything else yields none.
401
359
  def collection_element_types(type)
402
360
  case type
403
361
  when Type::Tuple
@@ -405,17 +363,16 @@ module Rigor
405
363
  when Type::Nominal
406
364
  type.class_name == "Array" ? type.type_args : []
407
365
  when Type::Union
408
- # A loop's single-pass join can union the widened collection with
409
- # its un-widened literal seed (`Array[0] | [0]`); pull element
410
- # evidence from every Array-ish member.
366
+ # A loop's single-pass join can union the widened collection with its un-widened
367
+ # literal seed (`Array[0] | [0]`); pull element evidence from every Array-ish member.
411
368
  type.members.flat_map { |m| collection_element_types(m) }
412
369
  else
413
370
  []
414
371
  end
415
372
  end
416
373
 
417
- # `[keys, values]` evidence from a Hash-ish pre-state binding —
418
- # a `HashShape` (literal pairs) or a `Nominal[Hash, [K, V]]`.
374
+ # `[keys, values]` evidence from a Hash-ish pre-state binding — a `HashShape` (literal
375
+ # pairs) or a `Nominal[Hash, [K, V]]`.
419
376
  def hash_shape_key_values(type)
420
377
  case type
421
378
  when Type::HashShape