rigortype 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -4,30 +4,22 @@ require_relative "blueprint"
4
4
 
5
5
  module Rigor
6
6
  module Plugin
7
- # ADR-52 WD1 — the compiled contribution table. Categorises a loaded
8
- # plugin set by which per-call contribution paths each plugin
9
- # actually implements, AND compiles the declarative gates (method
10
- # names, `block_as_methods` method names, `owns_receivers`) into frozen
11
- # lookup structures, so the engine's hot sites discover "no plugin
12
- # cares about this call" in O(1) instead of O(plugins × rules) — a
13
- # top hotspot on plugin-heavy projects (GitLab's 11 plugins, of
14
- # which only 2 implement any per-call path). Built once per
15
- # {Registry}.
7
+ # ADR-52 WD1 — the compiled contribution table. Categorises a loaded plugin set by which per-call
8
+ # contribution paths each plugin actually implements, AND compiles the declarative gates (method names,
9
+ # `block_as_methods` method names, `owns_receivers`) into frozen lookup structures, so the engine's hot
10
+ # sites discover "no plugin cares about this call" in O(1) instead of O(plugins × rules) — a top hotspot
11
+ # on plugin-heavy projects (GitLab's 11 plugins, of which only 2 implement any per-call path). Built once
12
+ # per {Registry}.
16
13
  #
17
- # Ordering contract: the gates only PRUNE consultations that could
18
- # not fire (every pruned rule would have failed its own `methods:` /
19
- # `method_names:` check); the engine still iterates the plugin subsets in
20
- # registry order and each plugin's rules in declaration order, so
21
- # the surviving contributions arrive in exactly the order the
22
- # ungated walk produced — diagnostics stay byte-identical. The
23
- # receiver-class ancestry match for `dynamic_return` still happens
24
- # per-dispatch inside `Plugin::Base#dynamic_return_type`.
14
+ # Ordering contract: the gates only PRUNE consultations that could not fire (every pruned rule would have
15
+ # failed its own `methods:` / `method_names:` check); the engine still iterates the plugin subsets in
16
+ # registry order and each plugin's rules in declaration order, so the surviving contributions arrive in
17
+ # exactly the order the ungated walk produced diagnostics stay byte-identical. The receiver-class
18
+ # ancestry match for `dynamic_return` still happens per-dispatch inside `Plugin::Base#dynamic_return_type`.
25
19
  class ContributionIndex
26
- # Ordered (registry-order) subsets relevant to each collector, and
27
- # the membership sets used to gate the paths within a plugin.
28
- # `for_file_diagnostics` is the subset the runner's per-file
29
- # diagnostic loop visits: plugins overriding
30
- # `#diagnostics_for_file` or declaring at least one `node_rule`.
20
+ # Ordered (registry-order) subsets relevant to each collector, and the membership sets used to gate the
21
+ # paths within a plugin. `for_file_diagnostics` is the subset the runner's per-file diagnostic loop
22
+ # visits: plugins overriding `#diagnostics_for_file` or declaring at least one `node_rule`.
31
23
  attr_reader :for_method_dispatch, :for_statement, :for_file_diagnostics
32
24
 
33
25
  EMPTY_BLOCK_ENTRIES = [].freeze
@@ -38,9 +30,8 @@ module Rigor
38
30
  compile_gates
39
31
  @block_entries_by_method_name = build_block_entries(plugins)
40
32
  @owns_receivers = plugins.flat_map { |p| manifest_for(p)&.owns_receivers || [] }.uniq.freeze
41
- # Per-run ancestry verdict memo, keyed by environment identity
42
- # then class name. Mutable inside the frozen index sound
43
- # because the class graph is fixed for the lifetime of a run.
33
+ # Per-run ancestry verdict memo, keyed by environment identity then class name. Mutable inside the
34
+ # frozen index — sound because the class graph is fixed for the lifetime of a run.
44
35
  @owns_receiver_memo = {}.compare_by_identity
45
36
  freeze
46
37
  end
@@ -48,30 +39,27 @@ module Rigor
48
39
  def dynamic?(plugin) = @dynamic.include?(plugin)
49
40
  def type_specifier?(plugin) = @type_specifier.include?(plugin)
50
41
 
51
- # O(1) "could any plugin contribute a return type for a call named
52
- # `method_name`?" false only when every `dynamic_return` rule is
53
- # `methods:`-gated on other names, in which case the ungated walk
54
- # would have produced zero contributions too.
42
+ # O(1) "could any plugin contribute a return type for a call named `method_name`?" — false only when
43
+ # every `dynamic_return` rule is `methods:`-gated on other names, in which case the ungated walk would
44
+ # have produced zero contributions too.
55
45
  def dispatch_candidate?(method_name)
56
46
  return true if @dynamic_global_gate.nil?
57
47
 
58
48
  @dynamic_global_gate.include?(method_name)
59
49
  end
60
50
 
61
- # O(1) statement-path sibling of {#dispatch_candidate?} over the
62
- # `type_specifier` rules (which are always `methods:`-gated).
51
+ # O(1) statement-path sibling of {#dispatch_candidate?} over the `type_specifier` rules (which are
52
+ # always `methods:`-gated).
63
53
  def statement_candidate?(method_name)
64
54
  return true if @type_specifier_global_gate.nil?
65
55
 
66
56
  @type_specifier_global_gate.include?(method_name)
67
57
  end
68
58
 
69
- # Per-plugin gate: false when the plugin declares no
70
- # `dynamic_return` rules at all, or when every rule is
71
- # `methods:`-gated and none lists `method_name` i.e. when
72
- # `#dynamic_return_type` would return nil without ever entering a
73
- # rule block. Subsumes the old `dynamic?(plugin)` membership
74
- # check at the collector's call site.
59
+ # Per-plugin gate: false when the plugin declares no `dynamic_return` rules at all, or when every rule
60
+ # is `methods:`-gated and none lists `method_name` — i.e. when `#dynamic_return_type` would return nil
61
+ # without ever entering a rule block. Subsumes the old `dynamic?(plugin)` membership check at the
62
+ # collector's call site.
75
63
  def dynamic_candidate_for?(plugin, method_name)
76
64
  return false unless @dynamic.include?(plugin)
77
65
 
@@ -79,8 +67,7 @@ module Rigor
79
67
  gate.nil? || gate.include?(method_name)
80
68
  end
81
69
 
82
- # Per-plugin gate over `type_specifier` rules; same contract as
83
- # {#dynamic_candidate_for?}.
70
+ # Per-plugin gate over `type_specifier` rules; same contract as {#dynamic_candidate_for?}.
84
71
  def type_specifier_candidate_for?(plugin, method_name)
85
72
  return false unless @type_specifier.include?(plugin)
86
73
 
@@ -88,18 +75,16 @@ module Rigor
88
75
  gate.nil? || gate.include?(method_name)
89
76
  end
90
77
 
91
- # The `Macro::BlockAsMethod` entries whose `method_names` include
92
- # `method_name`, in (plugin registration, manifest declaration)
93
- # order — the same first-match order the previous plugins ×
94
- # entries walk visited.
78
+ # The `Macro::BlockAsMethod` entries whose `method_names` include `method_name`, in (plugin
79
+ # registration, manifest declaration) order — the same first-match order the previous plugins × entries
80
+ # walk visited.
95
81
  def block_entries_for(method_name)
96
82
  @block_entries_by_method_name.fetch(method_name, EMPTY_BLOCK_ENTRIES)
97
83
  end
98
84
 
99
- # True when `class_name` equals or inherits from any plugin's
100
- # manifest-declared `owns_receivers:` entry. The union is compiled
101
- # at build time (almost always empty → O(1) false) and per-class
102
- # verdicts memoise per environment.
85
+ # True when `class_name` equals or inherits from any plugin's manifest-declared `owns_receivers:` entry.
86
+ # The union is compiled at build time (almost always empty → O(1) false) and per-class verdicts
87
+ # memoise per environment.
103
88
  def owns_receiver?(class_name, environment)
104
89
  return false if @owns_receivers.empty? || class_name.nil?
105
90
 
@@ -135,12 +120,10 @@ module Rigor
135
120
  @type_specifier_global_gate = union_gate(@type_specifier_gates)
136
121
  end
137
122
 
138
- # ADR-52 WD3 — the legacy ungated `flow_contribution_for` hook was
139
- # deleted pre-1.0. A plugin still defining it would silently never
140
- # be called, which is the worst failure mode for its author
141
- # surface a loud load-time error with the migration mapping
142
- # instead. (The Base default is gone, so any definer wrote it
143
- # themselves.)
123
+ # ADR-52 WD3 — the legacy ungated `flow_contribution_for` hook was deleted pre-1.0. A plugin still
124
+ # defining it would silently never be called, which is the worst failure mode for its author — surface
125
+ # a loud load-time error with the migration mapping instead. (The Base default is gone, so any definer
126
+ # wrote it themselves.)
144
127
  def reject_legacy_flow_hook!(plugin)
145
128
  return unless plugin.respond_to?(:flow_contribution_for)
146
129
 
@@ -151,26 +134,23 @@ module Rigor
151
134
  "facts via `narrowing_facts` — see the CHANGELOG migration note."
152
135
  end
153
136
 
154
- # Same `Method#owner` trick for the per-file diagnostics hook —
155
- # the Base default returns `[]`, so a non-overriding plugin can be
156
- # skipped without calling it.
137
+ # Same `Method#owner` trick for the per-file diagnostics hook — the Base default returns `[]`, so a
138
+ # non-overriding plugin can be skipped without calling it.
157
139
  def file_diagnostics_overridden?(plugin)
158
140
  plugin.method(:diagnostics_for_file).owner != Rigor::Plugin::Base
159
141
  end
160
142
 
161
- # `plugin → Set[Symbol] | nil`. A frozen Set when EVERY rule of
162
- # the plugin is `methods:`-gated (the union of those names); nil
163
- # when any rule is ungated (the plugin must be consulted for every
164
- # method name, exactly as before).
143
+ # `plugin → Set[Symbol] | nil`. A frozen Set when EVERY rule of the plugin is `methods:`-gated (the
144
+ # union of those names); nil when any rule is ungated (the plugin must be consulted for every method
145
+ # name, exactly as before).
165
146
  def build_name_gates(members)
166
147
  gates = {}
167
148
  members.each do |plugin|
168
149
  rules = yield(plugin)
169
- # A static method-name Array can be compiled into the gate. A
170
- # run-time callable `methods:` (ADR-52 slice 4) is unknown until
171
- # after `#prepare`, and a receiver-only rule has no `methods:`
172
- # either makes the plugin ungated-by-name (nil), consulted on
173
- # every dispatch and filtered in the instance path.
150
+ # A static method-name Array can be compiled into the gate. A run-time callable `methods:` (ADR-52
151
+ # slice 4) is unknown until after `#prepare`, and a receiver-only rule has no `methods:` — either
152
+ # makes the plugin ungated-by-name (nil), consulted on every dispatch and filtered in the instance
153
+ # path.
174
154
  gates[plugin] =
175
155
  if rules.all? { |rule| rule[:methods].is_a?(Array) }
176
156
  rules.flat_map { |rule| rule[:methods] }.to_set.freeze
@@ -179,19 +159,17 @@ module Rigor
179
159
  gates.freeze
180
160
  end
181
161
 
182
- # The registry-wide union of per-plugin gates, or nil when any
183
- # plugin is ungated (no global pruning possible). An empty
184
- # plugin set unions to an empty frozen Set — the collectors'
185
- # `relevant.empty?` early return fires before it is consulted.
162
+ # The registry-wide union of per-plugin gates, or nil when any plugin is ungated (no global pruning
163
+ # possible). An empty plugin set unions to an empty frozen Set — the collectors' `relevant.empty?`
164
+ # early return fires before it is consulted.
186
165
  def union_gate(gates)
187
166
  return nil if gates.value?(nil)
188
167
 
189
168
  gates.values.reduce(Set.new) { |acc, names| acc.merge(names) }.freeze
190
169
  end
191
170
 
192
- # `method-name Symbol → [BlockAsMethod entries]`, insertion-ordered
193
- # by (plugin, declaration). Method names are Symbol-normalised by
194
- # `Macro::BlockAsMethod#initialize`.
171
+ # `method-name Symbol → [BlockAsMethod entries]`, insertion-ordered by (plugin, declaration). Method
172
+ # names are Symbol-normalised by `Macro::BlockAsMethod#initialize`.
195
173
  def build_block_entries(plugins)
196
174
  table = {}
197
175
  plugins.each do |plugin|
@@ -204,9 +182,8 @@ module Rigor
204
182
  table.freeze
205
183
  end
206
184
 
207
- # Mirrors `MethodDispatcher`'s previous per-dispatch matching:
208
- # exact-name fast path, then `Environment#class_ordering`,
209
- # degrading to "no match" on any resolution failure.
185
+ # Mirrors `MethodDispatcher`'s previous per-dispatch matching: exact-name fast path, then
186
+ # `Environment#class_ordering`, degrading to "no match" on any resolution failure.
210
187
  def class_matches_owner?(class_name, owner, environment)
211
188
  return true if class_name == owner
212
189
  return false if environment.nil?
@@ -216,9 +193,8 @@ module Rigor
216
193
  false
217
194
  end
218
195
 
219
- # Manifest access tolerant of manifest-less plugin doubles in unit
220
- # specs (a real loaded plugin always carries one — the loader
221
- # validates it).
196
+ # Manifest access tolerant of manifest-less plugin doubles in unit specs (a real loaded plugin always
197
+ # carries one — the loader validates it).
222
198
  def manifest_for(plugin)
223
199
  plugin.manifest
224
200
  rescue StandardError
@@ -226,58 +202,43 @@ module Rigor
226
202
  end
227
203
  end
228
204
 
229
- # Read-side query API over the plugins loaded for a single
230
- # `Analysis::Runner.run`. Constructed by
231
- # {Rigor::Plugin::Loader.load} and exposed downstream so the
232
- # contribution merger (slice 3) and diagnostic provenance
233
- # (slice 5) can iterate over loaded plugin instances in
234
- # deterministic order.
205
+ # Read-side query API over the plugins loaded for a single `Analysis::Runner.run`. Constructed by
206
+ # {Rigor::Plugin::Loader.load} and exposed downstream so the contribution merger (slice 3) and diagnostic
207
+ # provenance (slice 5) can iterate over loaded plugin instances in deterministic order.
235
208
  #
236
- # The registry is read-only after construction; ordering is
237
- # the order in which {Rigor::Plugin::Loader} resolved
238
- # configuration entries, which is project-config order with
239
- # plugin-id alphabetical as the tie-breaker.
209
+ # The registry is read-only after construction; ordering is the order in which {Rigor::Plugin::Loader}
210
+ # resolved configuration entries, which is project-config order with plugin-id alphabetical as the
211
+ # tie-breaker.
240
212
  #
241
- # ADR-15 Phase 3 — alongside the instantiated `plugins`, the
242
- # registry carries `blueprints`: a frozen, Ractor-shareable
243
- # `Array<Blueprint>` that records how to re-instantiate the
244
- # same plugin set in a worker Ractor. The eventual Phase 4
245
- # pool ships `blueprints` across the boundary and calls
246
- # {.materialize} per-Ractor; the live `plugins` carriage on
247
- # the coordinator registry stays unchanged.
213
+ # ADR-15 Phase 3 — alongside the instantiated `plugins`, the registry carries `blueprints`: a frozen,
214
+ # Ractor-shareable `Array<Blueprint>` that records how to re-instantiate the same plugin set in a worker
215
+ # Ractor. The eventual Phase 4 pool ships `blueprints` across the boundary and calls {.materialize}
216
+ # per-Ractor; the live `plugins` carriage on the coordinator registry stays unchanged.
248
217
  class Registry
249
218
  attr_reader :plugins, :load_errors, :blueprints, :contribution_index
250
219
 
251
- # @param plugins [Array<Rigor::Plugin::Base>] instantiated
252
- # plugin instances in deterministic order.
253
- # @param load_errors [Array<Rigor::Plugin::LoadError>] failures
254
- # surfaced during loading. Each error is also turned into a
255
- # diagnostic by the runner.
256
- # @param blueprints [Array<Rigor::Plugin::Blueprint>] frozen,
257
- # Ractor-shareable replay descriptors aligned 1:1 with
258
- # `plugins`. The loader fills this in; callers that
259
- # construct Registry manually MAY pass `[]` and accept
260
- # that {.materialize} cannot replay the set.
220
+ # @param plugins [Array<Rigor::Plugin::Base>] instantiated plugin instances in deterministic order.
221
+ # @param load_errors [Array<Rigor::Plugin::LoadError>] failures surfaced during loading. Each error is
222
+ # also turned into a diagnostic by the runner.
223
+ # @param blueprints [Array<Rigor::Plugin::Blueprint>] frozen, Ractor-shareable replay descriptors
224
+ # aligned 1:1 with `plugins`. The loader fills this in; callers that construct Registry manually MAY
225
+ # pass `[]` and accept that {.materialize} cannot replay the set.
261
226
  def initialize(plugins: [], load_errors: [], blueprints: [])
262
227
  @plugins = plugins.dup.freeze
263
228
  @load_errors = load_errors.dup.freeze
264
229
  @blueprints = blueprints.dup.freeze
265
230
  @contribution_index = ContributionIndex.new(@plugins)
266
- # ADR-52 WD1 — aggregate queries the engine issues per def /
267
- # per diagnostic candidate / per path are compiled once here
268
- # (the registry is frozen, so the flat_map-on-every-call
269
- # versions re-derived an invariant). `@contracts_by_path` is a
270
- # mutable per-path memo inside the frozen registry — safe
271
- # because the contract set and the glob semantics are fixed
272
- # for the lifetime of the run.
231
+ # ADR-52 WD1 — aggregate queries the engine issues per def / per diagnostic candidate / per path are
232
+ # compiled once here (the registry is frozen, so the flat_map-on-every-call versions re-derived an
233
+ # invariant). `@contracts_by_path` is a mutable per-path memo inside the frozen registry — safe
234
+ # because the contract set and the glob semantics are fixed for the lifetime of the run.
273
235
  @additional_initializers = @plugins.flat_map { |p| safe_manifest(p)&.additional_initializers || [] }.freeze
274
236
  @open_receivers = @plugins.flat_map { |p| (safe_manifest(p)&.open_receivers || []).map(&:to_s) }.uniq.freeze
275
237
  @open_receivers_set = @open_receivers.to_set.freeze
276
238
  @protocol_contracts = @plugins.flat_map { |p| safe_protocol_contracts(p) }.freeze
277
239
  @contracts_by_path = {}
278
- # ADR-52 WD4 — the single engine-owned node-rule walk, compiled
279
- # once per run from the node-rule plugin subset (registry order).
280
- # The runner reuses it for every file; it builds fresh per-file
240
+ # ADR-52 WD4 — the single engine-owned node-rule walk, compiled once per run from the node-rule
241
+ # plugin subset (registry order). The runner reuses it for every file; it builds fresh per-file
281
242
  # state internally, so it is safe to freeze and share.
282
243
  @node_rule_walk = NodeRuleWalk.new(@plugins)
283
244
  freeze
@@ -286,15 +247,12 @@ module Rigor
286
247
  # ADR-52 WD4 — the per-run node-rule walk (see {NodeRuleWalk}).
287
248
  attr_reader :node_rule_walk
288
249
 
289
- # ADR-15 Phase 3 — build a fresh Registry from the supplied
290
- # blueprint set by replaying {Blueprint#materialize} per
291
- # entry against `services`. The returned registry carries
292
- # NEW plugin instances (mutable per-Ractor accumulators
293
- # included) and the same blueprint set, so a worker can
294
- # hand the materialised registry to Environment without
295
- # losing the replay handle. `load_errors` is intentionally
296
- # empty: load-time failures already surfaced in the
297
- # coordinator registry and don't repeat per worker.
250
+ # ADR-15 Phase 3 — build a fresh Registry from the supplied blueprint set by replaying
251
+ # {Blueprint#materialize} per entry against `services`. The returned registry carries NEW plugin
252
+ # instances (mutable per-Ractor accumulators included) and the same blueprint set, so a worker can hand
253
+ # the materialised registry to Environment without losing the replay handle. `load_errors` is
254
+ # intentionally empty: load-time failures already surfaced in the coordinator registry and don't repeat
255
+ # per worker.
298
256
  def self.materialize(blueprints:, services:)
299
257
  plugins = blueprints.map { |bp| bp.materialize(services: services) }
300
258
  new(plugins: plugins, blueprints: blueprints, load_errors: [])
@@ -317,27 +275,20 @@ module Rigor
317
275
  !load_errors.empty?
318
276
  end
319
277
 
320
- # ADR-13 — flat ordered list of every loaded plugin's
321
- # manifest-declared {TypeNodeResolver} instances, in plugin
322
- # registration order. `Environment#build_name_scope` builds a
323
- # `TypeNode::ResolverChain` from this list (environment.rb).
324
- # The first non-nil `#resolve(node, scope)` return wins per
325
- # ADR-13 WD3 / WD5 — registration order is the user's lever.
278
+ # ADR-13 — flat ordered list of every loaded plugin's manifest-declared {TypeNodeResolver} instances,
279
+ # in plugin registration order. `Environment#build_name_scope` builds a `TypeNode::ResolverChain` from
280
+ # this list (environment.rb). The first non-nil `#resolve(node, scope)` return wins per ADR-13 WD3 /
281
+ # WD5 registration order is the user's lever.
326
282
  def type_node_resolvers
327
283
  plugins.flat_map { |plugin| plugin.manifest.type_node_resolvers }
328
284
  end
329
285
 
330
- # ADR-20 slice 6 — aggregate every loaded plugin's
331
- # manifest-declared HKT registrations + definitions
332
- # into a single `Inference::HktRegistry` overlay that
333
- # `Environment#hkt_registry` merges on top of the
334
- # bundled `Builtins::HktBuiltins.registry`. Last
335
- # plugin to register a URI wins (registration order
336
- # determined by the user's `plugins:` list); user
337
- # `.rbs` overlays merge on top of this overlay last.
338
- # Returns `Inference::HktRegistry::EMPTY` when no
339
- # plugin contributes HKT entries so callers can skip
340
- # the merge.
286
+ # ADR-20 slice 6 — aggregate every loaded plugin's manifest-declared HKT registrations + definitions
287
+ # into a single `Inference::HktRegistry` overlay that `Environment#hkt_registry` merges on top of the
288
+ # bundled `Builtins::HktBuiltins.registry`. Last plugin to register a URI wins (registration order
289
+ # determined by the user's `plugins:` list); user `.rbs` overlays merge on top of this overlay last.
290
+ # Returns `Inference::HktRegistry::EMPTY` when no plugin contributes HKT entries so callers can skip the
291
+ # merge.
341
292
  def hkt_overlay_registry
342
293
  registrations = plugins.flat_map { |plugin| plugin.manifest.hkt_registrations }
343
294
  definitions = plugins.flat_map { |plugin| plugin.manifest.hkt_definitions }
@@ -346,25 +297,20 @@ module Rigor
346
297
  Inference::HktRegistry.new(registrations: registrations, definitions: definitions)
347
298
  end
348
299
 
349
- # ADR-25 — flat, ordered list of every loaded plugin's
350
- # resolved RBS signature directories (absolute paths), in
351
- # plugin registration order. `Environment.for_project`
352
- # merges these into the signature-path set fed to
353
- # `RbsLoader`, alongside the configuration's `signature_paths:`
354
- # and the `bundler:` / `rbs_collection:` discovery output.
300
+ # ADR-25 — flat, ordered list of every loaded plugin's resolved RBS signature directories (absolute
301
+ # paths), in plugin registration order. `Environment.for_project` merges these into the signature-path
302
+ # set fed to `RbsLoader`, alongside the configuration's `signature_paths:` and the `bundler:` /
303
+ # `rbs_collection:` discovery output.
355
304
  def signature_paths
356
305
  plugins.flat_map(&:signature_paths)
357
306
  end
358
307
 
359
- # ADR-26 — the aggregate set of "open" receiver class names
360
- # declared across loaded plugins (manifest `open_receivers:`).
361
- # A class is open when a plugin vouches that it responds
362
- # beyond its RBS-declared method surface. `open_receiver?`
363
- # is the membership predicate `Analysis::CheckRules` consults
364
- # to skip the `call.undefined-method` rule for such a class.
365
- # Compiled at construction (ADR-52 WD1) — the predicate runs per
366
- # undefined-method candidate, so the previous per-call flat_map
367
- # re-derived a frozen invariant.
308
+ # ADR-26 — the aggregate set of "open" receiver class names declared across loaded plugins (manifest
309
+ # `open_receivers:`). A class is open when a plugin vouches that it responds beyond its RBS-declared
310
+ # method surface. `open_receiver?` is the membership predicate `Analysis::CheckRules` consults to skip
311
+ # the `call.undefined-method` rule for such a class. Compiled at construction (ADR-52 WD1) — the
312
+ # predicate runs per undefined-method candidate, so the previous per-call flat_map re-derived a frozen
313
+ # invariant.
368
314
  attr_reader :open_receivers
369
315
 
370
316
  def open_receiver?(class_name)
@@ -373,42 +319,30 @@ module Rigor
373
319
  @open_receivers_set.include?(class_name.to_s)
374
320
  end
375
321
 
376
- # ADR-28 — flat, ordered list of every loaded plugin's
377
- # path-scoped method-protocol contracts, in plugin
378
- # registration order. Read from each plugin's
379
- # `#protocol_contracts` (which the manifest backs by default
380
- # but a plugin MAY override to fold in per-project config).
381
- # Consumed by `Inference::MethodParameterBinder` (the
382
- # parameter-type provision) and by contributing plugins'
383
- # `#diagnostics_for_file` hooks (the presence + return-type
384
- # check). Compiled at construction (ADR-52 WD1); a plugin's
385
- # config-folding `#protocol_contracts` override is stable for the
386
- # run because config is injected at construction.
322
+ # ADR-28 — flat, ordered list of every loaded plugin's path-scoped method-protocol contracts, in
323
+ # plugin registration order. Read from each plugin's `#protocol_contracts` (which the manifest backs
324
+ # by default but a plugin MAY override to fold in per-project config). Consumed by
325
+ # `Inference::MethodParameterBinder` (the parameter-type provision) and by contributing plugins'
326
+ # `#diagnostics_for_file` hooks (the presence + return-type check). Compiled at construction (ADR-52
327
+ # WD1); a plugin's config-folding `#protocol_contracts` override is stable for the run because config
328
+ # is injected at construction.
387
329
  attr_reader :protocol_contracts
388
330
 
389
- # ADR-38 — flat, ordered list of every loaded plugin's
390
- # manifest-declared `Rigor::Plugin::AdditionalInitializer`
391
- # entries. `Inference::ScopeIndexer` consults the set at its
392
- # read-before-write nil soundness gate: a `def` whose name an
393
- # entry covers, on a class that equals or inherits from the
394
- # entry's `receiver_constraint`, is treated like `initialize`.
395
- # Compiled at construction (ADR-52 WD1) — consulted per `def`
396
- # node, ×2 sites in `ScopeIndexer`.
331
+ # ADR-38 — flat, ordered list of every loaded plugin's manifest-declared
332
+ # `Rigor::Plugin::AdditionalInitializer` entries. `Inference::ScopeIndexer` consults the set at its
333
+ # read-before-write nil soundness gate: a `def` whose name an entry covers, on a class that equals or
334
+ # inherits from the entry's `receiver_constraint`, is treated like `initialize`. Compiled at
335
+ # construction (ADR-52 WD1) consulted per `def` node, ×2 sites in `ScopeIndexer`.
397
336
  attr_reader :additional_initializers
398
337
 
399
- # ADR-28 — the subset of `protocol_contracts` whose
400
- # `path_glob` matches `path`. Contract globs are authored
401
- # project-root-relative (`lib/controller/**/*.rb`); the
402
- # analyzer may hand this method either a project-relative
403
- # path (`rigor check` run from the project root) or an
404
- # absolute one (run from elsewhere, or a spec tmpdir), so the
405
- # glob is matched both directly and as a `**/`-prefixed path
406
- # suffix. `File::FNM_PATHNAME` keeps `*` from crossing `/`;
407
- # `File::FNM_EXTGLOB` enables `{a,b}` groups. Returns `[]` for
408
- # a nil path so the binder can call this unconditionally.
409
- # Memoised per path (ADR-52 WD1) — consulted per `def` node by
410
- # `MethodParameterBinder`, and the fnmatch sweep over every
411
- # contract is pure in (contract set, path).
338
+ # ADR-28 — the subset of `protocol_contracts` whose `path_glob` matches `path`. Contract globs are
339
+ # authored project-root-relative (`lib/controller/**/*.rb`); the analyzer may hand this method either a
340
+ # project-relative path (`rigor check` run from the project root) or an absolute one (run from
341
+ # elsewhere, or a spec tmpdir), so the glob is matched both directly and as a `**/`-prefixed path
342
+ # suffix. `File::FNM_PATHNAME` keeps `*` from crossing `/`; `File::FNM_EXTGLOB` enables `{a,b}` groups.
343
+ # Returns `[]` for a nil path so the binder can call this unconditionally. Memoised per path (ADR-52
344
+ # WD1) consulted per `def` node by `MethodParameterBinder`, and the fnmatch sweep over every contract
345
+ # is pure in (contract set, path).
412
346
  def contracts_for_path(path)
413
347
  return [] if path.nil?
414
348
 
@@ -417,17 +351,12 @@ module Rigor
417
351
  protocol_contracts.select { |contract| path_matches_glob?(contract.path_glob, path_s) }.freeze
418
352
  end
419
353
 
420
- # ADR-32 WD4 + WD5 — flat ordered list of
421
- # `[plugin, callable]` pairs for every loaded plugin that
422
- # declares a `source_rbs_synthesizer:` in its manifest. The
423
- # engine invokes each callable once per analysed Ruby source
424
- # file at env-build time; non-nil return strings are merged
425
- # into the RBS environment as virtual signature sources.
426
- # The full plugin instance is carried alongside the
427
- # callable so the engine's cache layer (WD5) can compose
428
- # `plugin.plugin_entry` into its per-file descriptor — a
429
- # config change to the plugin (e.g. flipping
430
- # `require_magic_comment:`) invalidates the dependent
354
+ # ADR-32 WD4 + WD5 — flat ordered list of `[plugin, callable]` pairs for every loaded plugin that
355
+ # declares a `source_rbs_synthesizer:` in its manifest. The engine invokes each callable once per
356
+ # analysed Ruby source file at env-build time; non-nil return strings are merged into the RBS
357
+ # environment as virtual signature sources. The full plugin instance is carried alongside the callable
358
+ # so the engine's cache layer (WD5) can compose `plugin.plugin_entry` into its per-file descriptor a
359
+ # config change to the plugin (e.g. flipping `require_magic_comment:`) invalidates the dependent
431
360
  # synthesizer cache without any plugin-side bookkeeping.
432
361
  def source_rbs_synthesizers
433
362
  plugins.filter_map do |plugin|
@@ -448,9 +377,8 @@ module Rigor
448
377
  File.fnmatch?(File.join("**", glob), path, FNMATCH_FLAGS)
449
378
  end
450
379
 
451
- # Construction-time manifest access tolerant of manifest-less
452
- # plugin doubles in unit specs (a real loaded plugin always
453
- # carries one — the loader validates it). Mirrors
380
+ # Construction-time manifest access tolerant of manifest-less plugin doubles in unit specs (a real
381
+ # loaded plugin always carries one the loader validates it). Mirrors
454
382
  # `ContributionIndex#manifest_for`.
455
383
  def safe_manifest(plugin)
456
384
  plugin.manifest
@@ -465,9 +393,8 @@ module Rigor
465
393
  end
466
394
  end
467
395
 
468
- # Assigned after the class body completes — `Registry.new` runs at
469
- # assignment time and `#initialize` calls private helpers defined
470
- # late in the body.
396
+ # Assigned after the class body completes — `Registry.new` runs at assignment time and `#initialize` calls
397
+ # private helpers defined late in the body.
471
398
  Registry::EMPTY = Registry.new.freeze
472
399
  end
473
400
  end
@@ -2,43 +2,32 @@
2
2
 
3
3
  module Rigor
4
4
  module Plugin
5
- # Dependency-injection container handed to every plugin's
6
- # {Rigor::Plugin::Base#init} method. Plugins read from the
7
- # container; they MUST NOT mutate it. The container is
8
- # constructed once per `Analysis::Runner.run` and destroyed
9
- # at the end of the run.
5
+ # Dependency-injection container handed to every plugin's {Rigor::Plugin::Base#init} method. Plugins read
6
+ # from the container; they MUST NOT mutate it. The container is constructed once per
7
+ # `Analysis::Runner.run` and destroyed at the end of the run.
10
8
  #
11
- # ADR-2 § "Registration, Configuration, and Caching" reserves
12
- # this surface for "constructor injection for analyzer
13
- # services such as reflection providers, type factories,
14
- # loggers, and configuration readers". Slice 1 wires four
15
- # of those:
9
+ # ADR-2 § "Registration, Configuration, and Caching" reserves this surface for "constructor injection for
10
+ # analyzer services such as reflection providers, type factories, loggers, and configuration readers".
11
+ # Slice 1 wires four of those:
16
12
  #
17
13
  # - `reflection`: the {Rigor::Reflection} read-side facade.
18
14
  # - `type`: the {Rigor::Type::Combinator} factory module.
19
15
  # - `configuration`: the project's {Rigor::Configuration}.
20
- # - `cache_store`: the {Rigor::Cache::Store} the run is using
21
- # (or `nil` when caching is disabled). Slice 6 wires
22
- # plugin-side cache producers through this entry.
16
+ # - `cache_store`: the {Rigor::Cache::Store} the run is using (or `nil` when caching is disabled). Slice 6
17
+ # wires plugin-side cache producers through this entry.
23
18
  #
24
- # Loggers are not yet a public surface in the core analyzer;
25
- # they will be added when the diagnostics formatter grows a
26
- # progress channel.
19
+ # Loggers are not yet a public surface in the core analyzer; they will be added when the diagnostics
20
+ # formatter grows a progress channel.
27
21
  #
28
- # Slice 2 (Plugin trust / I/O policy) extends the container
29
- # with `trust_policy` and a per-plugin `io_boundary_for(plugin_id)`
30
- # factory. Plugins should reach for the boundary rather than
31
- # raw `File.read` so reads stay within the trusted scope and
32
- # feed cache invalidation; ADR-2 § "Plugin Trust and I/O
33
- # Policy" documents the trust model the boundary enforces.
22
+ # Slice 2 (Plugin trust / I/O policy) extends the container with `trust_policy` and a per-plugin
23
+ # `io_boundary_for(plugin_id)` factory. Plugins should reach for the boundary rather than raw `File.read`
24
+ # so reads stay within the trusted scope and feed cache invalidation; ADR-2 § "Plugin Trust and I/O Policy"
25
+ # documents the trust model the boundary enforces.
34
26
  #
35
- # ADR-9 slice 2 adds `fact_store`: the per-run cross-plugin
36
- # `Plugin::FactStore`. Producer plugins publish their facts
37
- # in `#prepare(services)` (slice 3); consumer plugins read in
38
- # `#diagnostics_for_file` via `services.fact_store.read(...)`.
39
- # A fresh `FactStore` instance is constructed per Services
40
- # when none is supplied — the runner threads its own instance
41
- # in once slice 3 wires `#prepare` invocation.
27
+ # ADR-9 slice 2 adds `fact_store`: the per-run cross-plugin `Plugin::FactStore`. Producer plugins publish
28
+ # their facts in `#prepare(services)` (slice 3); consumer plugins read in `#diagnostics_for_file` via
29
+ # `services.fact_store.read(...)`. A fresh `FactStore` instance is constructed per Services when none is
30
+ # supplied — the runner threads its own instance in once slice 3 wires `#prepare` invocation.
42
31
  class Services
43
32
  attr_reader :reflection, :type, :configuration, :cache_store, :trust_policy, :fact_store
44
33
 
@@ -55,10 +44,9 @@ module Rigor
55
44
  freeze
56
45
  end
57
46
 
58
- # Returns a fresh {IoBoundary} bound to `plugin_id` and the
59
- # current `trust_policy`. The boundary accumulates per-plugin
60
- # cache descriptor entries; the loader / contribution merger
61
- # constructs one boundary per plugin per run.
47
+ # Returns a fresh {IoBoundary} bound to `plugin_id` and the current `trust_policy`. The boundary
48
+ # accumulates per-plugin cache descriptor entries; the loader / contribution merger constructs one
49
+ # boundary per plugin per run.
62
50
  def io_boundary_for(plugin_id)
63
51
  IoBoundary.new(policy: @trust_policy, plugin_id: plugin_id)
64
52
  end