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
@@ -9,49 +9,42 @@ require_relative "rbs_type_translator"
9
9
 
10
10
  module Rigor
11
11
  module Inference
12
- # Builds the entry scope of a method body by translating the method's
13
- # parameter list into a `name -> Rigor::Type` map.
12
+ # Builds the entry scope of a method body by translating the method's parameter list into a
13
+ # `name -> Rigor::Type` map.
14
14
  #
15
- # Parameter types come from the surrounding class's RBS signature
16
- # when one is available; otherwise every parameter defaults to
17
- # `Dynamic[Top]`. The default is the Slice 1 fail-soft answer for
18
- # unknown values, so a method whose RBS signature is missing or
19
- # whose parameters cannot be matched still binds every name into
20
- # the scope (a method body whose `Local x` reads return
21
- # `Dynamic[Top]` instead of falling through to the unbound-local
22
- # `Dynamic[Top]` event is the same observable type, but the
23
- # binding presence is what later slices need to attach narrowing
24
- # facts to).
15
+ # Parameter types come from the surrounding class's RBS signature when one is available;
16
+ # otherwise every parameter defaults to `Dynamic[Top]`. The default is the Slice 1 fail-soft
17
+ # answer for unknown values, so a method whose RBS signature is missing or whose parameters
18
+ # cannot be matched still binds every name into the scope (a method body whose `Local x`
19
+ # reads return `Dynamic[Top]` instead of falling through to the unbound-local
20
+ # `Dynamic[Top]` event is the same observable type, but the binding presence is what later
21
+ # slices need to attach narrowing facts to).
25
22
  #
26
- # The class context (`class_path:`) and `singleton:` flag are
27
- # supplied by the caller (the StatementEvaluator) which threads
28
- # them as the lexical class scope. The binder makes no assumption
29
- # about how that context was computed; it only uses it to build
30
- # the `(class_name, method_name)` lookup key for
31
- # `Rigor::Environment::RbsLoader#instance_method` /
32
- # `#singleton_method`.
23
+ # The class context (`class_path:`) and `singleton:` flag are supplied by the caller (the
24
+ # StatementEvaluator) which threads them as the lexical class scope. The binder makes no
25
+ # assumption about how that context was computed; it only uses it to build the
26
+ # `(class_name, method_name)` lookup key for `Rigor::Environment::RbsLoader#instance_method`
27
+ # / `#singleton_method`.
33
28
  #
34
29
  # See docs/internal-spec/inference-engine.md for the binding contract.
35
- # Leaf-name extraction for a destructured positional parameter
36
- # (`Prism::MultiTargetNode`). Stateless; lifted out of
37
- # {MethodParameterBinder} so the binder's class length stays in
30
+ # Leaf-name extraction for a destructured positional parameter (`Prism::MultiTargetNode`).
31
+ # Stateless; lifted out of {MethodParameterBinder} so the binder's class length stays in
38
32
  # budget.
39
33
  module Destructure
40
34
  module_function
41
35
 
42
- # Collect every leaf local name a `MultiTargetNode` binds,
43
- # recursing through nested destructures (`((a, b), c)`) and the
44
- # splat slot (`(a, *rest)`). Targets without a `#name` (an
45
- # index/call write target, vanishingly rare in a parameter
46
- # position) are skipped — there is no local to bind.
36
+ # Collect every leaf local name a `MultiTargetNode` binds, recursing through nested
37
+ # destructures (`((a, b), c)`) and the splat slot (`(a, *rest)`). Targets without a
38
+ # `#name` (an index/call write target, vanishingly rare in a parameter position) are
39
+ # skipped there is no local to bind.
47
40
  def target_names(multi_target)
48
41
  entries = multi_target.lefts + [multi_target.rest, *multi_target.rights].compact
49
42
  entries.flat_map { |entry| names_for_entry(entry) }
50
43
  end
51
44
 
52
45
  def names_for_entry(entry)
53
- # A splat sub-target (`*rest` inside the destructure) wraps its
54
- # real target in a `SplatNode#expression`; unwrap it.
46
+ # A splat sub-target (`*rest` inside the destructure) wraps its real target in a
47
+ # `SplatNode#expression`; unwrap it.
55
48
  entry = entry.expression if entry.is_a?(Prism::SplatNode) && entry.expression
56
49
  return [] if entry.nil?
57
50
  return target_names(entry) if entry.is_a?(Prism::MultiTargetNode)
@@ -63,19 +56,15 @@ module Rigor
63
56
 
64
57
  class MethodParameterBinder
65
58
  # @param environment [Rigor::Environment]
66
- # @param class_path [String, nil] the qualified name of the class
67
- # the method is defined in (e.g., `"Foo::Bar"`), or `nil` for a
68
- # top-level `def` outside any class. When `nil` (or when the
69
- # class is unknown to RBS), every parameter falls back to
70
- # `Dynamic[Top]`.
71
- # @param singleton [Boolean] `true` when the def is a singleton
72
- # method (either `def self.foo` or a `def foo` inside
73
- # `class << self`); routes the lookup through
59
+ # @param class_path [String, nil] the qualified name of the class the method is defined in
60
+ # (e.g., `"Foo::Bar"`), or `nil` for a top-level `def` outside any class. When `nil` (or
61
+ # when the class is unknown to RBS), every parameter falls back to `Dynamic[Top]`.
62
+ # @param singleton [Boolean] `true` when the def is a singleton method (either `def
63
+ # self.foo` or a `def foo` inside `class << self`); routes the lookup through
74
64
  # `RbsLoader#singleton_method`.
75
- # @param source_path [String, nil] the project-relative path of
76
- # the file the method is defined in. Used to match ADR-28
77
- # path-scoped protocol contracts; `nil` (the default for
78
- # synthetic / probe scopes) disables the contract tier.
65
+ # @param source_path [String, nil] the project-relative path of the file the method is
66
+ # defined in. Used to match ADR-28 path-scoped protocol contracts; `nil` (the default
67
+ # for synthetic / probe scopes) disables the contract tier.
79
68
  def initialize(environment:, class_path:, singleton:, source_path: nil)
80
69
  @environment = environment
81
70
  @class_path = class_path
@@ -84,9 +73,8 @@ module Rigor
84
73
  end
85
74
 
86
75
  # @param def_node [Prism::DefNode]
87
- # @return [Hash{Symbol => Rigor::Type}] ordered map from parameter
88
- # name to bound type. Anonymous parameters (`*` and `**` without
89
- # a name) are skipped.
76
+ # @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type.
77
+ # Anonymous parameters (`*` and `**` without a name) are skipped.
90
78
  def bind(def_node)
91
79
  slots = collect_slots(def_node.parameters)
92
80
  types = default_types_for(slots)
@@ -94,19 +82,15 @@ module Rigor
94
82
  rbs_method = lookup_rbs_method(def_node)
95
83
  if rbs_method
96
84
  apply_rbs_overloads(types, slots, rbs_method.method_types) unless rbs_method.method_types.empty?
97
- # `rigor:v1:param: <name> <refinement>` annotations
98
- # tighten the bound type for matching slots. Applied
99
- # after the RBS-overload pass so the override is the
100
- # authoritative answer regardless of what the RBS
101
- # signature declared.
85
+ # `rigor:v1:param: <name> <refinement>` annotations tighten the bound type for matching
86
+ # slots. Applied after the RBS-overload pass so the override is the authoritative
87
+ # answer regardless of what the RBS signature declared.
102
88
  apply_param_overrides(types, slots, rbs_method)
103
89
  end
104
- # ADR-28 — a path-scoped protocol contract supplies the
105
- # parameter type for a matching `def`. Applied last (most
106
- # authoritative) and regardless of RBS presence: the
107
- # methods a contract targets controller actions and the
108
- # like — typically have no RBS signature at all, so this
109
- # tier must run even when `rbs_method` is nil.
90
+ # ADR-28 — a path-scoped protocol contract supplies the parameter type for a matching
91
+ # `def`. Applied last (most authoritative) and regardless of RBS presence: the methods a
92
+ # contract targets — controller actions and the like — typically have no RBS signature
93
+ # at all, so this tier must run even when `rbs_method` is nil.
110
94
  apply_protocol_contract(types, slots, def_node)
111
95
  types
112
96
  end
@@ -120,14 +104,11 @@ module Rigor
120
104
  POSITIONAL_KINDS = %i[required_positional optional_positional].freeze
121
105
  private_constant :POSITIONAL_KINDS
122
106
 
123
- # Walk the Prism `ParametersNode` and emit one slot per named
124
- # parameter, in declaration order. Anonymous slots (rest /
125
- # keyword-rest with no name) are skipped because we have no
126
- # local name to bind. The slot's `:index` is the positional
127
- # index for required/optional/trailing positionals (used to look
128
- # up the matching RBS function param) and is `nil` for the
129
- # singleton kinds (`:rest_positional`, `:rest_keyword`,
130
- # `:block`).
107
+ # Walk the Prism `ParametersNode` and emit one slot per named parameter, in declaration
108
+ # order. Anonymous slots (rest / keyword-rest with no name) are skipped because we have no
109
+ # local name to bind. The slot's `:index` is the positional index for
110
+ # required/optional/trailing positionals (used to look up the matching RBS function param)
111
+ # and is `nil` for the singleton kinds (`:rest_positional`, `:rest_keyword`, `:block`).
131
112
  def collect_slots(params_node)
132
113
  return [] if params_node.nil?
133
114
 
@@ -153,17 +134,14 @@ module Rigor
153
134
  slots
154
135
  end
155
136
 
156
- # A destructured positional parameter — `def f((a, b))` — is a
157
- # `Prism::MultiTargetNode` in the `requireds`/`posts` list, not a
158
- # `RequiredParameterNode`, so it has no `#name`. Bind each leaf
159
- # sub-target local to `Dynamic[Top]` (a `:destructured_positional`
160
- # slot with no RBS index) instead of crashing on a blind `.name`.
161
- # Binding the names at all is what matters: it keeps the
162
- # destructured locals present in the entry scope so the body's
163
- # reads of them don't fall through to undefined-local noise. The
164
- # element types are not cheaply available from the parameter list
165
- # alone (no RBS function param maps onto a destructured slot), so
166
- # `Dynamic[Top]` is the sound default.
137
+ # A destructured positional parameter — `def f((a, b))` — is a `Prism::MultiTargetNode` in
138
+ # the `requireds`/`posts` list, not a `RequiredParameterNode`, so it has no `#name`. Bind
139
+ # each leaf sub-target local to `Dynamic[Top]` (a `:destructured_positional` slot with no
140
+ # RBS index) instead of crashing on a blind `.name`. Binding the names at all is what
141
+ # matters: it keeps the destructured locals present in the entry scope so the body's reads
142
+ # of them don't fall through to undefined-local noise. The element types are not cheaply
143
+ # available from the parameter list alone (no RBS function param maps onto a destructured
144
+ # slot), so `Dynamic[Top]` is the sound default.
167
145
  def append_positional_slot(slots, kind, param, index)
168
146
  if param.is_a?(Prism::MultiTargetNode)
169
147
  Destructure.target_names(param).each do |name|
@@ -207,9 +185,8 @@ module Rigor
207
185
  return nil if @class_path.nil?
208
186
 
209
187
  method_name = def_node.name
210
- # `def self.foo` always means a singleton method on the
211
- # immediate enclosing class. `def foo` inside `class << self`
212
- # is also a singleton method (the StatementEvaluator threads
188
+ # `def self.foo` always means a singleton method on the immediate enclosing class. `def
189
+ # foo` inside `class << self` is also a singleton method (the StatementEvaluator threads
213
190
  # the `singleton:` flag through this case).
214
191
  if def_node.receiver.is_a?(Prism::SelfNode) || @singleton
215
192
  Rigor::Reflection.singleton_method_definition(@class_path, method_name, environment: @environment)
@@ -218,13 +195,11 @@ module Rigor
218
195
  end
219
196
  end
220
197
 
221
- # Bind each parameter slot to the union of the matching parameter
222
- # types across every overload that *has* that slot. Overloads
223
- # that omit the slot (e.g., `Array#first` has both `()` and
224
- # `(int)` overloads only the second matches a `def first(n)`
225
- # redefinition) are silently skipped, so the binder defaults to
226
- # the most informative type the RBS signature provides without
227
- # having to know which overload the runtime will pick.
198
+ # Bind each parameter slot to the union of the matching parameter types across every
199
+ # overload that *has* that slot. Overloads that omit the slot (e.g., `Array#first` has
200
+ # both `()` and `(int)` overloads only the second matches a `def first(n)` redefinition)
201
+ # are silently skipped, so the binder defaults to the most informative type the RBS
202
+ # signature provides without having to know which overload the runtime will pick.
228
203
  def apply_rbs_overloads(types, slots, method_types)
229
204
  slots.each do |slot|
230
205
  next if slot.name.nil?
@@ -236,13 +211,11 @@ module Rigor
236
211
  end
237
212
  end
238
213
 
239
- # Reads the override map off the method's annotations and
240
- # replaces the binding for any slot whose name appears in
241
- # the map. Anonymous slots are skipped (no name to match).
242
- # The override is used verbatim no `:rest_*` re-wrapping —
243
- # so authors who tighten a `*rest` parameter to e.g.
244
- # `non-empty-array[Integer]` describe the parameter binding
245
- # they actually want, not its element type.
214
+ # Reads the override map off the method's annotations and replaces the binding for any
215
+ # slot whose name appears in the map. Anonymous slots are skipped (no name to match). The
216
+ # override is used verbatim no `:rest_*` re-wrapping — so authors who tighten a `*rest`
217
+ # parameter to e.g. `non-empty-array[Integer]` describe the parameter binding they
218
+ # actually want, not its element type.
246
219
  def apply_param_overrides(types, slots, rbs_method)
247
220
  override_map = RbsExtended.param_type_override_map(rbs_method, environment: @environment)
248
221
  return if override_map.empty?
@@ -257,13 +230,11 @@ module Rigor
257
230
  end
258
231
  end
259
232
 
260
- # ADR-28 — when a path-scoped protocol contract targets this
261
- # `def` (file path matches the contract's `path_glob`, method
262
- # name + singleton-ness match), replace each contracted
263
- # positional slot's binding with the contract's declared type.
264
- # The type name resolves against the environment lazily here;
265
- # an unresolvable name (the protocol's RBS not loaded) falls
266
- # through to whatever the prior tiers bound, fail-soft.
233
+ # ADR-28 — when a path-scoped protocol contract targets this `def` (file path matches the
234
+ # contract's `path_glob`, method name + singleton-ness match), replace each contracted
235
+ # positional slot's binding with the contract's declared type. The type name resolves
236
+ # against the environment lazily here; an unresolvable name (the protocol's RBS not
237
+ # loaded) falls through to whatever the prior tiers bound, fail-soft.
267
238
  def apply_protocol_contract(types, slots, def_node)
268
239
  return if @source_path.nil?
269
240
 
@@ -311,17 +282,14 @@ module Rigor
311
282
  wrap_for_kind(bound, kind)
312
283
  end
313
284
 
314
- # Dispatch table from slot kind to a small lambda that pulls the
315
- # matching RBS parameter type out of an `RBS::Types::Function`.
316
- # The hash keeps `rbs_type_for_slot` linear (one lookup, one
317
- # call) so the cyclomatic-complexity budget does not balloon as
318
- # future slices add more parameter kinds (e.g., `**Symbol kw` is
319
- # a candidate for a stricter route in Slice 5+).
320
- # Match keyword parameters by name across both required and
321
- # optional keyword maps. RBS may declare a keyword as optional
322
- # (`?by:`) while the Ruby `def` lists it as required (or vice
323
- # versa); the binding is by-name regardless of which side
324
- # defines it.
285
+ # Dispatch table from slot kind to a small lambda that pulls the matching RBS parameter
286
+ # type out of an `RBS::Types::Function`. The hash keeps `rbs_type_for_slot` linear (one
287
+ # lookup, one call) so the cyclomatic-complexity budget does not balloon as future slices
288
+ # add more parameter kinds (e.g., `**Symbol kw` is a candidate for a stricter route in
289
+ # Slice 5+).
290
+ # Match keyword parameters by name across both required and optional keyword maps. RBS may
291
+ # declare a keyword as optional (`?by:`) while the Ruby `def` lists it as required (or
292
+ # vice versa); the binding is by-name regardless of which side defines it.
325
293
  KEYWORD_PROVIDER = Ractor.make_shareable(lambda do |fn, slot|
326
294
  fn.required_keywords[slot.name]&.type || fn.optional_keywords[slot.name]&.type
327
295
  end)
@@ -355,11 +323,10 @@ module Rigor
355
323
  provider.call(function, slot)
356
324
  end
357
325
 
358
- # The variable bound to a `*rest` parameter is the *Array* of
359
- # rest-positional arguments, not a single element. Likewise
360
- # `**kw` is bound to a `Hash[Symbol, V]`. Wrap the translated
361
- # element/value type accordingly so `rest` reads as
362
- # `Array[Integer]` rather than `Integer`.
326
+ # The variable bound to a `*rest` parameter is the *Array* of rest-positional arguments,
327
+ # not a single element. Likewise `**kw` is bound to a `Hash[Symbol, V]`. Wrap the
328
+ # translated element/value type accordingly so `rest` reads as `Array[Integer]` rather
329
+ # than `Integer`.
363
330
  def wrap_for_kind(translated, kind)
364
331
  case kind
365
332
  when :rest_positional
@@ -8,57 +8,45 @@ module Rigor
8
8
  module Inference
9
9
  # Slice 5 phase 2 sub-phase 2 destructuring binder.
10
10
  #
11
- # `Rigor::Inference::MultiTargetBinder` decomposes a tuple-shaped
12
- # right-hand side type against a Prism multi-target tree and
13
- # produces a `name -> Rigor::Type` binding map. The binder is
14
- # shared between two surfaces:
11
+ # `Rigor::Inference::MultiTargetBinder` decomposes a tuple-shaped right-hand side type
12
+ # against a Prism multi-target tree and produces a `name -> Rigor::Type` binding map. The
13
+ # binder is shared between two surfaces:
15
14
  #
16
- # 1. `Rigor::Inference::StatementEvaluator#eval_multi_write` for
17
- # the statement-level `a, b = rhs` form (`Prism::MultiWriteNode`).
18
- # 2. `Rigor::Inference::BlockParameterBinder` for nested
19
- # destructuring inside block parameter lists
20
- # (`Prism::MultiTargetNode` under `BlockParametersNode#requireds`).
15
+ # 1. `Rigor::Inference::StatementEvaluator#eval_multi_write` for the statement-level `a, b =
16
+ # rhs` form (`Prism::MultiWriteNode`).
17
+ # 2. `Rigor::Inference::BlockParameterBinder` for nested destructuring inside block parameter
18
+ # lists (`Prism::MultiTargetNode` under `BlockParametersNode#requireds`).
21
19
  #
22
- # Both Prism nodes share the same `lefts` / `rest` (a
23
- # `Prism::SplatNode`) / `rights` triple, so the binder treats them
24
- # uniformly. The binder is pure: it MUST NOT mutate its inputs and
20
+ # Both Prism nodes share the same `lefts` / `rest` (a `Prism::SplatNode`) / `rights` triple,
21
+ # so the binder treats them uniformly. The binder is pure: it MUST NOT mutate its inputs and
25
22
  # MUST return a fresh `Hash` on every call.
26
23
  #
27
- # The binder threads `Type::Tuple` decompositions when the
28
- # right-hand side carrier is a known-arity tuple. Other carriers
29
- # (`Nominal[Array]`, `Dynamic[Top]`, `Top`, `Bot`, ...) collapse
30
- # to `Dynamic[Top]` per slot Slice 5 phase 2 sub-phase 2 stays
31
- # conservative on dynamic-arity right-hand sides until the
32
- # narrower receiver-shape lattice lands.
24
+ # The binder threads `Type::Tuple` decompositions when the right-hand side carrier is a
25
+ # known-arity tuple. Other carriers (`Nominal[Array]`, `Dynamic[Top]`, `Top`, `Bot`, ...)
26
+ # collapse to `Dynamic[Top]` per slot — Slice 5 phase 2 sub-phase 2 stays conservative on
27
+ # dynamic-arity right-hand sides until the narrower receiver-shape lattice lands.
33
28
  #
34
29
  # Targets the binder recognises:
35
30
  #
36
- # - `Prism::LocalVariableTargetNode` — used by the statement-level
37
- # `a, b = rhs` form. Binds `target.name` to its slice of the
38
- # right-hand side.
39
- # - `Prism::RequiredParameterNode` used by block-parameter
40
- # destructuring (`|(a, b), c|`). Prism encodes the inner names
41
- # of a block-side `MultiTargetNode` as parameter nodes rather
42
- # than target nodes; the binder treats them uniformly with
43
- # their `LocalVariableTargetNode` cousins because they carry
44
- # the same `name:` field and the same observable semantics
45
- # (binding a fresh local in the block-entry scope).
46
- # - `Prism::MultiTargetNode` recurses with the slot's type as
47
- # the new right-hand side.
48
- # - `Prism::SplatNode` (used for `rest`) — its `expression` MUST
49
- # be a `Prism::LocalVariableTargetNode` or a
50
- # `Prism::RequiredParameterNode` to be observable; an anonymous
51
- # `*` splat or a non-local target is skipped.
31
+ # - `Prism::LocalVariableTargetNode` — used by the statement-level `a, b = rhs` form. Binds
32
+ # `target.name` to its slice of the right-hand side.
33
+ # - `Prism::RequiredParameterNode` — used by block-parameter destructuring (`|(a, b), c|`).
34
+ # Prism encodes the inner names of a block-side `MultiTargetNode` as parameter nodes
35
+ # rather than target nodes; the binder treats them uniformly with their
36
+ # `LocalVariableTargetNode` cousins because they carry the same `name:` field and the
37
+ # same observable semantics (binding a fresh local in the block-entry scope).
38
+ # - `Prism::MultiTargetNode` recurses with the slot's type as the new right-hand side.
39
+ # - `Prism::SplatNode` (used for `rest`) its `expression` MUST be a
40
+ # `Prism::LocalVariableTargetNode` or a `Prism::RequiredParameterNode` to be observable;
41
+ # an anonymous `*` splat or a non-local target is skipped.
52
42
  #
53
- # Other target kinds (`InstanceVariableTargetNode`,
54
- # `ConstantTargetNode`, `IndexTargetNode`, `CallTargetNode`,
55
- # `ConstantPathTargetNode`, `ImplicitRestNode`, ...) MUST be
56
- # silently skipped: they have no observable contribution to the
57
- # local-variable scope the StatementEvaluator threads.
43
+ # Other target kinds (`InstanceVariableTargetNode`, `ConstantTargetNode`,
44
+ # `IndexTargetNode`, `CallTargetNode`, `ConstantPathTargetNode`, `ImplicitRestNode`, ...)
45
+ # MUST be silently skipped: they have no observable contribution to the local-variable scope
46
+ # the StatementEvaluator threads.
58
47
  #
59
- # See docs/internal-spec/inference-engine.md for the binding
60
- # contract and docs/adr/4-type-inference-engine.md for the slice
61
- # rationale.
48
+ # See docs/internal-spec/inference-engine.md for the binding contract and
49
+ # docs/adr/4-type-inference-engine.md for the slice rationale.
62
50
  module MultiTargetBinder
63
51
  module_function
64
52
 
@@ -85,11 +73,10 @@ module Rigor
85
73
  rights.each_with_index { |t, i| bind_target(t, backs[i], bindings) }
86
74
  end
87
75
 
88
- # Decomposes the right-hand side type into the per-slot
89
- # types. Returns a `[fronts, rest_type, backs]` triple, with
90
- # `fronts` and `backs` each an ordered array of length
91
- # `front_count`/`back_count`, and `rest_type` either a
92
- # `Rigor::Type` (when `rest_present:` is true) or `nil`.
76
+ # Decomposes the right-hand side type into the per-slot types. Returns a `[fronts,
77
+ # rest_type, backs]` triple, with `fronts` and `backs` each an ordered array of length
78
+ # `front_count`/`back_count`, and `rest_type` either a `Rigor::Type` (when
79
+ # `rest_present:` is true) or `nil`.
93
80
  def decompose(rhs_type, front_count, back_count, rest_present:)
94
81
  if rhs_type.is_a?(Type::Tuple)
95
82
  decompose_tuple(rhs_type, front_count, back_count, rest_present: rest_present)
@@ -113,28 +100,24 @@ module Rigor
113
100
  [fronts, rest_type, backs]
114
101
  end
115
102
 
116
- # The per-slot type for index `i` of a tuple decomposition, FP-safely
117
- # softened: a missing slot is `nil` (the runtime value of an
118
- # over-destructured positional), and a PRESENT but nil-bearing slot
119
- # (`X | nil`) is softened to its non-`nil` part for a heterogeneous
120
- # `Tuple` whose optional slot was made optional by flow.
103
+ # The per-slot type for index `i` of a tuple decomposition, FP-safely softened: a
104
+ # missing slot is `nil` (the runtime value of an over-destructured positional), and a
105
+ # PRESENT but nil-bearing slot (`X | nil`) is softened to its non-`nil` part — for a
106
+ # heterogeneous `Tuple` whose optional slot was made optional by flow.
121
107
  #
122
- # Rationale (ADR-57 slice 3 work-item 2): a destructure of a tuple
123
- # element that flow typed as optional is almost always guarded by a
124
- # CORRELATED invariant the flow engine cannot prove. The canonical case
125
- # is haml's `parse_tag`, which returns `[..., last_line || @line.index
126
- # + 1]` a 9-tuple whose `last_line` slot widens to `Dynamic[top]?`
127
- # through a loop-nested destructure; at the call site `..., last_line =
128
- # parse_tag(text); raise(..., last_line - 1) if parse && value.empty?`
129
- # the `last_line` is nil ONLY when an earlier element is too, and the
130
- # guard short-circuits but that correlation lives across slots, so
131
- # per-slot flow sees `last_line` as nil-able and `last_line - 1` fires a
132
- # spurious `possible nil receiver`. Manufacturing a `T?` for every
133
- # destructured slot frightens working code; FP discipline (the program
134
- # works) outranks the worst-case per-slot reading, so we drop the `nil`
135
- # from a destructured slot and keep the non-`nil` constituent (a bare
136
- # `nil` slot stays `nil` — there is nothing to soften). A pure non-
137
- # optional element keeps its precise type unchanged.
108
+ # Rationale (ADR-57 slice 3 work-item 2): a destructure of a tuple element that flow
109
+ # typed as optional is almost always guarded by a CORRELATED invariant the flow engine
110
+ # cannot prove. The canonical case is haml's `parse_tag`, which returns `[...,
111
+ # last_line || @line.index + 1]` a 9-tuple whose `last_line` slot widens to
112
+ # `Dynamic[top]?` through a loop-nested destructure; at the call site `..., last_line =
113
+ # parse_tag(text); raise(..., last_line - 1) if parse && value.empty?` the `last_line`
114
+ # is nil ONLY when an earlier element is too, and the guard short-circuits — but that
115
+ # correlation lives across slots, so per-slot flow sees `last_line` as nil-able and
116
+ # `last_line - 1` fires a spurious `possible nil receiver`. Manufacturing a `T?` for
117
+ # every destructured slot frightens working code; FP discipline (the program works)
118
+ # outranks the worst-case per-slot reading, so we drop the `nil` from a destructured slot
119
+ # and keep the non-`nil` constituent (a bare `nil` slot stays `nil` there is nothing to
120
+ # soften). A pure non-optional element keeps its precise type unchanged.
138
121
  def slot_type(elements, index)
139
122
  element = elements[index]
140
123
  return Type::Combinator.constant_of(nil) if element.nil?