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
@@ -6,32 +6,23 @@ module Rigor
6
6
  module Inference
7
7
  # Shared dispatch table for `Rigor::Type#accepts(other, mode:)`.
8
8
  #
9
- # The acceptance query answers "is `other` passable to `self` at a
10
- # method-parameter or assignment boundary?". It uses gradual-typing
11
- # rules from docs/type-specification/value-lattice.md and the
9
+ # The acceptance query answers "is `other` passable to `self` at a method-parameter or assignment
10
+ # boundary?". It uses gradual-typing rules from docs/type-specification/value-lattice.md and the
12
11
  # acceptance contract in docs/internal-spec/internal-type-api.md.
13
12
  #
14
- # Each concrete type's `accepts` method delegates here so the
15
- # case-analysis stays in one place. Type instances remain thin value
16
- # objects; routing logic lives in the inference layer.
13
+ # Each concrete type's `accepts` method delegates here so the case-analysis stays in one place. Type
14
+ # instances remain thin value objects; routing logic lives in the inference layer.
17
15
  #
18
- # Slice 4 phase 2c implements the `:gradual` mode in full and
19
- # reserves `:strict` for later slices (the entry point raises
20
- # ArgumentError on strict for now). The table covers the leaf and
21
- # combinator types added through phase 2b: Top, Bot, Dynamic,
22
- # Nominal, Singleton, Constant, and Union.
16
+ # Slice 4 phase 2c implements the `:gradual` mode in full and reserves `:strict` for later slices (the
17
+ # entry point raises ArgumentError on strict for now). The table covers the leaf and combinator types
18
+ # added through phase 2b: Top, Bot, Dynamic, Nominal, Singleton, Constant, and Union.
23
19
  #
24
- # Slice 5 registers the shape carriers `Tuple` and `HashShape`.
25
- # Tuple/HashShape acceptance compares per-position element types
26
- # (covariant) and per-key entry types (depth covariant), including
27
- # HashShape required/optional/closed-extra-key policy. When the
28
- # receiver side is a
29
- # generic `Nominal[Array, [E]]` or `Nominal[Hash, [K, V]]` the
30
- # shape is projected to its underlying nominal so the existing
31
- # generic-acceptance pipeline continues to apply; the converse
32
- # direction (a Tuple receiver accepting a generic Array) stays
33
- # conservative because the analyzer cannot verify arity from a
34
- # raw nominal alone.
20
+ # Slice 5 registers the shape carriers `Tuple` and `HashShape`. Tuple/HashShape acceptance compares
21
+ # per-position element types (covariant) and per-key entry types (depth covariant), including HashShape
22
+ # required/optional/closed-extra-key policy. When the receiver side is a generic `Nominal[Array, [E]]` or
23
+ # `Nominal[Hash, [K, V]]` the shape is projected to its underlying nominal so the existing
24
+ # generic-acceptance pipeline continues to apply; the converse direction (a Tuple receiver accepting a
25
+ # generic Array) stays conservative because the analyzer cannot verify arity from a raw nominal alone.
35
26
  # rubocop:disable Metrics/ModuleLength
36
27
  module Acceptance
37
28
  module_function
@@ -48,13 +39,10 @@ module Rigor
48
39
  return Type::AcceptsResult.yes(mode: mode, reasons: "gradual: Dynamic[T] passes any boundary")
49
40
  end
50
41
 
51
- # Structural equality short-circuit. Two identical carriers
52
- # describe the same value set, so they always accept each
53
- # other. This is sound for any mode and covers cases where
54
- # neither side has a per-class rule for the other's exact
55
- # carrier kind (the canonical example is
56
- # `Intersection.accepts(Intersection)`, where the disjunction
57
- # rule below would otherwise reject equal-but-narrow LHSes).
42
+ # Structural equality short-circuit. Two identical carriers describe the same value set, so they always
43
+ # accept each other. This is sound for any mode and covers cases where neither side has a per-class rule
44
+ # for the other's exact carrier kind (the canonical example is `Intersection.accepts(Intersection)`,
45
+ # where the disjunction rule below would otherwise reject equal-but-narrow LHSes).
58
46
  return Type::AcceptsResult.yes(mode: mode, reasons: "structural equality") if self_type == other_type
59
47
 
60
48
  return accepts_union_other(self_type, other_type, mode) if other_type.is_a?(Type::Union)
@@ -63,10 +51,9 @@ module Rigor
63
51
  accepts_one(self_type, other_type, mode)
64
52
  end
65
53
 
66
- # Hash dispatch keeps `accepts_one` linear and lets future shape
67
- # carriers register their handlers without re-tripping the
68
- # cyclomatic budget on a growing `case` arm. Anonymous Type
69
- # subclasses are not expected.
54
+ # Hash dispatch keeps `accepts_one` linear and lets future shape carriers register their handlers
55
+ # without re-tripping the cyclomatic budget on a growing `case` arm. Anonymous Type subclasses are not
56
+ # expected.
70
57
  TYPE_HANDLERS = {
71
58
  Type::Top => :accepts_top,
72
59
  Type::Bot => :accepts_bot,
@@ -107,9 +94,8 @@ module Rigor
107
94
  )
108
95
  end
109
96
 
110
- # Dynamic[T] in gradual mode is liberally inhabited; any concrete
111
- # other type is accepted because gradual consistency permits the
112
- # crossing. (Other being Dynamic was handled in {.accepts}.)
97
+ # Dynamic[T] in gradual mode is liberally inhabited; any concrete other type is accepted because
98
+ # gradual consistency permits the crossing. (Other being Dynamic was handled in {.accepts}.)
113
99
  def accepts_dynamic(_self_type, _other_type, mode)
114
100
  Type::AcceptsResult.yes(
115
101
  mode: mode,
@@ -117,10 +103,8 @@ module Rigor
117
103
  )
118
104
  end
119
105
 
120
- # Union[A,B].accepts(X) iff some member accepts X. Yes wins as
121
- # soon as we find one; otherwise we surface "maybe" only when at
122
- # least one member returned maybe (cannot rule out coverage),
123
- # else "no".
106
+ # Union[A,B].accepts(X) iff some member accepts X. Yes wins as soon as we find one; otherwise we
107
+ # surface "maybe" only when at least one member returned maybe (cannot rule out coverage), else "no".
124
108
  def accepts_union_self(union, other_type, mode)
125
109
  results = union.members.map { |m| accepts(m, other_type, mode: mode) }
126
110
 
@@ -141,12 +125,9 @@ module Rigor
141
125
  end
142
126
  end
143
127
 
144
- # self.accepts(Intersection[Y, Z]) iff self accepts at least
145
- # one Y_i. Disjunction across members because the intersection
146
- # is the meet of its members' value sets, so containment in
147
- # any one member implies containment of the whole
148
- # intersection. Symmetric counterpart to
149
- # `accepts_union_other`.
128
+ # self.accepts(Intersection[Y, Z]) iff self accepts at least one Y_i. Disjunction across members
129
+ # because the intersection is the meet of its members' value sets, so containment in any one member
130
+ # implies containment of the whole intersection. Symmetric counterpart to `accepts_union_other`.
150
131
  def accepts_intersection_other(self_type, intersection, mode)
151
132
  results = intersection.members.map { |m| accepts(self_type, m, mode: mode) }
152
133
 
@@ -162,9 +143,8 @@ module Rigor
162
143
  end
163
144
  end
164
145
 
165
- # self.accepts(Union[Y, Z]) iff self accepts every Y_i. Strict
166
- # AND across members: any "no" turns the whole result no, any
167
- # "maybe" without a "no" gives maybe, all "yes" gives yes.
146
+ # self.accepts(Union[Y, Z]) iff self accepts every Y_i. Strict AND across members: any "no" turns the
147
+ # whole result no, any "maybe" without a "no" gives maybe, all "yes" gives yes.
168
148
  def accepts_union_other(self_type, union, mode)
169
149
  results = union.members.map { |m| accepts(self_type, m, mode: mode) }
170
150
 
@@ -185,10 +165,9 @@ module Rigor
185
165
  end
186
166
  end
187
167
 
188
- # Singleton[C] only accepts another Singleton[D] where D is a
189
- # subclass of (or equal to) C. Any other carrier (instance,
190
- # constant, ...) is no, because the singleton type's inhabitants
191
- # are the class objects themselves.
168
+ # Singleton[C] only accepts another Singleton[D] where D is a subclass of (or equal to) C. Any other
169
+ # carrier (instance, constant, ...) is no, because the singleton type's inhabitants are the class
170
+ # objects themselves.
192
171
  def accepts_singleton(self_type, other_type, mode)
193
172
  unless other_type.is_a?(Type::Singleton)
194
173
  return Type::AcceptsResult.no(
@@ -206,17 +185,15 @@ module Rigor
206
185
  end
207
186
 
208
187
  # Nominal[C] accepts:
209
- # - Nominal[D] when D <= C (Ruby class subtype) and the
210
- # `type_args` are compatible (see {#accepts_nominal_args});
211
- # - Constant[v] when v.is_a?(klass(C)). The type_args of self
212
- # are ignored here because a Constant carries a concrete
213
- # value, not a generic instantiation, and the analyzer has no
214
- # way to refute the args from a literal alone.
215
- # - Tuple[*] when self is the Array (or a supertype) family.
216
- # The Tuple is projected to `Nominal[Array, [union(elements)]]`
217
- # so the existing generic-arg machinery handles it.
218
- # - HashShape{*} when self is the Hash (or a supertype) family,
219
- # projected to `Nominal[Hash, [union(keys), union(values)]]`.
188
+ # - Nominal[D] when D <= C (Ruby class subtype) and the `type_args` are compatible (see
189
+ # {#accepts_nominal_args});
190
+ # - Constant[v] when v.is_a?(klass(C)). The type_args of self are ignored here because a Constant
191
+ # carries a concrete value, not a generic instantiation, and the analyzer has no way to refute the
192
+ # args from a literal alone.
193
+ # - Tuple[*] when self is the Array (or a supertype) family. The Tuple is projected to
194
+ # `Nominal[Array, [union(elements)]]` so the existing generic-arg machinery handles it.
195
+ # - HashShape{*} when self is the Hash (or a supertype) family, projected to
196
+ # `Nominal[Hash, [union(keys), union(values)]]`.
220
197
  # - Singleton: never (wrong value kind).
221
198
  def accepts_nominal(self_type, other_type, mode)
222
199
  case other_type
@@ -228,13 +205,10 @@ module Rigor
228
205
  end
229
206
  end
230
207
 
231
- # Tail of `accepts_nominal` that handles structural shape
232
- # carriers (`Tuple` / `HashShape`) and refinement carriers
233
- # (`Difference` / `Refined`). Each branch projects the
234
- # other-side carrier to the nominal layer it sits above
235
- # and re-runs acceptance — soundness follows because the
236
- # carrier's value set is contained in the projected
237
- # nominal's value set.
208
+ # Tail of `accepts_nominal` that handles structural shape carriers (`Tuple` / `HashShape`) and
209
+ # refinement carriers (`Difference` / `Refined`). Each branch projects the other-side carrier to the
210
+ # nominal layer it sits above and re-runs acceptance — soundness follows because the carrier's value
211
+ # set is contained in the projected nominal's value set.
238
212
  def accepts_nominal_from_shape(self_type, other_type, mode)
239
213
  case other_type
240
214
  when Type::Tuple
@@ -244,26 +218,20 @@ module Rigor
244
218
  accepts(self_type, project_hash_shape_to_nominal(other_type), mode: mode)
245
219
  .with_reason("projected HashShape to Nominal[Hash]")
246
220
  when Type::DataInstance
247
- # ADR-48: a class-tagged member instance is exactly one value of
248
- # its tagging class, so it projects to that class's nominal (the
249
- # anonymous local-bound form projects to `Data` itself).
221
+ # ADR-48: a class-tagged member instance is exactly one value of its tagging class, so it projects
222
+ # to that class's nominal (the anonymous local-bound form projects to `Data` itself).
250
223
  accepts(self_type, project_data_instance_to_nominal(other_type), mode: mode)
251
224
  .with_reason("projected DataInstance to Nominal[#{other_type.class_name || 'Data'}]")
252
225
  when Type::StructInstance
253
- # ADR-48 Struct follow-up: same projection as DataInstance — a
254
- # class-tagged Struct value is exactly one value of its tagging
255
- # class (the anonymous form projects to `Struct` itself).
226
+ # ADR-48 Struct follow-up: same projection as DataInstance — a class-tagged Struct value is exactly
227
+ # one value of its tagging class (the anonymous form projects to `Struct` itself).
256
228
  accepts(self_type, project_struct_instance_to_nominal(other_type), mode: mode)
257
229
  .with_reason("projected StructInstance to Nominal[#{other_type.class_name || 'Struct'}]")
258
230
  when Type::Difference, Type::Refined
259
- # A refinement carrier's value set is a subset of its
260
- # base. So if `self` (Nominal) accepts the base, it
261
- # also accepts the refinement; if it rejects the
262
- # base, it cannot accept any subset of it. Forward
263
- # through to the base nominal so the standard subtype
264
- # check applies. The recursion is bounded because
265
- # every refinement carrier's `base` is closer to the
266
- # nominal layer.
231
+ # A refinement carrier's value set is a subset of its base. So if `self` (Nominal) accepts the
232
+ # base, it also accepts the refinement; if it rejects the base, it cannot accept any subset of
233
+ # it. Forward through to the base nominal so the standard subtype check applies. The recursion
234
+ # is bounded because every refinement carrier's `base` is closer to the nominal layer.
267
235
  accepts(self_type, other_type.base, mode: mode)
268
236
  .with_reason("projected #{other_type.class.name.split('::').last} to its base")
269
237
  else
@@ -274,9 +242,9 @@ module Rigor
274
242
  end
275
243
  end
276
244
 
277
- # `Nominal[Integer]` (and anything Integer is-a, like Numeric) accepts
278
- # any `IntegerRange`; nothing else does. Argument-bearing `Nominal`s
279
- # never accept `IntegerRange` because IntegerRange has no type args.
245
+ # `Nominal[Integer]` (and anything Integer is-a, like Numeric) accepts any `IntegerRange`; nothing
246
+ # else does. Argument-bearing `Nominal`s never accept `IntegerRange` because IntegerRange has no
247
+ # type args.
280
248
  INTEGER_NOMINAL_ANCESTORS = %w[Integer Numeric Comparable Object BasicObject].freeze
281
249
  private_constant :INTEGER_NOMINAL_ANCESTORS
282
250
 
@@ -301,18 +269,12 @@ module Rigor
301
269
  end
302
270
  end
303
271
 
304
- # v0.0.2 — meta-type rule. A `Singleton[T]` is the
305
- # class object for `T`, so it is an instance of
306
- # `Class` (when `T` is a class) and always an instance
307
- # of `Module`. Without this rule a method whose
308
- # parameter is typed `Class | Module` would reject
309
- # every `is_a?(SomeClass)` call and similar
310
- # introspection patterns. The rule conservatively
311
- # answers `:yes` for `Module` (every singleton is at
312
- # least a Module) and for `Class` / `Object` /
313
- # `BasicObject` (the class object inherits from
314
- # those). Other Nominals fall through to the default
315
- # `:no`.
272
+ # v0.0.2 — meta-type rule. A `Singleton[T]` is the class object for `T`, so it is an instance of
273
+ # `Class` (when `T` is a class) and always an instance of `Module`. Without this rule a method whose
274
+ # parameter is typed `Class | Module` would reject every `is_a?(SomeClass)` call and similar
275
+ # introspection patterns. The rule conservatively answers `:yes` for `Module` (every singleton is at
276
+ # least a Module) and for `Class` / `Object` / `BasicObject` (the class object inherits from those).
277
+ # Other Nominals fall through to the default `:no`.
316
278
  META_NOMINALS_FROM_SINGLETON = %w[Module Class Object BasicObject].freeze
317
279
  private_constant :META_NOMINALS_FROM_SINGLETON
318
280
 
@@ -339,27 +301,21 @@ module Rigor
339
301
  )
340
302
  return class_result if class_result.no?
341
303
 
342
- # Parametrized-ancestor projection. When `actual <:= target`
343
- # holds at the class level but the type-arg arities differ,
344
- # the actual's parametrization has to be projected into the
345
- # target's view before the element-wise covariance check.
346
- # The canonical case is `Hash[K, V] <:= Enumerable[[K, V]]`:
347
- # Hash carries two type_args, Enumerable carries one, and
348
- # the inherited parametrization at the Enumerable boundary
349
- # is `Tuple[K, V]`. RBS encodes this as
350
- # `include Enumerable[[K, V]]` in `Hash`'s definition.
304
+ # Parametrized-ancestor projection. When `actual <:= target` holds at the class level but the
305
+ # type-arg arities differ, the actual's parametrization has to be projected into the target's view
306
+ # before the element-wise covariance check. The canonical case is `Hash[K, V] <:= Enumerable[[K,
307
+ # V]]`: Hash carries two type_args, Enumerable carries one, and the inherited parametrization at
308
+ # the Enumerable boundary is `Tuple[K, V]`. RBS encodes this as `include Enumerable[[K, V]]` in
309
+ # `Hash`'s definition.
351
310
  projected_other = project_to_target_arity(self_type, other_type) || other_type
352
311
  args_result = accepts_nominal_args(self_type, projected_other, mode)
353
312
  combine_results(class_result, args_result, mode)
354
313
  end
355
314
 
356
- # Returns `other_type` rewritten so its type_args have the
357
- # same arity as `self_type.type_args`, or `nil` if no
358
- # projection is known. Today only the Hash → Enumerable
359
- # projection is hand-rolled; a general RBS-driven
360
- # implementation that consults `definition.ancestors[i].args`
361
- # for arbitrary subclass / module-include relations is the
362
- # principled follow-up.
315
+ # Returns `other_type` rewritten so its type_args have the same arity as `self_type.type_args`, or
316
+ # `nil` if no projection is known. Today only the Hash → Enumerable projection is hand-rolled; a
317
+ # general RBS-driven implementation that consults `definition.ancestors[i].args` for arbitrary
318
+ # subclass / module-include relations is the principled follow-up.
363
319
  def project_to_target_arity(self_type, other_type)
364
320
  return nil if self_type.type_args.size == other_type.type_args.size
365
321
  return nil if self_type.type_args.empty? || other_type.type_args.empty?
@@ -410,13 +366,10 @@ module Rigor
410
366
  )
411
367
  end
412
368
 
413
- # Slice 4 phase 2d generic acceptance. Type arguments are
414
- # treated covariantly element-wise (gradual default; declared
415
- # variance lands in Slice 5+). When either side has no
416
- # type_args we are lenient: the absent side is the "raw" form
417
- # that historically meant "any instantiation", so we keep
418
- # backward compatibility for call sites that have not yet
419
- # learned to carry generics.
369
+ # Slice 4 phase 2d generic acceptance. Type arguments are treated covariantly element-wise (gradual
370
+ # default; declared variance lands in Slice 5+). When either side has no type_args we are lenient:
371
+ # the absent side is the "raw" form that historically meant "any instantiation", so we keep backward
372
+ # compatibility for call sites that have not yet learned to carry generics.
420
373
  def accepts_nominal_args(self_type, other_type, mode)
421
374
  shortcut = nominal_args_shortcut(self_type, other_type, mode)
422
375
  return shortcut if shortcut
@@ -427,9 +380,8 @@ module Rigor
427
380
  combine_arg_results(per_arg, mode)
428
381
  end
429
382
 
430
- # Returns an `AcceptsResult` for the universal short-circuits
431
- # (raw self, raw other, arity mismatch) or `nil` when the full
432
- # element-wise check still has to run.
383
+ # Returns an `AcceptsResult` for the universal short-circuits (raw self, raw other, arity mismatch)
384
+ # or `nil` when the full element-wise check still has to run.
433
385
  def nominal_args_shortcut(self_type, other_type, mode)
434
386
  return Type::AcceptsResult.yes(mode: mode, reasons: "self has no type_args") if self_type.type_args.empty?
435
387
  if other_type.type_args.empty?
@@ -468,14 +420,11 @@ module Rigor
468
420
  ruby_class = resolve_class(self_type.class_name)
469
421
  return constant_is_a_result(ruby_class, constant, self_type, mode) if ruby_class
470
422
 
471
- # The host process may not have required the constant's
472
- # declared self_type (e.g. `BigDecimal` since Ruby 3.4
473
- # is no longer a default gem). Fall back to inspecting
474
- # the value's own class ancestor chain always loadable
475
- # because the value already exists. Required for
476
- # OverloadSelector to reject `Integer#+(BigDecimal) ->
477
- # BigDecimal` overloads contributed by `bigdecimal`'s
478
- # RBS reopening when the actual arg is a Constant<Integer>.
423
+ # The host process may not have required the constant's declared self_type (e.g. `BigDecimal`
424
+ # since Ruby 3.4 is no longer a default gem). Fall back to inspecting the value's own class
425
+ # ancestor chain always loadable because the value already exists. Required for OverloadSelector
426
+ # to reject `Integer#+(BigDecimal) -> BigDecimal` overloads contributed by `bigdecimal`'s RBS
427
+ # reopening when the actual arg is a Constant<Integer>.
479
428
  ancestor_names = constant.value.class.ancestors.map(&:name)
480
429
  if ancestor_names.include?(self_type.class_name)
481
430
  Type::AcceptsResult.yes(
@@ -518,9 +467,8 @@ module Rigor
518
467
  # IntegerRange[a..b] accepts:
519
468
  # - Constant[n] where n is an Integer covered by [a..b];
520
469
  # - IntegerRange[c..d] where [c..d] ⊆ [a..b];
521
- # - Nominal[Integer] only when self is the universal range
522
- # (`int<min, max>`), since otherwise an arbitrary Integer
523
- # could fall outside the bound.
470
+ # - Nominal[Integer] only when self is the universal range (`int<min, max>`), since otherwise an
471
+ # arbitrary Integer could fall outside the bound.
524
472
  # Anything else is rejected.
525
473
  def accepts_integer_range(self_type, other_type, mode)
526
474
  case other_type
@@ -594,19 +542,13 @@ module Rigor
594
542
  end
595
543
  end
596
544
 
597
- # `Difference[base, removed]` accepts another type X when
598
- # the base accepts X *and* X's value set is provably
599
- # disjoint from `removed`. The disjointness test is the
600
- # subtle part it is NOT the same as `removed.accepts(X)`,
601
- # because `Nominal[String]` includes `""` even though
602
- # `Constant[""]` does not "accept" `Nominal[String]`.
603
- # The conservative rule here: we can prove disjointness
604
- # only when X is itself a `Constant` carrier (compare
605
- # values directly) or another `Difference` with the same
606
- # removed value (already exhibits the disjointness). Any
607
- # other shape — Nominal, Union, IntegerRange — could
608
- # overlap the removed value, so the difference rejects
609
- # it under gradual mode.
545
+ # `Difference[base, removed]` accepts another type X when the base accepts X *and* X's value set is
546
+ # provably disjoint from `removed`. The disjointness test is the subtle part — it is NOT the same as
547
+ # `removed.accepts(X)`, because `Nominal[String]` includes `""` even though `Constant[""]` does not
548
+ # "accept" `Nominal[String]`. The conservative rule here: we can prove disjointness only when X is
549
+ # itself a `Constant` carrier (compare values directly) or another `Difference` with the same
550
+ # removed value (already exhibits the disjointness). Any other shape — Nominal, Union, IntegerRange
551
+ # could overlap the removed value, so the difference rejects it under gradual mode.
610
552
  def accepts_difference(self_type, other_type, mode)
611
553
  base_result = accepts(self_type.base, other_type, mode: mode)
612
554
  return base_result if base_result.no?
@@ -626,43 +568,31 @@ module Rigor
626
568
  when Type::Constant
627
569
  !(removed.is_a?(Type::Constant) && removed.value == other_type.value)
628
570
  when Type::Difference
629
- # `Difference[A, R].accepts(Difference[B, R])`: the
630
- # other carrier already excludes `R` at its difference
631
- # layer, so the disjointness is exhibited regardless of
632
- # how `B` (its base) relates to `R`. We do NOT recurse
633
- # into `other_type.base` because that would always fail
634
- # (a Nominal base contains the removed value).
571
+ # `Difference[A, R].accepts(Difference[B, R])`: the other carrier already excludes `R` at its
572
+ # difference layer, so the disjointness is exhibited regardless of how `B` (its base) relates to
573
+ # `R`. We do NOT recurse into `other_type.base` because that would always fail (a Nominal base
574
+ # contains the removed value).
635
575
  other_type.removed == removed
636
576
  when Type::Intersection
637
- # Disjointness is monotonic over Intersection: if any
638
- # member is provably disjoint from `removed`, the meet
639
- # is too.
577
+ # Disjointness is monotonic over Intersection: if any member is provably disjoint from
578
+ # `removed`, the meet is too.
640
579
  other_type.members.any? { |m| provably_disjoint_from_removed?(m, removed) }
641
580
  end
642
581
  end
643
582
 
644
- # `Refined[base, predicate]` accepts another type X when
645
- # the base accepts the *base* of X *and* X is provably
646
- # contained in the predicate's value set. The base
647
- # check is delegated to `accepts(self.base, X.base)`
648
- # so handlers like `accepts_nominal` see Nominal-vs-
649
- # Nominal and return their normal answer (the inner
650
- # `accepts_nominal` does not register `Refined` /
651
- # `Difference` as direct other-shapes — projecting to
652
- # the base is what makes the comparison meaningful).
583
+ # `Refined[base, predicate]` accepts another type X when the base accepts the *base* of X *and* X is
584
+ # provably contained in the predicate's value set. The base check is delegated to
585
+ # `accepts(self.base, X.base)` so handlers like `accepts_nominal` see Nominal-vs-Nominal and return
586
+ # their normal answer (the inner `accepts_nominal` does not register `Refined` / `Difference` as
587
+ # direct other-shapes projecting to the base is what makes the comparison meaningful).
653
588
  #
654
- # Provability rules in gradual mode (the conservative
655
- # analogue of `accepts_difference`):
589
+ # Provability rules in gradual mode (the conservative analogue of `accepts_difference`):
656
590
  #
657
- # - X is a `Refined` with the *same* predicate_id —
658
- # exact predicate match, accept.
659
- # - X is a `Constant` whose value the predicate's
660
- # recogniser accepts — the value is statically
591
+ # - X is a `Refined` with the *same* predicate_id — exact predicate match, accept.
592
+ # - X is a `Constant` whose value the predicate's recogniser accepts — the value is statically
661
593
  # contained, accept. A recognised non-match is `:no`.
662
- # - Anything else (Nominal, Union, IntegerRange,
663
- # Difference) predicate-subset cannot be proven
664
- # without a runtime test, so reject under gradual
665
- # mode rather than degrade to `:maybe`. Mirrors the
594
+ # - Anything else (Nominal, Union, IntegerRange, Difference) — predicate-subset cannot be proven
595
+ # without a runtime test, so reject under gradual mode rather than degrade to `:maybe`. Mirrors the
666
596
  # `accepts_difference` policy.
667
597
  def accepts_refined(self_type, other_type, mode)
668
598
  case other_type
@@ -717,13 +647,10 @@ module Rigor
717
647
  )
718
648
  end
719
649
 
720
- # `Intersection[M1, M2, …]` accepts X iff *every* member
721
- # accepts X the meet of value sets is contained iff the
722
- # candidate is contained in each. Conjunctive combine: any
723
- # `:no` makes the result `:no`, any `:maybe` without a
724
- # `:no` makes the result `:maybe`, all `:yes` makes the
725
- # result `:yes`. The 0-member case is unreachable because
726
- # `Combinator.intersection` collapses empty intersections
650
+ # `Intersection[M1, M2, …]` accepts X iff *every* member accepts X — the meet of value sets is
651
+ # contained iff the candidate is contained in each. Conjunctive combine: any `:no` makes the result
652
+ # `:no`, any `:maybe` without a `:no` makes the result `:maybe`, all `:yes` makes the result `:yes`.
653
+ # The 0-member case is unreachable because `Combinator.intersection` collapses empty intersections
727
654
  # to `Top`.
728
655
  def accepts_intersection(self_type, other_type, mode)
729
656
  per_member = self_type.members.map { |m| accepts(m, other_type, mode: mode) }
@@ -748,9 +675,8 @@ module Rigor
748
675
  end
749
676
  end
750
677
 
751
- # Constant[v] accepts only Constant[v'] with structurally equal
752
- # value. Any other type is rejected (modulo the universal
753
- # Bot/Dynamic short-circuits already applied upstream).
678
+ # Constant[v] accepts only Constant[v'] with structurally equal value. Any other type is rejected
679
+ # (modulo the universal Bot/Dynamic short-circuits already applied upstream).
754
680
  def accepts_constant(self_type, other_type, mode)
755
681
  if other_type.is_a?(Type::Constant) && self_type == other_type
756
682
  Type::AcceptsResult.yes(mode: mode, reasons: "structural literal match")
@@ -763,10 +689,8 @@ module Rigor
763
689
  end
764
690
 
765
691
  # Tuple[A1..An] accepts:
766
- # - Tuple[B1..Bn] when arities match and each Ai accepts Bi
767
- # (covariant per-position).
768
- # - Anything else: no (we cannot prove the arity from a generic
769
- # nominal alone).
692
+ # - Tuple[B1..Bn] when arities match and each Ai accepts Bi (covariant per-position).
693
+ # - Anything else: no (we cannot prove the arity from a generic nominal alone).
770
694
  def accepts_tuple(self_type, other_type, mode)
771
695
  unless other_type.is_a?(Type::Tuple)
772
696
  return Type::AcceptsResult.no(
@@ -788,14 +712,11 @@ module Rigor
788
712
  combine_arg_results(per_element, mode)
789
713
  end
790
714
 
791
- # HashShape{k1: T1, ...} accepts another HashShape when every
792
- # required key of self is required on the other side and Ti
793
- # accepts Ui (depth covariant). Optional keys may be absent on
794
- # the other side; when present, their values are checked. A
795
- # closed self rejects known or possible extra keys. Other
796
- # types are rejected; the converse direction (a Nominal
797
- # accepting a HashShape) is handled by `accepts_nominal` via
798
- # projection.
715
+ # HashShape{k1: T1, ...} accepts another HashShape when every required key of self is required on
716
+ # the other side and Ti accepts Ui (depth covariant). Optional keys may be absent on the other side;
717
+ # when present, their values are checked. A closed self rejects known or possible extra keys. Other
718
+ # types are rejected; the converse direction (a Nominal accepting a HashShape) is handled by
719
+ # `accepts_nominal` via projection.
799
720
  def accepts_hash_shape(self_type, other_type, mode)
800
721
  unless other_type.is_a?(Type::HashShape)
801
722
  return Type::AcceptsResult.no(
@@ -832,30 +753,23 @@ module Rigor
832
753
  Type::AcceptsResult.no(mode: mode, reasons: reason)
833
754
  end
834
755
 
835
- # Uses Ruby's actual class hierarchy via Object.const_get to answer
836
- # "is D a subclass of C?" for core, stdlib, and application classes.
837
- # When either name fails to resolve we surface "maybe": the caller
838
- # (overload selector) treats yes/maybe identically, so the conservative
839
- # answer keeps overload coverage intact. RbsHierarchy exists but this
840
- # path does not yet consult it; migration to an RBS-driven lookup
841
- # is deferred.
756
+ # Uses Ruby's actual class hierarchy via Object.const_get to answer "is D a subclass of C?" for core,
757
+ # stdlib, and application classes. When either name fails to resolve we surface "maybe": the caller
758
+ # (overload selector) treats yes/maybe identically, so the conservative answer keeps overload
759
+ # coverage intact. RbsHierarchy exists but this path does not yet consult it; migration to an
760
+ # RBS-driven lookup is deferred.
842
761
  def class_subtype_result(target_name:, actual_name:, mode:, kind:)
843
762
  return Type::AcceptsResult.yes(mode: mode, reasons: "exact name match") if target_name == actual_name
844
763
 
845
764
  target_class = resolve_class(target_name)
846
765
  actual_class = resolve_class(actual_name)
847
- # When only `actual` resolves, we can still rule out
848
- # `actual <:= target` by inspecting `actual`'s ancestor
849
- # chain. The canonical case: `target=BigDecimal` is not
850
- # loadable in the host process (no `require` in rigor's
851
- # own runtime), but `actual=Integer` IS, and Integer's
852
- # ancestors do not include `BigDecimal`, so the subtype
853
- # relation MUST be `:no` rather than the conservative
854
- # `:maybe`. The reverse asymmetry (target resolves,
855
- # actual doesn't) does not let us conclude anything —
856
- # the unloaded `actual` could be an unrelated class or
857
- # a subclass of `target` we can't see, so we still
858
- # answer `:maybe` there.
766
+ # When only `actual` resolves, we can still rule out `actual <:= target` by inspecting `actual`'s
767
+ # ancestor chain. The canonical case: `target=BigDecimal` is not loadable in the host process (no
768
+ # `require` in rigor's own runtime), but `actual=Integer` IS, and Integer's ancestors do not
769
+ # include `BigDecimal`, so the subtype relation MUST be `:no` rather than the conservative
770
+ # `:maybe`. The reverse asymmetry (target resolves, actual doesn't) does not let us conclude
771
+ # anything the unloaded `actual` could be an unrelated class or a subclass of `target` we can't
772
+ # see, so we still answer `:maybe` there.
859
773
  return subtype_result_via_ancestors(actual_class, target_name, mode) if target_class.nil? && actual_class
860
774
  if target_class.nil? || actual_class.nil?
861
775
  return Type::AcceptsResult.maybe(