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
@@ -21,14 +21,11 @@ require_relative "../inference/flow_tracer"
21
21
 
22
22
  module Rigor
23
23
  module Type
24
- # Factory entry point that routes every public construction through the
25
- # deterministic normalization rules. Production code paths MUST go
26
- # through Rigor::Type::Combinator. Direct constructor calls are an
27
- # internal escape hatch for tests and for combinator's own
28
- # implementation.
24
+ # Factory entry point that routes every public construction through the deterministic normalization
25
+ # rules. Production code paths MUST go through Rigor::Type::Combinator. Direct constructor calls are
26
+ # an internal escape hatch for tests and for combinator's own implementation.
29
27
  #
30
- # See docs/internal-spec/internal-type-api.md and
31
- # docs/type-specification/normalization.md.
28
+ # See docs/internal-spec/internal-type-api.md and docs/type-specification/normalization.md.
32
29
  module Combinator # rubocop:disable Metrics/ModuleLength
33
30
  module_function
34
31
 
@@ -40,19 +37,16 @@ module Rigor
40
37
  Bot.instance
41
38
  end
42
39
 
43
- # ADR-15 Phase 4b.x — read the eagerly-allocated
44
- # `@untyped` ivar instead of `||=`. The singleton-class
45
- # `@untyped = Dynamic.new(top)` initializer runs at module
46
- # body (below) on the main Ractor at load time. Workers
47
- # READ the populated ivar without performing the lazy
48
- # write that non-main Ractors are forbidden from doing.
40
+ # ADR-15 Phase 4b.x — read the eagerly-allocated `@untyped` ivar instead of `||=`. The
41
+ # singleton-class `@untyped = Dynamic.new(top)` initializer runs at module body (below) on the main
42
+ # Ractor at load time. Workers READ the populated ivar without performing the lazy write that
43
+ # non-main Ractors are forbidden from doing.
49
44
  def untyped
50
45
  @untyped
51
46
  end
52
47
 
53
- # Wraps the static facet in a Dynamic[T] carrier. Idempotent on the
54
- # static facet so Dynamic[Dynamic[T]] collapses to Dynamic[T] per the
55
- # value-lattice algebra.
48
+ # Wraps the static facet in a Dynamic[T] carrier. Idempotent on the static facet so
49
+ # Dynamic[Dynamic[T]] collapses to Dynamic[T] per the value-lattice algebra.
56
50
  def dynamic(static_facet)
57
51
  return untyped if static_facet.equal?(top)
58
52
 
@@ -62,12 +56,10 @@ module Rigor
62
56
  Dynamic.new(facet)
63
57
  end
64
58
 
65
- # Constructs a Nominal type. Slice 4 phase 2d accepts an optional
66
- # `type_args:` array, an ordered list of Rigor::Type values that
67
- # carry the receiver's generic instantiation (`Array[Integer]` is
68
- # `Nominal["Array", [Nominal["Integer"]]]`). Omitting the keyword
69
- # produces the raw form `Nominal["Array"]`, which is structurally
70
- # distinct from any applied form.
59
+ # Constructs a Nominal type. Slice 4 phase 2d accepts an optional `type_args:` array, an ordered
60
+ # list of Rigor::Type values that carry the receiver's generic instantiation (`Array[Integer]` is
61
+ # `Nominal["Array", [Nominal["Integer"]]]`). Omitting the keyword produces the raw form
62
+ # `Nominal["Array"]`, which is structurally distinct from any applied form.
71
63
  def nominal_of(class_name_or_object, type_args: [])
72
64
  Nominal.new(resolve_class_name(class_name_or_object), type_args)
73
65
  end
@@ -80,27 +72,23 @@ module Rigor
80
72
  Constant.new(value)
81
73
  end
82
74
 
83
- # Widens every value-pinned (`Constant`) constituent of `type` to its
84
- # nominal base (`Constant[1]` -> `Nominal["Integer"]`), recursing
85
- # through unions and leaving non-pinned constituents untouched.
86
- # `Constant[nil]` is preserved (no nominal base of interest). Shared
87
- # by the ADR-55 recursive-return fixpoint and the ADR-56 block /
88
- # loop-body fixpoint (`BodyFixpoint`) to force convergence on the
75
+ # Widens every value-pinned (`Constant`) constituent of `type` to its nominal base (`Constant[1]` ->
76
+ # `Nominal["Integer"]`), recursing through unions and leaving non-pinned constituents untouched.
77
+ # `Constant[nil]` is preserved (no nominal base of interest). Shared by the ADR-55 recursive-return
78
+ # fixpoint and the ADR-56 block / loop-body fixpoint (`BodyFixpoint`) to force convergence on the
89
79
  # final permitted iteration.
90
80
  def widen_value_pinned(type)
91
81
  case type
92
82
  when Constant
93
83
  type.value.nil? ? type : nominal_of(type.value.class.name)
94
84
  when Refined
95
- # A refinement (`non-empty-string`) is a value-narrowed nominal;
96
- # widen it to its base so an accumulator whose per-pass result is
97
- # a bounded refinement converges on the final iteration rather
98
- # than flooring to `Dynamic[top]` (ADR-56).
85
+ # A refinement (`non-empty-string`) is a value-narrowed nominal; widen it to its base so an
86
+ # accumulator whose per-pass result is a bounded refinement converges on the final iteration
87
+ # rather than flooring to `Dynamic[top]` (ADR-56).
99
88
  widen_value_pinned(type.base)
100
89
  when IntegerRange
101
- # `int<1, 6>` is likewise a value-narrowed `Integer` (it erases to
102
- # `Integer` in RBS); widen it so a bounded-int accumulator
103
- # converges (ADR-56).
90
+ # `int<1, 6>` is likewise a value-narrowed `Integer` (it erases to `Integer` in RBS); widen it
91
+ # so a bounded-int accumulator converges (ADR-56).
104
92
  nominal_of("Integer")
105
93
  when Union
106
94
  union(*type.members.map { |member| widen_value_pinned(member) })
@@ -109,24 +97,20 @@ module Rigor
109
97
  end
110
98
  end
111
99
 
112
- # `Object#method(:name)` carrier. Stores the bound
113
- # `(receiver, method_name)` pair so the dispatcher can
114
- # substitute the original dispatch at `.call` / `.()` /
115
- # `[]` time. See {Type::BoundMethod}.
100
+ # `Object#method(:name)` carrier. Stores the bound `(receiver, method_name)` pair so the dispatcher
101
+ # can substitute the original dispatch at `.call` / `.()` / `[]` time. See {Type::BoundMethod}.
116
102
  def bound_method_of(receiver_type, method_name)
117
103
  BoundMethod.new(receiver_type: receiver_type, method_name: method_name)
118
104
  end
119
105
 
120
- # Bounded-integer carrier. Each bound is either an `Integer` or
121
- # one of `:neg_infinity` / `:pos_infinity` (sentinels exposed as
122
- # `IntegerRange::NEG_INFINITY` / `POS_INFINITY`).
106
+ # Bounded-integer carrier. Each bound is either an `Integer` or one of `:neg_infinity` /
107
+ # `:pos_infinity` (sentinels exposed as `IntegerRange::NEG_INFINITY` / `POS_INFINITY`).
123
108
  def integer_range(min, max)
124
109
  IntegerRange.new(min, max)
125
110
  end
126
111
 
127
- # Convenience aliases for the most common bounded shapes. The
128
- # named alias survives roundtrip through `describe` for nicer
129
- # human-facing output.
112
+ # Convenience aliases for the most common bounded shapes. The named alias survives roundtrip
113
+ # through `describe` for nicer human-facing output.
130
114
  def positive_int
131
115
  IntegerRange.new(1, IntegerRange::POS_INFINITY)
132
116
  end
@@ -147,11 +131,9 @@ module Rigor
147
131
  IntegerRange.new(IntegerRange::NEG_INFINITY, IntegerRange::POS_INFINITY)
148
132
  end
149
133
 
150
- # Point-removal refinement carrier (ADR-3 OQ3 Option C). Use
151
- # `non_empty_string` / `non_zero_int` / `non_empty_array` /
152
- # `non_empty_hash` for the imported built-in shapes; raw
153
- # `difference(base, removed)` for ad-hoc refinements an
154
- # `RBS::Extended` annotation introduces.
134
+ # Point-removal refinement carrier (ADR-3 OQ3 Option C). Use `non_empty_string` / `non_zero_int` /
135
+ # `non_empty_array` / `non_empty_hash` for the imported built-in shapes; raw `difference(base,
136
+ # removed)` for ad-hoc refinements an `RBS::Extended` annotation introduces.
155
137
  def difference(base, removed)
156
138
  Difference.new(base, removed)
157
139
  end
@@ -164,11 +146,9 @@ module Rigor
164
146
  Difference.new(nominal_of("Integer"), constant_of(0))
165
147
  end
166
148
 
167
- # `non-empty-array[T]` requires the element type so the
168
- # `Nominal[Array, [T]]` projection through Array#first /
169
- # #last keeps element precision intact. The default
170
- # `Top` admits any array element when the caller does
171
- # not have a more specific element type.
149
+ # `non-empty-array[T]` requires the element type so the `Nominal[Array, [T]]` projection through
150
+ # Array#first / #last keeps element precision intact. The default `Top` admits any array element
151
+ # when the caller does not have a more specific element type.
172
152
  def non_empty_array(element = top)
173
153
  Difference.new(
174
154
  nominal_of("Array", type_args: [element]),
@@ -183,12 +163,10 @@ module Rigor
183
163
  )
184
164
  end
185
165
 
186
- # Predicate-subset refinement carrier (ADR-3 OQ3 Option C,
187
- # second half). Use `lowercase_string` /
188
- # `uppercase_string` / `numeric_string` for the imported
189
- # built-in shapes; raw `refined(base, predicate_id)` for
190
- # ad-hoc refinements introduced by an `RBS::Extended`
191
- # annotation or a plugin-contributed predicate.
166
+ # Predicate-subset refinement carrier (ADR-3 OQ3 Option C, second half). Use `lowercase_string` /
167
+ # `uppercase_string` / `numeric_string` for the imported built-in shapes; raw `refined(base,
168
+ # predicate_id)` for ad-hoc refinements introduced by an `RBS::Extended` annotation or a
169
+ # plugin-contributed predicate.
192
170
  def refined(base, predicate_id)
193
171
  Refined.new(base, predicate_id)
194
172
  end
@@ -197,12 +175,10 @@ module Rigor
197
175
  Refined.new(nominal_of("String"), :lowercase)
198
176
  end
199
177
 
200
- # Complement of `lowercase-string`: a `String` with at least
201
- # one non-lowercase character (i.e. `v != v.downcase`).
202
- # Registered as the paired complement of
203
- # `:lowercase` in {Refined::COMPLEMENT_PAIRS} so
204
- # `~lowercase-string` narrows to this carrier instead of
205
- # falling back to `Difference[String, lowercase-string]`.
178
+ # Complement of `lowercase-string`: a `String` with at least one non-lowercase character (i.e. `v !=
179
+ # v.downcase`). Registered as the paired complement of `:lowercase` in {Refined::COMPLEMENT_PAIRS}
180
+ # so `~lowercase-string` narrows to this carrier instead of falling back to `Difference[String,
181
+ # lowercase-string]`.
206
182
  def non_lowercase_string
207
183
  Refined.new(nominal_of("String"), :not_lowercase)
208
184
  end
@@ -211,9 +187,8 @@ module Rigor
211
187
  Refined.new(nominal_of("String"), :uppercase)
212
188
  end
213
189
 
214
- # Complement of `uppercase-string`: a `String` with at least
215
- # one non-uppercase character. Paired with `:uppercase` in
216
- # {Refined::COMPLEMENT_PAIRS}.
190
+ # Complement of `uppercase-string`: a `String` with at least one non-uppercase character. Paired
191
+ # with `:uppercase` in {Refined::COMPLEMENT_PAIRS}.
217
192
  def non_uppercase_string
218
193
  Refined.new(nominal_of("String"), :not_uppercase)
219
194
  end
@@ -222,11 +197,9 @@ module Rigor
222
197
  Refined.new(nominal_of("String"), :numeric)
223
198
  end
224
199
 
225
- # Complement of `numeric-string`: a `String` that is not
226
- # accepted by Rigor's Ruby numeric-string predicate
227
- # (contains at least one non-digit, has a malformed numeric
228
- # form, etc.). Paired with `:numeric` in
229
- # {Refined::COMPLEMENT_PAIRS}.
200
+ # Complement of `numeric-string`: a `String` that is not accepted by Rigor's Ruby numeric-string
201
+ # predicate (contains at least one non-digit, has a malformed numeric form, etc.). Paired with
202
+ # `:numeric` in {Refined::COMPLEMENT_PAIRS}.
230
203
  def non_numeric_string
231
204
  Refined.new(nominal_of("String"), :not_numeric)
232
205
  end
@@ -243,36 +216,27 @@ module Rigor
243
216
  Refined.new(nominal_of("String"), :hex_int)
244
217
  end
245
218
 
246
- # `literal-string` — a `String` that is statically known to
247
- # come from a source-code literal (or a composition of
248
- # literals). v0.0.9 tracks this flow through interpolation
249
- # `"#{...}"`, leaving propagation through `+` / `<<` to a
250
- # later slice. Every `Constant<String>` is implicitly
251
- # literal-string-compatible; the carrier exists for cases
252
- # where the concrete value is unknown but literal-ness has
253
- # been established (an RBS::Extended `return: literal-string`
254
- # annotation, or interpolation over literal-bearing parts).
219
+ # `literal-string` — a `String` that is statically known to come from a source-code literal (or a
220
+ # composition of literals). v0.0.9 tracks this flow through interpolation `"#{...}"`, leaving
221
+ # propagation through `+` / `<<` to a later slice. Every `Constant<String>` is implicitly
222
+ # literal-string-compatible; the carrier exists for cases where the concrete value is unknown but
223
+ # literal-ness has been established (an RBS::Extended `return: literal-string` annotation, or
224
+ # interpolation over literal-bearing parts).
255
225
  def literal_string
256
226
  Refined.new(nominal_of("String"), :literal_string)
257
227
  end
258
228
 
259
- # `non-empty-literal-string` = `non-empty-string ∩ literal-string`.
260
- # Composes the point-removal half (`Difference[String, ""]`)
261
- # with the predicate-subset half. Both members erase to
262
- # `String`.
229
+ # `non-empty-literal-string` = `non-empty-string ∩ literal-string`. Composes the point-removal half
230
+ # (`Difference[String, ""]`) with the predicate-subset half. Both members erase to `String`.
263
231
  def non_empty_literal_string
264
232
  intersection(non_empty_string, literal_string)
265
233
  end
266
234
 
267
- # Recognises the carriers that participate in literal-string
268
- # flow tracking: any `Constant<String>` (constants are literal
269
- # by construction), the `literal-string` Refined carrier, an
270
- # `Intersection` containing `literal-string`, or a `Union`
271
- # whose every member qualifies. Used by
272
- # `ExpressionTyper#type_of_interpolated_string` and the
273
- # `LiteralStringFolding` dispatcher tier so propagation
274
- # through interpolation and `+`/`*` composition stays
275
- # consistent.
235
+ # Recognises the carriers that participate in literal-string flow tracking: any `Constant<String>`
236
+ # (constants are literal by construction), the `literal-string` Refined carrier, an `Intersection`
237
+ # containing `literal-string`, or a `Union` whose every member qualifies. Used by
238
+ # `ExpressionTyper#type_of_interpolated_string` and the `LiteralStringFolding` dispatcher tier so
239
+ # propagation through interpolation and `+`/`*` composition stays consistent.
276
240
  def literal_string_compatible?(type)
277
241
  case type
278
242
  when Constant then type.value.is_a?(String)
@@ -289,14 +253,12 @@ module Rigor
289
253
  refined.base.class_name == "String"
290
254
  end
291
255
 
292
- # Returns true when `type` is statically known to be a
293
- # non-empty String i.e. its value can never be `""`.
294
- # Used at String binary-operator dispatch sites to propagate
295
- # the non-empty guarantee through `+` and `*`.
256
+ # Returns true when `type` is statically known to be a non-empty String — i.e. its value can never
257
+ # be `""`. Used at String binary-operator dispatch sites to propagate the non-empty guarantee
258
+ # through `+` and `*`.
296
259
  #
297
260
  # - `Constant[s]` where `s != ""` — a concrete non-empty literal.
298
- # - `Difference[Nominal[String], Constant[""]]` — the canonical
299
- # `non-empty-string` carrier.
261
+ # - `Difference[Nominal[String], Constant[""]]` — the canonical `non-empty-string` carrier.
300
262
  # - `Intersection[…]` — any member suffices (set-theoretic subset).
301
263
  # - `Union[…]` — all members must qualify (the join may include "").
302
264
  def non_empty_string_compatible?(type)
@@ -316,16 +278,14 @@ module Rigor
316
278
  diff.removed.value == ""
317
279
  end
318
280
 
319
- # Returns true when `type` is statically known to be a
320
- # non-zero Integer i.e. its value can never be `0`.
321
- # Used at Integer arithmetic dispatch sites to propagate
322
- # the non-zero guarantee through `*` and identity methods.
281
+ # Returns true when `type` is statically known to be a non-zero Integer — i.e. its value can never
282
+ # be `0`. Used at Integer arithmetic dispatch sites to propagate the non-zero guarantee through `*`
283
+ # and identity methods.
323
284
  #
324
285
  # - `Constant[n]` where `n != 0` — a concrete non-zero literal.
325
- # - `Difference[Nominal[Integer], Constant[0]]` — the canonical
326
- # `non-zero-int` carrier.
327
- # - `IntegerRange` that does not cover 0 — both `positive-int`
328
- # ([1,+∞)) and `negative-int` ([-∞,-1]) qualify.
286
+ # - `Difference[Nominal[Integer], Constant[0]]` — the canonical `non-zero-int` carrier.
287
+ # - `IntegerRange` that does not cover 0 — both `positive-int` ([1,+∞)) and `negative-int` ([-∞,-1])
288
+ # qualify.
329
289
  # - `Intersection[…]` — any member suffices.
330
290
  # - `Union[…]` — all members must qualify.
331
291
  def non_zero_int_compatible?(type)
@@ -346,23 +306,18 @@ module Rigor
346
306
  diff.removed.value.zero?
347
307
  end
348
308
 
349
- # Normalised intersection. Flattens nested Intersections,
350
- # drops `Top` members, collapses to `Bot` if any member is
351
- # `Bot`, deduplicates structurally-equal members, sorts the
352
- # survivors by `describe(:short)`, and collapses 0-/1-member
353
- # results so a degenerate intersection never reaches the
354
- # carrier. See ADR-3 OQ3 for the rationale; the lattice
355
- # algebra is in
309
+ # Normalised intersection. Flattens nested Intersections, drops `Top` members, collapses to `Bot` if
310
+ # any member is `Bot`, deduplicates structurally-equal members, sorts the survivors by
311
+ # `describe(:short)`, and collapses 0-/1-member results so a degenerate intersection never reaches
312
+ # the carrier. See ADR-3 OQ3 for the rationale; the lattice algebra is in
356
313
  # [`value-lattice.md`](docs/type-specification/value-lattice.md).
357
314
  def intersection(*members)
358
315
  collapse_intersection(normalised_intersection_members(members))
359
316
  end
360
317
 
361
- # `non-empty-lowercase-string` = non-empty-string ∩
362
- # lowercase-string. Composes the point-removal half
363
- # (`Difference[String, ""]`) with the predicate-subset half
364
- # (`Refined[String, :lowercase]`). Both members erase to
365
- # `String` so the carrier's RBS erasure is unambiguous.
318
+ # `non-empty-lowercase-string` = non-empty-string ∩ lowercase-string. Composes the point-removal
319
+ # half (`Difference[String, ""]`) with the predicate-subset half (`Refined[String, :lowercase]`).
320
+ # Both members erase to `String` so the carrier's RBS erasure is unambiguous.
366
321
  def non_empty_lowercase_string
367
322
  intersection(non_empty_string, lowercase_string)
368
323
  end
@@ -371,16 +326,14 @@ module Rigor
371
326
  intersection(non_empty_string, uppercase_string)
372
327
  end
373
328
 
374
- # Constructs a heterogeneous, fixed-arity Tuple from positional
375
- # element types. `tuple_of()` produces the empty tuple `Tuple[]`,
376
- # which is structurally distinct from the raw `Nominal[Array]`.
329
+ # Constructs a heterogeneous, fixed-arity Tuple from positional element types. `tuple_of()`
330
+ # produces the empty tuple `Tuple[]`, which is structurally distinct from the raw `Nominal[Array]`.
377
331
  def tuple_of(*elements)
378
332
  Tuple.new(elements)
379
333
  end
380
334
 
381
- # Constructs a HashShape from an ordered (Symbol|String) -> type
382
- # map. The argument is duped and frozen by the carrier; callers
383
- # MUST NOT rely on later mutation.
335
+ # Constructs a HashShape from an ordered (Symbol|String) -> type map. The argument is duped and
336
+ # frozen by the carrier; callers MUST NOT rely on later mutation.
384
337
  def hash_shape_of(pairs = nil, **options)
385
338
  if pairs.nil?
386
339
  pairs = options
@@ -390,64 +343,57 @@ module Rigor
390
343
  HashShape.new(pairs, **options)
391
344
  end
392
345
 
393
- # ADR-48 — the class object produced by `Data.define(:x, :y)`.
394
- # `members` is the ordered Symbol member-name list; `class_name`
395
- # tags the class when known (the named-subclass form) and is nil
396
- # for the anonymous `Data.define(...)` result.
346
+ # ADR-48 — the class object produced by `Data.define(:x, :y)`. `members` is the ordered Symbol
347
+ # member-name list; `class_name` tags the class when known (the named-subclass form) and is nil for
348
+ # the anonymous `Data.define(...)` result.
397
349
  def data_class_of(members:, class_name: nil)
398
350
  DataClass.new(members, class_name)
399
351
  end
400
352
 
401
- # ADR-48 — a `Data.define` value instance. `members` is the ordered
402
- # member-name -> value-type map; `class_name` tags the instance's
403
- # class when known.
353
+ # ADR-48 — a `Data.define` value instance. `members` is the ordered member-name -> value-type map;
354
+ # `class_name` tags the instance's class when known.
404
355
  def data_instance_of(members:, class_name: nil)
405
356
  DataInstance.new(members, class_name)
406
357
  end
407
358
 
408
- # ADR-48 Struct follow-up — the class object produced by
409
- # `Struct.new(:x, :y)`. `members` is the ordered Symbol member-name
410
- # list; `keyword_init` records the `keyword_init:` flag; `class_name`
359
+ # ADR-48 Struct follow-up — the class object produced by `Struct.new(:x, :y)`. `members` is the
360
+ # ordered Symbol member-name list; `keyword_init` records the `keyword_init:` flag; `class_name`
411
361
  # tags the class when known (the named-subclass form).
412
362
  def struct_class_of(members:, class_name: nil, keyword_init: false)
413
363
  StructClass.new(members, class_name, keyword_init: keyword_init)
414
364
  end
415
365
 
416
- # ADR-48 Struct follow-up — a `Struct.new` value instance. `members`
417
- # is the ordered member-name -> value-type map; `class_name` tags the
418
- # instance's class when known.
366
+ # ADR-48 Struct follow-up — a `Struct.new` value instance. `members` is the ordered member-name ->
367
+ # value-type map; `class_name` tags the instance's class when known.
419
368
  def struct_instance_of(members:, class_name: nil)
420
369
  StructInstance.new(members, class_name)
421
370
  end
422
371
 
423
- # Normalized union. Flattens nested Unions, deduplicates structurally
424
- # equal members, drops Bot, and collapses 0/1-member results.
372
+ # Normalized union. Flattens nested Unions, deduplicates structurally equal members, drops Bot, and
373
+ # collapses 0/1-member results.
425
374
  def union(*types)
426
375
  result = collapse_union(normalized_union_members(types))
427
376
  if Inference::BudgetTrace.enabled? && result.is_a?(Union)
428
377
  Inference::BudgetTrace.observe(Inference::BudgetTrace::UNION_ARITY, result.members.size)
429
378
  end
430
- # `rigor trace` — degenerate merges (`1 | 1 → 1`, Dynamic
431
- # absorption) are recorded too; the collapse itself is the
432
- # teachable moment.
379
+ # `rigor trace` — degenerate merges (`1 | 1 → 1`, Dynamic absorption) are recorded too; the
380
+ # collapse itself is the teachable moment.
433
381
  Inference::FlowTracer.union(types, result) if Inference::FlowTracer.active? && types.size >= 2
434
382
  result
435
383
  end
436
384
 
437
- # `key_of[T]` type function — projects the type-level
438
- # union of `T`'s known keys. Recognised shapes:
385
+ # `key_of[T]` type function — projects the type-level union of `T`'s known keys. Recognised shapes:
439
386
  #
440
387
  # - `Type::HashShape{a: A, b: B}` → `Constant<:a> | Constant<:b>`.
441
388
  # - `Type::Tuple[A, B, C]` → `Constant<0> | Constant<1> | Constant<2>`.
442
389
  # - `Type::Nominal["Hash", [K, V]]` → `K` (untyped if absent).
443
390
  # - `Type::Nominal["Array", [E]]` → `non-negative-int`.
444
- # - `Type::Constant` whose value is a Hash / Array / Range —
445
- # project through the literal's per-element keys.
391
+ # - `Type::Constant` whose value is a Hash / Array / Range — project through the literal's
392
+ # per-element keys.
446
393
  #
447
- # Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`,
448
- # `Refined`, `Difference`, `Intersection`) project to `top`
449
- # so the type function always returns a value — callers may
450
- # narrow further when they know more.
394
+ # Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
395
+ # `Intersection`) project to `top` so the type function always returns a value — callers may narrow
396
+ # further when they know more.
451
397
  def key_of(type)
452
398
  case type
453
399
  when HashShape then hash_shape_keys(type)
@@ -458,15 +404,15 @@ module Rigor
458
404
  end
459
405
  end
460
406
 
461
- # `value_of[T]` type function — projects the type-level
462
- # union of `T`'s known values. Mirror of `key_of`:
407
+ # `value_of[T]` type function — projects the type-level union of `T`'s known values. Mirror of
408
+ # `key_of`:
463
409
  #
464
410
  # - `Type::HashShape{a: A, b: B}` → `A | B`.
465
411
  # - `Type::Tuple[A, B, C]` → `A | B | C`.
466
412
  # - `Type::Nominal["Hash", [K, V]]` → `V` (untyped if absent).
467
413
  # - `Type::Nominal["Array", [E]]` → `E` (untyped if absent).
468
- # - `Type::Constant` whose value is a Hash / Array / Range —
469
- # union of `Constant<…>` for each element.
414
+ # - `Type::Constant` whose value is a Hash / Array / Range — union of `Constant<…>` for each
415
+ # element.
470
416
  def value_of(type)
471
417
  case type
472
418
  when HashShape then hash_shape_values(type)
@@ -477,15 +423,11 @@ module Rigor
477
423
  end
478
424
  end
479
425
 
480
- # `int_mask[1, 2, 4]` type function — every Integer
481
- # representable by a bitwise OR over the listed flags,
482
- # including 0. The closure of `[1, 2, 4]` is
483
- # `{0, 1, 2, 3, 4, 5, 6, 7}`. Returns a `Union[Constant…]`
484
- # for small closures and a covering `IntegerRange` once
485
- # the cardinality exceeds `INT_MASK_UNION_LIMIT`. Returns
486
- # `nil` when the input is malformed (non-integer flag,
487
- # negative flag, or too many flags to compute the closure
488
- # cheaply).
426
+ # `int_mask[1, 2, 4]` type function — every Integer representable by a bitwise OR over the listed
427
+ # flags, including 0. The closure of `[1, 2, 4]` is `{0, 1, 2, 3, 4, 5, 6, 7}`. Returns a
428
+ # `Union[Constant…]` for small closures and a covering `IntegerRange` once the cardinality exceeds
429
+ # `INT_MASK_UNION_LIMIT`. Returns `nil` when the input is malformed (non-integer flag, negative
430
+ # flag, or too many flags to compute the closure cheaply).
489
431
  INT_MASK_FLAG_LIMIT = 6
490
432
  INT_MASK_UNION_LIMIT = 16
491
433
  private_constant :INT_MASK_FLAG_LIMIT, :INT_MASK_UNION_LIMIT
@@ -505,12 +447,9 @@ module Rigor
505
447
  end
506
448
  end
507
449
 
508
- # `int_mask_of[T]` — derives the int_mask closure from
509
- # a finite integer-literal type:
510
- # `Constant<n>` (single flag), `Union[Constant…]` (every
511
- # member must be a `Constant<Integer>`). Returns nil for
512
- # incompatible inputs (Top, Dynamic, IntegerRange, mixed
513
- # member shapes).
450
+ # `int_mask_of[T]` — derives the int_mask closure from a finite integer-literal type: `Constant<n>`
451
+ # (single flag), `Union[Constant…]` (every member must be a `Constant<Integer>`). Returns nil for
452
+ # incompatible inputs (Top, Dynamic, IntegerRange, mixed member shapes).
514
453
  def int_mask_of(type)
515
454
  flags = extract_constant_int_set(type)
516
455
  return nil if flags.nil?
@@ -518,20 +457,16 @@ module Rigor
518
457
  int_mask(flags)
519
458
  end
520
459
 
521
- # `T[K]` indexed-access type operator — extracts the type
522
- # at index / key `K` from a structured `T`:
460
+ # `T[K]` indexed-access type operator — extracts the type at index / key `K` from a structured `T`:
523
461
  #
524
- # - `Tuple[A, B, C][Constant<i>]` → `A` / `B` / `C` (out-of-
525
- # range indices return `Top` for safety).
462
+ # - `Tuple[A, B, C][Constant<i>]` → `A` / `B` / `C` (out-of-range indices return `Top` for safety).
526
463
  # - `HashShape{a: A, b: B}[Constant<:a>]` → `A`.
527
464
  # - `Nominal[Hash, [K, V]][_]` → `V` (untyped if absent).
528
465
  # - `Nominal[Array, [E]][_]` → `E` (untyped if absent).
529
466
  #
530
- # Other shapes (`Top`, `Dynamic`, untyped Nominals,
531
- # `Union`, `Refined`, `Difference`, `Intersection`)
532
- # project to `Top`. The key argument is itself a
533
- # `Type::t`; only `Type::Constant` keys produce a precise
534
- # answer.
467
+ # Other shapes (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
468
+ # `Intersection`) project to `Top`. The key argument is itself a `Type::t`; only `Type::Constant`
469
+ # keys produce a precise answer.
535
470
  def indexed_access(type, key)
536
471
  case type
537
472
  when Tuple then tuple_indexed_access(type, key)
@@ -542,18 +477,14 @@ module Rigor
542
477
  end
543
478
  end
544
479
 
545
- # `pick_of[T, K]` shape-projection — keeps only the entries
546
- # of `T` whose key is in the literal-key set extracted from
547
- # `K`. ADR-13 § "Shape projection / Restriction and removal".
480
+ # `pick_of[T, K]` shape-projection — keeps only the entries of `T` whose key is in the literal-key
481
+ # set extracted from `K`. ADR-13 § "Shape projection / Restriction and removal".
548
482
  #
549
- # Phase A handles `Type::HashShape` (literal-key K).
550
- # Phase B (slice 5) extends to `Type::Tuple` (integer-index
551
- # K) `pick_of[Tuple[A, B, C], 0 | 2]` evaluates to
552
- # `Tuple[A, C]`. Non-shape inputs (`Type::Nominal`, etc.)
553
- # return `type` unchanged ("lossy degradation"; the
554
- # `dynamic.shape.lossy-projection` diagnostic that flags
555
- # the boundary lands when caller-side diagnostic threading
556
- # arrives).
483
+ # Phase A handles `Type::HashShape` (literal-key K). Phase B (slice 5) extends to `Type::Tuple`
484
+ # (integer-index K) — `pick_of[Tuple[A, B, C], 0 | 2]` evaluates to `Tuple[A, C]`. Non-shape inputs
485
+ # (`Type::Nominal`, etc.) return `type` unchanged ("lossy degradation"; the
486
+ # `dynamic.shape.lossy-projection` diagnostic that flags the boundary lands when caller-side
487
+ # diagnostic threading arrives).
557
488
  def pick_of(type, keys)
558
489
  case type
559
490
  when HashShape then hash_shape_pick(type, keys)
@@ -562,9 +493,8 @@ module Rigor
562
493
  end
563
494
  end
564
495
 
565
- # `omit_of[T, K]` shape-projection — dual of {pick_of}.
566
- # Drops the entries / positions whose key (or index, for a
567
- # `Tuple`) is in the literal-key set extracted from `K`.
496
+ # `omit_of[T, K]` shape-projection — dual of {pick_of}. Drops the entries / positions whose key (or
497
+ # index, for a `Tuple`) is in the literal-key set extracted from `K`.
568
498
  def omit_of(type, keys)
569
499
  case type
570
500
  when HashShape then hash_shape_omit(type, keys)
@@ -573,11 +503,9 @@ module Rigor
573
503
  end
574
504
  end
575
505
 
576
- # `partial_of[T]` shape-projection — flips every required
577
- # entry of `T` to optional. ADR-13 § "Required-ness flips".
578
- # Does NOT add `nil` to value types Rigor's HashShape
579
- # distinguishes "key absent" from "key present with nil
580
- # value", so flipping required-ness is sufficient.
506
+ # `partial_of[T]` shape-projection — flips every required entry of `T` to optional. ADR-13 §
507
+ # "Required-ness flips". Does NOT add `nil` to value types Rigor's HashShape distinguishes "key
508
+ # absent" from "key present with nil value", so flipping required-ness is sufficient.
581
509
  def partial_of(type)
582
510
  return type unless type.is_a?(HashShape)
583
511
 
@@ -590,8 +518,8 @@ module Rigor
590
518
  )
591
519
  end
592
520
 
593
- # `required_of[T]` shape-projection — inverse of
594
- # {partial_of}; flips every optional entry to required.
521
+ # `required_of[T]` shape-projection — inverse of {partial_of}; flips every optional entry to
522
+ # required.
595
523
  def required_of(type)
596
524
  return type unless type.is_a?(HashShape)
597
525
 
@@ -604,9 +532,8 @@ module Rigor
604
532
  )
605
533
  end
606
534
 
607
- # `readonly_of[T]` shape-projection — marks every entry of
608
- # `T` as read-only in the current view. View-level only
609
- # does NOT prove the underlying Ruby Hash is frozen.
535
+ # `readonly_of[T]` shape-projection — marks every entry of `T` as read-only in the current view.
536
+ # View-level only does NOT prove the underlying Ruby Hash is frozen.
610
537
  def readonly_of(type)
611
538
  return type unless type.is_a?(HashShape)
612
539
 
@@ -619,18 +546,14 @@ module Rigor
619
546
  )
620
547
  end
621
548
 
622
- # Predicate that a shape-projection (`pick_of`, `omit_of`,
623
- # `partial_of`, `required_of`, `readonly_of`) would degrade
624
- # to "input unchanged" on this carrier. Callers consult
625
- # this BEFORE invoking the projection so they can emit a
626
- # `dynamic.shape.lossy-projection` diagnostic at the site
627
- # where the projection was authored.
549
+ # Predicate that a shape-projection (`pick_of`, `omit_of`, `partial_of`, `required_of`,
550
+ # `readonly_of`) would degrade to "input unchanged" on this carrier. Callers consult this BEFORE
551
+ # invoking the projection so they can emit a `dynamic.shape.lossy-projection` diagnostic at the
552
+ # site where the projection was authored.
628
553
  #
629
- # `HashShape` and `Tuple` carry shape-level information
630
- # the projections honour; every other carrier is lossy.
631
- # Slice 5b wires diagnostic emission through `RbsExtended`
632
- # / parser callers; this predicate stands alone in slice 5
633
- # for unit-test coverage and future composition.
554
+ # `HashShape` and `Tuple` carry shape-level information the projections honour; every other carrier
555
+ # is lossy. Slice 5b wires diagnostic emission through `RbsExtended` / parser callers; this
556
+ # predicate stands alone in slice 5 for unit-test coverage and future composition.
634
557
  def shape_projection_lossy?(type)
635
558
  !type.is_a?(HashShape) && !type.is_a?(Tuple)
636
559
  end
@@ -684,20 +607,12 @@ module Rigor
684
607
  end
685
608
  end
686
609
 
687
- # `Type::Constant` only carries scalar literals (Integer
688
- # / Float / String / Symbol / Range / Rational / Complex
689
- # / true / false / nil); Array and Hash literals become
690
- # Tuple / HashShape carriers earlier in the typer. Range
691
- # is the only scalar with meaningful key/value
692
- # projections.
693
610
  def compute_int_mask_closure(flags)
694
611
  unique = flags.uniq
695
612
  return [0] if unique.empty?
696
613
 
697
- # Closure under bitwise OR over a set of non-negative
698
- # integers is `0..(max_or_value)` only when the flags
699
- # are bit-disjoint; otherwise it's a strict subset.
700
- # Enumerate every subset's OR.
614
+ # Closure under bitwise OR over a set of non-negative integers is `0..(max_or_value)` only when
615
+ # the flags are bit-disjoint; otherwise it's a strict subset. Enumerate every subset's OR.
701
616
  closure = Set.new([0])
702
617
  unique.each do |flag|
703
618
  closure |= closure.map { |c| c | flag }
@@ -714,13 +629,10 @@ module Rigor
714
629
  end
715
630
  end
716
631
 
717
- # Literal-key set extraction for {pick_of} / {omit_of}.
718
- # Accepts `Constant<Symbol|String>` or `Union[Constant…]`
719
- # where every member is such a Constant. Returns `nil`
720
- # when the shape can't be reduced to a finite key set
721
- # (untyped, Top, Difference, Refined, mixed-kind union,
722
- # etc.) — callers degrade to "input unchanged" per
723
- # ADR-13's lossy-projection rule.
632
+ # Literal-key set extraction for {pick_of} / {omit_of}. Accepts `Constant<Symbol|String>` or
633
+ # `Union[Constant…]` where every member is such a Constant. Returns `nil` when the shape can't be
634
+ # reduced to a finite key set (untyped, Top, Difference, Refined, mixed-kind union, etc.)
635
+ # callers degrade to "input unchanged" per ADR-13's lossy-projection rule.
724
636
  def extract_constant_key_set(type)
725
637
  case type
726
638
  when Constant then constant_key_set(type)
@@ -743,12 +655,10 @@ module Rigor
743
655
  value.is_a?(Symbol) || value.is_a?(String)
744
656
  end
745
657
 
746
- # Rebuild a {HashShape} from the subset of `keys` the
747
- # caller decided to keep. Preserves required / optional /
748
- # read-only classification AND the extra-keys policy of
749
- # the source shape; entries dropped from `pairs` also
750
- # drop from each policy list. Used by both {pick_of}
751
- # (intersection with K) and {omit_of} (set difference).
658
+ # Rebuild a {HashShape} from the subset of `keys` the caller decided to keep. Preserves required /
659
+ # optional / read-only classification AND the extra-keys policy of the source shape; entries
660
+ # dropped from `pairs` also drop from each policy list. Used by both {pick_of} (intersection with
661
+ # K) and {omit_of} (set difference).
752
662
  def rebuild_hash_shape_with_keys(shape, kept_keys)
753
663
  HashShape.new(
754
664
  shape.pairs.slice(*kept_keys),
@@ -773,12 +683,10 @@ module Rigor
773
683
  rebuild_hash_shape_with_keys(type, type.pairs.keys - key_set)
774
684
  end
775
685
 
776
- # ADR-13 slice 5 — Tuple support. `K` MUST be a
777
- # `Constant<Integer>` or `Union[Constant<Integer>, …]`;
778
- # other K shapes (or non-integer Constants in a Union)
779
- # return the input unchanged. Negative or out-of-range
780
- # indices are dropped silently per slice 5's permissive
781
- # take — surface diagnostics are slice 5b material.
686
+ # ADR-13 slice 5 — Tuple support. `K` MUST be a `Constant<Integer>` or `Union[Constant<Integer>,
687
+ # …]`; other K shapes (or non-integer Constants in a Union) return the input unchanged. Negative
688
+ # or out-of-range indices are dropped silently per slice 5's permissive take — surface
689
+ # diagnostics are slice 5b material.
782
690
  def tuple_pick(type, keys)
783
691
  index_set = extract_tuple_index_set(keys, type.elements.size)
784
692
  return type if index_set.nil?
@@ -794,12 +702,10 @@ module Rigor
794
702
  Tuple.new(type.elements.each_with_index.reject { |_, i| dropped.include?(i) }.map(&:first))
795
703
  end
796
704
 
797
- # Extracts a sorted, deduplicated set of in-range integer
798
- # indices from a `Constant<Integer>` / `Union[Constant<Integer>, …]`
799
- # carrier. Out-of-range indices are dropped silently; the
800
- # caller decides whether an empty result still means
801
- # "lossy projection" (current pick / omit just produce an
802
- # empty Tuple).
705
+ # Extracts a sorted, deduplicated set of in-range integer indices from a `Constant<Integer>` /
706
+ # `Union[Constant<Integer>, …]` carrier. Out-of-range indices are dropped silently; the caller
707
+ # decides whether an empty result still means "lossy projection" (current pick / omit just
708
+ # produce an empty Tuple).
803
709
  def extract_tuple_index_set(type, size)
804
710
  flags = extract_constant_int_set(type)
805
711
  return nil if flags.nil?
@@ -841,6 +747,9 @@ module Rigor
841
747
  top
842
748
  end
843
749
 
750
+ # `Type::Constant` only carries scalar literals (Integer / Float / String / Symbol / Range /
751
+ # Rational / Complex / true / false / nil); Array and Hash literals become Tuple / HashShape
752
+ # carriers earlier in the typer. Range is the only scalar with meaningful key/value projections.
844
753
  def constant_keys(value)
845
754
  return non_negative_int if value.is_a?(Range) && value.begin.is_a?(Integer)
846
755
 
@@ -888,10 +797,9 @@ module Rigor
888
797
  end
889
798
  end
890
799
 
891
- # Symmetric counterparts to the Union normalisers. The
892
- # absorbing element is `Bot` (anything intersected with
893
- # nothing is nothing) and the identity element is `Top`
894
- # (intersecting with the universal type is a no-op).
800
+ # Symmetric counterparts to the Union normalisers. The absorbing element is `Bot` (anything
801
+ # intersected with nothing is nothing) and the identity element is `Top` (intersecting with the
802
+ # universal type is a no-op).
895
803
  def normalised_intersection_members(types)
896
804
  flattened = []
897
805
  types.each { |t| flatten_intersection_into(flattened, t) }