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,40 +7,31 @@ require_relative "singleton_folding"
7
7
  module Rigor
8
8
  module Inference
9
9
  module MethodDispatcher
10
- # Folds `Shellwords` module-function calls on statically known
11
- # string constants.
10
+ # Folds `Shellwords` module-function calls on statically known string constants.
12
11
  #
13
- # `Shellwords` is a pure, side-effect-free module whose functions
14
- # are deterministic over their inputs the same string always
15
- # produces the same escaped / split / joined result. When all
16
- # relevant arguments are `Constant[String]` (or `Tuple` of them
17
- # for `join`), the analyzer can evaluate the call at inference
18
- # time and return the concrete `Constant<T>` result.
12
+ # `Shellwords` is a pure, side-effect-free module whose functions are deterministic over their
13
+ # inputs the same string always produces the same escaped / split / joined result. When all
14
+ # relevant arguments are `Constant[String]` (or `Tuple` of them for `join`), the analyzer can
15
+ # evaluate the call at inference time and return the concrete `Constant<T>` result.
19
16
  #
20
17
  # === Supported methods
21
18
  #
22
- # * `escape` / `shellescape(str)` — returns `Constant[String]`.
23
- # `Shellwords.escape("")` `"''"`, so the result is always
24
- # non-empty; callers that care about the `non-empty-string`
25
- # refinement will get it for free once that carrier is wired
26
- # to scalar-constant returns.
19
+ # * `escape` / `shellescape(str)` — returns `Constant[String]`. `Shellwords.escape("")` → `"''"`, so
20
+ # the result is always non-empty; callers that care about the `non-empty-string` refinement will
21
+ # get it for free once that carrier is wired to scalar-constant returns.
27
22
  #
28
- # * `split` / `shellsplit` / `shellwords(line)` — splits the
29
- # shell command line into tokens, returns
30
- # `Tuple[Constant[String], …]`. Raises `ArgumentError` on
31
- # unmatched quotes; the handler declines (returns `nil`) so
32
- # the RBS tier widens gracefully.
23
+ # * `split` / `shellsplit` / `shellwords(line)` — splits the shell command line into tokens, returns
24
+ # `Tuple[Constant[String], …]`. Raises `ArgumentError` on unmatched quotes; the handler declines
25
+ # (returns `nil`) so the RBS tier widens gracefully.
33
26
  #
34
- # * `join` / `shelljoin(array)` — joins an array of tokens into
35
- # a shell-safe string. Requires a `Tuple` whose every element
36
- # is a `Constant[String]`; returns `Constant[String]`.
27
+ # * `join` / `shelljoin(array)` — joins an array of tokens into a shell-safe string. Requires a
28
+ # `Tuple` whose every element is a `Constant[String]`; returns `Constant[String]`.
37
29
  #
38
30
  # === Non-constant / unsupported cases
39
31
  #
40
32
  # Any call where:
41
33
  # - the receiver is not `Singleton[Shellwords]`,
42
- # - the required argument is not a `Constant[String]` (or
43
- # `Tuple[Constant[String]…]` for `join`),
34
+ # - the required argument is not a `Constant[String]` (or `Tuple[Constant[String]…]` for `join`),
44
35
  # - the method is not in the supported set, or
45
36
  # - `Shellwords.split` raises on malformed input
46
37
  #
@@ -53,9 +44,8 @@ module Rigor
53
44
  SHELLWORDS_ESCAPE_METHODS | SHELLWORDS_SPLIT_METHODS | SHELLWORDS_JOIN_METHODS
54
45
  ).freeze
55
46
 
56
- # Maximum number of tokens `split` may produce before we
57
- # decline the fold (same rationale as STRING_ARRAY_LIFT_LIMIT
58
- # in ConstantFolding — keep the result Tuple manageable).
47
+ # Maximum number of tokens `split` may produce before we decline the fold (same rationale as
48
+ # STRING_ARRAY_LIFT_LIMIT in ConstantFolding keep the result Tuple manageable).
59
49
  SHELLWORDS_SPLIT_LIMIT = 64
60
50
 
61
51
  private_constant :SHELLWORDS_ESCAPE_METHODS, :SHELLWORDS_SPLIT_METHODS,
@@ -91,8 +81,8 @@ module Rigor
91
81
  Type::Combinator.constant_of(Shellwords.escape(str))
92
82
  end
93
83
 
94
- # `Shellwords.split(line)` / `.shellsplit` / `.shellwords` —
95
- # one String arg, result lifted to Tuple[Constant[String]…].
84
+ # `Shellwords.split(line)` / `.shellsplit` / `.shellwords` — one String arg, result lifted to
85
+ # Tuple[Constant[String]…].
96
86
  def fold_split(args)
97
87
  return nil unless args.size == 1
98
88
 
@@ -104,13 +94,13 @@ module Rigor
104
94
 
105
95
  Type::Combinator.tuple_of(*tokens.map { |t| Type::Combinator.constant_of(t) })
106
96
  rescue ArgumentError
107
- # Unmatched quotes / invalid shell syntax — decline so the
108
- # RBS tier returns the widened Array[String] envelope.
97
+ # Unmatched quotes / invalid shell syntax — decline so the RBS tier returns the widened
98
+ # Array[String] envelope.
109
99
  nil
110
100
  end
111
101
 
112
- # `Shellwords.join(array)` / `.shelljoin` — one Tuple arg
113
- # whose every element is `Constant[String]`.
102
+ # `Shellwords.join(array)` / `.shelljoin` — one Tuple arg whose every element is
103
+ # `Constant[String]`.
114
104
  def fold_join(args)
115
105
  return nil unless args.size == 1
116
106
 
@@ -5,38 +5,32 @@ require_relative "../../type"
5
5
  module Rigor
6
6
  module Inference
7
7
  module MethodDispatcher
8
- # Shared helpers for the per-singleton folding tiers (CGI, URI,
9
- # Shellwords, Math, Time, Regexp, Set, File, …).
8
+ # Shared helpers for the per-singleton folding tiers (CGI, URI, Shellwords, Math, Time, Regexp, Set,
9
+ # File, …).
10
10
  #
11
- # Each of those tiers folds a pure module-/class-function call on
12
- # `Constant` receivers, and every one of them opens with the same
13
- # two questions:
11
+ # Each of those tiers folds a pure module-/class-function call on `Constant` receivers, and every one
12
+ # of them opens with the same two questions:
14
13
  #
15
14
  # 1. "Is the receiver the `Singleton[X]` I handle?" — a one-line
16
- # `is_a?(Type::Singleton) && class_name == "X"` that used to be
17
- # copied verbatim into nine modules.
18
- # 2. "Is this argument a foldable `Constant[String]`?" — the
19
- # `is_a?(Type::Constant) && value.is_a?(String)` unwrap the
20
- # string-folding tiers (CGI / URI / Shellwords / Regexp) gate
21
- # on before evaluating the literal.
15
+ # `is_a?(Type::Singleton) && class_name == "X"` that used to be copied verbatim into nine
16
+ # modules.
17
+ # 2. "Is this argument a foldable `Constant[String]`?" — the `is_a?(Type::Constant) &&
18
+ # value.is_a?(String)` unwrap the string-folding tiers (CGI / URI / Shellwords / Regexp) gate on
19
+ # before evaluating the literal.
22
20
  #
23
- # Centralising both here keeps the receiver-shape and
24
- # constant-unwrap knowledge in one place: if `Type::Singleton` or
25
- # `Type::Constant` ever changes shape, there is a single edit
26
- # rather than nine. The per-tier fold bodies stay where they are —
27
- # only the shared gate moves.
21
+ # Centralising both here keeps the receiver-shape and constant-unwrap knowledge in one place: if
22
+ # `Type::Singleton` or `Type::Constant` ever changes shape, there is a single edit rather than nine.
23
+ # The per-tier fold bodies stay where they are — only the shared gate moves.
28
24
  module SingletonFolding
29
25
  module_function
30
26
 
31
- # True when `receiver` is the class/module singleton named
32
- # `class_name` (e.g. `Singleton[Math]`).
27
+ # True when `receiver` is the class/module singleton named `class_name` (e.g. `Singleton[Math]`).
33
28
  def receiver?(receiver, class_name)
34
29
  receiver.is_a?(Type::Singleton) && receiver.class_name == class_name
35
30
  end
36
31
 
37
- # The `String` value carried by a `Constant[String]` argument, or
38
- # `nil` for any other carrier. Callers fold when this is non-nil
39
- # and decline (return nil, deferring to the RBS tier) otherwise.
32
+ # The `String` value carried by a `Constant[String]` argument, or `nil` for any other carrier.
33
+ # Callers fold when this is non-nil and decline (return nil, deferring to the RBS tier) otherwise.
40
34
  def constant_string(arg)
41
35
  return nil unless arg.is_a?(Type::Constant)
42
36
 
@@ -7,38 +7,32 @@ require_relative "member_shape_projection"
7
7
  module Rigor
8
8
  module Inference
9
9
  module MethodDispatcher
10
- # ADR-48 Struct follow-up — `Struct.new` value folding, the mutable
11
- # sibling of {DataFolding}. Same fully-decidable-shape, degrade-on-any-
12
- # uncertainty discipline, with one extra premise the immutable `Data`
13
- # path does not need: **mutation soundness.**
10
+ # ADR-48 Struct follow-up — `Struct.new` value folding, the mutable sibling of {DataFolding}. Same
11
+ # fully-decidable-shape, degrade-on-any-uncertainty discipline, with one extra premise the immutable
12
+ # `Data` path does not need: **mutation soundness.**
14
13
  #
15
- # A `Struct` instance is mutable (`s.x = v`, `s[:x] = v`, escape), so a
16
- # member map bound to a variable can be invalidated by a later write.
17
- # This slice (ADR-48 slices 1+2 in the sound *transient* form) folds a
18
- # member read ONLY off a **fresh** instance the transient receiver of
19
- # a `.new(...).x` / `.with(...).x` chain, which provably cannot have
20
- # been mutated between materialisation and the read. A member read off
21
- # a *stored* binding degrades to `Dynamic[top]` rather than fold a
22
- # possibly-stale value. Promoting the fold to mutation-free bound
23
- # locals is the deferred slice 3 (relax the fresh-receiver gate to a
24
- # fold-safe-local scan); precise mutated-member re-typing is slice 4.
14
+ # A `Struct` instance is mutable (`s.x = v`, `s[:x] = v`, escape), so a member map bound to a
15
+ # variable can be invalidated by a later write. This slice (ADR-48 slices 1+2 in the sound
16
+ # *transient* form) folds a member read ONLY off a **fresh** instance — the transient receiver of a
17
+ # `.new(...).x` / `.with(...).x` chain, which provably cannot have been mutated between
18
+ # materialisation and the read. A member read off a *stored* binding degrades to `Dynamic[top]`
19
+ # rather than fold a possibly-stale value. Promoting the fold to mutation-free bound locals is the
20
+ # deferred slice 3 (relax the fresh-receiver gate to a fold-safe-local scan); precise mutated-member
21
+ # re-typing is slice 4.
25
22
  #
26
23
  # Responsibilities:
27
24
  #
28
- # 1. `Struct.new(:x, :y [, keyword_init: <bool>])` on a
29
- # `Singleton[Struct]` receiver, literal-Symbol members, NO block ->
30
- # `StructClass{members:, keyword_init:}`. A block / non-literal
31
- # members defer.
32
- # 2. `.new` / `.[]` on a `StructClass` (or a `Singleton[Point]` with a
33
- # recorded struct layout) -> a `StructInstance`, the member map built
34
- # from the call's arguments (positional or keyword per the class's
35
- # `keyword_init`). A form / arity mismatch degrades to `Dynamic[top]`
36
- # rather than a wrong member map. `.members` on the class folds.
37
- # 3. member reads + `[]` / `to_h` / `deconstruct` / `deconstruct_keys`
38
- # / `members` / `with` on a *fresh* `StructInstance` -> the precise
39
- # projected type; member *setters* (`s.x = v`) return the assigned
40
- # value type. Unhandled / stored-receiver calls return nil so the
41
- # pipeline projects the instance to its nominal through RbsDispatch.
25
+ # 1. `Struct.new(:x, :y [, keyword_init: <bool>])` on a `Singleton[Struct]` receiver, literal-Symbol
26
+ # members, NO block -> `StructClass{members:, keyword_init:}`. A block / non-literal members
27
+ # defer.
28
+ # 2. `.new` / `.[]` on a `StructClass` (or a `Singleton[Point]` with a recorded struct layout) -> a
29
+ # `StructInstance`, the member map built from the call's arguments (positional or keyword per the
30
+ # class's `keyword_init`). A form / arity mismatch degrades to `Dynamic[top]` rather than a wrong
31
+ # member map. `.members` on the class folds.
32
+ # 3. member reads + `[]` / `to_h` / `deconstruct` / `deconstruct_keys` / `members` / `with` on a
33
+ # *fresh* `StructInstance` -> the precise projected type; member *setters* (`s.x = v`) return the
34
+ # assigned value type. Unhandled / stored-receiver calls return nil so the pipeline projects the
35
+ # instance to its nominal through RbsDispatch.
42
36
  #
43
37
  # See docs/adr/48-data-struct-value-folding.md § "Struct follow-up".
44
38
  module StructFolding
@@ -64,12 +58,10 @@ module Rigor
64
58
  end
65
59
  end
66
60
 
67
- # A `Struct.new`-defined class assigned to a constant (or a
68
- # `class Point < Struct.new(...)` subclass) is canonicalised by the
69
- # engine to `Singleton[Point]`, not a `StructClass` so its member
70
- # layout is read from the project side-table the scope indexer built
71
- # (`Scope#struct_member_layout`) rather than from the receiver
72
- # carrier.
61
+ # A `Struct.new`-defined class assigned to a constant (or a `class Point < Struct.new(...)`
62
+ # subclass) is canonicalised by the engine to `Singleton[Point]`, not a `StructClass` — so its
63
+ # member layout is read from the project side-table the scope indexer built
64
+ # (`Scope#struct_member_layout`) rather than from the receiver carrier.
73
65
  def fold_named_new(singleton, context)
74
66
  scope = context.scope
75
67
  return nil if scope.nil?
@@ -84,8 +76,8 @@ module Rigor
84
76
 
85
77
  def fold_struct_new(context)
86
78
  return nil unless context.method_name == :new
87
- # Block-form (`Struct.new(:x) do ... end`) defers — the named
88
- # constant/subclass forms still fold via the layout side-table.
79
+ # Block-form (`Struct.new(:x) do ... end`) defers — the named constant/subclass forms still fold
80
+ # via the layout side-table.
89
81
  return nil unless context.block_type.nil?
90
82
 
91
83
  parsed = parse_struct_new_args(context.args)
@@ -94,10 +86,9 @@ module Rigor
94
86
  Type::Combinator.struct_class_of(members: parsed[:members], keyword_init: parsed[:keyword_init])
95
87
  end
96
88
 
97
- # Parses `Struct.new`'s arguments into `{ members:, keyword_init: }`,
98
- # or nil when any argument is non-conforming (a dynamic member name,
99
- # an unexpected trailing keyword). Handles the optional leading String
100
- # class name and the trailing `keyword_init:` option hash.
89
+ # Parses `Struct.new`'s arguments into `{ members:, keyword_init: }`, or nil when any argument is
90
+ # non-conforming (a dynamic member name, an unexpected trailing keyword). Handles the optional
91
+ # leading String class name and the trailing `keyword_init:` option hash.
101
92
  def parse_struct_new_args(args)
102
93
  rest = args.dup
103
94
  keyword_init = false
@@ -125,9 +116,9 @@ module Rigor
125
116
  { members: members, keyword_init: keyword_init }
126
117
  end
127
118
 
128
- # A trailing hash is the `Struct.new` options hash only when it is a
129
- # closed shape whose sole key is `:keyword_init`. Any other key means
130
- # the call is not a recognisable member-list definition -> defer.
119
+ # A trailing hash is the `Struct.new` options hash only when it is a closed shape whose sole key is
120
+ # `:keyword_init`. Any other key means the call is not a recognisable member-list definition ->
121
+ # defer.
131
122
  def struct_options_hash?(shape)
132
123
  shape.closed? && shape.optional_keys.empty? &&
133
124
  shape.pairs.keys.all? { |key| key == :keyword_init }
@@ -154,10 +145,9 @@ module Rigor
154
145
  Type::Combinator.struct_instance_of(members: map, class_name: class_name)
155
146
  end
156
147
 
157
- # Builds the member -> type map honouring the class's `keyword_init`
158
- # flag: a `keyword_init: true` struct accepts only the keyword form,
159
- # a positional struct only the positional form. The mismatched form
160
- # is a different runtime shape, so it degrades rather than fold.
148
+ # Builds the member -> type map honouring the class's `keyword_init` flag: a `keyword_init: true`
149
+ # struct accepts only the keyword form, a positional struct only the positional form. The
150
+ # mismatched form is a different runtime shape, so it degrades rather than fold.
161
151
  def member_map_for_new(members, keyword_init, context)
162
152
  if keyword_new?(context)
163
153
  keyword_init ? keyword_member_map(members, context.args) : nil
@@ -166,9 +156,9 @@ module Rigor
166
156
  end
167
157
  end
168
158
 
169
- # `Point.new(x: 1)` arrives as a single trailing `HashShape` whose
170
- # call node is a `KeywordHashNode`; distinguishing it from a
171
- # positional hash needs the call node (both type to a `HashShape`).
159
+ # `Point.new(x: 1)` arrives as a single trailing `HashShape` whose call node is a
160
+ # `KeywordHashNode`; distinguishing it from a positional hash needs the call node (both type to a
161
+ # `HashShape`).
172
162
  def keyword_new?(context)
173
163
  node = context.call_node
174
164
  return false if node.nil?
@@ -179,9 +169,9 @@ module Rigor
179
169
  arguments.last.is_a?(Prism::KeywordHashNode)
180
170
  end
181
171
 
182
- # `Struct.new(:a, :b).new(v)` is legal — trailing members default to
183
- # `nil` — so fewer positionals than members pad with `Constant[nil]`;
184
- # more positionals than members is an ArgumentError -> degrade.
172
+ # `Struct.new(:a, :b).new(v)` is legal — trailing members default to `nil` — so fewer positionals
173
+ # than members pad with `Constant[nil]`; more positionals than members is an ArgumentError ->
174
+ # degrade.
185
175
  def positional_member_map(members, args)
186
176
  return nil if args.size > members.size
187
177
 
@@ -190,9 +180,8 @@ module Rigor
190
180
  end
191
181
  end
192
182
 
193
- # `Struct.new(:a, :b).new(a: 1)` is legal — omitted members default
194
- # to `nil` — so a keyword subset pads the rest; an unknown key is an
195
- # ArgumentError -> degrade.
183
+ # `Struct.new(:a, :b).new(a: 1)` is legal — omitted members default to `nil` — so a keyword subset
184
+ # pads the rest; an unknown key is an ArgumentError -> degrade.
196
185
  def keyword_member_map(members, args)
197
186
  return nil unless args.size == 1
198
187
 
@@ -204,9 +193,8 @@ module Rigor
204
193
  members.to_h { |name| [name, shape.pairs[name] || Type::Combinator.constant_of(nil)] }
205
194
  end
206
195
 
207
- # A `.new` whose arguments cannot soundly populate the member map
208
- # degrades to `Dynamic[top]` (today's behaviour for `Struct.new(...)`
209
- # instances), never a wrong map.
196
+ # A `.new` whose arguments cannot soundly populate the member map degrades to `Dynamic[top]`
197
+ # (today's behaviour for `Struct.new(...)` instances), never a wrong map.
210
198
  def degraded_instance
211
199
  Type::Combinator.untyped
212
200
  end
@@ -218,27 +206,24 @@ module Rigor
218
206
  args = context.args
219
207
  members = instance.members
220
208
 
221
- # Member setter `s.x = v`: returns the assigned value type. Sound
222
- # regardless of mutation state (it models the setter's own return),
223
- # and avoids a fall-through undefined-method on a writer the
224
- # existence table does not register.
209
+ # Member setter `s.x = v`: returns the assigned value type. Sound regardless of mutation state
210
+ # (it models the setter's own return), and avoids a fall-through undefined-method on a writer
211
+ # the existence table does not register.
225
212
  setter = member_setter_target(method_name, members)
226
213
  return args.first if setter && args.size == 1
227
214
 
228
215
  foldable = foldable_receiver?(context)
229
216
 
230
217
  if members.key?(method_name) && args.empty? && !reader_overridden?(instance, method_name, context.scope)
231
- # A stored receiver folds only when the bound local is proven
232
- # fold-safe (ADR-48 slice 3 — never mutated / aliased / escaped);
233
- # otherwise the member value may be stale, so it degrades to
218
+ # A stored receiver folds only when the bound local is proven fold-safe (ADR-48 slice 3 — never
219
+ # mutated / aliased / escaped); otherwise the member value may be stale, so it degrades to
234
220
  # `Dynamic[top]` (not nil -> no undefined-method fall-through).
235
221
  return foldable ? members.fetch(method_name) : Type::Combinator.untyped
236
222
  end
237
223
 
238
- # Projections fold only off a foldable (fresh, or proven fold-safe)
239
- # instance; off any other stored binding they defer to Struct's RBS
240
- # (`to_h` / `[]` / `members` / `deconstruct*` all exist on `Struct`),
241
- # which is sound and non-regressive.
224
+ # Projections fold only off a foldable (fresh, or proven fold-safe) instance; off any other
225
+ # stored binding they defer to Struct's RBS (`to_h` / `[]` / `members` / `deconstruct*` all exist
226
+ # on `Struct`), which is sound and non-regressive.
242
227
  return nil unless foldable
243
228
 
244
229
  fold_fresh_projection(instance, method_name, args)
@@ -258,9 +243,9 @@ module Rigor
258
243
  end
259
244
  end
260
245
 
261
- # The member name a `:<member>=` setter targets, or nil. Comparison
262
- # operators (`==`, `>=`, ...) end with `=` too but never strip to a
263
- # member symbol, so they fall through to the normal dispatch path.
246
+ # The member name a `:<member>=` setter targets, or nil. Comparison operators (`==`, `>=`, ...) end
247
+ # with `=` too but never strip to a member symbol, so they fall through to the normal dispatch
248
+ # path.
264
249
  def member_setter_target(method_name, members)
265
250
  name = method_name.to_s
266
251
  return nil unless name.end_with?("=")
@@ -269,17 +254,16 @@ module Rigor
269
254
  members.key?(base) ? base : nil
270
255
  end
271
256
 
272
- # A member read is foldable when the receiver is either FRESH (the
273
- # transient result of a `.new(...)`/`.with(...)` chain, which cannot
274
- # have been mutated between materialisation and this read) or a
275
- # FOLD-SAFE stored local (ADR-48 slice 3 `StructFoldSafety` proved
276
- # the binding is never mutated / aliased / escaped in its scope).
257
+ # A member read is foldable when the receiver is either FRESH (the transient result of a
258
+ # `.new(...)`/`.with(...)` chain, which cannot have been mutated between materialisation and this
259
+ # read) or a FOLD-SAFE stored local (ADR-48 slice 3 — `StructFoldSafety` proved the binding is
260
+ # never mutated / aliased / escaped in its scope).
277
261
  def foldable_receiver?(context)
278
262
  fresh_receiver?(context) || fold_safe_local_receiver?(context)
279
263
  end
280
264
 
281
- # A fresh receiver is the transient result of a chained call
282
- # (`Point.new(1, 2).x`, `inst.with(x: 9).y`).
265
+ # A fresh receiver is the transient result of a chained call (`Point.new(1, 2).x`,
266
+ # `inst.with(x: 9).y`).
283
267
  def fresh_receiver?(context)
284
268
  node = context.call_node
285
269
  return false if node.nil?
@@ -287,8 +271,8 @@ module Rigor
287
271
  node.receiver.is_a?(Prism::CallNode)
288
272
  end
289
273
 
290
- # A fold-safe stored receiver is a local-variable read whose name the
291
- # body's fold-safe set (on the scope) marks as never mutated.
274
+ # A fold-safe stored receiver is a local-variable read whose name the body's fold-safe set (on the
275
+ # scope) marks as never mutated.
292
276
  def fold_safe_local_receiver?(context)
293
277
  node = context.call_node
294
278
  receiver = node&.receiver
@@ -6,28 +6,23 @@ require_relative "singleton_folding"
6
6
  module Rigor
7
7
  module Inference
8
8
  module MethodDispatcher
9
- # Folds the zone-pinned `Time` class constructors on statically
10
- # known arguments.
9
+ # Folds the zone-pinned `Time` class constructors on statically known arguments.
11
10
  #
12
- # Only `Time.utc` / `Time.gm` are folded. They pin the result
13
- # to UTC, so the literal is machine-independent every reader
14
- # (`year`, `hour`, `utc_offset`, `strftime`, …) yields the same
11
+ # Only `Time.utc` / `Time.gm` are folded. They pin the result to UTC, so the literal is
12
+ # machine-independent every reader (`year`, `hour`, `utc_offset`, `strftime`, …) yields the same
15
13
  # answer on any analysis host.
16
14
  #
17
- # `Time.now` (non-deterministic), `Time.at` / `Time.local` /
18
- # `Time.mktime` / `Time.new` (local-zone the result's wall
19
- # clock and `utc_offset` depend on the analysis machine's
20
- # timezone) are deliberately NOT folded; they keep their
21
- # `Nominal[Time]` RBS answer. For the same reason `Time#getlocal`
22
- # is blocklisted in `TIME_CATALOG` so a folded `Constant[Time]`
23
- # cannot produce a machine-dependent local-zone copy.
15
+ # `Time.now` (non-deterministic), `Time.at` / `Time.local` / `Time.mktime` / `Time.new` (local-zone —
16
+ # the result's wall clock and `utc_offset` depend on the analysis machine's timezone) are
17
+ # deliberately NOT folded; they keep their `Nominal[Time]` RBS answer. For the same reason
18
+ # `Time#getlocal` is blocklisted in `TIME_CATALOG` so a folded `Constant[Time]` cannot produce a
19
+ # machine-dependent local-zone copy.
24
20
  module TimeFolding
25
21
  TIME_UTC_METHODS = Set[:utc, :gm].freeze
26
22
  private_constant :TIME_UTC_METHODS
27
23
 
28
- # `Time.utc` accepts the 1–7 positional (year-first) form and
29
- # a 10-arg (sec-first) form; cap the arity so a malformed
30
- # call declines cheaply rather than reaching the constructor.
24
+ # `Time.utc` accepts the 1–7 positional (year-first) form and a 10-arg (sec-first) form; cap the
25
+ # arity so a malformed call declines cheaply rather than reaching the constructor.
31
26
  MAX_TIME_ARITY = 10
32
27
  private_constant :MAX_TIME_ARITY
33
28
 
@@ -7,23 +7,19 @@ require_relative "singleton_folding"
7
7
  module Rigor
8
8
  module Inference
9
9
  module MethodDispatcher
10
- # Folds `URI` module-function calls on statically known
11
- # string constants.
10
+ # Folds `URI` module-function calls on statically known string constants.
12
11
  #
13
- # `URI.encode_www_form_component` / `decode_www_form_component`
14
- # and the newer `encode_uri_component` / `decode_uri_component`
15
- # are pure, deterministic functions over their string inputs.
16
- # When the argument is a `Constant[String]`, the analyzer can
17
- # evaluate the call at inference time and return the concrete
18
- # `Constant[String]` result.
12
+ # `URI.encode_www_form_component` / `decode_www_form_component` and the newer `encode_uri_component`
13
+ # / `decode_uri_component` are pure, deterministic functions over their string inputs. When the
14
+ # argument is a `Constant[String]`, the analyzer can evaluate the call at inference time and return
15
+ # the concrete `Constant[String]` result.
19
16
  #
20
17
  # === Supported methods
21
18
  #
22
- # * `encode_www_form_component(str)` / `decode_www_form_component(str)` —
23
- # RFC 3986 percent-encode / decode. Returns `Constant[String]`.
24
- # * `encode_uri_component(str)` / `decode_uri_component(str)` —
25
- # Same encoding but may preserve additional reserved chars
26
- # (Ruby 3.2+). Returns `Constant[String]`.
19
+ # * `encode_www_form_component(str)` / `decode_www_form_component(str)` — RFC 3986 percent-encode /
20
+ # decode. Returns `Constant[String]`.
21
+ # * `encode_uri_component(str)` / `decode_uri_component(str)` — Same encoding but may preserve
22
+ # additional reserved chars (Ruby 3.2+). Returns `Constant[String]`.
27
23
  #
28
24
  # === Non-constant / unsupported cases
29
25
  #