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
@@ -5,40 +5,29 @@ require_relative "budget_trace"
5
5
 
6
6
  module Rigor
7
7
  module Inference
8
- # ADR-20 Slice 2a — reducer that walks a `Definition`'s
9
- # `body_tree` against a concrete `Type::App` and returns a
10
- # fully-typed `Rigor::Type`.
8
+ # ADR-20 Slice 2a — reducer that walks a `Definition`'s `body_tree` against a concrete
9
+ # `Type::App` and returns a fully-typed `Rigor::Type`.
11
10
  #
12
- # Reduction is the operational interpretation of ADR-20
13
- # § D4 ("Evaluation rules"):
11
+ # Reduction is the operational interpretation of ADR-20 § D4 ("Evaluation rules"):
14
12
  #
15
- # 1. **Resolve `F`.** Look up the registered body via
16
- # `registry.definition(uri)`.
17
- # 2. **Substitute arguments.** Walk the body tree, replacing
18
- # `{HktBody::Param}` nodes with the matching positional
19
- # arg from the application.
20
- # 3. **Build types.** `{HktBody::TypeLeaf}` returns its
21
- # wrapped type as-is; `{HktBody::Union}` and
22
- # `{HktBody::NominalApp}` route their reduced children
23
- # through `Type::Combinator.union` / `.nominal_of` so
24
- # normalization applies.
25
- # 4. **Recurse on `{HktBody::AppRef}` nodes.** Reduce the
26
- # args first; if the resulting `(uri, args)` matches an
27
- # App already on the current reduction stack, return the
28
- # in-progress `Type::App` carrier as-is (lazy
29
- # self-reference handling — the standard "tying the
30
- # knot" trick for recursive type aliases like
31
- # `json::value`). Otherwise build a fresh
32
- # `Type::App` and recursively reduce it against the
33
- # same registry, sharing the fuel budget.
34
- # 5. **Fuel budget.** Each visited node consumes one unit.
35
- # On exhaustion, reduction unwinds to `app.bound`.
13
+ # 1. **Resolve `F`.** Look up the registered body via `registry.definition(uri)`.
14
+ # 2. **Substitute arguments.** Walk the body tree, replacing `{HktBody::Param}` nodes with
15
+ # the matching positional arg from the application.
16
+ # 3. **Build types.** `{HktBody::TypeLeaf}` returns its wrapped type as-is; `{HktBody::Union}`
17
+ # and `{HktBody::NominalApp}` route their reduced children through
18
+ # `Type::Combinator.union` / `.nominal_of` so normalization applies.
19
+ # 4. **Recurse on `{HktBody::AppRef}` nodes.** Reduce the args first; if the resulting
20
+ # `(uri, args)` matches an App already on the current reduction stack, return the
21
+ # in-progress `Type::App` carrier as-is (lazy self-reference handling — the standard
22
+ # "tying the knot" trick for recursive type aliases like `json::value`). Otherwise build a
23
+ # fresh `Type::App` and recursively reduce it against the same registry, sharing the fuel
24
+ # budget.
25
+ # 5. **Fuel budget.** Each visited node consumes one unit. On exhaustion, reduction unwinds to
26
+ # `app.bound`.
36
27
  #
37
- # The reducer is **pure** with respect to its inputs (the
38
- # registry + the App) but uses a per-call mutable state
39
- # bag for fuel + cycle tracking. Concurrent reductions
40
- # MUST allocate fresh reducers (or fresh `_reduce` calls)
41
- # — the per-call state is not shared.
28
+ # The reducer is **pure** with respect to its inputs (the registry + the App) but uses a
29
+ # per-call mutable state bag for fuel + cycle tracking. Concurrent reductions MUST allocate
30
+ # fresh reducers (or fresh `_reduce` calls) the per-call state is not shared.
42
31
  class HktReducer
43
32
  DEFAULT_FUEL = 64
44
33
 
@@ -53,12 +42,10 @@ module Rigor
53
42
  # Reduce `app` against the registry.
54
43
  #
55
44
  # @param app [Rigor::Type::App]
56
- # @param fuel [Integer] reduction-step budget (default 64
57
- # per ADR-20 WD3). Each visited body node costs one
58
- # unit. On exhaustion the reduction returns `app.bound`.
59
- # @return [Rigor::Type] the reduced type, or `app.bound`
60
- # when reduction is impossible (URI not defined, arity
61
- # mismatch, body_tree absent, fuel exhausted).
45
+ # @param fuel [Integer] reduction-step budget (default 64 per ADR-20 WD3). Each visited
46
+ # body node costs one unit. On exhaustion the reduction returns `app.bound`.
47
+ # @return [Rigor::Type] the reduced type, or `app.bound` when reduction is impossible (URI
48
+ # not defined, arity mismatch, body_tree absent, fuel exhausted).
62
49
  def reduce(app, fuel: DEFAULT_FUEL)
63
50
  raise ArgumentError, "expected a Rigor::Type::App, got #{app.class}" unless app.is_a?(Type::App)
64
51
 
@@ -109,13 +96,12 @@ module Rigor
109
96
  end
110
97
  end
111
98
 
112
- # ADR-20 § D3 conditional reduction. Resolves the test
113
- # against the current bindings and picks a branch:
99
+ # ADR-20 § D3 conditional reduction. Resolves the test against the current bindings and
100
+ # picks a branch:
114
101
  #
115
102
  # - test = `yes` → return the reduced `then_branch`.
116
103
  # - test = `no` → return the reduced `else_branch`.
117
- # - test = `maybe` → widen to the union of both
118
- # reduced branches (per ADR-20 WD7).
104
+ # - test = `maybe` → widen to the union of both reduced branches (per ADR-20 WD7).
119
105
  def walk_conditional(node, bindings:, state:)
120
106
  verdict = evaluate_test(node.test, bindings: bindings, state: state)
121
107
  case verdict
@@ -150,25 +136,21 @@ module Rigor
150
136
  end
151
137
  end
152
138
 
153
- # `left <: right`. Slice 7a verdict policy: structural
154
- # equality is `:yes`; clearly-disjoint nominal /
155
- # constant pairs are `:no`; everything else is `:maybe`
156
- # (widens to the union per ADR-20 WD7 — robustness
157
- # principle keeps us conservative on undecided tests).
139
+ # `left <: right`. Slice 7a verdict policy: structural equality is `:yes`; clearly-disjoint
140
+ # nominal / constant pairs are `:no`; everything else is `:maybe` (widens to the union per
141
+ # ADR-20 WD7 robustness principle keeps us conservative on undecided tests).
158
142
  def subtype_verdict(left, right)
159
143
  return :yes if left == right
160
144
 
161
- # Both Nominals with different class names AND
162
- # neither carries Dynamic `:no` (statically
163
- # disjoint). For any other shape pair, `:maybe`.
145
+ # Both Nominals with different class names AND neither carries Dynamic — `:no`
146
+ # (statically disjoint). For any other shape pair, `:maybe`.
164
147
  return :no if disjoint_nominals?(left, right)
165
148
  return :no if disjoint_constants?(left, right)
166
149
 
167
150
  :maybe
168
151
  end
169
152
 
170
- # Structural equality verdict — same shape as
171
- # subtype_verdict but symmetric.
153
+ # Structural equality verdict — same shape as subtype_verdict but symmetric.
172
154
  def equality_verdict(left, right)
173
155
  return :yes if left == right
174
156
  return :no if disjoint_nominals?(left, right)
@@ -198,11 +180,9 @@ module Rigor
198
180
  end
199
181
 
200
182
  def reduce_app_ref(uri, reduced_args, state:)
201
- # Cycle detection — when the same `(uri, args)` is
202
- # already on the reduction stack, return the
203
- # in-progress App carrier as-is so recursive type
204
- # aliases (`Array[App[json::value, K]]` inside the
205
- # `json::value` body) terminate.
183
+ # Cycle detection — when the same `(uri, args)` is already on the reduction stack, return
184
+ # the in-progress App carrier as-is so recursive type aliases (`Array[App[json::value,
185
+ # K]]` inside the `json::value` body) terminate.
206
186
  existing = state.in_progress_for(uri, reduced_args)
207
187
  return existing if existing
208
188
 
@@ -219,9 +199,8 @@ module Rigor
219
199
  end
220
200
  end
221
201
 
222
- # Per-call mutable bag carrying the remaining fuel
223
- # budget and the active reduction stack (for cycle
224
- # detection). Not shared across `reduce` calls.
202
+ # Per-call mutable bag carrying the remaining fuel budget and the active reduction stack
203
+ # (for cycle detection). Not shared across `reduce` calls.
225
204
  class State
226
205
  def initialize(fuel:)
227
206
  @fuel = fuel
@@ -4,41 +4,32 @@ require_relative "hkt_body"
4
4
 
5
5
  module Rigor
6
6
  module Inference
7
- # ADR-20 § "Decision D1 / D2" — registry of Lightweight HKT
8
- # tag registrations + type-function bodies parsed off the
9
- # `%a{rigor:v1:hkt_register: ...}` /
10
- # `%a{rigor:v1:hkt_define: ...}` annotations in shipped
11
- # `.rbs` files.
7
+ # ADR-20 § "Decision D1 / D2" — registry of Lightweight HKT tag registrations + type-function
8
+ # bodies parsed off the `%a{rigor:v1:hkt_register: ...}` / `%a{rigor:v1:hkt_define: ...}`
9
+ # annotations in shipped `.rbs` files.
12
10
  #
13
- # The registry stores registration metadata (arity, variance,
14
- # bound) and the definition body as both a raw String and an
15
- # evaluable `HktBody` node tree. `HktReducer` (Slice 2a) and
16
- # `HktBodyParser` (Slice 2b) are both shipped and reduce
17
- # `Type::App` instances against the definition. The carrier
18
- # never needs to read from the registry
19
- # because Slice 1's `Type::App` carries its `bound` directly;
20
- # the registry exists at this slice solely so the parser
11
+ # The registry stores registration metadata (arity, variance, bound) and the definition body
12
+ # as both a raw String and an evaluable `HktBody` node tree. `HktReducer` (Slice 2a) and
13
+ # `HktBodyParser` (Slice 2b) are both shipped and reduce `Type::App` instances against the
14
+ # definition. The carrier never needs to read from the registry because Slice 1's `Type::App`
15
+ # carries its `bound` directly; the registry exists at this slice solely so the parser
21
16
  # round-trip and downstream slices have a stable target API.
22
17
  #
23
- # The registry is immutable after construction. Callers that
24
- # need to extend it (e.g. plugin registrations layered on top
25
- # of stdlib registrations) MUST build a new registry via
26
- # `merge` rather than mutating an existing one. This keeps the
27
- # registry shareable across Ractor boundaries per ADR-15.
18
+ # The registry is immutable after construction. Callers that need to extend it (e.g. plugin
19
+ # registrations layered on top of stdlib registrations) MUST build a new registry via `merge`
20
+ # rather than mutating an existing one. This keeps the registry shareable across Ractor
21
+ # boundaries per ADR-15.
28
22
  class HktRegistry
29
23
  # Frozen value object recording one tag registration.
30
24
  #
31
- # - `uri`: namespaced Symbol per ADR-20 WD1 (must include
32
- # `"::"`).
33
- # - `arity`: positive Integer — the number of formal
34
- # parameters the registered constructor takes.
35
- # - `variance`: ordered Array of Symbols, one per
36
- # parameter, each `:out` (covariant), `:in`
25
+ # - `uri`: namespaced Symbol per ADR-20 WD1 (must include `"::"`).
26
+ # - `arity`: positive Integer — the number of formal parameters the registered constructor
27
+ # takes.
28
+ # - `variance`: ordered Array of Symbols, one per parameter, each `:out` (covariant), `:in`
37
29
  # (contravariant), or `:inv` (invariant; default).
38
- # - `bound`: a `Rigor::Type` to erase to when an `App`
39
- # referring to this URI cannot be reduced. Defaults to
40
- # `Dynamic[Top]` (the parser fills in the default when
41
- # the annotation omits `bound:`).
30
+ # - `bound`: a `Rigor::Type` to erase to when an `App` referring to this URI cannot be
31
+ # reduced. Defaults to `Dynamic[Top]` (the parser fills in the default when the
32
+ # annotation omits `bound:`).
42
33
  Registration = Data.define(:uri, :arity, :variance, :bound) do
43
34
  def initialize(uri:, arity:, variance:, bound:)
44
35
  raise ArgumentError, "uri must be a Symbol, got #{uri.class}" unless uri.is_a?(Symbol)
@@ -61,21 +52,16 @@ module Rigor
61
52
  end
62
53
  end
63
54
 
64
- # Frozen value object recording one type-function
65
- # definition.
55
+ # Frozen value object recording one type-function definition.
66
56
  #
67
- # `body` is the raw String payload from the `%a{...}`
68
- # annotation (Slice 1's parser populates it); parsed into
69
- # `body_tree` by `HktBodyParser` (Slice 2b, shipped).
57
+ # `body` is the raw String payload from the `%a{...}` annotation (Slice 1's parser
58
+ # populates it); parsed into `body_tree` by `HktBodyParser` (Slice 2b, shipped).
70
59
  #
71
- # `body_tree` is the evaluable form: a
72
- # `Rigor::Inference::HktBody::*` node tree the Slice 2a
73
- # reducer walks against the application's concrete
74
- # arguments. Plugin and Rigor-bundled overlay authors
75
- # construct it programmatically through
76
- # {.definition_with_body_tree}. The reducer treats a
77
- # `nil` `body_tree` as "definition not yet evaluable"
78
- # and returns the registered bound.
60
+ # `body_tree` is the evaluable form: a `Rigor::Inference::HktBody::*` node tree the Slice 2a
61
+ # reducer walks against the application's concrete arguments. Plugin and Rigor-bundled
62
+ # overlay authors construct it programmatically through {.definition_with_body_tree}. The
63
+ # reducer treats a `nil` `body_tree` as "definition not yet evaluable" and returns the
64
+ # registered bound.
79
65
  Definition = Data.define(:uri, :params, :body, :body_tree, :source_path, :source_line) do
80
66
  def initialize(uri:, params:, body:, body_tree: nil, source_path: nil, source_line: nil)
81
67
  raise ArgumentError, "uri must be a Symbol, got #{uri.class}" unless uri.is_a?(Symbol)
@@ -97,11 +83,9 @@ module Rigor
97
83
  end
98
84
  end
99
85
 
100
- # Convenience constructor for callers that have a body
101
- # tree but no raw String typically Rigor-bundled HKT
102
- # overlays that build the body programmatically. The
103
- # raw `body` slot is filled with an empty placeholder
104
- # so existing consumers keep their type contract.
86
+ # Convenience constructor for callers that have a body tree but no raw String — typically
87
+ # Rigor-bundled HKT overlays that build the body programmatically. The raw `body` slot is
88
+ # filled with an empty placeholder so existing consumers keep their type contract.
105
89
  def self.definition_with_body_tree(uri:, params:, body_tree:, source_path: nil, source_line: nil)
106
90
  Definition.new(
107
91
  uri: uri,
@@ -139,10 +123,8 @@ module Rigor
139
123
  @definitions[uri]
140
124
  end
141
125
 
142
- # @return [HktRegistry] a new registry whose entries are
143
- # the union of this registry's and `other`'s. On URI
144
- # collisions `other`'s entries win (last-write-wins; OQ3
145
- # tentative).
126
+ # @return [HktRegistry] a new registry whose entries are the union of this registry's and
127
+ # `other`'s. On URI collisions `other`'s entries win (last-write-wins; OQ3 tentative).
146
128
  def merge(other)
147
129
  raise ArgumentError, "merge target must be an HktRegistry, got #{other.class}" unless other.is_a?(HktRegistry)
148
130
 
@@ -156,41 +138,33 @@ module Rigor
156
138
  @registrations.empty? && @definitions.empty?
157
139
  end
158
140
 
159
- # ADR-20 Slice 2a — reduce an `App` against this
160
- # registry. Convenience wrapper around `HktReducer.new(self).reduce`.
161
- # Each call allocates a fresh reducer; concurrent
162
- # reductions are safe.
141
+ # ADR-20 Slice 2a — reduce an `App` against this registry. Convenience wrapper around
142
+ # `HktReducer.new(self).reduce`. Each call allocates a fresh reducer; concurrent reductions
143
+ # are safe.
163
144
  def reduce(app, fuel: HktReducer::DEFAULT_FUEL)
164
145
  HktReducer.new(self).reduce(app, fuel: fuel)
165
146
  end
166
147
 
167
- # ADR-20 slice 2e — scan a Rigor RbsLoader for
168
- # `rigor:v1:hkt_register` / `rigor:v1:hkt_define`
169
- # annotations attached to class- or module-level
170
- # declarations in the loaded RBS env, parse them via
171
- # {Rigor::RbsExtended::HktDirectives}, and return a new
172
- # registry that is the union of `base` and every parsed
173
- # entry. Last-write-wins on URI collisions per
174
- # {#merge}'s contract. Fail-soft on per-annotation parse
175
- # errors (the reporter records an `:info` entry; the
176
- # other annotations still apply).
148
+ # ADR-20 slice 2e — scan a Rigor RbsLoader for `rigor:v1:hkt_register` /
149
+ # `rigor:v1:hkt_define` annotations attached to class- or module-level declarations in the
150
+ # loaded RBS env, parse them via {Rigor::RbsExtended::HktDirectives}, and return a new
151
+ # registry that is the union of `base` and every parsed entry. Last-write-wins on URI
152
+ # collisions per {#merge}'s contract. Fail-soft on per-annotation parse errors (the reporter
153
+ # records an `:info` entry; the other annotations still apply).
177
154
  #
178
155
  # @param rbs_loader [Rigor::Environment::RbsLoader]
179
- # @param base [HktRegistry] starting registry (typically
180
- # the bundled `Rigor::Builtins::HktBuiltins.registry`).
181
- # @param name_scope [Rigor::Environment::NameScope, nil]
182
- # threaded through to the bound resolver for class-name
183
- # lookups; safe to omit during scanning since hkt
184
- # bounds are typically `untyped` or stdlib classes.
185
- # @param reporter [#record, nil] same fail-soft reporter
186
- # contract the other RBS-extended parsers use.
156
+ # @param base [HktRegistry] starting registry (typically the bundled
157
+ # `Rigor::Builtins::HktBuiltins.registry`).
158
+ # @param name_scope [Rigor::Environment::NameScope, nil] threaded through to the bound
159
+ # resolver for class-name lookups; safe to omit during scanning since hkt bounds are
160
+ # typically `untyped` or stdlib classes.
161
+ # @param reporter [#record, nil] same fail-soft reporter contract the other RBS-extended
162
+ # parsers use.
187
163
  def self.scan_rbs_loader(rbs_loader, base: EMPTY, name_scope: nil, reporter: nil)
188
164
  return base if rbs_loader.nil?
189
165
 
190
- # Required lazily here to avoid a hard circular
191
- # require between hkt_registry / hkt_directives;
192
- # HktDirectives requires HktRegistry to construct its
193
- # value objects.
166
+ # Required lazily here to avoid a hard circular require between hkt_registry /
167
+ # hkt_directives; HktDirectives requires HktRegistry to construct its value objects.
194
168
  require_relative "../rbs_extended/hkt_directives"
195
169
 
196
170
  registrations = []
@@ -7,62 +7,50 @@ require_relative "mutation_widening"
7
7
 
8
8
  module Rigor
9
9
  module Inference
10
- # Closes the "`params[:f] ||= []; params[:f] << x`" precision
11
- # gap surfaced by the Redmine 6.1.2 `Query#as_params` survey
12
- # (ROADMAP § Future cycles / Type-language / engine —
10
+ # Closes the "`params[:f] ||= []; params[:f] << x`" precision gap surfaced by the Redmine
11
+ # 6.1.2 `Query#as_params` survey (ROADMAP § Future cycles / Type-language / engine —
13
12
  # "Indexed-collection narrowing through `Hash[k] ||= default`").
14
13
  #
15
- # After `receiver[key] ||= default` the next read at
16
- # `receiver[key]` is known non-nil, but Rigor types each
17
- # `Hash#[]` independently and the subsequent `<<` / `[]=` /
18
- # other mutator dispatches against the un-narrowed result —
19
- # which on a `HashShape{}` carrier folds to `Constant[nil]`.
14
+ # After `receiver[key] ||= default` the next read at `receiver[key]` is known non-nil, but
15
+ # Rigor types each `Hash#[]` independently and the subsequent `<<` / `[]=` / other mutator
16
+ # dispatches against the un-narrowed result which on a `HashShape{}` carrier folds to
17
+ # `Constant[nil]`.
20
18
  #
21
- # This module is the address-recogniser + invalidator shared
22
- # by {Inference::StatementEvaluator}'s `eval_index_or_write`
23
- # handler (which RECORDS the narrowing) and `eval_call`
24
- # (which INVALIDATES on intervening writes / mutators) and
25
- # by {Inference::ExpressionTyper}'s `call_type_for` (which
26
- # CONSUMES the narrowing when typing a follow-up `[]` read).
19
+ # This module is the address-recogniser + invalidator shared by
20
+ # {Inference::StatementEvaluator}'s `eval_index_or_write` handler (which RECORDS the
21
+ # narrowing) and `eval_call` (which INVALIDATES on intervening writes / mutators) and by
22
+ # {Inference::ExpressionTyper}'s `call_type_for` (which CONSUMES the narrowing when typing a
23
+ # follow-up `[]` read).
27
24
  #
28
- # **Stable receivers.** A receiver is "stable" iff it is a
29
- # `LocalVariableReadNode` or `InstanceVariableReadNode`.
30
- # Method-call chains (`foo.bar[:k]`) and other shapes are
31
- # rejected because a follow-up read against an identical-
32
- # looking AST chain has no guarantee of resolving to the
33
- # same runtime value — narrowing it would invent a fact.
25
+ # **Stable receivers.** A receiver is "stable" iff it is a `LocalVariableReadNode` or
26
+ # `InstanceVariableReadNode`. Method-call chains (`foo.bar[:k]`) and other shapes are
27
+ # rejected because a follow-up read against an identical-looking AST chain has no guarantee
28
+ # of resolving to the same runtime value — narrowing it would invent a fact.
34
29
  #
35
- # **Stable keys.** A key is "stable" iff it is a literal
36
- # `SymbolNode` / `StringNode` / `IntegerNode`. Local-variable
37
- # keys (`params[field]`) are excluded for the same
38
- # invent-a-fact reason: the local could be rebound between
39
- # the `||=` and the read.
30
+ # **Stable keys.** A key is "stable" iff it is a literal `SymbolNode` / `StringNode` /
31
+ # `IntegerNode`. Local-variable keys (`params[field]`) are excluded for the same
32
+ # invent-a-fact reason: the local could be rebound between the `||=` and the read.
40
33
  #
41
- # **Invalidation.** Three conditions drop a recorded
42
- # narrowing:
43
- # - The receiver variable is rebound (handled inside
44
- # `Scope#with_local` / `Scope#with_ivar`).
45
- # - An intervening `receiver[key] = value` writes the same
46
- # slot `:[]=` could rebind the slot to nil; conservative
47
- # drop.
48
- # - An intervening mutator from {MutationWidening::HASH_MUTATORS}
49
- # or {MutationWidening::ARRAY_MUTATORS} runs against the
50
- # receiver (e.g. `params.delete(:f)`, `params.clear`).
34
+ # **Invalidation.** Three conditions drop a recorded narrowing:
35
+ # - The receiver variable is rebound (handled inside `Scope#with_local` / `Scope#with_ivar`).
36
+ # - An intervening `receiver[key] = value` writes the same slot — `:[]=` could rebind the
37
+ # slot to nil; conservative drop.
38
+ # - An intervening mutator from {MutationWidening::HASH_MUTATORS} or
39
+ # {MutationWidening::ARRAY_MUTATORS} runs against the receiver (e.g. `params.delete(:f)`,
40
+ # `params.clear`).
51
41
  #
52
- # All three are implemented in `StatementEvaluator#eval_call`'s
53
- # post-dispatch path through {.invalidate_after_call}.
42
+ # All three are implemented in `StatementEvaluator#eval_call`'s post-dispatch path through
43
+ # {.invalidate_after_call}.
54
44
  module IndexedNarrowing
55
- # Literal Prism nodes whose Ruby value the analyzer trusts
56
- # as a stable address. Symbol / String are the dominant
57
- # Hash key shapes; Integer covers numerically-keyed Hashes
58
- # and Array indices.
45
+ # Literal Prism nodes whose Ruby value the analyzer trusts as a stable address. Symbol /
46
+ # String are the dominant Hash key shapes; Integer covers numerically-keyed Hashes and
47
+ # Array indices.
59
48
  STABLE_KEY_NODES = [Prism::SymbolNode, Prism::StringNode, Prism::IntegerNode].freeze
60
49
 
61
50
  module_function
62
51
 
63
- # Returns `[receiver_kind, receiver_name]` when `node` is a
64
- # `LocalVariableReadNode` or `InstanceVariableReadNode`,
65
- # otherwise nil.
52
+ # Returns `[receiver_kind, receiver_name]` when `node` is a `LocalVariableReadNode` or
53
+ # `InstanceVariableReadNode`, otherwise nil.
66
54
  def stable_receiver(node)
67
55
  case node
68
56
  when Prism::LocalVariableReadNode then [:local, node.name]
@@ -70,9 +58,8 @@ module Rigor
70
58
  end
71
59
  end
72
60
 
73
- # Returns the literal Ruby value when `node` is a stable
74
- # key shape, otherwise nil. Symbols → `Symbol`,
75
- # Strings → `String` (unescaped), Integers → `Integer`.
61
+ # Returns the literal Ruby value when `node` is a stable key shape, otherwise nil.
62
+ # Symbols `Symbol`, Strings `String` (unescaped), Integers → `Integer`.
76
63
  def stable_key(node)
77
64
  case node
78
65
  when Prism::SymbolNode then node.unescaped.to_sym
@@ -81,14 +68,11 @@ module Rigor
81
68
  end
82
69
  end
83
70
 
84
- # Returns `[receiver_kind, receiver_name, key]` when the
85
- # CallNode is a `receiver[key]` read or write whose
86
- # receiver and key are both stable, otherwise nil. Used
87
- # by both the recorder (for `IndexOrWriteNode`'s
88
- # receiver/arguments triplet) and the invalidator (for
89
- # `CallNode :[]=` / mutator calls). Treats only the FIRST
90
- # argument as the key; `:[]=`'s second argument is the
91
- # rvalue and is not part of the address.
71
+ # Returns `[receiver_kind, receiver_name, key]` when the CallNode is a `receiver[key]` read
72
+ # or write whose receiver and key are both stable, otherwise nil. Used by both the recorder
73
+ # (for `IndexOrWriteNode`'s receiver/arguments triplet) and the invalidator (for `CallNode
74
+ # :[]=` / mutator calls). Treats only the FIRST argument as the key; `:[]=`'s second
75
+ # argument is the rvalue and is not part of the address.
92
76
  def stable_address(receiver_node, key_node)
93
77
  receiver = stable_receiver(receiver_node)
94
78
  return nil if receiver.nil?
@@ -99,10 +83,9 @@ module Rigor
99
83
  [receiver.first, receiver.last, key]
100
84
  end
101
85
 
102
- # Looks up a recorded narrowing for `receiver[key]` against
103
- # `scope`, returning the narrowed type or nil when no
104
- # entry applies. Used by ExpressionTyper's `[]` dispatch
105
- # to refine the result of a stable indexed read.
86
+ # Looks up a recorded narrowing for `receiver[key]` against `scope`, returning the narrowed
87
+ # type or nil when no entry applies. Used by ExpressionTyper's `[]` dispatch to refine the
88
+ # result of a stable indexed read.
106
89
  def lookup_for_call(node, scope)
107
90
  return nil unless node.is_a?(Prism::CallNode)
108
91
  return nil unless node.name == :[]
@@ -115,18 +98,14 @@ module Rigor
115
98
  scope.indexed_narrowing(*address)
116
99
  end
117
100
 
118
- # Removes recorded narrowings invalidated by `call_node`.
119
- # Two patterns:
101
+ # Removes recorded narrowings invalidated by `call_node`. Two patterns:
120
102
  #
121
- # - `receiver[key] = value` (a `:[]=` against a stable
122
- # address): drop the specific `(receiver, key)` entry.
123
- # - Any mutator from `HASH_MUTATORS` / `ARRAY_MUTATORS`
124
- # against a stable receiver: drop EVERY entry rooted
125
- # at that receiver, because the mutator could rebind
126
- # any slot.
103
+ # - `receiver[key] = value` (a `:[]=` against a stable address): drop the specific
104
+ # `(receiver, key)` entry.
105
+ # - Any mutator from `HASH_MUTATORS` / `ARRAY_MUTATORS` against a stable receiver: drop
106
+ # EVERY entry rooted at that receiver, because the mutator could rebind any slot.
127
107
  #
128
- # Returns the updated scope. Always-safe (only forgets;
129
- # never invents).
108
+ # Returns the updated scope. Always-safe (only forgets; never invents).
130
109
  def invalidate_after_call(call_node:, current_scope:)
131
110
  return current_scope unless call_node.is_a?(Prism::CallNode)
132
111
 
@@ -161,18 +140,13 @@ module Rigor
161
140
  current_scope.without_indexed_narrowings_for(*receiver)
162
141
  end
163
142
 
164
- # Companion invalidator for single-hop method-chain
165
- # narrowings (ROADMAP § Future cycles "Method-call
166
- # receiver narrowing across stable receivers", B2 from
167
- # the slice's design notes). Drops every
168
- # `(receiver, *)` chain narrowing rooted at the call's
169
- # OUTER stable receiver matching the ROADMAP's "any
170
- # intervening method call against the same receiver"
171
- # criterion. A call against `x.last` (the OUTER receiver
172
- # is a `CallNode`, not a stable root) does NOT drop
173
- # narrowings keyed on `x`, so the worked-site
174
- # `x.last << y` pattern correctly preserves the chain
175
- # narrowing for any further `x.last` read in the same
143
+ # Companion invalidator for single-hop method-chain narrowings (ROADMAP § Future cycles —
144
+ # "Method-call receiver narrowing across stable receivers", B2 from the slice's design
145
+ # notes). Drops every `(receiver, *)` chain narrowing rooted at the call's OUTER stable
146
+ # receiver — matching the ROADMAP's "any intervening method call against the same
147
+ # receiver" criterion. A call against `x.last` (the OUTER receiver is a `CallNode`, not a
148
+ # stable root) does NOT drop narrowings keyed on `x`, so the worked-site `x.last << y`
149
+ # pattern correctly preserves the chain narrowing for any further `x.last` read in the same
176
150
  # body. Always-safe (only forgets; never invents).
177
151
  def invalidate_chain_after_call(call_node:, current_scope:)
178
152
  return current_scope unless call_node.is_a?(Prism::CallNode)
@@ -4,35 +4,27 @@ require_relative "../type"
4
4
 
5
5
  module Rigor
6
6
  module Inference
7
- # ADR-16 Tier A — engine hook. Consults every registered
8
- # plugin manifest's `block_as_methods` entries to decide
9
- # whether a block call site qualifies for `Scope#self_type`
10
- # narrowing.
7
+ # ADR-16 Tier A — engine hook. Consults every registered plugin manifest's `block_as_methods`
8
+ # entries to decide whether a block call site qualifies for `Scope#self_type` narrowing.
11
9
  #
12
10
  # The match contract for a class-level DSL like Sinatra's
13
11
  # `class MyApp < Sinatra::Base; get '/foo' do ... end; end`:
14
12
  #
15
- # - the call's lexical receiver type is `Singleton[X]`
16
- # (the implicit-self in a class body, or an explicit
17
- # `MyApp.get(...)` call);
18
- # - the underlying class `X` equals or inherits from the
19
- # entry's `receiver_constraint`;
13
+ # - the call's lexical receiver type is `Singleton[X]` (the implicit-self in a class body, or
14
+ # an explicit `MyApp.get(...)` call);
15
+ # - the underlying class `X` equals or inherits from the entry's `receiver_constraint`;
20
16
  # - the call's method name is in the entry's `method_names`.
21
17
  #
22
- # On a match the helper returns the **instance** type of
23
- # the receiver class (`Nominal[X]`) the narrowed
24
- # `self_type` for the block body, matching Sinatra's
25
- # runtime semantics where `Sinatra::Base#generate_method`
26
- # turns the block into an instance method of the user's
27
- # app class.
18
+ # On a match the helper returns the **instance** type of the receiver class (`Nominal[X]`) —
19
+ # the narrowed `self_type` for the block body, matching Sinatra's runtime semantics where
20
+ # `Sinatra::Base#generate_method` turns the block into an instance method of the user's app
21
+ # class.
28
22
  #
29
- # Slice 1b ships the floor only (per ADR-16 § WD13):
30
- # bare-identifier method lookups inside the block resolve
31
- # through the inference engine's normal `self_type`-driven
32
- # path, so methods declared on `Sinatra::Base` (RBS or
33
- # otherwise) become visible. Precision additions —
34
- # parameter-typed block params, declared per-verb argument
35
- # contracts — are ceiling concerns for later slices.
23
+ # Slice 1b ships the floor only (per ADR-16 § WD13): bare-identifier method lookups inside the
24
+ # block resolve through the inference engine's normal `self_type`-driven path, so methods
25
+ # declared on `Sinatra::Base` (RBS or otherwise) become visible. Precision additions —
26
+ # parameter-typed block params, declared per-verb argument contracts — are ceiling concerns
27
+ # for later slices.
36
28
  module MacroBlockSelfType
37
29
  module_function
38
30
 
@@ -51,12 +43,9 @@ module Rigor
51
43
  receiver_class_name = singleton_receiver_class_name(receiver_type)
52
44
  return nil if receiver_class_name.nil?
53
45
 
54
- # ADR-52 WD1 — the verb-keyed table compiled at registry build
55
- # replaces the per-call plugins × block_as_methods linear scan.
56
- # Entries arrive in (plugin registration, declaration) order, so
57
- # the first ancestry match below is the same entry the previous
58
- # walk returned; the method-name membership the old `matches?` checked
59
- # is guaranteed by the table key.
46
+ # ADR-52 WD1 — the verb-keyed table compiled at registry build. Entries arrive in
47
+ # (plugin registration, declaration) order; the method-name membership is guaranteed
48
+ # by the table key.
60
49
  entries = registry.contribution_index.block_entries_for(call_node.name)
61
50
  entries.each do |entry|
62
51
  if receiver_class_inherits_from?(receiver_class_name, entry.receiver_constraint, environment)
@@ -66,12 +55,10 @@ module Rigor
66
55
  nil
67
56
  end
68
57
 
69
- # Tier A's match contract is intentionally narrow:
70
- # class-level DSL calls (receiver is `Singleton[X]`) only.
71
- # Instance-receiver calls and DSL forms whose block body
72
- # binds a different `self` (Concern's `included do`,
73
- # `instance_eval { ... }`) are handled by later slices
74
- # (Concern walker, Tier D, etc.) — not Tier A.
58
+ # Tier A's match contract is intentionally narrow: class-level DSL calls (receiver is
59
+ # `Singleton[X]`) only. Instance-receiver calls and DSL forms whose block body binds a
60
+ # different `self` (Concern's `included do`, `instance_eval { ... }`) are handled by later
61
+ # slices (Concern walker, Tier D, etc.) — not Tier A.
75
62
  def singleton_receiver_class_name(receiver_type)
76
63
  return nil unless receiver_type.is_a?(Type::Singleton)
77
64