rigortype 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -7,42 +7,33 @@ require_relative "../type_node"
7
7
 
8
8
  module Rigor
9
9
  module Builtins
10
- # Canonical-name registry for the imported-built-in
11
- # refinement catalogue. See `imported-built-in-types.md`
12
- # in `docs/type-specification/` for the full catalogue
10
+ # Canonical-name registry for the imported-built-in refinement catalogue. See
11
+ # `imported-built-in-types.md` in `docs/type-specification/` for the full catalogue
13
12
  # rationale and the kebab-case naming rule.
14
13
  #
15
- # Maps kebab-case names (`non-empty-string`, `positive-int`,
16
- # `non-empty-array`, …) to the Rigor type each name denotes.
17
- # The registry is the single integration point for:
14
+ # Maps kebab-case names (`non-empty-string`, `positive-int`, `non-empty-array`, …) to the
15
+ # Rigor type each name denotes. The registry is the single integration point for:
18
16
  #
19
17
  # - The `rigor:v1:return:` RBS::Extended directive
20
- # ([`Rigor::RbsExtended.read_return_type_override`](../rbs_extended.rb)),
21
- # which overrides a method's RBS-declared return type
22
- # with a refinement carrier.
23
- # - Future `RBS::Extended` directives that accept a
24
- # refinement name in any type position (`param:`,
25
- # `assert: x is non-empty-string`, …).
26
- # - The display side: `Type::Difference#describe` already
27
- # recognises the same shapes and prints the kebab-case
28
- # spelling without consulting the registry.
18
+ # ([`Rigor::RbsExtended.read_return_type_override`](../rbs_extended.rb)), which overrides
19
+ # a method's RBS-declared return type with a refinement carrier.
20
+ # - Future `RBS::Extended` directives that accept a refinement name in any type position
21
+ # (`param:`, `assert: x is non-empty-string`, …).
22
+ # - The display side: `Type::Difference#describe` already recognises the same shapes and
23
+ # prints the kebab-case spelling without consulting the registry.
29
24
  #
30
- # Names not in the registry resolve to `nil`; callers
31
- # decide whether to fall back to the RBS-declared type or
32
- # raise a parse error.
25
+ # Names not in the registry resolve to `nil`; callers decide whether to fall back to the
26
+ # RBS-declared type or raise a parse error.
33
27
  #
34
28
  # The registry covers two surfaces:
35
29
  #
36
- # - **No-argument refinement names** (`non-empty-string`,
37
- # `non-zero-int`, `lowercase-string`, …) live in `REGISTRY`
38
- # and resolve through `lookup(name)`.
30
+ # - **No-argument refinement names** (`non-empty-string`, `non-zero-int`,
31
+ # `lowercase-string`, …) live in `REGISTRY` and resolve through `lookup(name)`.
39
32
  # - **Parameterised refinement payloads** (`non-empty-array[Integer]`,
40
- # `non-empty-hash[Symbol, Integer]`, `int<5, 10>`) are
41
- # accepted by `parse(payload)`. The full grammar is documented
42
- # on `Parser`. The two surfaces share `REGISTRY` for the
43
- # no-arg head names; the parameterised head names live in
44
- # `PARAMETERISED_TYPE_BUILDERS` (square-bracket form, type
45
- # args) and `PARAMETERISED_INT_BUILDERS` (angle-bracket form,
33
+ # `non-empty-hash[Symbol, Integer]`, `int<5, 10>`) are accepted by `parse(payload)`. The
34
+ # full grammar is documented on `Parser`. The two surfaces share `REGISTRY` for the
35
+ # no-arg head names; the parameterised head names live in `PARAMETERISED_TYPE_BUILDERS`
36
+ # (square-bracket form, type args) and `PARAMETERISED_INT_BUILDERS` (angle-bracket form,
46
37
  # integer bounds).
47
38
  module ImportedRefinements
48
39
  REGISTRY = {
@@ -70,10 +61,9 @@ module Rigor
70
61
  }.freeze
71
62
  private_constant :REGISTRY
72
63
 
73
- # `name[T]` / `name[K, V]` — type-arg parameterised
74
- # refinements. Each builder takes an `Array<Rigor::Type>`
75
- # and returns a `Rigor::Type` (or `nil` on arity / shape
76
- # mismatch so the caller surfaces a parse failure).
64
+ # `name[T]` / `name[K, V]` — type-arg parameterised refinements. Each builder takes an
65
+ # `Array<Rigor::Type>` and returns a `Rigor::Type` (or `nil` on arity / shape mismatch
66
+ # so the caller surfaces a parse failure).
77
67
  PARAMETERISED_TYPE_BUILDERS = {
78
68
  "non-empty-array" => lambda { |args|
79
69
  return nil unless args.size == 1
@@ -85,12 +75,10 @@ module Rigor
85
75
 
86
76
  Type::Combinator.non_empty_hash(args[0], args[1])
87
77
  },
88
- # v0.0.7 — `key_of[T]` and `value_of[T]` type functions.
89
- # Each takes a single type argument and projects the
90
- # known-keys (resp. known-values) union out of `T`. See
91
- # `Type::Combinator.key_of` for the per-shape projection
92
- # rules. Use `lower_snake` per the
93
- # imported-built-in-types.md type-function naming rule.
78
+ # v0.0.7 — `key_of[T]` and `value_of[T]` type functions. Each takes a single type
79
+ # argument and projects the known-keys (resp. known-values) union out of `T`. See
80
+ # `Type::Combinator.key_of` for the per-shape projection rules. Use `lower_snake` per
81
+ # the imported-built-in-types.md type-function naming rule.
94
82
  "key_of" => lambda { |args|
95
83
  return nil unless args.size == 1
96
84
 
@@ -101,32 +89,27 @@ module Rigor
101
89
 
102
90
  Type::Combinator.value_of(args.first)
103
91
  },
104
- # `int_mask[1, 2, 4]` — every integer representable by
105
- # a bitwise OR over the listed flags. Each arg must be a
106
- # `Constant<Integer>`; the parser wraps integer literals
107
- # for this purpose. Builder declines on any non-integer
108
- # arg.
92
+ # `int_mask[1, 2, 4]` — every integer representable by a bitwise OR over the listed
93
+ # flags. Each arg must be a `Constant<Integer>`; the parser wraps integer literals for
94
+ # this purpose. Builder declines on any non-integer arg.
109
95
  "int_mask" => lambda { |args|
110
96
  flags = args.map { |arg| arg.is_a?(Type::Constant) && arg.value.is_a?(Integer) ? arg.value : nil }
111
97
  return nil if flags.any?(&:nil?)
112
98
 
113
99
  Type::Combinator.int_mask(flags)
114
100
  },
115
- # `int_mask_of[T]` — derives the closure from a finite
116
- # integer literal type (single Constant<Integer> or a
117
- # Union of them).
101
+ # `int_mask_of[T]` — derives the closure from a finite integer literal type (single
102
+ # Constant<Integer> or a Union of them).
118
103
  "int_mask_of" => lambda { |args|
119
104
  return nil unless args.size == 1
120
105
 
121
106
  Type::Combinator.int_mask_of(args.first)
122
107
  },
123
- # ADR-13 § "Canonical type-function additions" — five
124
- # shape-projection type functions that the
125
- # `rigor-typescript-utility-types` plugin (and any other
126
- # plugin that ships a shape-projection vocabulary) maps
127
- # onto. Phase A handles `HashShape` carriers; non-shape
128
- # inputs return the input unchanged (the lossy-projection
129
- # diagnostic lands in slice 5).
108
+ # ADR-13 § "Canonical type-function additions" — five shape-projection type functions
109
+ # that the `rigor-typescript-utility-types` plugin (and any other plugin that ships a
110
+ # shape-projection vocabulary) maps onto. Phase A handles `HashShape` carriers;
111
+ # non-shape inputs return the input unchanged (the lossy-projection diagnostic lands
112
+ # in slice 5).
130
113
  "pick_of" => lambda { |args|
131
114
  return nil unless args.size == 2
132
115
 
@@ -155,12 +138,10 @@ module Rigor
155
138
  }.freeze
156
139
  private_constant :PARAMETERISED_TYPE_BUILDERS
157
140
 
158
- # `name<min, max>` — integer-bound parameterised
159
- # refinements. Each builder takes an `Array<Integer>` and
160
- # returns a `Rigor::Type` (or `nil`). Bounds are signed
161
- # integer literals; `min` MUST be ≤ `max` for the carrier
162
- # to construct successfully (`Type::IntegerRange` enforces
163
- # the invariant).
141
+ # `name<min, max>` — integer-bound parameterised refinements. Each builder takes an
142
+ # `Array<Integer>` and returns a `Rigor::Type` (or `nil`). Bounds are signed integer
143
+ # literals; `min` MUST be ≤ `max` for the carrier to construct successfully
144
+ # (`Type::IntegerRange` enforces the invariant).
164
145
  PARAMETERISED_INT_BUILDERS = {
165
146
  "int" => lambda { |bounds|
166
147
  return nil unless bounds.size == 2
@@ -173,39 +154,32 @@ module Rigor
173
154
  module_function
174
155
 
175
156
  # @param name [String] kebab-case refinement name.
176
- # @return [Rigor::Type, nil] the matching refinement
177
- # carrier, or `nil` if the name is not registered.
157
+ # @return [Rigor::Type, nil] the matching refinement carrier, or `nil` if the name is
158
+ # not registered.
178
159
  def lookup(name)
179
160
  builder = REGISTRY[name.to_s]
180
161
  builder&.call
181
162
  end
182
163
 
183
- # @param payload [String] the trailing payload of a
184
- # `rigor:v1:return:` (or sibling) directive. Accepts
185
- # the bare-name forms `lookup` already handles plus the
164
+ # @param payload [String] the trailing payload of a `rigor:v1:return:` (or sibling)
165
+ # directive. Accepts the bare-name forms `lookup` already handles plus the
186
166
  # parameterised forms documented on {Parser}.
187
167
  # @param name_scope [Rigor::TypeNode::NameScope, nil]
188
- # ADR-13 slice 3 — when provided, the parser consults the
189
- # scope's `#resolver` chain after the built-in registry
190
- # and built-in parametric forms but before the RBS Nominal
191
- # fallback. `nil` (default) preserves the slice-1 / slice-2
192
- # behaviour of consulting only built-ins + RBS.
168
+ # ADR-13 slice 3 — when provided, the parser consults the scope's `#resolver` chain
169
+ # after the built-in registry and built-in parametric forms but before the RBS
170
+ # Nominal fallback. `nil` (default) preserves the slice-1 / slice-2 behaviour of
171
+ # consulting only built-ins + RBS.
193
172
  # @param reporter [Rigor::RbsExtended::Reporter, nil]
194
- # ADR-13 slice 3b — collector that the Resolver feeds
195
- # `dynamic.shape.lossy-projection` events into when a
196
- # shape-projection head (`pick_of`, `omit_of`,
197
- # `partial_of`, `required_of`, `readonly_of`) is applied
198
- # to a carrier that does not preserve shape information.
199
- # `nil` (default) suppresses event accumulation; legacy
200
- # call sites that have no reporter to thread keep the
201
- # pre-slice-3b silent fall-through.
202
- # @param source_location [RBS::Location, nil] location
203
- # attribution for the events the Resolver records. Carries
204
- # the annotation's filename / line / column so the runner
173
+ # ADR-13 slice 3b — collector that the Resolver feeds `dynamic.shape.lossy-projection`
174
+ # events into when a shape-projection head (`pick_of`, `omit_of`, `partial_of`,
175
+ # `required_of`, `readonly_of`) is applied to a carrier that does not preserve shape
176
+ # information. `nil` (default) suppresses event accumulation; legacy call sites that
177
+ # have no reporter to thread keep the pre-slice-3b silent fall-through.
178
+ # @param source_location [RBS::Location, nil] location attribution for the events the
179
+ # Resolver records. Carries the annotation's filename / line / column so the runner
205
180
  # can stamp diagnostics with the user-visible source site.
206
- # @return [Rigor::Type, nil] the resolved refinement
207
- # carrier, or `nil` when the payload is unparseable or
208
- # names a refinement / class no registered source resolved.
181
+ # @return [Rigor::Type, nil] the resolved refinement carrier, or `nil` when the payload
182
+ # is unparseable or names a refinement / class no registered source resolved.
209
183
  def parse(payload, name_scope: nil, reporter: nil, source_location: nil)
210
184
  Parser.new(
211
185
  payload.to_s,
@@ -251,16 +225,14 @@ module Rigor
251
225
  #
252
226
  # Whitespace between tokens is ignored. The parser fails
253
227
  # soft (returns `nil` from `parse`) on any deviation so the
254
- # `RBS::Extended` directive site can fall back to the
255
- # RBS-declared type rather than crash on a typo.
228
+ # `RBS::Extended` directive site can fall back to the RBS-declared type rather than
229
+ # crash on a typo.
256
230
  #
257
- # ADR-13 slice 3 split the original "scan + resolve" loop
258
- # into two passes: the parser emits a {Rigor::TypeNode} AST,
259
- # and a sibling {Resolver} walks the AST to produce a
260
- # {Rigor::Type} carrier consulting the built-in registry,
261
- # the plugin {Rigor::TypeNode::ResolverChain}, and finally
262
- # the RBS Nominal fallback in that order. Plugin resolvers
263
- # never see partial parses.
231
+ # ADR-13 slice 3 split the original "scan + resolve" loop into two passes: the parser
232
+ # emits a {Rigor::TypeNode} AST, and a sibling {Resolver} walks the AST to produce a
233
+ # {Rigor::Type} carrier consulting the built-in registry, the plugin
234
+ # {Rigor::TypeNode::ResolverChain}, and finally the RBS Nominal fallback in that order.
235
+ # Plugin resolvers never see partial parses.
264
236
  class Parser
265
237
  def initialize(input, name_scope: nil, reporter: nil, source_location: nil)
266
238
  @scanner = StringScanner.new(input.strip)
@@ -275,11 +247,10 @@ module Rigor
275
247
  ast = parse_type_ast
276
248
  return nil if ast.nil?
277
249
 
278
- # v0.0.7 — trailing `[K]` indexed-access projects into
279
- # the parsed type. Multiple `[K]` segments chain
280
- # (`Tuple[A, B, C][1][0]`). Each segment wraps the
281
- # previous AST in an {IndexedAccess} node so the chain
282
- # composes cleanly through the resolver pass.
250
+ # v0.0.7 — trailing `[K]` indexed-access projects into the parsed type. Multiple
251
+ # `[K]` segments chain (`Tuple[A, B, C][1][0]`). Each segment wraps the previous AST
252
+ # in an {IndexedAccess} node so the chain composes cleanly through the resolver
253
+ # pass.
283
254
  ast = parse_indexed_access_chain_ast(ast)
284
255
  return nil if ast.nil?
285
256
  return nil unless @scanner.eos?
@@ -289,21 +260,17 @@ module Rigor
289
260
 
290
261
  private
291
262
 
292
- # Refinement names use kebab-case (`non-empty-string`),
293
- # type-function names use lower_snake (`key_of`,
294
- # `value_of`, `int_mask`). The regex accepts both shapes;
295
- # the registry lookup decides which family the name
296
- # belongs to.
263
+ # Refinement names use kebab-case (`non-empty-string`), type-function names use
264
+ # lower_snake (`key_of`, `value_of`, `int_mask`). The regex accepts both shapes; the
265
+ # registry lookup decides which family the name belongs to.
297
266
  SIMPLE_NAME = /[a-z][a-z0-9_-]*/
298
267
  CLASS_NAME = /[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*/
299
268
  SIGNED_INT = /-?\d+/
300
- # ADR-13 follow-up — literal-key tokens at type-arg
301
- # position. Symbol literals match Ruby's bare-symbol
302
- # identifier shape (`:name`); string literals are
303
- # double-quoted without escape sequences (the most
304
- # common TS-style key-union shape). The `?<value>`
305
- # capture lets the parser pull the inner text without
306
- # post-stripping the delimiter.
269
+ # ADR-13 follow-up — literal-key tokens at type-arg position. Symbol literals match
270
+ # Ruby's bare-symbol identifier shape (`:name`); string literals are double-quoted
271
+ # without escape sequences (the most common TS-style key-union shape). The
272
+ # `?<value>` capture lets the parser pull the inner text without post-stripping the
273
+ # delimiter.
307
274
  SYMBOL_LITERAL = /:(?<value>[a-zA-Z_][a-zA-Z0-9_]*[?!=]?)/
308
275
  STRING_LITERAL = /"(?<value>[^"\\]*)"/
309
276
  private_constant :SIMPLE_NAME, :CLASS_NAME, :SIGNED_INT, :SYMBOL_LITERAL, :STRING_LITERAL
@@ -431,10 +398,9 @@ module Rigor
431
398
  end
432
399
  end
433
400
 
434
- # Class-name-headed type argument with optional `[T_1,
435
- # ]` type-args tail. Used so `key_of[Hash[Symbol,
436
- # Integer]]` parses as the projection of a parameterised
437
- # nominal carrier rather than rejecting the inner brackets.
401
+ # Class-name-headed type argument with optional `[T_1, …]` type-args tail. Used so
402
+ # `key_of[Hash[Symbol, Integer]]` parses as the projection of a parameterised nominal
403
+ # carrier rather than rejecting the inner brackets.
438
404
  def parse_class_arg_tail_ast(class_name)
439
405
  return TypeNode::Identifier.new(name: class_name) unless @scanner.peek(1) == "["
440
406
 
@@ -460,8 +426,8 @@ module Rigor
460
426
  end
461
427
  private_constant :Parser
462
428
 
463
- # AST → {Rigor::Type} resolver. ADR-13's resolution order
464
- # for every named-type production:
429
+ # AST → {Rigor::Type} resolver. ADR-13's resolution order for every named-type
430
+ # production:
465
431
  #
466
432
  # 1. Built-in `ImportedRefinements.lookup` (no-arg
467
433
  # refinements like `non-empty-string`).
@@ -473,16 +439,14 @@ module Rigor
473
439
  # 4. RBS Nominal fallback for class-shaped names
474
440
  # (PascalCase head, with or without type args).
475
441
  #
476
- # Returns `nil` when every step declined — preserves the
477
- # parser's fail-soft contract so callers fall back to the
478
- # RBS-declared type instead of raising.
442
+ # Returns `nil` when every step declined — preserves the parser's fail-soft contract so
443
+ # callers fall back to the RBS-declared type instead of raising.
479
444
  class Resolver
480
- # ADR-13 slice 3b — heads that consume a shape-bearing
481
- # first argument. When the first arg is not a `HashShape`
482
- # / `Tuple` (per {Rigor::Type::Combinator.shape_projection_lossy?}),
483
- # the projection degrades to "input unchanged" and the
484
- # Resolver records a `dynamic.shape.lossy-projection`
485
- # event on the reporter (if any).
445
+ # ADR-13 slice 3b — heads that consume a shape-bearing first argument. When the first
446
+ # arg is not a `HashShape` / `Tuple` (per
447
+ # {Rigor::Type::Combinator.shape_projection_lossy?}), the projection degrades to
448
+ # "input unchanged" and the Resolver records a `dynamic.shape.lossy-projection` event
449
+ # on the reporter (if any).
486
450
  SHAPE_PROJECTION_HEADS = %w[pick_of omit_of partial_of required_of readonly_of].freeze
487
451
  private_constant :SHAPE_PROJECTION_HEADS
488
452
 
@@ -494,10 +458,9 @@ module Rigor
494
458
  @source_location = source_location
495
459
  end
496
460
 
497
- # ADR-13 follow-up — every leaf-literal AST node
498
- # (`IntegerLiteral` / `SymbolLiteral` / `StringLiteral`)
499
- # carries a Ruby value that lifts directly to a
500
- # `Constant<value>` carrier through the same helper.
461
+ # ADR-13 follow-up — every leaf-literal AST node (`IntegerLiteral` / `SymbolLiteral` /
462
+ # `StringLiteral`) carries a Ruby value that lifts directly to a `Constant<value>`
463
+ # carrier through the same helper.
501
464
  LITERAL_AST_NODES = [
502
465
  TypeNode::IntegerLiteral, TypeNode::SymbolLiteral, TypeNode::StringLiteral
503
466
  ].freeze
@@ -513,13 +476,11 @@ module Rigor
513
476
  end
514
477
  end
515
478
 
516
- # ADR-13 follow-up — resolves each node recursively and
517
- # folds into a `Type::Combinator.union(...)`. When any
518
- # node resolves to `nil` (unknown name, plugin decline,
519
- # RBS Nominal fallback miss), the whole union collapses
520
- # to `nil` so the caller falls back to the underlying
521
- # RBS-declared type rather than a half-resolved Union
522
- # carrier.
479
+ # ADR-13 follow-up — resolves each node recursively and folds into a
480
+ # `Type::Combinator.union(...)`. When any node resolves to `nil` (unknown name,
481
+ # plugin decline, RBS Nominal fallback miss), the whole union collapses to `nil` so
482
+ # the caller falls back to the underlying RBS-declared type rather than a
483
+ # half-resolved Union carrier.
523
484
  def resolve_union(node)
524
485
  resolved = node.nodes.map { |child| resolve_ast(child) }
525
486
  return nil if resolved.any?(&:nil?)
@@ -527,14 +488,12 @@ module Rigor
527
488
  Type::Combinator.union(*resolved)
528
489
  end
529
490
 
530
- # Public {Rigor::Plugin::TypeNodeResolver}-shaped interface
531
- # so a {Rigor::TypeNode::NameScope} can point its
532
- # `#resolver` at the Resolver itself. Plugin resolvers
533
- # call `scope.resolver.resolve(arg, scope)` to recursively
534
- # resolve a nested argument through the FULL pass
535
- # (built-in registry plugin chain → RBS fallback), not
536
- # just back through the chain. The `_scope` argument is
537
- # ignored — the Resolver owns the scope state internally.
491
+ # Public {Rigor::Plugin::TypeNodeResolver}-shaped interface so a
492
+ # {Rigor::TypeNode::NameScope} can point its `#resolver` at the Resolver itself.
493
+ # Plugin resolvers call `scope.resolver.resolve(arg, scope)` to recursively resolve a
494
+ # nested argument through the FULL pass (built-in registry plugin chain → RBS
495
+ # fallback), not just back through the chain. The `_scope` argument is ignored — the
496
+ # Resolver owns the scope state internally.
538
497
  def resolve(node, _scope)
539
498
  resolve_ast(node)
540
499
  end
@@ -599,13 +558,11 @@ module Rigor
599
558
  result
600
559
  end
601
560
 
602
- # ADR-13 slice 3b — record one `dynamic.shape.lossy-projection`
603
- # event per (head, source_location) pair when the projection
604
- # actually degraded. The builders return the source carrier
605
- # unchanged on non-HashShape / non-Tuple receivers (see
606
- # `Type::Combinator.pick_of` / `omit_of` and the
607
- # HashShape-only `partial_of` / `required_of` /
608
- # `readonly_of`), so detection is "first arg was lossy".
561
+ # ADR-13 slice 3b — record one `dynamic.shape.lossy-projection` event per (head,
562
+ # source_location) pair when the projection actually degraded. The builders return
563
+ # the source carrier unchanged on non-HashShape / non-Tuple receivers (see
564
+ # `Type::Combinator.pick_of` / `omit_of` and the HashShape-only `partial_of` /
565
+ # `required_of` / `readonly_of`), so detection is "first arg was lossy".
609
566
  def record_lossy_projection_if_applicable(node, args, result)
610
567
  return if @reporter.nil?
611
568
  return if result.nil?
@@ -4,37 +4,31 @@ require_relative "../type"
4
4
 
5
5
  module Rigor
6
6
  module Builtins
7
- # Refined types for predefined Ruby / stdlib constants whose upstream
8
- # RBS signatures are broader than the constants' documented runtime
9
- # invariants.
7
+ # Refined types for predefined Ruby / stdlib constants whose upstream RBS signatures are
8
+ # broader than the constants' documented runtime invariants.
10
9
  #
11
10
  # Resolution is two-tiered:
12
11
  #
13
12
  # **Tier 1 — exact-value whitelist** (`FOLDED_CONSTANTS`):
14
- # Constants whose value is bit-for-bit identical across every Ruby
15
- # version and platform are folded to `Constant[T]`: the `Math::PI`
16
- # / `Math::E` math constants (C's `M_PI` / `M_E`) and the four
17
- # IEEE 754 binary64 magnitude constants `Float::INFINITY` /
18
- # `::MAX` / `::MIN` / `::EPSILON` (each a single format-mandated bit
19
- # pattern). Add new entries only when the value is truly
20
- # cross-implementation invariant AND compares reflexively under
21
- # `==` the latter is why `Float::NAN` is deliberately EXCLUDED:
22
- # `NaN == NaN` is `false`, so a `Constant[NAN]` would violate the
23
- # `Type::Constant` `==` / `eql?` / `hash` contract (it would hash
24
- # equal to itself yet compare unequal), corrupting type-equality
25
- # and union dedup. The binary64 *integer* shape parameters
26
- # (`Float::DIG` / `MANT_DIG` / `MAX_EXP` / …) are intentionally NOT
27
- # folded: upstream RBS hedges them as "Usually defaults to …", and
28
- # as plain `Integer`s they fall through Tier 2 to the RBS type
29
- # harmlessly. `Complex::I` is deferred (no complex-fold consumer).
13
+ # Constants whose value is bit-for-bit identical across every Ruby version and platform
14
+ # are folded to `Constant[T]`: the `Math::PI` / `Math::E` math constants (C's `M_PI` /
15
+ # `M_E`) and the four IEEE 754 binary64 magnitude constants `Float::INFINITY` / `::MAX` /
16
+ # `::MIN` / `::EPSILON` (each a single format-mandated bit pattern). Add new entries only
17
+ # when the value is truly cross-implementation invariant AND compares reflexively under
18
+ # `==` the latter is why `Float::NAN` is deliberately EXCLUDED: `NaN == NaN` is `false`,
19
+ # so a `Constant[NAN]` would violate the `Type::Constant` `==` / `eql?` / `hash` contract
20
+ # (it would hash equal to itself yet compare unequal), corrupting type-equality and union
21
+ # dedup. The binary64 *integer* shape parameters (`Float::DIG` / `MANT_DIG` / `MAX_EXP` /
22
+ # …) are intentionally NOT folded: upstream RBS hedges them as "Usually defaults to …",
23
+ # and as plain `Integer`s they fall through Tier 2 to the RBS type harmlessly.
24
+ # `Complex::I` is deferred (no complex-fold consumer).
30
25
  #
31
26
  # **Tier 2 — runtime String inspection**:
32
- # For any other constant, the module resolves it via `const_get`
33
- # against the analyzer's own Ruby runtime. Core / stdlib constants
34
- # (e.g. `RUBY_VERSION`, `RUBY_PLATFORM`) are always loaded into the
35
- # analyzer process; project-defined constants are not (they live only
36
- # in ASTs), so their `const_get` raises `NameError` and the lookup
37
- # falls through to the RBS type tier.
27
+ # For any other constant, the module resolves it via `const_get` against the analyzer's
28
+ # own Ruby runtime. Core / stdlib constants (e.g. `RUBY_VERSION`, `RUBY_PLATFORM`) are
29
+ # always loaded into the analyzer process; project-defined constants are not (they live
30
+ # only in ASTs), so their `const_get` raises `NameError` and the lookup falls through to
31
+ # the RBS type tier.
38
32
  #
39
33
  # For a successfully resolved `String` value:
40
34
  # - empty string → no refinement (fall through to RBS `String`)
@@ -42,34 +36,29 @@ module Rigor
42
36
  # - non-empty otherwise → `non-empty-string`
43
37
  #
44
38
  # **Exclusion set** (`RUNTIME_INSPECTION_EXCLUDED`):
45
- # String constants that appear non-empty in the current runtime but
46
- # are documented to be potentially empty in some build configuration
47
- # or alternative implementation. Exclusions are populated by
48
- # scanning Ruby's C source (version.c, etc.) and RBS comments for
49
- # any constant whose documentation says "may be empty" or
50
- # "platform-specific default". None are known today; the set
51
- # exists as a safety net.
39
+ # String constants that appear non-empty in the current runtime but are documented to be
40
+ # potentially empty in some build configuration or alternative implementation. Exclusions
41
+ # are populated by scanning Ruby's C source (version.c, etc.) and RBS comments for any
42
+ # constant whose documentation says "may be empty" or "platform-specific default". None
43
+ # are known today; the set exists as a safety net.
52
44
  #
53
- # This module is consulted by `Environment#constant_for_name` BEFORE
54
- # the RBS constant-type table (widest types) but AFTER in-source
55
- # constant writes (the user's own `Math::PI = 0.0` takes precedence
56
- # via the lexical-candidate walk in `ExpressionTyper`).
45
+ # This module is consulted by `Environment#constant_for_name` BEFORE the RBS
46
+ # constant-type table (widest types) but AFTER in-source constant writes (the user's own
47
+ # `Math::PI = 0.0` takes precedence via the lexical-candidate walk in `ExpressionTyper`).
57
48
  module PredefinedConstantRefinements
58
49
  # --- tier 1 -------------------------------------------------------
59
50
 
60
- # Exact-value fold whitelist. Keys are unqualified constant paths
61
- # (no leading "::") matching what `Environment#constant_for_name`
62
- # receives.
51
+ # Exact-value fold whitelist. Keys are unqualified constant paths (no leading "::")
52
+ # matching what `Environment#constant_for_name` receives.
63
53
  FOLDED_CONSTANTS = {
64
- # Math module — IEEE 754 bit-identical across all MRI / JRuby /
65
- # TruffleRuby builds; folding enables precise constant arithmetic.
54
+ # Math module — IEEE 754 bit-identical across all MRI / JRuby / TruffleRuby builds;
55
+ # folding enables precise constant arithmetic.
66
56
  "Math::PI" => Type::Combinator.constant_of(::Math::PI).freeze,
67
57
  "Math::E" => Type::Combinator.constant_of(::Math::E).freeze,
68
58
 
69
- # Float magnitude limits — each a single format-mandated IEEE 754
70
- # binary64 bit pattern (`+Inf`, `DBL_MAX`, `DBL_MIN`,
71
- # `DBL_EPSILON`), reflexive under `==`. `Float::NAN` is excluded
72
- # (non-reflexive `==` — see the module-level note).
59
+ # Float magnitude limits — each a single format-mandated IEEE 754 binary64 bit
60
+ # pattern (`+Inf`, `DBL_MAX`, `DBL_MIN`, `DBL_EPSILON`), reflexive under `==`.
61
+ # `Float::NAN` is excluded (non-reflexive `==` see the module-level note).
73
62
  "Float::INFINITY" => Type::Combinator.constant_of(::Float::INFINITY).freeze,
74
63
  "Float::MAX" => Type::Combinator.constant_of(::Float::MAX).freeze,
75
64
  "Float::MIN" => Type::Combinator.constant_of(::Float::MIN).freeze,
@@ -79,14 +68,13 @@ module Rigor
79
68
 
80
69
  # --- tier 2 -------------------------------------------------------
81
70
 
82
- # String constants whose runtime value is non-empty in the current
83
- # Ruby but that should NOT be narrowed because they are documented
84
- # to be potentially empty in some build or implementation.
71
+ # String constants whose runtime value is non-empty in the current Ruby but that should
72
+ # NOT be narrowed because they are documented to be potentially empty in some build or
73
+ # implementation.
85
74
  #
86
- # Methodology: grep Ruby's version.c and similar C sources, and the
87
- # RBS comment corpus, for any constant annotated with "may be empty"
88
- # or "platform-specific default". Add the full qualified path
89
- # (without leading "::") when a genuine risk is found.
75
+ # Methodology: grep Ruby's version.c and similar C sources, and the RBS comment corpus,
76
+ # for any constant annotated with "may be empty" or "platform-specific default". Add
77
+ # the full qualified path (without leading "::") when a genuine risk is found.
90
78
  RUNTIME_INSPECTION_EXCLUDED = Set[].freeze
91
79
  private_constant :RUNTIME_INSPECTION_EXCLUDED
92
80
 
@@ -105,24 +93,22 @@ module Rigor
105
93
 
106
94
  # --- private ------------------------------------------------------
107
95
 
108
- # Resolves `name` via `const_get` in the analyzer's runtime and
109
- # returns a refined String carrier, or nil.
96
+ # Resolves `name` via `const_get` in the analyzer's runtime and returns a refined
97
+ # String carrier, or nil.
110
98
  def self.inspect_runtime_string(name)
111
99
  return nil if RUNTIME_INSPECTION_EXCLUDED.include?(name)
112
100
 
113
101
  mod = ::Object
114
102
  name.split("::").each do |part|
115
- # Resolve only constants already present — never let analysing a
116
- # reference drive the analyzer's own runtime to autoload or run a
117
- # `const_missing` hook. A `Digest::UUID` reference in project code
118
- # otherwise makes `const_get` trigger `Digest.const_missing` →
119
- # `require "digest/uuid"`, and a missing optional library raises
120
- # `LoadError` (a `ScriptError`, not the `NameError` the const_get
121
- # walk expects), which would abort the whole run rather than fall
122
- # through to the RBS tier. `const_defined?(part, false)` answers
123
- # the same "is this resolvable here" question without the side
124
- # effect — a project-defined constant (the common case) is simply
125
- # absent and returns nil, no exception raised.
103
+ # Resolve only constants already present — never let analysing a reference drive
104
+ # the analyzer's own runtime to autoload or run a `const_missing` hook. A
105
+ # `Digest::UUID` reference in project code otherwise makes `const_get` trigger
106
+ # `Digest.const_missing` → `require "digest/uuid"`, and a missing optional library
107
+ # raises `LoadError` (a `ScriptError`, not the `NameError` the const_get walk
108
+ # expects), which would abort the whole run rather than fall through to the RBS
109
+ # tier. `const_defined?(part, false)` answers the same "is this resolvable here"
110
+ # question without the side effect — a project-defined constant (the common case)
111
+ # is simply absent and returns nil, no exception raised.
126
112
  return nil unless mod.is_a?(::Module) && mod.const_defined?(part, false)
127
113
 
128
114
  mod = mod.const_get(part, false)