rigortype 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -4,24 +4,20 @@ require_relative "../type"
4
4
 
5
5
  module Rigor
6
6
  module Builtins
7
- # Maps a curated table of canonical regex sub-patterns onto the
8
- # imported refinement carriers Rigor already ships
9
- # (`decimal-int-string`, `hex-int-string`, `octal-int-string`,
10
- # `lowercase-string`, `uppercase-string`, `numeric-string`).
11
- # See `docs/type-specification/imported-built-in-types.md` for
12
- # the registry the refinements come from and `docs/ROADMAP.md`
13
- # § "v0.1.1 — Planned" Track 1 slice 1 for the binding scope of
14
- # this recogniser.
7
+ # Maps a curated table of canonical regex sub-patterns onto the imported refinement
8
+ # carriers Rigor already ships (`decimal-int-string`, `hex-int-string`,
9
+ # `octal-int-string`, `lowercase-string`, `uppercase-string`, `numeric-string`). See
10
+ # `docs/type-specification/imported-built-in-types.md` for the registry the refinements
11
+ # come from and `docs/ROADMAP.md` § "v0.1.1 — Planned" Track 1 slice 1 for the binding
12
+ # scope of this recogniser.
15
13
  #
16
- # The intended consumer is `Inference::Narrowing.analyse_match_write`:
17
- # given `if /(?<year>\d+)/ =~ str; year; end`, the v0.1.0
18
- # baseline narrows `year` to plain `String`; v0.1.1 introspects
19
- # the regex source and narrows further to
20
- # `decimal-int-string` whenever the named-capture body matches
21
- # one of the rows in {RULES}.
14
+ # The intended consumer is `Inference::Narrowing.analyse_match_write`: given `if
15
+ # /(?<year>\d+)/ =~ str; year; end`, the v0.1.0 baseline narrows `year` to plain `String`;
16
+ # v0.1.1 introspects the regex source and narrows further to `decimal-int-string`
17
+ # whenever the named-capture body matches one of the rows in {RULES}.
22
18
  #
23
- # Recognised body shapes (each row admits the `+` quantifier
24
- # and the bounded `{n}` / `{n,m}` forms with `n >= 1`):
19
+ # Recognised body shapes (each row admits the `+` quantifier and the bounded `{n}` /
20
+ # `{n,m}` forms with `n >= 1`):
25
21
  #
26
22
  # - `\d` -> decimal-int-string
27
23
  # - `\h` -> hex-int-string
@@ -32,25 +28,20 @@ module Rigor
32
28
  # - `[A-Z]` -> uppercase-string
33
29
  # - `[[:digit:]]` -> numeric-string
34
30
  #
35
- # Anything outside the table returns `nil` so the calling
36
- # narrowing site falls back to its previous behaviour
37
- # (plain `String`). Arbitrary regex semantic equivalence is
38
- # undecidable, so the table is intentionally a small audited
39
- # set of canonical shapes rather than a general equivalence
40
- # checker.
31
+ # Anything outside the table returns `nil` so the calling narrowing site falls back to
32
+ # its previous behaviour (plain `String`). Arbitrary regex semantic equivalence is
33
+ # undecidable, so the table is intentionally a small audited set of canonical shapes
34
+ # rather than a general equivalence checker.
41
35
  module RegexRefinement
42
- # `+` (one-or-more) or `{n}` / `{n,m}` (n >= 1, m >= n).
43
- # The bound check is enforced separately by
44
- # {valid_bounds?} after the structural match succeeds, so
45
- # forms like `\d{0,5}` or `\d{5,3}` reject even though they
46
- # parse syntactically.
36
+ # `+` (one-or-more) or `{n}` / `{n,m}` (n >= 1, m >= n). The bound check is enforced
37
+ # separately by {valid_bounds?} after the structural match succeeds, so forms like
38
+ # `\d{0,5}` or `\d{5,3}` reject even though they parse syntactically.
47
39
  QUANTIFIER_SOURCE = '(?:\+|\{\d+(?:,\d+)?\})'
48
40
  private_constant :QUANTIFIER_SOURCE
49
41
 
50
- # ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
51
- # because the outer Array contains two-element `[Regexp, Symbol]`
52
- # rows whose inner Arrays are not frozen by the outer freeze.
53
- # A worker Ractor iterating `RULES.find { ... }` would trip
42
+ # ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`) because the outer Array
43
+ # contains two-element `[Regexp, Symbol]` rows whose inner Arrays are not frozen by the
44
+ # outer freeze. A worker Ractor iterating `RULES.find { ... }` would trip
54
45
  # `Ractor::IsolationError` on the first row access.
55
46
  RULES = Ractor.make_shareable([
56
47
  [/\A\\d#{QUANTIFIER_SOURCE}\z/, :decimal_int_string],
@@ -70,13 +61,12 @@ module Rigor
70
61
 
71
62
  module_function
72
63
 
73
- # @param body [String, nil] a regex sub-pattern, typically the
74
- # inner body of a `(?<name>body)` named capture. Anchors
75
- # (`\A`, `\z`, `^`, `$`) are not stripped the recogniser
76
- # table targets bodies that the regex engine treats as
77
- # anchored to the capture group bounds.
78
- # @return [Rigor::Type, nil] the matching imported refinement
79
- # carrier, or `nil` if `body` is not a recognised shape.
64
+ # @param body [String, nil] a regex sub-pattern, typically the inner body of a
65
+ # `(?<name>body)` named capture. Anchors (`\A`, `\z`, `^`, `$`) are not stripped — the
66
+ # recogniser table targets bodies that the regex engine treats as anchored to the
67
+ # capture group bounds.
68
+ # @return [Rigor::Type, nil] the matching imported refinement carrier, or `nil` if
69
+ # `body` is not a recognised shape.
80
70
  def for_capture_body(body)
81
71
  return nil if body.nil? || body.empty?
82
72
 
@@ -87,11 +77,10 @@ module Rigor
87
77
  Type::Combinator.public_send(rule.last)
88
78
  end
89
79
 
90
- # Filters the bounded-quantifier forms to ones whose lower
91
- # bound is at least 1 and whose upper bound (if any) is at
92
- # least the lower bound. Without this, `\d{0,5}` would be
93
- # accepted even though it admits the empty string, which is
94
- # not a valid `decimal-int-string`.
80
+ # Filters the bounded-quantifier forms to ones whose lower bound is at least 1 and
81
+ # whose upper bound (if any) is at least the lower bound. Without this, `\d{0,5}` would
82
+ # be accepted even though it admits the empty string, which is not a valid
83
+ # `decimal-int-string`.
95
84
  def valid_bounds?(body)
96
85
  m = BOUND_RE.match(body)
97
86
  return true if m.nil?
@@ -4,46 +4,35 @@ require_relative "../type"
4
4
 
5
5
  module Rigor
6
6
  module Builtins
7
- # Static return-type refinements for stdlib (or other built-in)
8
- # methods whose upstream RBS signature is broader than the
9
- # method's documented behaviour and where adding a
10
- # `%a{rigor:v1:return: ...}` annotation upstream is impractical
11
- # (the RBS lives in the vendored `ruby/rbs` submodule).
7
+ # Static return-type refinements for stdlib (or other built-in) methods whose upstream
8
+ # RBS signature is broader than the method's documented behaviour and where adding a
9
+ # `%a{rigor:v1:return: ...}` annotation upstream is impractical (the RBS lives in the
10
+ # vendored `ruby/rbs` submodule).
12
11
  #
13
- # This tier sits in `MethodDispatcher.dispatch` between the
14
- # HKT-builtin tier (which handles parametric / shape-bearing
15
- # returns like `JSON.parse`'s `json::value`) and the RBS
16
- # dispatch tier (the canonical lookup). It is consulted only
17
- # when the method name and arg shape match an entry in the
18
- # table below, so the standard RBS path stays in charge of
19
- # every other call.
12
+ # This tier sits in `MethodDispatcher.dispatch` between the HKT-builtin tier (which
13
+ # handles parametric / shape-bearing returns like `JSON.parse`'s `json::value`) and the
14
+ # RBS dispatch tier (the canonical lookup). It is consulted only when the method name and
15
+ # arg shape match an entry in the table below, so the standard RBS path stays in charge
16
+ # of every other call.
20
17
  #
21
18
  # Match policy:
22
19
  #
23
- # - Entries are keyed by `(owner_class_name, method_name,
24
- # kind)`. `owner_class_name` is the class that *defines*
25
- # the method (e.g., `"Kernel"`), not necessarily the
26
- # receiver's static class Kernel methods are mixed into
27
- # every non-BasicObject class, so a `__dir__` call on any
28
- # receiver routes here.
29
- # - `kind: :both` matches both the singleton-receiver
30
- # shape (`Kernel.__dir__`, `Singleton[Kernel]` receiver)
31
- # AND the instance-receiver shape (an implicit-self call
32
- # like `__dir__` inside any class body, or `obj.__dir__`
33
- # on an instance).
34
- # - `kind: :singleton` / `kind: :instance` restrict the
35
- # match to one of the two shapes.
36
- # - The handler is called with `(arg_types)` so future
37
- # entries can refine based on argument types (e.g. a
38
- # `File.expand_path(string)` entry that returns
20
+ # - Entries are keyed by `(owner_class_name, method_name, kind)`. `owner_class_name` is
21
+ # the class that *defines* the method (e.g., `"Kernel"`), not necessarily the
22
+ # receiver's static class Kernel methods are mixed into every non-BasicObject class,
23
+ # so a `__dir__` call on any receiver routes here.
24
+ # - `kind: :both` matches both the singleton-receiver shape (`Kernel.__dir__`,
25
+ # `Singleton[Kernel]` receiver) AND the instance-receiver shape (an implicit-self call
26
+ # like `__dir__` inside any class body, or `obj.__dir__` on an instance).
27
+ # - `kind: :singleton` / `kind: :instance` restrict the match to one of the two shapes.
28
+ # - The handler is called with `(arg_types)` so future entries can refine based on
29
+ # argument types (e.g. a `File.expand_path(string)` entry that returns
39
30
  # `non-empty-string` regardless of the upstream return).
40
31
  #
41
- # The override fires ABOVE RBS dispatch — if RBS would have
42
- # returned a wider type (`String?` for `Kernel#__dir__`), the
43
- # override returns the refined union (`non-empty-string | nil`)
44
- # instead. RBS erasure of the refined return goes back to the
45
- # original upstream shape, so downstream RBS-shaped observers
46
- # see no difference.
32
+ # The override fires ABOVE RBS dispatch — if RBS would have returned a wider type
33
+ # (`String?` for `Kernel#__dir__`), the override returns the refined union
34
+ # (`non-empty-string | nil`) instead. RBS erasure of the refined return goes back to the
35
+ # original upstream shape, so downstream RBS-shaped observers see no difference.
47
36
  module StaticReturnRefinements
48
37
  # Pre-built carrier reused across calls so structural
49
38
  # equality matches across analyzer invocations.
@@ -56,35 +45,29 @@ module Rigor
56
45
  NON_EMPTY_STRING = Type::Combinator.non_empty_string.freeze
57
46
  private_constant :NON_EMPTY_STRING
58
47
 
59
- # `Kernel#__dir__` returns the canonical directory of the
60
- # source file the call appears in, or `nil` when the file
61
- # is invalid / not available (typically `-e` and similar
62
- # one-liner contexts). When non-nil the value is always a
63
- # filesystem-canonical path — never the empty string — so
64
- # `non-empty-string` is exact.
48
+ # `Kernel#__dir__` returns the canonical directory of the source file the call appears
49
+ # in, or `nil` when the file is invalid / not available (typically `-e` and similar
50
+ # one-liner contexts). When non-nil the value is always a filesystem-canonical path —
51
+ # never the empty string — so `non-empty-string` is exact.
65
52
  KERNEL_DIR = ->(_arg_types) { NON_EMPTY_STRING_OR_NIL }
66
53
  private_constant :KERNEL_DIR
67
54
 
68
- # `File.expand_path(path, ?dir_string)` always returns an
69
- # absolute path string. Even `File.expand_path("")` expands
70
- # to the current working directory's absolute path. The
71
- # upstream RBS row is `(path file_name, ?path dir_string) ->
72
- # String`; the refinement tightens to `non-empty-string`.
73
- #
74
- # `File.dirname(path, ?level)` always returns at least `"."`
75
- # (or `"/"` for absolute roots), so the return is never the
76
- # empty string. Upstream RBS returns `String`; the refinement
55
+ # `File.expand_path(path, ?dir_string)` always returns an absolute path string. Even
56
+ # `File.expand_path("")` expands to the current working directory's absolute path. The
57
+ # upstream RBS row is `(path file_name, ?path dir_string) -> String`; the refinement
77
58
  # tightens to `non-empty-string`.
78
59
  #
79
- # `File.basename` is intentionally NOT refined: it returns
80
- # `""` for `File.basename("")`, so `non-empty-string` would
81
- # be unsound.
60
+ # `File.dirname(path, ?level)` always returns at least `"."` (or `"/"` for absolute
61
+ # roots), so the return is never the empty string. Upstream RBS returns `String`; the
62
+ # refinement tightens to `non-empty-string`.
63
+ #
64
+ # `File.basename` is intentionally NOT refined: it returns `""` for
65
+ # `File.basename("")`, so `non-empty-string` would be unsound.
82
66
  FILE_NON_EMPTY = ->(_arg_types) { NON_EMPTY_STRING }
83
67
  private_constant :FILE_NON_EMPTY
84
68
 
85
- # Frozen ((owner_class_name, method_name, kind) => handler)
86
- # table. The kind tag is `:both`, `:singleton`, or
87
- # `:instance`. New entries SHOULD prefer `:both` unless the
69
+ # Frozen ((owner_class_name, method_name, kind) => handler) table. The kind tag is
70
+ # `:both`, `:singleton`, or `:instance`. New entries SHOULD prefer `:both` unless the
88
71
  # singleton- and instance-side shapes genuinely differ.
89
72
  OVERRIDES = {
90
73
  ["Kernel", :__dir__, :both] => KERNEL_DIR,
@@ -95,21 +78,17 @@ module Rigor
95
78
 
96
79
  # Looks up a refined return type for the given call.
97
80
  #
98
- # @param owner_class_name [String, nil] the class on which
99
- # the method is defined (e.g., `"Kernel"`). Pass `nil`
100
- # when the caller hasn't resolved a defining owner yet
101
- # the lookup will then fall back to matching by
102
- # `(method_name, kind)` against entries whose owner is
103
- # currently in the table.
81
+ # @param owner_class_name [String, nil] the class on which the method is defined
82
+ # (e.g., `"Kernel"`). Pass `nil` when the caller hasn't resolved a defining owner
83
+ # yet the lookup will then fall back to matching by `(method_name, kind)` against
84
+ # entries whose owner is currently in the table.
104
85
  # @param method_name [Symbol]
105
- # @param kind [Symbol] one of `:singleton`, `:instance`. The
106
- # caller passes the shape of the actual call site; the
107
- # table stores `:both` for entries that match either.
108
- # @param arg_types [Array<Rigor::Type>] positional argument
109
- # types. Forwarded to the handler so future entries can
110
- # discriminate on argument shape.
111
- # @return [Rigor::Type, nil] the refined return type, or
112
- # `nil` when no override matches.
86
+ # @param kind [Symbol] one of `:singleton`, `:instance`. The caller passes the shape of
87
+ # the actual call site; the table stores `:both` for entries that match either.
88
+ # @param arg_types [Array<Rigor::Type>] positional argument types. Forwarded to the
89
+ # handler so future entries can discriminate on argument shape.
90
+ # @return [Rigor::Type, nil] the refined return type, or `nil` when no override
91
+ # matches.
113
92
  def self.lookup(owner_class_name:, method_name:, kind:, arg_types: [])
114
93
  return nil if owner_class_name.nil?
115
94
 
@@ -119,27 +98,24 @@ module Rigor
119
98
  handler&.call(arg_types)
120
99
  end
121
100
 
122
- # Indexed view by `(method_name, kind)` — used by the
123
- # dispatcher when the receiver's owner is not yet resolved
124
- # but the method name alone uniquely identifies a stdlib
125
- # override (today: `__dir__` Kernel). The table is small
126
- # and the index rebuild cost trivial, but precomputing keeps
127
- # `dispatch`'s hot path free of an O(n) scan.
101
+ # Indexed view by `(method_name, kind)` — used by the dispatcher when the receiver's
102
+ # owner is not yet resolved but the method name alone uniquely identifies a stdlib
103
+ # override (today: `__dir__` Kernel). The table is small and the index rebuild cost
104
+ # trivial, but precomputing keeps `dispatch`'s hot path free of an O(n) scan.
128
105
  OWNERS_BY_METHOD = OVERRIDES.each_with_object({}) do |((owner, mname, _kind), _h), acc|
129
106
  acc[mname] ||= []
130
107
  acc[mname] << owner unless acc[mname].include?(owner)
131
108
  end.freeze
132
109
  private_constant :OWNERS_BY_METHOD
133
110
 
134
- # @return [Array<String>] the candidate owner class names
135
- # for a bare method-name lookup. Empty when no override
136
- # names this method.
111
+ # @return [Array<String>] the candidate owner class names for a bare method-name
112
+ # lookup. Empty when no override names this method.
137
113
  NO_OWNERS = [].freeze
138
114
  private_constant :NO_OWNERS
139
115
 
140
- # Consulted on every dispatch (`try_static_refinement`); the miss
141
- # case is overwhelmingly common, so share one frozen empty array
142
- # instead of allocating a fresh `[]` per non-refined method.
116
+ # Consulted on every dispatch (`try_static_refinement`); the miss case is overwhelmingly
117
+ # common, so share one frozen empty array instead of allocating a fresh `[]` per
118
+ # non-refined method.
143
119
  def self.owners_for(method_name)
144
120
  OWNERS_BY_METHOD[method_name.to_sym] || NO_OWNERS
145
121
  end
@@ -8,40 +8,28 @@ module Rigor
8
8
  module Cache
9
9
  # Cache invalidation descriptor — the typed-slot schema fixed by
10
10
  # [`docs/design/20260505-cache-slice-taxonomy.md`](../../../docs/design/20260505-cache-slice-taxonomy.md).
11
- # Pure value object: no I/O, no global state, fully immutable
12
- # after construction. The storage layer
13
- # ([`Rigor::Cache::Store`](store.rb), v0.0.8 slice 2) consumes
14
- # descriptors but does not extend them.
11
+ # Pure value object: no I/O, no global state, fully immutable after construction. The storage layer
12
+ # ([`Rigor::Cache::Store`](store.rb), v0.0.8 slice 2) consumes descriptors but does not extend them.
15
13
  #
16
- # The descriptor has six slots (`files`, `gems`, `plugins`,
17
- # `configs`, `dependencies`, `globs`); every slot is an array of typed entries; an empty
18
- # array means "no dependency in this slot". Composition unions
19
- # by key per slot; conflicts on the comparison fields raise
20
- # {Conflict}.
14
+ # The descriptor has six slots (`files`, `gems`, `plugins`, `configs`, `dependencies`, `globs`); every slot is
15
+ # an array of typed entries; an empty array means "no dependency in this slot". Composition unions by key per
16
+ # slot; conflicts on the comparison fields raise {Conflict}.
21
17
  #
22
- # See ADR-2 § "Registration, Configuration, and Caching" for
23
- # the design rationale and ADR-6 for the storage backend
24
- # decisions that consume this schema.
18
+ # See ADR-2 § "Registration, Configuration, and Caching" for the design rationale and ADR-6 for the storage
19
+ # backend decisions that consume this schema.
25
20
  class Descriptor
26
- # Bumped on incompatible schema changes. The storage layer
27
- # mixes this into the cache key, so a bump implicitly
28
- # invalidates every cached value. v2 added the
29
- # `dependencies` slot for ADR-10 per-gem-version cache slice
30
- # invalidation. v3: `RbsLoader.build_env_for` now synthesizes
31
- # `module`s for namespaces a project's `signature_paths:` RBS
32
- # references but never declares, so the marshalled RBS env
33
- # cached by an older Rigor (which would leave those signatures
34
- # inert) MUST be rebuilt for the synthesis to take effect.
35
- # v4: ADR-60 WD3 added the `globs` slot ({GlobEntry}) for the
36
- # record-and-validate plugin-producer cache; the new slot
37
- # changes `#to_canonical_hash` (and is Marshal-dumped inside
38
- # `fetch_or_validate` entry pairs), so entries written by an
39
- # older Rigor must read as misses.
21
+ # Bumped on incompatible schema changes. The storage layer mixes this into the cache key, so a bump
22
+ # implicitly invalidates every cached value. v2 added the `dependencies` slot for ADR-10 per-gem-version
23
+ # cache slice invalidation. v3: `RbsLoader.build_env_for` now synthesizes `module`s for namespaces a
24
+ # project's `signature_paths:` RBS references but never declares, so the marshalled RBS env cached by an
25
+ # older Rigor (which would leave those signatures inert) MUST be rebuilt for the synthesis to take effect.
26
+ # v4: ADR-60 WD3 added the `globs` slot ({GlobEntry}) for the record-and-validate plugin-producer cache;
27
+ # the new slot changes `#to_canonical_hash` (and is Marshal-dumped inside `fetch_or_validate` entry
28
+ # pairs), so entries written by an older Rigor must read as misses.
40
29
  SCHEMA_VERSION = 4
41
30
 
42
- # Per-slot entry value objects. Constructors validate enums /
43
- # required fields and freeze the resulting struct so no caller
44
- # can mutate after the entry is in a Descriptor.
31
+ # Per-slot entry value objects. Constructors validate enums / required fields and freeze the resulting
32
+ # struct so no caller can mutate after the entry is in a Descriptor.
45
33
 
46
34
  class FileEntry
47
35
  include Rigor::ValueSemantics
@@ -125,20 +113,14 @@ module Rigor
125
113
  end
126
114
  end
127
115
 
128
- # Per-(gem, version, mode) row carrying the cache slice
129
- # boundary for ADR-10 dependency-source inference. A
130
- # `bundle update` that bumps a listed gem's pinned version
131
- # produces a different `gem_version` here and therefore a
132
- # fresh cache key — invalidating exactly that gem's slice
133
- # without disturbing other gems' slices or the project's
134
- # own cache.
116
+ # Per-(gem, version, mode) row carrying the cache slice boundary for ADR-10 dependency-source inference.
117
+ # A `bundle update` that bumps a listed gem's pinned version produces a different `gem_version` here and
118
+ # therefore a fresh cache key invalidating exactly that gem's slice without disturbing other gems'
119
+ # slices or the project's own cache.
135
120
  #
136
- # `mode` mirrors the
137
- # [Configuration::Dependencies::VALID_MODES](../configuration/dependencies.rb)
138
- # enum (`:disabled` / `:when_missing` / `:full`); a mode
139
- # change for the same gem also forces invalidation because
140
- # the inferred shapes depend on whether RBS overrides the
141
- # walk.
121
+ # `mode` mirrors the [Configuration::Dependencies::VALID_MODES](../configuration/dependencies.rb) enum
122
+ # (`:disabled` / `:when_missing` / `:full`); a mode change for the same gem also forces invalidation
123
+ # because the inferred shapes depend on whether RBS overrides the walk.
142
124
  class DependencyEntry
143
125
  include Rigor::ValueSemantics
144
126
 
@@ -165,14 +147,11 @@ module Rigor
165
147
  end
166
148
  end
167
149
 
168
- # ADR-60 WD3 — one glob's-worth of watched files, digested as a
169
- # single value so the entry covers content change, addition,
170
- # AND removal in one row: the digest is the SHA-256 over the
171
- # sorted `"<path>\0<sha256-of-content>\n"` rows of every file
172
- # matching `File.join(root, pattern)`. A new file adds a row, a
173
- # deleted file drops one, an edit changes one — all three move
174
- # the digest. {Descriptor#fresh?} re-runs the same computation
175
- # and compares.
150
+ # ADR-60 WD3 — one glob's-worth of watched files, digested as a single value so the entry covers content
151
+ # change, addition, AND removal in one row: the digest is the SHA-256 over the sorted
152
+ # `"<path>\0<sha256-of-content>\n"` rows of every file matching `File.join(root, pattern)`. A new file
153
+ # adds a row, a deleted file drops one, an edit changes one — all three move the digest.
154
+ # {Descriptor#fresh?} re-runs the same computation and compares.
176
155
  class GlobEntry
177
156
  include Rigor::ValueSemantics
178
157
 
@@ -192,13 +171,12 @@ module Rigor
192
171
  new(root: root, pattern: pattern, value: digest_for(root: root, pattern: pattern))
193
172
  end
194
173
 
195
- # The digest the entry's `value` carries. Per-file read
196
- # failures (a file vanishing between the glob and the
197
- # digest) are treated as the file being absent — same
198
- # race posture as {Descriptor#file_entry_fresh?}.
174
+ # The digest the entry's `value` carries. Per-file read failures (a file vanishing between the glob and
175
+ # the digest) are treated as the file being absent — same race posture as
176
+ # {Descriptor#file_entry_fresh?}.
199
177
  def self.digest_for(root:, pattern:)
200
- # Dir.glob returns sorted entries by default (sort: true),
201
- # so the row order — and therefore the digest — is stable.
178
+ # Dir.glob returns sorted entries by default (sort: true), so the row order — and therefore the
179
+ # digest — is stable.
202
180
  rows = Dir.glob(File.join(root, pattern)).filter_map do |path|
203
181
  next nil unless File.file?(path)
204
182
 
@@ -209,9 +187,8 @@ module Rigor
209
187
  Digest::SHA256.hexdigest(rows.join)
210
188
  end
211
189
 
212
- # Composition key — {.compose} unions per (root, pattern)
213
- # slot; two contributions for the same slot must agree on
214
- # the digest or {Conflict} is raised.
190
+ # Composition key — {.compose} unions per (root, pattern) slot; two contributions for the same slot
191
+ # must agree on the digest or {Conflict} is raised.
215
192
  def slot_key
216
193
  "#{root}\0#{pattern}"
217
194
  end
@@ -221,11 +198,9 @@ module Rigor
221
198
  end
222
199
  end
223
200
 
224
- # Raised when {.compose} encounters incompatible entries
225
- # under the same key (file digest mismatch, gem-locked
226
- # disagreement, …). Callers handle the exception by
227
- # invalidating the cache slice rather than choosing one
228
- # contribution silently.
201
+ # Raised when {.compose} encounters incompatible entries under the same key (file digest mismatch,
202
+ # gem-locked disagreement, …). Callers handle the exception by invalidating the cache slice rather than
203
+ # choosing one contribution silently.
229
204
  class Conflict < StandardError; end
230
205
 
231
206
  attr_reader :files, :gems, :plugins, :configs, :dependencies, :globs
@@ -240,17 +215,13 @@ module Rigor
240
215
  freeze
241
216
  end
242
217
 
243
- # ADR-45 — re-validates this descriptor's recorded {FileEntry}s
244
- # against the current filesystem. Used by the record-and-validate
245
- # run-result cache: a value cached alongside its dependency
246
- # descriptor is fresh iff every recorded file still matches. Only
247
- # `files` are checked non-file inputs (config / gems / version)
248
- # belong in the cache *key*, not the validated dependency set so
249
- # a descriptor carrying any non-file slot is never considered fresh
250
- # (it was built wrong for this use). ADR-60 WD3 adds `globs`
251
- # alongside `files` as a re-validatable slot: a {GlobEntry} is
252
- # fresh when re-globbing + re-digesting reproduces its recorded
253
- # value.
218
+ # ADR-45 — re-validates this descriptor's recorded {FileEntry}s against the current filesystem. Used by
219
+ # the record-and-validate run-result cache: a value cached alongside its dependency descriptor is fresh
220
+ # iff every recorded file still matches. Only `files` are checked — non-file inputs (config / gems /
221
+ # version) belong in the cache *key*, not the validated dependency set — so a descriptor carrying any
222
+ # non-file slot is never considered fresh (it was built wrong for this use). ADR-60 WD3 adds `globs`
223
+ # alongside `files` as a re-validatable slot: a {GlobEntry} is fresh when re-globbing + re-digesting
224
+ # reproduces its recorded value.
254
225
  def fresh?
255
226
  return false unless gems.empty? && plugins.empty? && configs.empty? && dependencies.empty?
256
227
 
@@ -258,18 +229,15 @@ module Rigor
258
229
  globs.all? { |entry| glob_entry_fresh?(entry) }
259
230
  end
260
231
 
261
- # File-comparator strictness ordering. `:digest` is strictest
262
- # (deterministic across machines); `:mtime` is cheaper but
263
- # local; `:exists` is the weakest signal. When two
264
- # contributors disagree on the comparator for the same
265
- # `path`, the stricter one wins.
232
+ # File-comparator strictness ordering. `:digest` is strictest (deterministic across machines); `:mtime`
233
+ # is cheaper but local; `:exists` is the weakest signal. When two contributors disagree on the
234
+ # comparator for the same `path`, the stricter one wins.
266
235
  COMPARATOR_STRICTNESS = { digest: 2, mtime: 1, exists: 0 }.freeze
267
236
  private_constant :COMPARATOR_STRICTNESS
268
237
 
269
- # Composes any number of descriptors into a single descriptor
270
- # whose slots are the union of the inputs' slots. Conflicts
271
- # raise {Conflict}; idempotent contributions (same key, same
272
- # value) collapse to a single entry.
238
+ # Composes any number of descriptors into a single descriptor whose slots are the union of the inputs'
239
+ # slots. Conflicts raise {Conflict}; idempotent contributions (same key, same value) collapse to a
240
+ # single entry.
273
241
  def self.compose(*descriptors)
274
242
  return new if descriptors.empty?
275
243
 
@@ -296,9 +264,8 @@ module Rigor
296
264
  Digest::SHA256.hexdigest(JSON.generate(payload))
297
265
  end
298
266
 
299
- # Canonical UTF-8 JSON serialisation. Slots appear in
300
- # lexicographic order; entries are sorted by their key field
301
- # so two equivalent descriptors produce identical bytes.
267
+ # Canonical UTF-8 JSON serialisation. Slots appear in lexicographic order; entries are sorted by their
268
+ # key field so two equivalent descriptors produce identical bytes.
302
269
  def to_canonical_bytes
303
270
  JSON.generate(to_canonical_hash).b
304
271
  end
@@ -325,10 +292,8 @@ module Rigor
325
292
  end
326
293
 
327
294
  class << self
328
- # Recursively coerces a Ruby value into a JSON-canonical
329
- # structure: hash keys are stringified and sorted; arrays
330
- # preserve order; symbols stringify; everything else is
331
- # JSON-renderable.
295
+ # Recursively coerces a Ruby value into a JSON-canonical structure: hash keys are stringified and
296
+ # sorted; arrays preserve order; symbols stringify; everything else is JSON-renderable.
332
297
  def canonicalize_value(value)
333
298
  case value
334
299
  when Hash
@@ -360,9 +325,8 @@ module Rigor
360
325
  false
361
326
  end
362
327
 
363
- # ADR-60 WD3 — re-runs the entry's glob + digest and compares
364
- # against the recorded value. Any failure reads as stale
365
- # (recompute), never a crash.
328
+ # ADR-60 WD3 — re-runs the entry's glob + digest and compares against the recorded value. Any failure
329
+ # reads as stale (recompute), never a crash.
366
330
  def glob_entry_fresh?(entry)
367
331
  GlobEntry.digest_for(root: entry.root, pattern: entry.pattern) == entry.value
368
332
  rescue StandardError