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
@@ -7,25 +7,19 @@ require_relative "member_shape_projection"
7
7
  module Rigor
8
8
  module Inference
9
9
  module MethodDispatcher
10
- # ADR-48 — `Data.define` value folding. Three responsibilities, all
11
- # gated on a fully-decidable shape and degrading to today's behaviour
12
- # (no carrier / the `Data` nominal) the moment a premise is uncertain,
13
- # so the tier is precision-additive and adds no false-positive
14
- # surface:
10
+ # ADR-48 — `Data.define` value folding. Three responsibilities, all gated on a fully-decidable shape
11
+ # and degrading to today's behaviour (no carrier / the `Data` nominal) the moment a premise is
12
+ # uncertain, so the tier is precision-additive and adds no false-positive surface:
15
13
  #
16
- # 1. `Data.define(:x, :y)` on a `Singleton[Data]` receiver with
17
- # literal-Symbol args and NO block -> `DataClass{members: [...]}`.
18
- # A block (`Data.define(:x) do ... end`) defers (slice 4 hardens
19
- # the block-body case); non-literal members (`Data.define(*names)`)
20
- # defer.
21
- # 2. `.new` / `.[]` on a `DataClass` receiver -> a `DataInstance`
22
- # whose member map is built from the call's positional or keyword
23
- # arguments. An arity / key mismatch degrades to the `Data` (or the
14
+ # 1. `Data.define(:x, :y)` on a `Singleton[Data]` receiver with literal-Symbol args and NO block ->
15
+ # `DataClass{members: [...]}`. A block (`Data.define(:x) do ... end`) defers (slice 4 hardens the
16
+ # block-body case); non-literal members (`Data.define(*names)`) defer.
17
+ # 2. `.new` / `.[]` on a `DataClass` receiver -> a `DataInstance` whose member map is built from the
18
+ # call's positional or keyword arguments. An arity / key mismatch degrades to the `Data` (or the
24
19
  # tagged class) nominal rather than a wrong member map.
25
- # 3. member reads + `[]` / `to_h` / `deconstruct` / `deconstruct_keys`
26
- # / `members` / `with` on a `DataInstance` receiver -> the precise
27
- # projected type. Unhandled methods return nil so the pipeline
28
- # projects the instance to its nominal through RbsDispatch.
20
+ # 3. member reads + `[]` / `to_h` / `deconstruct` / `deconstruct_keys` / `members` / `with` on a
21
+ # `DataInstance` receiver -> the precise projected type. Unhandled methods return nil so the
22
+ # pipeline projects the instance to its nominal through RbsDispatch.
29
23
  #
30
24
  # See docs/adr/48-data-struct-value-folding.md.
31
25
  module DataFolding
@@ -51,11 +45,10 @@ module Rigor
51
45
  end
52
46
  end
53
47
 
54
- # A `Data.define` value object assigned to a constant (or a
55
- # `class Point < Data.define(...)` subclass) is canonicalised by the
56
- # engine to `Singleton[Point]`, not a `DataClass` so its member
57
- # layout is read from the project side-table the scope indexer built
58
- # (`Scope#data_member_layout`) rather than from the receiver carrier.
48
+ # A `Data.define` value object assigned to a constant (or a `class Point < Data.define(...)`
49
+ # subclass) is canonicalised by the engine to `Singleton[Point]`, not a `DataClass` — so its member
50
+ # layout is read from the project side-table the scope indexer built (`Scope#data_member_layout`)
51
+ # rather than from the receiver carrier.
59
52
  def fold_named_new(singleton, context)
60
53
  scope = context.scope
61
54
  return nil if scope.nil?
@@ -79,8 +72,8 @@ module Rigor
79
72
  Type::Combinator.data_class_of(members: members)
80
73
  end
81
74
 
82
- # The ordered Symbol member names, or nil when any argument is not
83
- # a literal `Constant[Symbol]` (a splat or dynamic name).
75
+ # The ordered Symbol member names, or nil when any argument is not a literal `Constant[Symbol]` (a
76
+ # splat or dynamic name).
84
77
  def member_names_from_args(args)
85
78
  names = args.map do |arg|
86
79
  return nil unless arg.is_a?(Type::Constant) && arg.value.is_a?(Symbol)
@@ -104,9 +97,8 @@ module Rigor
104
97
  Type::Combinator.data_instance_of(members: map, class_name: class_name)
105
98
  end
106
99
 
107
- # Builds the member -> type map from the call's arguments, honouring
108
- # the keyword vs positional distinction read off the call node. nil
109
- # when the arguments cannot soundly populate every member.
100
+ # Builds the member -> type map from the call's arguments, honouring the keyword vs positional
101
+ # distinction read off the call node. nil when the arguments cannot soundly populate every member.
110
102
  def member_map_for_new(members, context)
111
103
  if keyword_new?(context)
112
104
  keyword_member_map(members, context.args)
@@ -115,10 +107,9 @@ module Rigor
115
107
  end
116
108
  end
117
109
 
118
- # `Point.new(x: 1, y: 2)` arrives as a single trailing `HashShape`
119
- # arg whose call node is a `KeywordHashNode`. Distinguishing it from
120
- # a positional hash (`Point.new({x: 1})`, a `HashNode`) needs the
121
- # call node, since both type to a `HashShape`.
110
+ # `Point.new(x: 1, y: 2)` arrives as a single trailing `HashShape` arg whose call node is a
111
+ # `KeywordHashNode`. Distinguishing it from a positional hash (`Point.new({x: 1})`, a `HashNode`)
112
+ # needs the call node, since both type to a `HashShape`.
122
113
  def keyword_new?(context)
123
114
  node = context.call_node
124
115
  return false if node.nil?
@@ -146,9 +137,8 @@ module Rigor
146
137
  members.zip(args).to_h
147
138
  end
148
139
 
149
- # A `.new` whose arguments do not fold to a precise map still has a
150
- # sound, more-precise-than-Dynamic answer: an instance of the
151
- # tagged class (or the `Data` supertype).
140
+ # A `.new` whose arguments do not fold to a precise map still has a sound, more-precise-than-Dynamic
141
+ # answer: an instance of the tagged class (or the `Data` supertype).
152
142
  def degraded_instance(class_name)
153
143
  Type::Combinator.nominal_of(class_name || "Data")
154
144
  end
@@ -8,51 +8,36 @@ module Rigor
8
8
  module MethodDispatcher
9
9
  # IO / File support — the pure-path-manipulation tier.
10
10
  #
11
- # File and IO carry a lot of side-effecting surface (filesystem
12
- # reads, descriptor mutations, line iteration) the analyzer
13
- # cannot fold. Several `File` class methods, however, are
14
- # functions over their path-string arguments — they do NOT
15
- # touch the filesystem and do NOT depend on the current
16
- # working directory.
11
+ # File and IO carry a lot of side-effecting surface (filesystem reads, descriptor mutations, line iteration)
12
+ # the analyzer cannot fold. Several `File` class methods, however, are functions over their path-string
13
+ # arguments they do NOT touch the filesystem and do NOT depend on the current working directory.
17
14
  #
18
- # Folding them is platform-sensitive: every recognised method
19
- # ([:basename, :dirname, :extname, :join, :split,
20
- # :absolute_path?]) reads `File::SEPARATOR` /
21
- # `File::ALT_SEPARATOR` and produces different answers on
22
- # Windows vs POSIX hosts. The Ruby process running the
23
- # analyzer hosts ONE platform; folding to a `Constant<String>`
24
- # would silently bake that platform's answer into the
25
- # analyzer's result and mis-report it on a host with a
15
+ # Folding them is platform-sensitive: every recognised method ([:basename, :dirname, :extname, :join, :split,
16
+ # :absolute_path?]) reads `File::SEPARATOR` / `File::ALT_SEPARATOR` and produces different answers on Windows
17
+ # vs POSIX hosts. The Ruby process running the analyzer hosts ONE platform; folding to a `Constant<String>`
18
+ # would silently bake that platform's answer into the analyzer's result and mis-report it on a host with a
26
19
  # different separator policy.
27
20
  #
28
- # Default policy (`fold_platform_specific_paths == false`):
29
- # decline the fold so the RBS tier answers with `Nominal[String]`
30
- # / `Tuple[Nominal[String], Nominal[String]]` / `bool`. That is
31
- # the platform-agnostic envelope every concrete answer the
32
- # method could legally return on any platform fits inside it.
33
- # The future `non-empty-string` refinement carrier (see
34
- # [imported-built-in-types.md](../../../../../docs/type-specification/imported-built-in-types.md))
35
- # will tighten the basename/dirname/join cases further without
36
- # leaking platform specifics; today we leave them at the
37
- # nominal envelope.
21
+ # Default policy (`fold_platform_specific_paths == false`): decline the fold so the RBS tier answers with
22
+ # `Nominal[String]` / `Tuple[Nominal[String], Nominal[String]]` / `bool`. That is the platform-agnostic
23
+ # envelope every concrete answer the method could legally return on any platform fits inside it. The future
24
+ # `non-empty-string` refinement carrier (see
25
+ # [imported-built-in-types.md](../../../../../docs/type-specification/imported-built-in-types.md)) will
26
+ # tighten the basename/dirname/join cases further without leaking platform specifics; today we leave them at
27
+ # the nominal envelope.
38
28
  #
39
- # Opt-in policy (`fold_platform_specific_paths == true`):
40
- # the analyzer trusts that its host platform matches the
41
- # callers' deployment target and folds to a precise
42
- # `Constant<String>`. Single-platform projects (most internal
43
- # tooling, Rails apps deployed to Linux containers) can
44
- # enable this in `.rigor.yml`:
29
+ # Opt-in policy (`fold_platform_specific_paths == true`): the analyzer trusts that its host platform matches
30
+ # the callers' deployment target and folds to a precise `Constant<String>`. Single-platform projects (most
31
+ # internal tooling, Rails apps deployed to Linux containers) can enable this in `.rigor.yml`:
45
32
  #
46
33
  # fold_platform_specific_paths: true
47
34
  #
48
- # The runner reads this on startup (`Rigor::Analysis::Runner`)
49
- # and writes the flag here. Tests toggle the flag explicitly.
35
+ # The runner reads this on startup (`Rigor::Analysis::Runner`) and writes the flag here. Tests toggle the
36
+ # flag explicitly.
50
37
  #
51
- # See [ADR-5 — robustness principle](../../../../../docs/adr/5-robustness-principle.md):
52
- # the platform-agnostic default is clause-1 of the principle
53
- # applied with the constraint that "as strict as can be
54
- # *correctness-preservingly* proved" excludes Constants whose
55
- # value is host-specific.
38
+ # See [ADR-5 — robustness principle](../../../../../docs/adr/5-robustness-principle.md): the
39
+ # platform-agnostic default is clause-1 of the principle applied with the constraint that "as strict as can
40
+ # be *correctness-preservingly* proved" excludes Constants whose value is host-specific.
56
41
  module FileFolding
57
42
  # File class methods the analyzer can fold when the opt-in
58
43
  # flag is set. Currently identical to PLATFORM_DEPENDENT_METHODS
@@ -8,26 +8,20 @@ module Rigor
8
8
  module MethodDispatcher
9
9
  # Iterator-style block-parameter typing.
10
10
  #
11
- # Sits ahead of `RbsDispatch.block_param_types` so the precise
12
- # integer bounds for `Integer#times` / `Integer#upto` /
13
- # `Integer#downto` reach the block body's parameter binder.
14
- # Without this tier the RBS signature for `Integer#times`
15
- # widens the index to `Nominal[Integer]`, dropping every
16
- # bound the receiver carries.
11
+ # Sits ahead of `RbsDispatch.block_param_types` so the precise integer bounds for `Integer#times` /
12
+ # `Integer#upto` / `Integer#downto` reach the block body's parameter binder. Without this tier the
13
+ # RBS signature for `Integer#times` widens the index to `Nominal[Integer]`, dropping every bound the
14
+ # receiver carries.
17
15
  #
18
16
  # Each rule mirrors Ruby's actual iteration semantics:
19
17
  #
20
- # - `n.times { |i| … }` yields `i ∈ [0, n-1]` when `n > 0`,
21
- # nothing otherwise. The block-param type is therefore
22
- # `int<0, n-1>` for a `Constant<Integer>` receiver,
23
- # `int<0, upper-1>` for a finite `IntegerRange`, and
24
- # `non_negative_int` for any unbounded-above shape.
25
- # - `a.upto(b) { |i| … }` yields `i [a, b]` when `a <= b`.
26
- # Lower bound from the receiver, upper bound from the
27
- # argument.
28
- # - `a.downto(b) { |i| … }` yields the same domain `[b, a]`,
29
- # just iterated in reverse. Lower bound from the
30
- # argument, upper bound from the receiver.
18
+ # - `n.times { |i| … }` yields `i ∈ [0, n-1]` when `n > 0`, nothing otherwise. The block-param type is
19
+ # therefore `int<0, n-1>` for a `Constant<Integer>` receiver, `int<0, upper-1>` for a finite
20
+ # `IntegerRange`, and `non_negative_int` for any unbounded-above shape.
21
+ # - `a.upto(b) { |i| … }` yields `i ∈ [a, b]` when `a <= b`. Lower bound from the receiver, upper
22
+ # bound from the argument.
23
+ # - `a.downto(b) { |i| … }` yields the same domain `[b, a]`, just iterated in reverse. Lower bound
24
+ # from the argument, upper bound from the receiver.
31
25
  module IteratorDispatch
32
26
  module_function
33
27
 
@@ -50,15 +44,12 @@ module Rigor
50
44
  end
51
45
  end
52
46
 
53
- # `Class.new { |c| … }` and `Class.new(Parent) { |c| … }`
54
- # the block parameter is the freshly-created anonymous
55
- # class, statically representable as the parent's singleton
56
- # type (the new class inherits every singleton method the
57
- # parent exposes, which is what callers use this form to
58
- # configure: `c.table_name = …`, `c.attribute :foo`, etc.).
59
- # No parent → `singleton(Object)`. RBS would otherwise widen
60
- # the block param to bare `Nominal[Class]`, dropping access
61
- # to the parent's class-side surface.
47
+ # `Class.new { |c| … }` and `Class.new(Parent) { |c| … }` — the block parameter is the
48
+ # freshly-created anonymous class, statically representable as the parent's singleton type (the new
49
+ # class inherits every singleton method the parent exposes, which is what callers use this form to
50
+ # configure: `c.table_name = …`, `c.attribute :foo`, etc.). No parent → `singleton(Object)`. RBS
51
+ # would otherwise widen the block param to bare `Nominal[Class]`, dropping access to the parent's
52
+ # class-side surface.
62
53
  def class_new_block_params(receiver, args)
63
54
  return nil unless class_metaclass_receiver?(receiver)
64
55
 
@@ -95,24 +86,20 @@ module Rigor
95
86
  [build_index_range(lower_bound_of(end_arg), upper_bound_of(receiver))]
96
87
  end
97
88
 
98
- # Generalised iterator: every Enumerable-shaped collection
99
- # in v0.0.4 yields `(element, index)` where the index is
100
- # always `non-negative-int`. The element comes from the
101
- # receiver's shape:
89
+ # Generalised iterator: every Enumerable-shaped collection in v0.0.4 yields `(element, index)` where
90
+ # the index is always `non-negative-int`. The element comes from the receiver's shape:
102
91
  #
103
92
  # - `Array[T]` / `Set[T]` / `Range[T]` → T
104
93
  # - `Tuple[A, B, C]` → A | B | C
105
- # (empty tuple cannot iterate, but we conservatively
106
- # fall through to RBS so a missing rule never throws)
94
+ # (empty tuple cannot iterate, but we conservatively fall through to RBS so a missing rule never
95
+ # throws)
107
96
  # - `Hash[K, V]` / `HashShape{...}` → Tuple[K, V]
108
97
  # (Ruby yields `[key, value]` pairs as the element)
109
98
  # - `Constant<Array>` / `Constant<Range>` / `Constant<Set>`
110
99
  # → corresponding Constant element
111
100
  #
112
- # Receivers we cannot project (Top, Dynamic, unknown
113
- # nominals, IO, …) decline so the RBS tier still answers
114
- # — its element type is correct, only the index would
115
- # widen to plain Integer.
101
+ # Receivers we cannot project (Top, Dynamic, unknown nominals, IO, …) decline so the RBS tier still
102
+ # answers its element type is correct, only the index would widen to plain Integer.
116
103
  def each_with_index_block_params(receiver)
117
104
  element = element_type_of(receiver)
118
105
  return nil if element.nil?
@@ -120,13 +107,11 @@ module Rigor
120
107
  [element, Type::Combinator.non_negative_int]
121
108
  end
122
109
 
123
- # `each_with_object(memo) { |elem, memo_inner| … }` yields
124
- # `(element, memo)` where `memo` is the second argument's
125
- # type (passed by reference and threaded across iterations
126
- # at runtime Rigor reflects that by binding the block's
127
- # second parameter to whatever the call site supplied).
128
- # When the call has no memo argument the dispatcher
129
- # declines so the user's RBS / overload selector decides.
110
+ # `each_with_object(memo) { |elem, memo_inner| … }` yields `(element, memo)` where `memo` is the
111
+ # second argument's type (passed by reference and threaded across iterations at runtime — Rigor
112
+ # reflects that by binding the block's second parameter to whatever the call site supplied). When
113
+ # the call has no memo argument the dispatcher declines so the user's RBS / overload selector
114
+ # decides.
130
115
  def each_with_object_block_params(receiver, memo_arg)
131
116
  return nil if memo_arg.nil?
132
117
 
@@ -136,21 +121,15 @@ module Rigor
136
121
  [element, memo_arg]
137
122
  end
138
123
 
139
- # `inject(seed) { |memo, elem| … }` and `reduce` accept
140
- # three call shapes:
124
+ # `inject(seed) { |memo, elem| … }` and `reduce` accept three call shapes:
141
125
  #
142
- # - `(seed) { |memo, elem| … }` — block params `[seed, element]`.
143
- # The memo's static type is the seed's; we cannot prove the
144
- # block return type here without round-tripping through the
145
- # block analyser, so the binding is the seed's type and
146
- # downstream inference widens as needed.
147
- # - `() { |memo, elem| }` — the first iteration uses the
148
- # first element as the memo, so `[element, element]` is the
149
- # sound binding.
150
- # - `(seed, :sym)` / `(:sym)` — Symbol method-name forms have
151
- # no block. `inject` with a Symbol final arg is recognised
152
- # and declined (returns nil) so the dispatcher does not
153
- # pretend a block existed.
126
+ # - `(seed) { |memo, elem| … }` — block params `[seed, element]`. The memo's static type is the
127
+ # seed's; we cannot prove the block return type here without round-tripping through the block
128
+ # analyser, so the binding is the seed's type and downstream inference widens as needed.
129
+ # - `() { |memo, elem| … }` — the first iteration uses the first element as the memo, so
130
+ # `[element, element]` is the sound binding.
131
+ # - `(seed, :sym)` / `(:sym)`Symbol method-name forms have no block. `inject` with a Symbol final
132
+ # arg is recognised and declined (returns nil) so the dispatcher does not pretend a block existed.
154
133
  def inject_block_params(receiver, args)
155
134
  element = element_type_of(receiver)
156
135
  return nil if element.nil?
@@ -175,18 +154,15 @@ module Rigor
175
154
  type.is_a?(Type::Constant) && type.value.is_a?(Symbol)
176
155
  end
177
156
 
178
- # Element-yielding Enumerable methods covered as a placeholder.
179
- # RBS already binds the block parameter correctly for plain
180
- # `Array[T]` / `Set[T]` / `Range[T]` receivers via generic
181
- # substitution; this tier exists so Tuple- and HashShape-shaped
182
- # receivers reach the block body with the precise per-position
183
- # element union / `Tuple[K, V]` pair rather than the projected
157
+ # Element-yielding Enumerable methods covered as a placeholder. RBS already binds the block
158
+ # parameter correctly for plain `Array[T]` / `Set[T]` / `Range[T]` receivers via generic
159
+ # substitution; this tier exists so Tuple- and HashShape-shaped receivers reach the block body with
160
+ # the precise per-position element union / `Tuple[K, V]` pair rather than the projected
184
161
  # `Array[union]` / `Hash[K, V]` widening.
185
162
  #
186
- # NOTE: `Plugin::NodeRuleWalk` (ADR-52 WD4) is now in place as
187
- # the intended migration target for these Enumerable projections.
188
- # The four methods (group_by, partition, each_slice, each_cons)
189
- # remain here pending that migration.
163
+ # NOTE: `Plugin::NodeRuleWalk` (ADR-52 WD4) is now in place as the intended migration target for
164
+ # these Enumerable projections. The four methods (group_by, partition, each_slice, each_cons) remain
165
+ # here pending that migration.
190
166
  def single_element_block_params(receiver)
191
167
  element = element_type_of(receiver)
192
168
  return nil if element.nil?
@@ -194,11 +170,9 @@ module Rigor
194
170
  [element]
195
171
  end
196
172
 
197
- # `each_slice(n) { |slice| … }` and `each_cons(n) { |window| … }`
198
- # both yield an `Array[element]` once per iteration. The
199
- # tier ignores the slice-size argument (a Constant<Integer>
200
- # `n` could in principle bound the slice's length, but a
201
- # tighter Tuple-of-`n` carrier is reserved for the plugin
173
+ # `each_slice(n) { |slice| … }` and `each_cons(n) { |window| … }` both yield an `Array[element]`
174
+ # once per iteration. The tier ignores the slice-size argument (a Constant<Integer> `n` could in
175
+ # principle bound the slice's length, but a tighter Tuple-of-`n` carrier is reserved for the plugin
202
176
  # tier per the NOTE above).
203
177
  def slice_block_params(receiver)
204
178
  element = element_type_of(receiver)
@@ -278,14 +252,12 @@ module Rigor
278
252
  return build_index_range(beg, upper)
279
253
  end
280
254
 
281
- # Mixed / non-integer ranges decline: the dispatcher
282
- # falls through to RBS's element-type answer.
255
+ # Mixed / non-integer ranges decline: the dispatcher falls through to RBS's element-type answer.
283
256
  nil
284
257
  end
285
258
 
286
- # `Constant<Integer>`, `IntegerRange`, and `Nominal[Integer]`
287
- # all participate. Non-integer types (Float, String, …) and
288
- # `Top`/`Dynamic` decline so the RBS tier answers.
259
+ # `Constant<Integer>`, `IntegerRange`, and `Nominal[Integer]` all participate. Non-integer types
260
+ # (Float, String, …) and `Top`/`Dynamic` decline so the RBS tier answers.
289
261
  def integer_rooted?(type)
290
262
  case type
291
263
  when Type::Constant then type.value.is_a?(Integer)
@@ -311,10 +283,9 @@ module Rigor
311
283
  end
312
284
  end
313
285
 
314
- # Builds a `Constant`/`IntegerRange` from possibly-symbolic
315
- # bounds. Vacuous ranges (lower > upper, indicating the
316
- # iterator does not fire) collapse to `non_negative_int` so
317
- # the body still type-checks against a sensible binding.
286
+ # Builds a `Constant`/`IntegerRange` from possibly-symbolic bounds. Vacuous ranges (lower > upper,
287
+ # indicating the iterator does not fire) collapse to `non_negative_int` so the body still
288
+ # type-checks against a sensible binding.
318
289
  def build_index_range(lower, upper)
319
290
  return Type::Combinator.non_negative_int if vacuous_range?(lower, upper)
320
291
  return Type::Combinator.constant_of(lower) if lower.is_a?(Integer) && lower == upper
@@ -5,15 +5,13 @@ require_relative "../../type"
5
5
  module Rigor
6
6
  module Inference
7
7
  module MethodDispatcher
8
- # Kernel intrinsic shape-folding — precision tier for the
9
- # `Kernel` module-functions whose return type is a function
10
- # of the argument's *shape*, not just its class.
8
+ # Kernel intrinsic shape-folding — precision tier for the `Kernel` module-functions whose return type
9
+ # is a function of the argument's *shape*, not just its class.
11
10
  #
12
- # Today the only catalogued intrinsic is `Kernel#Array`. The
13
- # default RBS sig is `Array(untyped) -> Array[untyped]`, which
14
- # collapses to `Array[Dynamic[top]]` for every caller. This
15
- # tier short-circuits with a precise answer when the argument's
16
- # type lattice tells us what the result element type MUST be:
11
+ # Today the only catalogued intrinsic is `Kernel#Array`. The default RBS sig is
12
+ # `Array(untyped) -> Array[untyped]`, which collapses to `Array[Dynamic[top]]` for every caller. This
13
+ # tier short-circuits with a precise answer when the argument's type lattice tells us what the result
14
+ # element type MUST be:
17
15
  #
18
16
  # Array(Constant[nil]) -> Array[bot] # `[]`
19
17
  # Array(Nominal["Array",[E]]) -> Array[E] # already an Array
@@ -21,20 +19,17 @@ module Rigor
21
19
  # Array(Union[A,B,…]) -> distribute, then unify
22
20
  # Array(other Nominal[T]) -> Array[Nominal[T]]
23
21
  #
24
- # For receiver shapes we cannot prove (`Top`, `Dynamic`, …)
25
- # the tier returns nil and the RBS tier answers with the
26
- # generic `Array[untyped]` envelope.
22
+ # For receiver shapes we cannot prove (`Top`, `Dynamic`, …) the tier returns nil and the RBS tier
23
+ # answers with the generic `Array[untyped]` envelope.
27
24
  #
28
- # See `docs/type-specification/value-lattice.md` for the
29
- # union-distribution contract this tier mirrors.
25
+ # See `docs/type-specification/value-lattice.md` for the union-distribution contract this tier
26
+ # mirrors.
30
27
  module KernelDispatch
31
28
  module_function
32
29
 
33
- # `Kernel#Rational` / `Kernel#Complex` constructor folds.
34
- # When every argument is a `Type::Constant` whose value is
35
- # numeric, we can run the actual Ruby constructor and lift
36
- # the result into a `Constant<Rational>` / `Constant<Complex>`.
37
- # The factory accepts the same shapes as Ruby:
30
+ # `Kernel#Rational` / `Kernel#Complex` constructor folds. When every argument is a `Type::Constant`
31
+ # whose value is numeric, we can run the actual Ruby constructor and lift the result into a
32
+ # `Constant<Rational>` / `Constant<Complex>`. The factory accepts the same shapes as Ruby:
38
33
  # `Rational(a)`, `Rational(a, b)`, `Complex(a)`, `Complex(a, b)`.
39
34
  NUMERIC_CONSTRUCTORS = Ractor.make_shareable({
40
35
  Rational: Ractor.make_shareable(->(*args) { Rational(*args) }),
@@ -42,21 +37,15 @@ module Rigor
42
37
  })
43
38
  private_constant :NUMERIC_CONSTRUCTORS
44
39
 
45
- # `Kernel#Integer(s)` predicate-aware refinement set
46
- # (v0.1.1 Track 1 slice 2b). `decimal-int-string` is the
47
- # only string refinement whose every inhabitant `Integer(s)`
48
- # parses without remainder, so the result is a plain
49
- # `Integer` but NOT `non-negative-int`: the predicate
50
- # `/\A-?\d+\z/` admits a leading sign, so `"-7"` is a valid
51
- # decimal-int-string and `Integer("-7") == -7 < 0`. The
52
- # narrowing is total (every inhabitant parses) but not `>= 0`,
53
- # so it lands on `universal_int`. `numeric-string` is
54
- # deliberately NOT in this set at all: since it was widened to
55
- # the full Ruby numeric-literal grammar (floats, hex, rational,
56
- # imaginary, signs), `Integer(numeric_string)` would raise for
57
- # a `"1.5"` / `"2i"` inhabitant — not even total — so it falls
58
- # through to RBS `Integer`. The `Integer(s, base)` overload is
59
- # left for a later slice.
40
+ # `Kernel#Integer(s)` predicate-aware refinement set (v0.1.1 Track 1 slice 2b). `decimal-int-string`
41
+ # is the only string refinement whose every inhabitant `Integer(s)` parses without remainder, so
42
+ # the result is a plain `Integer` — but NOT `non-negative-int`: the predicate `/\A-?\d+\z/` admits a
43
+ # leading sign, so `"-7"` is a valid decimal-int-string and `Integer("-7") == -7 < 0`. The narrowing
44
+ # is total (every inhabitant parses) but not `>= 0`, so it lands on `universal_int`.
45
+ # `numeric-string` is deliberately NOT in this set at all: since it was widened to the full Ruby
46
+ # numeric-literal grammar (floats, hex, rational, imaginary, signs), `Integer(numeric_string)` would
47
+ # raise for a `"1.5"` / `"2i"` inhabitant not even total — so it falls through to RBS `Integer`.
48
+ # The `Integer(s, base)` overload is left for a later slice.
60
49
  INTEGER_REFINEMENT_PREDICATES = Set[:decimal_int].freeze
61
50
  private_constant :INTEGER_REFINEMENT_PREDICATES
62
51
 
@@ -73,16 +62,12 @@ module Rigor
73
62
  nil
74
63
  end
75
64
 
76
- # `Kernel#Integer(arg)` / `Integer(arg, base)`. Two folding
77
- # paths, tried in order:
65
+ # `Kernel#Integer(arg)` / `Integer(arg, base)`. Two folding paths, tried in order:
78
66
  #
79
- # 1. A `Refined[String, predicate]` argument whose predicate
80
- # is a total-parse carrier narrows to `universal_int`
81
- # (see {try_integer_from_refinement}).
82
- # 2. A `Constant` String or Numeric argument optionally
83
- # with a `Constant[Integer]` base — runs the actual
84
- # `Integer()` conversion and lifts the result to
85
- # `Constant[Integer]`.
67
+ # 1. A `Refined[String, predicate]` argument whose predicate is a total-parse carrier narrows to
68
+ # `universal_int` (see {try_integer_from_refinement}).
69
+ # 2. A `Constant` String or Numeric argument — optionally with a `Constant[Integer]` base — runs
70
+ # the actual `Integer()` conversion and lifts the result to `Constant[Integer]`.
86
71
  def try_integer(args)
87
72
  refined = try_integer_from_refinement(args)
88
73
  return refined if refined
@@ -90,10 +75,9 @@ module Rigor
90
75
  try_integer_constant(args)
91
76
  end
92
77
 
93
- # Constant-folding path for `Integer()`. A non-parseable
94
- # string raises `ArgumentError` (or `TypeError` for a base
95
- # against a non-string) at fold time; the handler declines
96
- # so the RBS tier answers with the widened `Integer`.
78
+ # Constant-folding path for `Integer()`. A non-parseable string raises `ArgumentError` (or
79
+ # `TypeError` for a base against a non-string) at fold time; the handler declines so the RBS tier
80
+ # answers with the widened `Integer`.
97
81
  def try_integer_constant(args)
98
82
  return nil unless [1, 2].include?(args.size)
99
83
  return nil unless args.all?(Type::Constant)
@@ -107,9 +91,8 @@ module Rigor
107
91
  nil
108
92
  end
109
93
 
110
- # `Kernel#Float(arg)` — folds a `Constant` String or Numeric
111
- # argument to `Constant[Float]`. A non-parseable string
112
- # raises `ArgumentError` at fold time; the handler declines.
94
+ # `Kernel#Float(arg)` — folds a `Constant` String or Numeric argument to `Constant[Float]`. A
95
+ # non-parseable string raises `ArgumentError` at fold time; the handler declines.
113
96
  def try_float(args)
114
97
  return nil unless args.size == 1
115
98
 
@@ -124,17 +107,13 @@ module Rigor
124
107
  nil
125
108
  end
126
109
 
127
- # `Kernel#Integer(s)` over a `Refined[String, predicate]`
128
- # whose predicate is in {INTEGER_REFINEMENT_PREDICATES}.
129
- # Mirrors the `String#to_i` projection in `ShapeDispatch`
130
- # (v0.1.1 slice 2a) the result is `universal_int`, NOT
131
- # `non-negative-int`: a decimal-int-string admits a leading
132
- # sign (`"-7"`), so the parsed Integer can be negative. The
133
- # carrier stays an `IntegerRange` (rather than declining to
134
- # the RBS `Nominal[Integer]`) so downstream range narrowing
135
- # still has a range to intersect. Returns nil for any other
136
- # arg shape so the RBS tier handles the generic `Integer(arg)`
137
- # case.
110
+ # `Kernel#Integer(s)` over a `Refined[String, predicate]` whose predicate is in
111
+ # {INTEGER_REFINEMENT_PREDICATES}. Mirrors the `String#to_i` projection in `ShapeDispatch` (v0.1.1
112
+ # slice 2a) — the result is `universal_int`, NOT `non-negative-int`: a decimal-int-string admits a
113
+ # leading sign (`"-7"`), so the parsed Integer can be negative. The carrier stays an `IntegerRange`
114
+ # (rather than declining to the RBS `Nominal[Integer]`) so downstream range narrowing still has a
115
+ # range to intersect. Returns nil for any other arg shape so the RBS tier handles the generic
116
+ # `Integer(arg)` case.
138
117
  def try_integer_from_refinement(args)
139
118
  return nil unless args.size == 1
140
119
 
@@ -157,11 +136,9 @@ module Rigor
157
136
  Type::Combinator.nominal_of("Array", type_args: [element])
158
137
  end
159
138
 
160
- # `Rational(int)` / `Rational(num, den)` and `Complex(re)`
161
- # / `Complex(re, im)` fold when every arg is a numeric
162
- # Constant. The actual Ruby constructor runs at fold time
163
- # (host-side), so the result respects Ruby's normalisation
164
- # (`Rational(2, 4)` → `Rational(1, 2)`).
139
+ # `Rational(int)` / `Rational(num, den)` and `Complex(re)` / `Complex(re, im)` fold when every arg
140
+ # is a numeric Constant. The actual Ruby constructor runs at fold time (host-side), so the result
141
+ # respects Ruby's normalisation (`Rational(2, 4)` `Rational(1, 2)`).
165
142
  def try_numeric_constructor(method_name, args)
166
143
  return nil unless [1, 2].include?(args.size)
167
144
  return nil unless args.all? { |arg| numeric_constant?(arg) }
@@ -181,21 +158,17 @@ module Rigor
181
158
  type.value.is_a?(Complex))
182
159
  end
183
160
 
184
- # Computes the element type the argument contributes to the
185
- # `Array(arg)` result, mirroring Ruby's coercion contract:
161
+ # Computes the element type the argument contributes to the `Array(arg)` result, mirroring Ruby's
162
+ # coercion contract:
186
163
  #
187
- # - `nil` becomes `[]` (element type Bot — the empty array
188
- # contributes no inhabitants).
189
- # - An existing `Array[E]` is returned as-is, so its element
190
- # type is `E`.
191
- # - A `Tuple[T1, T2, ]` is materialised as `Array[T1|T2|…]`
192
- # (every tuple inhabitant is a tuple, hence Array-like).
193
- # - Any other value `v` becomes `[v]`, so the element type
194
- # is the value's own type.
164
+ # - `nil` becomes `[]` (element type Bot — the empty array contributes no inhabitants).
165
+ # - An existing `Array[E]` is returned as-is, so its element type is `E`.
166
+ # - A `Tuple[T1, T2, …]` is materialised as `Array[T1|T2|…]` (every tuple inhabitant is a tuple,
167
+ # hence Array-like).
168
+ # - Any other value `v` becomes `[v]`, so the element type is the value's own type.
195
169
  #
196
- # Returns nil for receiver shapes the tier cannot prove
197
- # (Top, Dynamic, Bot in pre-coercion position) so the
198
- # caller falls back to the RBS-tier envelope.
170
+ # Returns nil for receiver shapes the tier cannot prove (Top, Dynamic, Bot in pre-coercion position)
171
+ # so the caller falls back to the RBS-tier envelope.
199
172
  def element_type_of(type)
200
173
  case type
201
174
  when Type::Union