rigortype 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -6,58 +6,49 @@ require_relative "../source/constant_path"
6
6
 
7
7
  module Rigor
8
8
  module Inference
9
- # ADR-48 Struct follow-up, slice 3 — the fold-safe-local scan. Determines,
10
- # for a single local-variable scope (a method body or the program
11
- # top-level), which `Struct`-materialised locals are **provably never
12
- # mutated, aliased, or escaped** and so may have their member reads folded
13
- # off a *stored* binding (relaxing the slice-2 fresh-receiver gate).
9
+ # ADR-48 Struct follow-up, slice 3 — the fold-safe-local scan. Determines, for a single local-variable scope (a
10
+ # method body or the program top-level), which `Struct`-materialised locals are **provably never mutated, aliased,
11
+ # or escaped** and so may have their member reads folded off a *stored* binding (relaxing the slice-2 fresh-receiver
12
+ # gate).
14
13
  #
15
- # The analysis is a conservative ALLOW-LIST, not a deny-list: a local is
16
- # fold-safe only when *every* read of it is the receiver of a known-pure
17
- # read call. Anything the scan does not recognise as a pure read a
18
- # setter, an `[]=` / operator-write, an argument / alias / container store
19
- # / return (escape), or an unknown method call (which could mutate `self`
20
- # internally) — disqualifies the local. A missed case therefore makes the
21
- # scan over-conservative (no fold), **never unsound** (folding a mutated
22
- # value) — the false-positive-safe direction.
14
+ # The analysis is a conservative ALLOW-LIST, not a deny-list: a local is fold-safe only when *every* read of it is
15
+ # the receiver of a known-pure read call. Anything the scan does not recognise as a pure read — a setter, an `[]=` /
16
+ # operator-write, an argument / alias / container store / return (escape), or an unknown method call (which could
17
+ # mutate `self` internally) disqualifies the local. A missed case therefore makes the scan over-conservative (no
18
+ # fold), **never unsound** (folding a mutated value) the false-positive-safe direction.
23
19
  #
24
- # Soundness rests on a counting identity: a local `n` is fold-safe iff
25
- # *every* `LocalVariableReadNode(n)` is the receiver of a pure-read call.
26
- # Equivalently `total_reads(n) == pure_receiver_reads(n)`. Any other
27
- # occurrence `n` as a setter receiver (`n.x = v` is a `:x=` call, not a
28
- # pure read), an `[]=`/operator-write receiver (the receiver read is not
29
- # under a pure call), a call argument, an assignment RHS (alias), a
30
- # container element, a bare value (return/escape) — leaves a read that is
31
- # not a pure-receiver read, so the counts diverge.
20
+ # Soundness rests on a counting identity: a local `n` is fold-safe iff *every* `LocalVariableReadNode(n)` is the
21
+ # receiver of a pure-read call. Equivalently `total_reads(n) == pure_receiver_reads(n)`. Any other occurrence `n`
22
+ # as a setter receiver (`n.x = v` is a `:x=` call, not a pure read), an `[]=`/operator-write receiver (the receiver
23
+ # read is not under a pure call), a call argument, an assignment RHS (alias), a container element, a bare value
24
+ # (return/escape) — leaves a read that is not a pure-receiver read, so the counts diverge.
32
25
  #
33
- # See docs/notes/20260615-struct-folding-slice3-design.md and
34
- # docs/adr/48-data-struct-value-folding.md § "Struct follow-up".
26
+ # See docs/notes/20260615-struct-folding-slice3-design.md and docs/adr/48-data-struct-value-folding.md § "Struct
27
+ # follow-up".
35
28
  module StructFoldSafety
36
29
  module_function
37
30
 
38
31
  EMPTY = Set.new.freeze
39
32
 
40
- # The fixed `Struct` read methods that never mutate. A member-reader name
41
- # (`:x`) is added per-local from the local's recorded layout. A setter
42
- # (`:x=`), `:[]=`, `store`, `push`, etc. are deliberately absent.
33
+ # The fixed `Struct` read methods that never mutate. A member-reader name (`:x`) is added per-local from the
34
+ # local's recorded layout. A setter (`:x=`), `:[]=`, `store`, `push`, etc. are deliberately absent.
43
35
  FIXED_READS = %i[
44
36
  [] dig to_h to_hash to_a values members deconstruct deconstruct_keys
45
37
  == != eql? equal? hash inspect to_s size length frozen? each each_pair
46
38
  values_at with
47
39
  ].to_set.freeze
48
40
 
49
- # Nested `def` / `class` / `module` bodies open a *new* local-variable
50
- # scope, so the scan does not descend into them a local of the same
51
- # name there is a different binding. Blocks share the enclosing locals
52
- # (closures), so the scan does descend into them.
41
+ # Nested `def` / `class` / `module` bodies open a *new* local-variable scope, so the scan does not descend into
42
+ # them a local of the same name there is a different binding. Blocks share the enclosing locals (closures), so
43
+ # the scan does descend into them.
53
44
  def scope_boundary?(node)
54
45
  node.is_a?(Prism::DefNode) || node.is_a?(Prism::ClassNode) ||
55
46
  node.is_a?(Prism::ModuleNode) || node.is_a?(Prism::SingletonClassNode)
56
47
  end
57
48
 
58
49
  # @param root [Prism::Node, nil] the local-variable scope to scan.
59
- # @param layout_lookup [#call] a `String -> Array[Symbol] | nil` resolver
60
- # mapping a constant receiver name to its struct member list.
50
+ # @param layout_lookup [#call] a `String -> Array[Symbol] | nil` resolver mapping a constant receiver name to its
51
+ # struct member list.
61
52
  # @return [Set<Symbol>] the fold-safe local names.
62
53
  def fold_safe_locals(root, layout_lookup)
63
54
  return EMPTY if root.nil?
@@ -77,9 +68,8 @@ module Rigor
77
68
  safe.empty? ? EMPTY : safe.to_set
78
69
  end
79
70
 
80
- # Pass 1 — record each local's single struct materialisation (its member
81
- # set) and count its assignments. A local assigned more than once is
82
- # later excluded (the static fold-safe set cannot track a rebinding).
71
+ # Pass 1 — record each local's single struct materialisation (its member set) and count its assignments. A local
72
+ # assigned more than once is later excluded (the static fold-safe set cannot track a rebinding).
83
73
  def collect_struct_locals(node, layout_lookup, members, writes)
84
74
  return if node.nil?
85
75
 
@@ -94,8 +84,7 @@ module Rigor
94
84
  end
95
85
  end
96
86
 
97
- # Pass 2 — count, per recorded struct local, total reads vs. reads that
98
- # are the receiver of a pure-read call.
87
+ # Pass 2 — count, per recorded struct local, total reads vs. reads that are the receiver of a pure-read call.
99
88
  def count_uses(node, members, total, pure)
100
89
  return if node.nil?
101
90
 
@@ -114,17 +103,15 @@ module Rigor
114
103
  end
115
104
  end
116
105
 
117
- # A call is a pure read of the receiver when its name is a fixed Struct
118
- # read or one of the receiver's member readers. Setters (`:x=`), `:[]=`,
119
- # and any unknown method are excluded.
106
+ # A call is a pure read of the receiver when its name is a fixed Struct read or one of the receiver's member
107
+ # readers. Setters (`:x=`), `:[]=`, and any unknown method are excluded.
120
108
  def pure_read_call?(call_node, member_set)
121
109
  name = call_node.name
122
110
  FIXED_READS.include?(name) || member_set.include?(name)
123
111
  end
124
112
 
125
- # The member set of a `<Struct chain>.new(...)` / `.[]` materialisation,
126
- # or nil. Handles the inline `Struct.new(:a, :b).new(...)` form and the
127
- # `Const.new(...)` form (resolved through the layout side-table).
113
+ # The member set of a `<Struct chain>.new(...)` / `.[]` materialisation, or nil. Handles the inline
114
+ # `Struct.new(:a, :b).new(...)` form and the `Const.new(...)` form (resolved through the layout side-table).
128
115
  def struct_materialization_members(value_node, layout_lookup)
129
116
  return nil unless value_node.is_a?(Prism::CallNode)
130
117
  return nil unless %i[new []].include?(value_node.name)
@@ -140,9 +127,8 @@ module Rigor
140
127
  end
141
128
  end
142
129
 
143
- # The Symbol member set of a literal `Struct.new(:a, :b [, keyword_init:])`
144
- # call, or nil. (A leading String name and the trailing options hash are
145
- # ignored — only the literal-Symbol positionals contribute.)
130
+ # The Symbol member set of a literal `Struct.new(:a, :b [, keyword_init:])` call, or nil. (A leading String name
131
+ # and the trailing options hash are ignored only the literal-Symbol positionals contribute.)
146
132
  def struct_new_member_set(call_node)
147
133
  return nil unless call_node.is_a?(Prism::CallNode) && call_node.name == :new
148
134
  return nil unless meta_constant?(call_node.receiver, :Struct)
@@ -167,8 +153,8 @@ module Rigor
167
153
  end
168
154
  end
169
155
 
170
- # Yields each child to recurse into, skipping the subtree of a nested
171
- # local-variable-scope boundary (a `def` / `class` / `module`).
156
+ # Yields each child to recurse into, skipping the subtree of a nested local-variable-scope boundary (a `def` /
157
+ # `class` / `module`).
172
158
  def each_local_scope_child(node)
173
159
  node.compact_child_nodes.each do |child|
174
160
  next if scope_boundary?(child)
@@ -2,24 +2,17 @@
2
2
 
3
3
  module Rigor
4
4
  module Inference
5
- # ADR-16 Tier C output — one synthetic method declared by a
6
- # plugin's `Plugin::Macro::HeredocTemplate` entry, after the
7
- # pre-pass has interpolated the call-site literal symbol into
8
- # the template name. Stored in {SyntheticMethodIndex} and
9
- # consulted by {MethodDispatcher} below the RBS dispatch tier.
5
+ # ADR-16 Tier C output — one synthetic method declared by a plugin's `Plugin::Macro::HeredocTemplate` entry, after
6
+ # the pre-pass has interpolated the call-site literal symbol into the template name. Stored in
7
+ # {SyntheticMethodIndex} and consulted by {MethodDispatcher} below the RBS dispatch tier.
10
8
  #
11
- # Per ADR-16 § WD13 (cost-bounded best-effort): method names
12
- # emit with return types promoted by the dispatcher's Slice 6
13
- # tiers Slice 6a promotes via `origin_module` (TierB) and
14
- # Slice 6b via `return_type` nominal lookup (TierC). The
15
- # `return_type` string is preserved so promotion can resolve
16
- # it without re-walking; `"untyped"` / `"void"` placeholders
17
- # fall back to `Dynamic[Top]`.
9
+ # Per ADR-16 § WD13 (cost-bounded best-effort): method names emit with return types promoted by the dispatcher's
10
+ # Slice 6 tiers Slice 6a promotes via `origin_module` (TierB) and Slice 6b via `return_type` nominal lookup
11
+ # (TierC). The `return_type` string is preserved so promotion can resolve it without re-walking; `"untyped"` /
12
+ # `"void"` placeholders fall back to `Dynamic[Top]`.
18
13
  #
19
- # The `provenance` Hash carries debug / `--explain` metadata:
20
- # plugin id, the template's call shape, and the source
21
- # location of the originating DSL call. Surfaced through the
22
- # dispatcher's `macro.tier_c.*` provenance markers.
14
+ # The `provenance` Hash carries debug / `--explain` metadata: plugin id, the template's call shape, and the source
15
+ # location of the originating DSL call. Surfaced through the dispatcher's `macro.tier_c.*` provenance markers.
23
16
  class SyntheticMethod
24
17
  INSTANCE = :instance
25
18
  SINGLETON = :singleton
@@ -4,43 +4,30 @@ require_relative "synthetic_method"
4
4
 
5
5
  module Rigor
6
6
  module Inference
7
- # Frozen, Ractor-shareable lookup table for the synthetic
8
- # methods emitted by ADR-16 Tier C declarations during a
9
- # single `Analysis::Runner#run`. Constructed by the pre-pass
10
- # scanner (see {SyntheticMethodScanner}) and consulted by
11
- # {MethodDispatcher} below `RbsDispatch.try_dispatch` (per WD13:
12
- # user-authored RBS overrides substrate synthesis).
7
+ # Frozen, Ractor-shareable lookup table for the synthetic methods emitted by ADR-16 Tier C declarations during a
8
+ # single `Analysis::Runner#run`. Constructed by the pre-pass scanner (see {SyntheticMethodScanner}) and consulted by
9
+ # {MethodDispatcher} below `RbsDispatch.try_dispatch` (per WD13: user-authored RBS overrides substrate synthesis).
13
10
  #
14
- # The index is keyed by `(class_name, method_name, kind)`. A
15
- # single key may resolve to multiple {SyntheticMethod} records
16
- # if two plugins emit the same name (e.g. `rigor-dry-struct`
17
- # and a hypothetical `rigor-dry-struct-extras` both registering
18
- # the same attribute). Per ADR-16 WD11 / the WD-discussion in
19
- # `## Open questions` the dispatcher uses first-wins by
20
- # registration order; this index preserves that order in
21
- # `lookup`'s return.
11
+ # The index is keyed by `(class_name, method_name, kind)`. A single key may resolve to multiple {SyntheticMethod}
12
+ # records if two plugins emit the same name (e.g. `rigor-dry-struct` and a hypothetical `rigor-dry-struct-extras`
13
+ # both registering the same attribute). Per ADR-16 WD11 / the WD-discussion in `## Open questions` the dispatcher
14
+ # uses first-wins by registration order; this index preserves that order in `lookup`'s return.
22
15
  #
23
16
  # ## Slice 2b — return-type precision posture
24
17
  #
25
- # The recorded `SyntheticMethod#return_type` is a String
26
- # (e.g. `"ActiveStorage::Attached::One"`), preserved verbatim
27
- # from the manifest's emit table. Slice 2b's engine wiring
28
- # treats every match as returning `Dynamic[T]` per WD13's
29
- # floor — the recorded string is the input to a later slice's
30
- # precision promotion via ADR-13's `Plugin::TypeNodeResolver`.
18
+ # The recorded `SyntheticMethod#return_type` is a String (e.g. `"ActiveStorage::Attached::One"`), preserved verbatim
19
+ # from the manifest's emit table. Slice 2b's engine wiring treats every match as returning `Dynamic[T]` per WD13's
20
+ # floor the recorded string is the input to a later slice's precision promotion via ADR-13's
21
+ # `Plugin::TypeNodeResolver`.
31
22
  class SyntheticMethodIndex
32
23
  attr_reader :entries, :class_names
33
24
 
34
25
  # @param entries [Array<SyntheticMethod>]
35
- # @param class_names [Array<String>, Set<String>] names of
36
- # classes the substrate synthesises wholesale (ADR-36
37
- # nested-class emission the variant subclasses that have
38
- # no RBS/source declaration of their own). Recorded so
39
- # `Environment#class_known?` can resolve them as classes
40
- # (their constant reference + `.new` dispatch) even though
41
- # nothing else in the type universe declares them. Tier B/C
42
- # method emissions leave this empty (their receiver classes
43
- # are already real).
26
+ # @param class_names [Array<String>, Set<String>] names of classes the substrate synthesises wholesale (ADR-36
27
+ # nested-class emission — the variant subclasses that have no RBS/source declaration of their own). Recorded so
28
+ # `Environment#class_known?` can resolve them as classes (their constant reference + `.new` dispatch) even
29
+ # though nothing else in the type universe declares them. Tier B/C method emissions leave this empty (their
30
+ # receiver classes are already real).
44
31
  def initialize(entries: [], class_names: [])
45
32
  unless entries.is_a?(Array) && entries.all?(SyntheticMethod)
46
33
  raise ArgumentError,
@@ -59,16 +46,14 @@ module Rigor
59
46
  entries.empty? && class_names.empty?
60
47
  end
61
48
 
62
- # True when `name` is a substrate-synthesised class (an
63
- # ADR-36 variant subclass). Used by `Environment#class_known?`
64
- # so the constant resolves and `.new` dispatches.
49
+ # True when `name` is a substrate-synthesised class (an ADR-36 variant subclass). Used by
50
+ # `Environment#class_known?` so the constant resolves and `.new` dispatches.
65
51
  def knows_class?(name)
66
52
  @class_name_set.include?(name.to_s)
67
53
  end
68
54
 
69
- # Returns an Array of matching {SyntheticMethod} records in
70
- # plugin-registration order. Empty Array when no plugin has
71
- # declared a Tier C entry that interpolates to this name.
55
+ # Returns an Array of matching {SyntheticMethod} records in plugin-registration order. Empty Array when no plugin
56
+ # has declared a Tier C entry that interpolates to this name.
72
57
  def lookup_instance(class_name, method_name)
73
58
  @by_instance.fetch([class_name, method_name.to_sym], EMPTY_ROW)
74
59
  end
@@ -10,13 +10,10 @@ require_relative "synthetic_method_index"
10
10
 
11
11
  module Rigor
12
12
  module Inference
13
- # ADR-16 slice 2b pre-pass — scans the project's source paths
14
- # for class-level DSL calls that match any registered plugin's
15
- # `Plugin::Macro::HeredocTemplate` entry, instantiates the
16
- # corresponding {SyntheticMethod} records, and returns a frozen
17
- # {SyntheticMethodIndex} the dispatcher consults below the RBS
18
- # tier (per WD13 — user-authored RBS overrides substrate
19
- # synthesis).
13
+ # ADR-16 slice 2b pre-pass — scans the project's source paths for class-level DSL calls that match any registered
14
+ # plugin's `Plugin::Macro::HeredocTemplate` entry, instantiates the corresponding {SyntheticMethod} records, and
15
+ # returns a frozen {SyntheticMethodIndex} the dispatcher consults below the RBS tier (per WD13 — user-authored RBS
16
+ # overrides substrate synthesis).
20
17
  #
21
18
  # Two-phase walk:
22
19
  #
@@ -32,36 +29,31 @@ module Rigor
32
29
  # class must equal or inherit (lexically OR through the
33
30
  # RBS env) from the template's `receiver_constraint`.
34
31
  #
35
- # Per WD4 the pre-pass mechanism is "scan all files once at
36
- # startup, populate the index before per-file inference."
37
- # Slice 2b ships this strategy; future iterations may revisit
38
- # to lazy emit (per WD4 alternatives) if the warm-cache profile
39
- # justifies it.
32
+ # Per WD4 the pre-pass mechanism is "scan all files once at startup, populate the index before per-file inference."
33
+ # Slice 2b ships this strategy; future iterations may revisit to lazy emit (per WD4 alternatives) if the warm-cache
34
+ # profile justifies it.
40
35
  #
41
- # Per WD13 floor — `return_type` is recorded but not resolved.
42
- # `Macro::HeredocTemplate::Emit#returns` strings round-trip
43
- # through {SyntheticMethod#return_type} verbatim; the
44
- # dispatcher's slice-2b tier translates every match to
45
- # `Dynamic[T]`. Precise resolution via the ADR-13 resolver
46
- # chain is the ceiling, deferred.
36
+ # Per WD13 floor — `return_type` is recorded but not resolved. `Macro::HeredocTemplate::Emit#returns` strings
37
+ # round-trip through {SyntheticMethod#return_type} verbatim; the dispatcher's slice-2b tier translates every match
38
+ # to `Dynamic[T]`. Precise resolution via the ADR-13 resolver chain is the ceiling, deferred.
47
39
  module SyntheticMethodScanner # rubocop:disable Metrics/ModuleLength
48
40
  module_function
49
41
 
50
42
  # @param plugin_registry [Rigor::Plugin::Registry]
51
- # @param paths [Array<String>] absolute paths to the project
43
+ # @param paths [Array<String>] absolute paths to the project
52
44
  # source files to scan.
53
- # @param environment [Rigor::Environment, nil] used for
45
+ # @param environment [Rigor::Environment, nil] used for
54
46
  # inheritance resolution against RBS-known classes
55
47
  # (ActiveRecord::Base, Dry::Struct, etc.) that aren't
56
48
  # declared in project source.
57
- # @param fact_store [Rigor::Plugin::FactStore, nil]
49
+ # @param fact_store [Rigor::Plugin::FactStore, nil]
58
50
  # the per-run cross-plugin fact store. ADR-18 lookups
59
51
  # (`Plugin::Macro::HeredocTemplate::Emit#returns_from_arg`)
60
52
  # consult this at scan time to resolve per-call-site
61
53
  # return types from published facts; without it, those
62
54
  # emit rows fall back to their static `returns:` (or
63
55
  # `"untyped"` → `Dynamic[Top]`).
64
- # @param buffer [Rigor::Analysis::BufferBinding, nil]
56
+ # @param buffer [Rigor::Analysis::BufferBinding, nil]
65
57
  # editor-mode buffer binding. When set, reads for the
66
58
  # logical path resolve to the buffer's physical path so
67
59
  # the pre-pass sees the in-flight bytes instead of the
@@ -94,8 +86,7 @@ module Rigor
94
86
  SyntheticMethodIndex.new(entries: entries, class_names: class_names)
95
87
  end
96
88
 
97
- # Aggregates `(plugin_id, template)` pairs across every
98
- # plugin's `manifest.heredoc_templates` in registration
89
+ # Aggregates `(plugin_id, template)` pairs across every plugin's `manifest.heredoc_templates` in registration
99
90
  # order. Empty when no plugin contributes Tier C entries.
100
91
  def collect_templates(plugin_registry)
101
92
  return [] if plugin_registry.nil? || plugin_registry.empty?
@@ -108,10 +99,8 @@ module Rigor
108
99
  end
109
100
  end
110
101
 
111
- # ADR-16 Tier B (slice 3b). Aggregates `(plugin_id, registry)`
112
- # pairs across every plugin's `manifest.trait_registries` in
113
- # registration order. Empty when no plugin contributes Tier B
114
- # entries.
102
+ # ADR-16 Tier B (slice 3b). Aggregates `(plugin_id, registry)` pairs across every plugin's
103
+ # `manifest.trait_registries` in registration order. Empty when no plugin contributes Tier B entries.
115
104
  def collect_trait_registries(plugin_registry)
116
105
  return [] if plugin_registry.nil? || plugin_registry.empty?
117
106
 
@@ -123,9 +112,8 @@ module Rigor
123
112
  end
124
113
  end
125
114
 
126
- # ADR-36 — aggregates `(plugin_id, template)` pairs across
127
- # every plugin's `manifest.nested_class_templates`. Empty when
128
- # no plugin contributes the nested-class emission tier.
115
+ # ADR-36 — aggregates `(plugin_id, template)` pairs across every plugin's `manifest.nested_class_templates`. Empty
116
+ # when no plugin contributes the nested-class emission tier.
129
117
  def collect_nested_class_templates(plugin_registry)
130
118
  return [] if plugin_registry.nil? || plugin_registry.empty?
131
119
 
@@ -137,10 +125,8 @@ module Rigor
137
125
  end
138
126
  end
139
127
 
140
- # ADR-36 nested-class emission. For each class that `extend`s a
141
- # template's `receiver_constraint` and carries a
142
- # `<block_method> do ... end` block, mint one synthetic
143
- # subclass per `<variant_method> <Const>, <Type>` row:
128
+ # ADR-36 nested-class emission. For each class that `extend`s a template's `receiver_constraint` and carries a
129
+ # `<block_method> do ... end` block, mint one synthetic subclass per `<variant_method> <Const>, <Type>` row:
144
130
  #
145
131
  # class Shape
146
132
  # extend Mangrove::Enum
@@ -149,13 +135,10 @@ module Rigor
149
135
  # end
150
136
  # end
151
137
  #
152
- # yields synthetic class `Shape::Circle` + instance method
153
- # `Shape::Circle#inner -> Float`. The variant subclass name is
154
- # recorded in `class_names` so `Environment#class_known?`
155
- # resolves the constant (and `.new` dispatches through
156
- # `meta_new`); `#inner`'s return type is the literal constant
157
- # type argument (non-constant inner shapes degrade to
158
- # `Dynamic[Top]` per the slice-A floor).
138
+ # yields synthetic class `Shape::Circle` + instance method `Shape::Circle#inner -> Float`. The variant subclass
139
+ # name is recorded in `class_names` so `Environment#class_known?` resolves the constant (and `.new` dispatches
140
+ # through `meta_new`); `#inner`'s return type is the literal constant type argument (non-constant inner shapes
141
+ # degrade to `Dynamic[Top]` per the slice-A floor).
159
142
  def collect_nested_class_entries(entries, class_names, nested_templates, ast, path)
160
143
  return if ast.nil?
161
144
 
@@ -173,9 +156,8 @@ module Rigor
173
156
  end
174
157
  end
175
158
 
176
- # Walks every class declaration, yielding its fully-qualified
177
- # name and the `Prism::ClassNode`. Mirrors `walk_class_bodies`'
178
- # scope-stack bookkeeping but hands back the class node itself.
159
+ # Walks every class declaration, yielding its fully-qualified name and the `Prism::ClassNode`. Mirrors
160
+ # `walk_class_bodies`' scope-stack bookkeeping but hands back the class node itself.
179
161
  def walk_classes(node, scope_stack = [], &)
180
162
  return unless node.respond_to?(:compact_child_nodes)
181
163
 
@@ -198,9 +180,8 @@ module Rigor
198
180
  body.respond_to?(:body) ? body.body.compact : []
199
181
  end
200
182
 
201
- # True when the class body carries `extend <constraint>`
202
- # (receiverless `extend` call with the constraint constant as
203
- # its first argument).
183
+ # True when the class body carries `extend <constraint>` (receiverless `extend` call with the constraint constant
184
+ # as its first argument).
204
185
  def body_extends?(body, constraint)
205
186
  body.any? do |stmt|
206
187
  stmt.is_a?(Prism::CallNode) && stmt.receiver.nil? && stmt.name == :extend &&
@@ -208,9 +189,8 @@ module Rigor
208
189
  end
209
190
  end
210
191
 
211
- # Yields `(variant_const_name, inner_type_node)` for every
212
- # `<variant_method> <Const>, <Type>` call inside the template's
213
- # `<block_method> do ... end` block(s).
192
+ # Yields `(variant_const_name, inner_type_node)` for every `<variant_method> <Const>, <Type>` call inside the
193
+ # template's `<block_method> do ... end` block(s).
214
194
  def each_variant_call(body, template, &)
215
195
  body.each do |stmt|
216
196
  next unless variants_block_call?(stmt, template)
@@ -275,8 +255,7 @@ module Rigor
275
255
 
276
256
  # ADR-16 slice 4 — Concern re-targeting index.
277
257
  #
278
- # Walks every top-level / nested `module M` decl looking for
279
- # the ActiveSupport::Concern shape:
258
+ # Walks every top-level / nested `module M` decl looking for the ActiveSupport::Concern shape:
280
259
  #
281
260
  # module M
282
261
  # extend ActiveSupport::Concern
@@ -287,9 +266,8 @@ module Rigor
287
266
  # end
288
267
  # end
289
268
  #
290
- # The returned Hash maps `module_name => [deferred_call_node, ...]`.
291
- # When a class body later contains `include M`, the substrate
292
- # replays each deferred call against the including class.
269
+ # The returned Hash maps `module_name => [deferred_call_node, ...]`. When a class body later contains `include M`,
270
+ # the substrate replays each deferred call against the including class.
293
271
  #
294
272
  # Slice 4 scope (floor):
295
273
  # - constant-path `include M` only (not `include some_var`).
@@ -334,8 +312,8 @@ module Rigor
334
312
  end
335
313
  end
336
314
 
337
- # Recognises a module body that begins with (or contains at
338
- # top level) an `extend ActiveSupport::Concern` statement.
315
+ # Recognises a module body that begins with (or contains at top level) an `extend ActiveSupport::Concern`
316
+ # statement.
339
317
  def concern_module_body?(body)
340
318
  return false unless body.respond_to?(:body)
341
319
 
@@ -358,13 +336,10 @@ module Rigor
358
336
  end
359
337
  end
360
338
 
361
- # Slice 4 hook. When the current class body contains
362
- # `include M` and M is a Concern with deferred DSL calls,
363
- # replay each deferred call against the including class.
364
- # Acts as a re-targeting walker — no new manifest entries
365
- # needed; downstream `collect_entries` /
366
- # `collect_trait_entries` fire just as if the calls had been
367
- # written directly in X's body.
339
+ # Slice 4 hook. When the current class body contains `include M` and M is a Concern with deferred DSL calls,
340
+ # replay each deferred call against the including class. Acts as a re-targeting walker no new manifest entries
341
+ # needed; downstream `collect_entries` / `collect_trait_entries` fire just as if the calls had been written
342
+ # directly in X's body.
368
343
  def collect_concern_re_targeted_entries(entries, call_node, class_name, concern_index, # rubocop:disable Metrics/ParameterLists
369
344
  templates, registries, hierarchy, environment, path, fact_store = nil)
370
345
  return unless call_node.name == :include && call_node.receiver.nil?
@@ -383,9 +358,8 @@ module Rigor
383
358
  end
384
359
  end
385
360
 
386
- # Builds a lexical inheritance map `class_name => parent_class_name`
387
- # by walking every top-level / nested `class X < Y` decl
388
- # across the AST set.
361
+ # Builds a lexical inheritance map `class_name => parent_class_name` by walking every top-level / nested `class X
362
+ # < Y` decl across the AST set.
389
363
  def build_hierarchy(asts)
390
364
  hierarchy = {}
391
365
  asts.each_value do |ast|
@@ -415,11 +389,9 @@ module Rigor
415
389
  end
416
390
  end
417
391
 
418
- # Yields `(class_name, call_node)` for every Prism::CallNode
419
- # at class-body top level (singleton-context calls). Nested
420
- # method bodies, blocks, and conditionals are skipped — the
421
- # Tier C call shapes the substrate targets all live at the
422
- # class body's top level.
392
+ # Yields `(class_name, call_node)` for every Prism::CallNode at class-body top level (singleton-context calls).
393
+ # Nested method bodies, blocks, and conditionals are skipped — the Tier C call shapes the substrate targets all
394
+ # live at the class body's top level.
423
395
  def walk_class_bodies(node, scope_stack = [], &) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
424
396
  return unless node.respond_to?(:compact_child_nodes)
425
397
 
@@ -487,18 +459,14 @@ module Rigor
487
459
  end
488
460
  end
489
461
 
490
- # ADR-16 Tier B (slice 3b). For each matching call like
491
- # `<X>.<method_name>(:trait_a, :trait_b)` where X inherits
492
- # from the registry's receiver_constraint: collect every
493
- # registered trait symbol's module (silently skipping
494
- # unknown traits per design decision (2)) plus the
495
- # always_included modules, then per-method-explode each
496
- # module's RBS instance methods into the index.
462
+ # ADR-16 Tier B (slice 3b). For each matching call like `<X>.<method_name>(:trait_a, :trait_b)` where X inherits
463
+ # from the registry's receiver_constraint: collect every registered trait symbol's module (silently skipping
464
+ # unknown traits per design decision (2)) plus the always_included modules, then per-method-explode each module's
465
+ # RBS instance methods into the index.
497
466
  #
498
- # Per slice 3 floor (per user agreement): the synthesised
499
- # methods adopt `return_type: "untyped"` (Dynamic[T] at
500
- # dispatch). Precision promotion — looking up the module's
501
- # actual RBS return type — is reserved for the ceiling slice.
467
+ # Per slice 3 floor (per user agreement): the synthesised methods adopt `return_type: "untyped"` (Dynamic[T] at
468
+ # dispatch). Precision promotion looking up the module's actual RBS return type — is reserved for the ceiling
469
+ # slice.
502
470
  def collect_trait_entries(entries, registries, class_name, call_node, hierarchy, environment, path)
503
471
  registries.each do |(plugin_id, registry)|
504
472
  next unless call_node.name == registry.method_name
@@ -511,16 +479,14 @@ module Rigor
511
479
  end
512
480
  end
513
481
 
514
- # Resolves the set of modules to include from a Tier B
515
- # call site:
482
+ # Resolves the set of modules to include from a Tier B call site:
516
483
  #
517
484
  # - `always_included` modules (unconditional);
518
485
  # - one module per literal Symbol argument the call carries
519
486
  # (resolved through `registry.modules_by_symbol`; unknown
520
487
  # symbols silently skipped per design decision (2)).
521
488
  #
522
- # Returns an Array<String> of module names in
523
- # `always_included` order followed by argument order.
489
+ # Returns an Array<String> of module names in `always_included` order followed by argument order.
524
490
  def resolve_trait_modules(registry, call_node)
525
491
  modules = registry.always_included.dup
526
492
  positional_symbols(call_node, registry).each do |symbol|
@@ -558,10 +524,9 @@ module Rigor
558
524
  end
559
525
  end
560
526
 
561
- # Returns the Symbol method-name list defined on `module_name`'s
562
- # RBS instance definition. Empty Array when the module is not
563
- # in the RBS env (silent skip — the synthetic emit produces
564
- # nothing rather than fabricating method names).
527
+ # Returns the Symbol method-name list defined on `module_name`'s RBS instance definition. Empty Array when the
528
+ # module is not in the RBS env (silent skip the synthetic emit produces nothing rather than fabricating method
529
+ # names).
565
530
  def module_instance_method_names(module_name, environment)
566
531
  return [] if environment.nil?
567
532
 
@@ -630,8 +595,7 @@ module Rigor
630
595
  )
631
596
  end
632
597
 
633
- # ADR-18 three-tier fallback for the synthetic method's
634
- # `return_type` string:
598
+ # ADR-18 three-tier fallback for the synthetic method's `return_type` string:
635
599
  #
636
600
  # 1. When `row.returns_from_arg` is present AND the
637
601
  # call-site argument at the declared position is a
@@ -663,13 +627,10 @@ module Rigor
663
627
  fact[source_rep]
664
628
  end
665
629
 
666
- # Extracts the source-text qualified-constant representation
667
- # of the call's positional argument (e.g.,
668
- # `"Types::String"`). Returns nil for non-constant shapes
669
- # (literals, method chains, blocks, …). The floor
670
- # intentionally accepts only ConstantReadNode /
671
- # ConstantPathNode per ADR-18; chained-call argument
672
- # resolution stays deferred.
630
+ # Extracts the source-text qualified-constant representation of the call's positional argument (e.g.,
631
+ # `"Types::String"`). Returns nil for non-constant shapes (literals, method chains, blocks, …). The floor
632
+ # intentionally accepts only ConstantReadNode / ConstantPathNode per ADR-18; chained-call argument resolution
633
+ # stays deferred.
673
634
  def argument_source_representation(call_node, position)
674
635
  args = call_node.arguments&.arguments
675
636
  return nil if args.nil? || position >= args.size
@@ -709,8 +670,7 @@ module Rigor
709
670
  current = parent
710
671
  end
711
672
 
712
- # Fall back to the env's RBS-aware ordering for the case
713
- # where the chain terminates at an RBS-known class
673
+ # Fall back to the env's RBS-aware ordering for the case where the chain terminates at an RBS-known class
714
674
  # (ActiveRecord::Base, Dry::Struct, Sinatra::Base, …).
715
675
  return false if environment.nil?
716
676
 
@@ -6,18 +6,15 @@ module Rigor
6
6
  module LanguageServer
7
7
  # Shared buffer lookup for the LSP providers.
8
8
  #
9
- # Every provider's `provide` opens with the same two steps: translate
10
- # the document URI to an on-disk path, then fetch the open buffer
11
- # entry from the buffer table returning nil to the editor when
12
- # either is missing. The parse step that follows differs per provider
13
- # (strict vs error-tolerant, whole-buffer vs cursor-recovery), so
14
- # only this resolution is shared here.
9
+ # Every provider's `provide` opens with the same two steps: translate the document URI to an on-disk path,
10
+ # then fetch the open buffer entry from the buffer table returning nil to the editor when either is
11
+ # missing. The parse step that follows differs per provider (strict vs error-tolerant, whole-buffer vs
12
+ # cursor-recovery), so only this resolution is shared here.
15
13
  module BufferResolution
16
14
  private
17
15
 
18
- # Resolves `[path, entry]` for the document `uri`, or nil when the
19
- # uri has no file path or no open buffer. A caller that destructures
20
- # `path, entry = buffer_for(uri)` can guard on `entry.nil?` to cover
16
+ # Resolves `[path, entry]` for the document `uri`, or nil when the uri has no file path or no open
17
+ # buffer. A caller that destructures `path, entry = buffer_for(uri)` can guard on `entry.nil?` to cover
21
18
  # both misses (a nil return leaves both locals nil).
22
19
  def buffer_for(uri)
23
20
  path = Uri.to_path(uri)