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,28 +4,24 @@ require_relative "box"
4
4
 
5
5
  module Rigor
6
6
  module Plugin
7
- # ADR-39 slice 5 — the selectable isolation strategy for target-library
8
- # invocation. A plugin invokes a pure method on a trusted target
9
- # library (e.g. `ActiveSupport::Inflector.pluralize("post")`) through
10
- # {.call}; how much the invocation is isolated from Rigor's own process
11
- # is a **configurable strategy** (`RIGOR_PLUGIN_ISOLATION` env; the
12
- # `exe/rigor` launcher maps `.rigor.yml`'s `plugins_isolation:` onto it
7
+ # ADR-39 slice 5 — the selectable isolation strategy for target-library invocation. A plugin invokes a
8
+ # pure method on a trusted target library (e.g. `ActiveSupport::Inflector.pluralize("post")`) through
9
+ # {.call}; how much the invocation is isolated from Rigor's own process is a **configurable strategy**
10
+ # (`RIGOR_PLUGIN_ISOLATION` env; the `exe/rigor` launcher maps `.rigor.yml`'s `plugins_isolation:` onto it
13
11
  # before re-exec). Three backends behind one interface:
14
12
  #
15
- # - `none` — load into the main space and call directly.
16
- # Lowest cost; no isolation. Used as the fallback where fork is
17
- # unavailable; fine because the invoked library is trusted + pure.
18
- # - `ruby_box` call inside a {Box} (`Ruby::Box`, `RUBY_BOX=1`). Isolates
19
- # core-class monkey-patches + lets gem versions coexist, but a native
20
- # crash in the boxed work still takes the process down (in-process).
21
- # - `process` (**default**) call in a forked worker ({Process}); returns data over a
22
- # pipe. The strongest: a child crash (even `SIGSEGV`) is contained —
23
- # the parent survives and declines. Higher cost (fork + IPC).
13
+ # - `none` — load into the main space and call directly. Lowest cost; no isolation. Used as the fallback
14
+ # where fork is unavailable; fine because the invoked library is trusted + pure.
15
+ # - `ruby_box` call inside a {Box} (`Ruby::Box`, `RUBY_BOX=1`). Isolates core-class monkey-patches +
16
+ # lets gem versions coexist, but a native crash in the boxed work still takes the process down
17
+ # (in-process).
18
+ # - `process` (**default**) call in a forked worker ({Process}); returns data over a pipe. The
19
+ # strongest: a child crash (even `SIGSEGV`) is contained the parent survives and declines. Higher cost
20
+ # (fork + IPC).
24
21
  #
25
- # All three answer with the method's return value, or raise
26
- # {Unavailable} (never approximate) when the target library cannot be
27
- # reached in the chosen strategy — the caller's per-plugin rescue turns
28
- # that into silence, never a wrong fact.
22
+ # All three answer with the method's return value, or raise {Unavailable} (never approximate) when the
23
+ # target library cannot be reached in the chosen strategy the caller's per-plugin rescue turns that into
24
+ # silence, never a wrong fact.
29
25
  module Isolation
30
26
  class Unavailable < StandardError
31
27
  end
@@ -34,36 +30,31 @@ module Rigor
34
30
 
35
31
  module_function
36
32
 
37
- # The default strategy. `process` (a crash-contained forked worker)
38
- # is the default: it isolates the target library's monkey-patches +
39
- # crashes from Rigor with no in-process contamination, and forks a
40
- # single persistent worker (not one per call). It falls back to
41
- # `none` where fork is unavailable (see {#backend}).
33
+ # The default strategy. `process` (a crash-contained forked worker) is the default: it isolates the
34
+ # target library's monkey-patches + crashes from Rigor with no in-process contamination, and forks a
35
+ # single persistent worker (not one per call). It falls back to `none` where fork is unavailable (see
36
+ # {#backend}).
42
37
  DEFAULT = "process"
43
38
 
44
- # The configured strategy name (`RIGOR_PLUGIN_ISOLATION`), defaulting
45
- # to {DEFAULT} for any unset / unrecognised value.
39
+ # The configured strategy name (`RIGOR_PLUGIN_ISOLATION`), defaulting to {DEFAULT} for any unset /
40
+ # unrecognised value.
46
41
  def strategy_name
47
42
  name = ENV["RIGOR_PLUGIN_ISOLATION"].to_s
48
43
  STRATEGIES.include?(name) ? name : DEFAULT
49
44
  end
50
45
 
51
- # Invokes `receiver.method(*args)` on a target library, requiring
52
- # `feature` first, under the configured isolation strategy. `receiver`
53
- # is a constant name (String), `method` a Symbol from the caller's
54
- # allow-list, and `args` simple, Marshal-able / inspectable values
55
- # (Strings) — never free input. Returns the result, or raises
56
- # {Unavailable}.
46
+ # Invokes `receiver.method(*args)` on a target library, requiring `feature` first, under the configured
47
+ # isolation strategy. `receiver` is a constant name (String), `method` a Symbol from the caller's
48
+ # allow-list, and `args` simple, Marshal-able / inspectable values (Strings) never free input. Returns
49
+ # the result, or raises {Unavailable}.
57
50
  def call(feature:, receiver:, method:, args:)
58
51
  backend.call(feature: feature, receiver: receiver, method: method, args: args)
59
52
  end
60
53
 
61
- # The backend module for the configured strategy. `process`
62
- # (including the default) falls back to `Direct` where `fork` is
63
- # unavailable (Windows / JRuby) so inflection still works rather than
64
- # silently degrading the libraries are trusted + pure, so the
65
- # main-space fallback is acceptable when no fork-based isolation can
66
- # be had.
54
+ # The backend module for the configured strategy. `process` (including the default) falls back to
55
+ # `Direct` where `fork` is unavailable (Windows / JRuby) so inflection still works rather than silently
56
+ # degrading the libraries are trusted + pure, so the main-space fallback is acceptable when no
57
+ # fork-based isolation can be had.
67
58
  def backend
68
59
  case strategy_name
69
60
  when "ruby_box" then RubyBox
@@ -72,8 +63,8 @@ module Rigor
72
63
  end
73
64
  end
74
65
 
75
- # `none` — load the trusted library into the main space and call it
76
- # directly. No isolation; lowest cost; the fork-unavailable fallback.
66
+ # `none` — load the trusted library into the main space and call it directly. No isolation; lowest
67
+ # cost; the fork-unavailable fallback.
77
68
  module Direct
78
69
  module_function
79
70
 
@@ -85,9 +76,8 @@ module Rigor
85
76
  end
86
77
  end
87
78
 
88
- # `ruby_box` — call inside the shared {Box}. The expression is built
89
- # from the fixed `receiver` / allow-listed `method` and `inspect`-ed
90
- # args (safe Ruby literals), so the box's `eval` carries no free
79
+ # `ruby_box` — call inside the shared {Box}. The expression is built from the fixed `receiver` /
80
+ # allow-listed `method` and `inspect`-ed args (safe Ruby literals), so the box's `eval` carries no free
91
81
  # input.
92
82
  module RubyBox
93
83
  module_function
@@ -96,9 +86,8 @@ module Rigor
96
86
  raise Unavailable, "ruby_box isolation requested but Ruby::Box is not active (RUBY_BOX=1)" unless Box.enabled?
97
87
  raise Unavailable, "#{feature} could not be loaded into the Ruby::Box" unless Box.require_feature(feature)
98
88
 
99
- # `receiver` is a fixed constant name and `method` an allow-listed
100
- # symbol; args are rendered via `inspect` (safe Ruby literals), so
101
- # the expression is e.g. `ActiveSupport::Inflector.pluralize("x")`
89
+ # `receiver` is a fixed constant name and `method` an allow-listed symbol; args are rendered via
90
+ # `inspect` (safe Ruby literals), so the expression is e.g. `ActiveSupport::Inflector.pluralize("x")`
102
91
  # — no free input reaches the box's eval.
103
92
  rendered = args.map(&:inspect).join(", ")
104
93
  expression = "#{receiver}.#{method}(#{rendered})"
@@ -106,9 +95,8 @@ module Rigor
106
95
  end
107
96
  end
108
97
 
109
- # `process` — run the call in a forked worker so a crash (even a C
110
- # extension `SIGSEGV`) is contained: the parent detects the dead
111
- # worker (broken pipe / EOF) and declines instead of dying. A single
98
+ # `process` — run the call in a forked worker so a crash (even a C extension `SIGSEGV`) is contained:
99
+ # the parent detects the dead worker (broken pipe / EOF) and declines instead of dying. A single
112
100
  # persistent worker handles all calls over a Marshal pipe pair.
113
101
  module Process
114
102
  module_function
@@ -128,9 +116,9 @@ module Rigor
128
116
  rescue Unavailable
129
117
  raise
130
118
  rescue StandardError => e
131
- # A dead worker surfaces as EOFError (Marshal.load) or Errno::EPIPE
132
- # (Marshal.dump) — both StandardError. The crash is contained: the
133
- # parent resets the worker (respawn next call) and declines.
119
+ # A dead worker surfaces as EOFError (Marshal.load) or Errno::EPIPE (Marshal.dump) — both
120
+ # StandardError. The crash is contained: the parent resets the worker (respawn next call) and
121
+ # declines.
134
122
  @worker = nil
135
123
  raise Unavailable, "process isolation worker failed (#{e.class})"
136
124
  end
@@ -164,9 +152,8 @@ module Rigor
164
152
  end
165
153
  end
166
154
 
167
- # The child loop: read a `[feature, receiver, method, args]`
168
- # request, require + call, and write `[:ok, result]` or
169
- # `[:error, message]`. EOF (parent gone) ends the loop.
155
+ # The child loop: read a `[feature, receiver, method, args]` request, require + call, and write
156
+ # `[:ok, result]` or `[:error, message]`. EOF (parent gone) ends the loop.
170
157
  def run_worker_loop(req_r, res_w)
171
158
  loop do
172
159
  feature, receiver, method, args = Marshal.load(req_r) # rubocop:disable Security/MarshalLoad -- parent input
@@ -2,28 +2,23 @@
2
2
 
3
3
  module Rigor
4
4
  module Plugin
5
- # Raised inside the loader (and surfaced as a diagnostic by the
6
- # analyzer) when a plugin entry cannot be resolved or
7
- # instantiated. Carries the failing plugin reference plus the
8
- # underlying cause so the diagnostic message stays precise.
5
+ # Raised inside the loader (and surfaced as a diagnostic by the analyzer) when a plugin entry cannot be
6
+ # resolved or instantiated. Carries the failing plugin reference plus the underlying cause so the diagnostic
7
+ # message stays precise.
9
8
  #
10
- # ADR-2 § "Plugin Trust and I/O Policy" requires plugin failures
11
- # to be isolated at the analyzer boundary; this class is the
12
- # carrier for that contract on the loading side.
9
+ # ADR-2 § "Plugin Trust and I/O Policy" requires plugin failures to be isolated at the analyzer boundary;
10
+ # this class is the carrier for that contract on the loading side.
13
11
  class LoadError < StandardError
14
12
  attr_reader :plugin_ref, :cause_class, :reason
15
13
 
16
- # ADR-9 slice 5 introduces two new reason codes alongside the
17
- # implicit "load failure" used for require / configuration /
18
- # init failures:
14
+ # ADR-9 slice 5 introduces two new reason codes alongside the implicit "load failure" used for require /
15
+ # configuration / init failures:
19
16
  #
20
- # - `:missing-producer` — a non-optional `manifest(consumes:)`
21
- # entry names a `(plugin_id, name)` no loaded plugin
22
- # produces.
17
+ # - `:missing-producer` — a non-optional `manifest(consumes:)` entry names a `(plugin_id, name)` no
18
+ # loaded plugin produces.
23
19
  # - `:dependency-cycle` — the consumes graph forms a cycle.
24
20
  #
25
- # Older callers omit `reason:` and the field defaults to nil
26
- # (the legacy "load failure" envelope).
21
+ # Older callers omit `reason:` and the field defaults to nil (the legacy "load failure" envelope).
27
22
  def initialize(message, plugin_ref:, cause: nil, reason: nil)
28
23
  super(message)
29
24
  @plugin_ref = plugin_ref
@@ -6,34 +6,27 @@ require_relative "load_error"
6
6
 
7
7
  module Rigor
8
8
  module Plugin
9
- # Resolves the project's `.rigor.yml` `plugins:` entries into
10
- # instantiated plugin instances, paired with a service container.
11
- # Internal slice-1 implementation; the public surface is
12
- # {Loader.load} returning a {Registry}.
9
+ # Resolves the project's `.rigor.yml` `plugins:` entries into instantiated plugin instances, paired with a
10
+ # service container. Internal slice-1 implementation; the public surface is {Loader.load} returning a
11
+ # {Registry}.
13
12
  #
14
13
  # Steps per entry (in order):
15
14
  #
16
15
  # 1. Normalise the entry into `{ gem:, id:, config: }`.
17
16
  # 2. `require` the gem (failures surface as a {LoadError}).
18
- # 3. Look up the registered plugin class by id (or by gem
19
- # name if the entry omitted an explicit id).
20
- # 4. Validate the user's config against the manifest's
21
- # `config_schema`.
17
+ # 3. Look up the registered plugin class by id (or by gem name if the entry omitted an explicit id).
18
+ # 4. Validate the user's config against the manifest's `config_schema`.
22
19
  # 5. Instantiate the plugin and call `init(services)`.
23
20
  #
24
- # Loading is deterministic: configuration order, with plugin
25
- # id alphabetical as the tie-breaker for entries that resolve
26
- # to the same gem. Failures do not abort the run; the loader
27
- # collects them on the {Registry} so the runner can convert
28
- # each one into a `:plugin_loader` diagnostic.
21
+ # Loading is deterministic: configuration order, with plugin id alphabetical as the tie-breaker for
22
+ # entries that resolve to the same gem. Failures do not abort the run; the loader collects them on the
23
+ # {Registry} so the runner can convert each one into a `:plugin_loader` diagnostic.
29
24
  class Loader # rubocop:disable Metrics/ClassLength
30
25
  attr_reader :services, :requirer
31
26
 
32
27
  # @param services [Rigor::Plugin::Services]
33
- # @param requirer [#call] takes a gem name and returns truthy
34
- # on successful require. Defaulted to `Kernel.require` via
35
- # a lambda; the spec injects a fake to avoid touching the
36
- # real load path.
28
+ # @param requirer [#call] takes a gem name and returns truthy on successful require. Defaulted to
29
+ # `Kernel.require` via a lambda; the spec injects a fake to avoid touching the real load path.
37
30
  def initialize(services:, requirer: ->(name) { require name })
38
31
  @services = services
39
32
  @requirer = requirer
@@ -43,8 +36,7 @@ module Rigor
43
36
  new(services: services, requirer: requirer).load(configuration.plugins)
44
37
  end
45
38
 
46
- # @param entries [Array<String, Hash>] the raw `plugins:`
47
- # list from the configuration.
39
+ # @param entries [Array<String, Hash>] the raw `plugins:` list from the configuration.
48
40
  # @return [Registry]
49
41
  def load(entries)
50
42
  plugins = []
@@ -64,12 +56,10 @@ module Rigor
64
56
  end
65
57
  end
66
58
 
67
- # ADR-9 slice 5 — topological sort by `manifest(consumes:)`
68
- # so producers run before consumers, plus early
69
- # `missing-producer` validation. Cycles surface as
70
- # `dependency-cycle` LoadErrors. When validation fails, the
71
- # offending plugin(s) drop from the returned plugins list
72
- # and the LoadError surfaces alongside any earlier failure.
59
+ # ADR-9 slice 5 — topological sort by `manifest(consumes:)` so producers run before consumers, plus
60
+ # early `missing-producer` validation. Cycles surface as `dependency-cycle` LoadErrors. When
61
+ # validation fails, the offending plugin(s) drop from the returned plugins list and the LoadError
62
+ # surfaces alongside any earlier failure.
73
63
  plugins, sort_errors = topo_sort_plugins(plugins)
74
64
  load_errors.concat(sort_errors)
75
65
 
@@ -130,13 +120,11 @@ module Rigor
130
120
  plugin
131
121
  end
132
122
 
133
- # ADR-25 — a plugin's manifest-declared `signature_paths:`
134
- # are resolved (by `Plugin::Base#signature_paths`) against
135
- # the plugin gem root. A declared directory that does not
136
- # exist is a load-time failure for that plugin — loud, not
137
- # silent, because a missing `sig/` means the bundle gem is
138
- # broken. The raised LoadError is collected like any other
139
- # load failure and the plugin drops from the registry.
123
+ # ADR-25 — a plugin's manifest-declared `signature_paths:` are resolved (by
124
+ # `Plugin::Base#signature_paths`) against the plugin gem root. A declared directory that does not exist
125
+ # is a load-time failure for that plugin loud, not silent, because a missing `sig/` means the bundle
126
+ # gem is broken. The raised LoadError is collected like any other load failure and the plugin drops
127
+ # from the registry.
140
128
  def validate_signature_paths!(plugin)
141
129
  plugin.signature_paths.each do |dir|
142
130
  next if File.directory?(dir)
@@ -221,19 +209,15 @@ module Rigor
221
209
  plugin_class.to_s
222
210
  end
223
211
 
224
- # ADR-9 slice 5 — topological sort of plugins by their
225
- # `manifest(consumes:)` declarations. Returns `[sorted_plugins,
226
- # load_errors]`. Determinism: when no dependency relation
227
- # forces an order, plugins are visited alphabetically by
228
- # manifest id. A non-optional consume of a `(plugin_id, name)`
229
- # whose producer is missing emits a `:missing-producer`
230
- # LoadError and drops the consumer; cycles emit a
212
+ # ADR-9 slice 5 — topological sort of plugins by their `manifest(consumes:)` declarations. Returns
213
+ # `[sorted_plugins, load_errors]`. Determinism: when no dependency relation forces an order, plugins
214
+ # are visited alphabetically by manifest id. A non-optional consume of a `(plugin_id, name)` whose
215
+ # producer is missing emits a `:missing-producer` LoadError and drops the consumer; cycles emit a
231
216
  # `:dependency-cycle` LoadError naming the offending chain.
232
217
  def topo_sort_plugins(plugins)
233
- # If no plugin opts into the cross-plugin API the loader's
234
- # legacy configuration-order contract is preserved
235
- # unchanged. Topo sort and missing-producer validation only
236
- # run when at least one plugin declares `consumes:`.
218
+ # If no plugin opts into the cross-plugin API the loader's legacy configuration-order contract is
219
+ # preserved unchanged. Topo sort and missing-producer validation only run when at least one plugin
220
+ # declares `consumes:`.
237
221
  return [plugins, []] unless plugins.any? { |p| p.manifest.consumes.any? }
238
222
 
239
223
  index = plugins.to_h { |plugin| [plugin.manifest.id, plugin] }
@@ -267,12 +251,9 @@ module Rigor
267
251
  producer.manifest.produces.include?(name)
268
252
  end
269
253
 
270
- # Kahn's algorithm with `Configuration#plugins`-order
271
- # tie-break. Edges go from producer -> consumer (producer
272
- # must visit first). When two plugins are simultaneously
273
- # ready, the configuration-order index decides the visit
274
- # order — preserves the v0.1.0 legacy contract for plugins
275
- # without dependencies.
254
+ # Kahn's algorithm with `Configuration#plugins`-order tie-break. Edges go from producer -> consumer
255
+ # (producer must visit first). When two plugins are simultaneously ready, the configuration-order index
256
+ # decides the visit order — preserves the v0.1.0 legacy contract for plugins without dependencies.
276
257
  def sort_in_topo_order(plugins, index, errors, config_order)
277
258
  in_degree, forward = build_consumes_graph(plugins, index, errors)
278
259
  ordered, cycle_errors = kahn_walk(plugins, in_degree, forward, config_order)
@@ -3,10 +3,8 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  module Macro
6
- # ADR-16 Tier A declaration: "the block passed to a
7
- # class-level DSL call of one of `method_names` runs as an
8
- # instance method on `receiver_constraint`'s subclass tree,
9
- # with `self` typed accordingly."
6
+ # ADR-16 Tier A declaration: "the block passed to a class-level DSL call of one of `method_names` runs as
7
+ # an instance method on `receiver_constraint`'s subclass tree, with `self` typed accordingly."
10
8
  #
11
9
  # Authored on a plugin manifest:
12
10
  #
@@ -21,41 +19,31 @@ module Rigor
21
19
  # ]
22
20
  # )
23
21
  #
24
- # Sinatra is the canonical worked target (`Sinatra::Base#generate_method`
25
- # at `lib/sinatra/base.rb:1788-1793` literally does
26
- # `define_method(name, &block); remove_method` the block IS
27
- # the method body, byte-for-byte). The substrate adopts the
28
- # same contract: declare the receiver constraint + the
29
- # class-level methods whose block argument runs as if it were
30
- # an instance method of the receiver.
22
+ # Sinatra is the canonical worked target (`Sinatra::Base#generate_method` at
23
+ # `lib/sinatra/base.rb:1788-1793` literally does `define_method(name, &block); remove_method` — the
24
+ # block IS the method body, byte-for-byte). The substrate adopts the same contract: declare the receiver
25
+ # constraint + the class-level methods whose block argument runs as if it were an instance method of the
26
+ # receiver.
31
27
  #
32
- # Engine wiring: `Inference::MacroBlockSelfType.narrow_self_type_for`
33
- # (called from expression_typer.rb) consults registered entries
34
- # and narrows `Scope#self_type` for matching block call sites.
28
+ # Engine wiring: `Inference::MacroBlockSelfType.narrow_self_type_for` (called from expression_typer.rb)
29
+ # consults registered entries and narrows `Scope#self_type` for matching block call sites.
35
30
  #
36
31
  # ## Fields
37
32
  #
38
- # - `receiver_constraint` — fully-qualified class name (String)
39
- # that the call's lexical receiver MUST be (or inherit from)
40
- # for the entry to fire. For Sinatra modular-style this is
41
- # `"Sinatra::Base"`; the substrate's class-context match
42
- # accepts every subclass.
43
- # - `method_names` — Array of Symbol method names. A call shape
44
- # `<receiver_subclass>.get('/path') { ... }` matches when
45
- # `:get` is in this list. (Named `verbs:` before ADR-60 WD2
46
- # normalised the macro value-object vocabulary.)
47
- # - `self_type` — Symbol selecting the kind of `self`-binding
48
- # the substrate applies inside the block. Slice 1a accepts
49
- # only `:receiver_instance` (the block runs as an instance
50
- # method of the receiver class). Other kinds (`:receiver_singleton`,
51
- # `:dsl_recorder`) are reserved for later slices.
33
+ # - `receiver_constraint` — fully-qualified class name (String) that the call's lexical receiver MUST be
34
+ # (or inherit from) for the entry to fire. For Sinatra modular-style this is `"Sinatra::Base"`; the
35
+ # substrate's class-context match accepts every subclass.
36
+ # - `method_names` Array of Symbol method names. A call shape `<receiver_subclass>.get('/path') { ... }`
37
+ # matches when `:get` is in this list. (Named `verbs:` before ADR-60 WD2 normalised the macro
38
+ # value-object vocabulary.)
39
+ # - `self_type` — Symbol selecting the kind of `self`-binding the substrate applies inside the block.
40
+ # Slice 1a accepts only `:receiver_instance` (the block runs as an instance method of the receiver
41
+ # class). Other kinds (`:receiver_singleton`, `:dsl_recorder`) are reserved for later slices.
52
42
  #
53
43
  # ## Ractor-shareability
54
44
  #
55
- # All fields are frozen at construction (ADR-15 Phase 1).
56
- # `method_names` is dup-frozen so the caller's mutable array
57
- # does not leak into the value. `Ractor.shareable?` returns
58
- # true after `#initialize`.
45
+ # All fields are frozen at construction (ADR-15 Phase 1). `method_names` is dup-frozen so the caller's
46
+ # mutable array does not leak into the value. `Ractor.shareable?` returns true after `#initialize`.
59
47
  class BlockAsMethod
60
48
  SELF_TYPE_RECEIVER_INSTANCE = :receiver_instance
61
49
  VALID_SELF_TYPES = [SELF_TYPE_RECEIVER_INSTANCE].freeze
@@ -3,17 +3,13 @@
3
3
  module Rigor
4
4
  module Plugin
5
5
  module Macro
6
- # ADR-16 Tier C declaration: "the class-level DSL call
7
- # `<receiver_constraint>.<method_name>(name_arg, ...)` emits
8
- # synthetic methods on the calling class, with names
9
- # interpolating the source-visible literal argument at
10
- # `symbol_arg_position`."
6
+ # ADR-16 Tier C declaration: "the class-level DSL call `<receiver_constraint>.<method_name>(name_arg,
7
+ # ...)` emits synthetic methods on the calling class, with names interpolating the source-visible
8
+ # literal argument at `symbol_arg_position`."
11
9
  #
12
- # Textbook target — dry-struct's `attribute :name, T` and
13
- # ActiveStorage's `has_one_attached :avatar` both have this
14
- # shape: a class-level call enumerates a literal Symbol
15
- # argument; the framework `class_eval`s a heredoc
16
- # interpolating that Symbol; the emit table is fixed.
10
+ # Textbook target — dry-struct's `attribute :name, T` and ActiveStorage's `has_one_attached :avatar`
11
+ # both have this shape: a class-level call enumerates a literal Symbol argument; the framework
12
+ # `class_eval`s a heredoc interpolating that Symbol; the emit table is fixed.
17
13
  #
18
14
  # ## Authoring shape
19
15
  #
@@ -39,38 +35,27 @@ module Rigor
39
35
  #
40
36
  # ## Fields
41
37
  #
42
- # - `receiver_constraint` — fully-qualified class name (String).
43
- # Synthesis fires when the call's lexical receiver class
44
- # equals or inherits from this constraint.
45
- # - `method_name` — Symbol naming the DSL method (e.g.
46
- # `:has_one_attached`, `:attribute`).
47
- # - `symbol_arg_position` — Integer (default 0) — the
48
- # argument index whose literal Symbol value becomes the
49
- # `name` interpolated into each emit row's `name:`
50
- # template. Slice 2a accepts non-negative integers only.
51
- # - `emit` — Array of `Emit` (or coerced Hash) — instance
52
- # methods to synthesise on the calling class.
53
- # - `class_level_emit` — same shape, but the synthesised
54
- # methods are singleton (class-level) methods.
38
+ # - `receiver_constraint` — fully-qualified class name (String). Synthesis fires when the call's
39
+ # lexical receiver class equals or inherits from this constraint.
40
+ # - `method_name` Symbol naming the DSL method (e.g. `:has_one_attached`, `:attribute`).
41
+ # - `symbol_arg_position` — Integer (default 0) — the argument index whose literal Symbol value becomes
42
+ # the `name` interpolated into each emit row's `name:` template. Slice 2a accepts non-negative
43
+ # integers only.
44
+ # - `emit` Array of `Emit` (or coerced Hash) — instance methods to synthesise on the calling class.
45
+ # - `class_level_emit` same shape, but the synthesised methods are singleton (class-level) methods.
55
46
  #
56
47
  # ## Floor / ceiling per ADR-16 WD13
57
48
  #
58
- # Slice 2 ships at the **floor**: each emit row's `name:`
59
- # is the source of truth for the synthetic method's name
60
- # (a single `"\#{name}"` placeholder gets interpolated with
61
- # the literal symbol argument at `symbol_arg_position`).
62
- # The `returns:` strings are **recorded in the manifest but
63
- # not resolved**; the engine emits synthetic methods with
64
- # `Dynamic[T]` returns plus a
65
- # `macro.tier_c.unresolved-return` provenance marker.
66
- # Precise return-type resolution via ADR-13's
67
- # `Plugin::TypeNodeResolver` is the **ceiling**, deferred
68
- # to a later slice — the `returns:` declarations cost
69
- # nothing to write today and unlock precision then.
49
+ # Slice 2 ships at the **floor**: each emit row's `name:` is the source of truth for the synthetic
50
+ # method's name (a single `"\#{name}"` placeholder gets interpolated with the literal symbol argument at
51
+ # `symbol_arg_position`). The `returns:` strings are **recorded in the manifest but not resolved**; the
52
+ # engine emits synthetic methods with `Dynamic[T]` returns plus a `macro.tier_c.unresolved-return`
53
+ # provenance marker. Precise return-type resolution via ADR-13's `Plugin::TypeNodeResolver` is the
54
+ # **ceiling**, deferred to a later slice — the `returns:` declarations cost nothing to write today and
55
+ # unlock precision then.
70
56
  #
71
- # Engine wiring: `Inference::SyntheticMethodScanner` (slice 2b,
72
- # `synthetic_method_scanner.rb`) consumes `manifest.heredoc_templates`.
73
- # Worked consumers: `plugins/rigor-dry-struct/` and
57
+ # Engine wiring: `Inference::SyntheticMethodScanner` (slice 2b, `synthetic_method_scanner.rb`) consumes
58
+ # `manifest.heredoc_templates`. Worked consumers: `plugins/rigor-dry-struct/` and
74
59
  # `plugins/rigor-dry-types/` (slice 2c).
75
60
  class HeredocTemplate
76
61
  NAME_PLACEHOLDER = "\#{name}"
@@ -109,15 +94,12 @@ module Rigor
109
94
  to_h.hash
110
95
  end
111
96
 
112
- # One row of an emit table: the synthetic method's
113
- # name-template (the analyzer interpolates `\#{name}` with
114
- # the call-site literal symbol) and its declared return
115
- # type. The return type can be a static String (resolved
116
- # via `Environment#nominal_for_name` per ADR-16 slice 6b)
117
- # or a per-call-site lookup ({ReturnsFromArg}) see
118
- # [ADR-18](../../../../../docs/adr/18-substrate-per-call-site-return-type.md).
119
- # When both are nil, the synthesised method's return type
120
- # falls back to `Dynamic[Top]`.
97
+ # One row of an emit table: the synthetic method's name-template (the analyzer interpolates
98
+ # `\#{name}` with the call-site literal symbol) and its declared return type. The return type can be
99
+ # a static String (resolved via `Environment#nominal_for_name` per ADR-16 slice 6b) or a
100
+ # per-call-site lookup ({ReturnsFromArg}) see
101
+ # [ADR-18](../../../../../docs/adr/18-substrate-per-call-site-return-type.md). When both are nil, the
102
+ # synthesised method's return type falls back to `Dynamic[Top]`.
121
103
  class Emit
122
104
  attr_reader :name, :returns, :returns_from_arg
123
105
 
@@ -155,10 +137,8 @@ module Rigor
155
137
  end
156
138
  end
157
139
 
158
- # ADR-18 — per-call-site return-type DSL. Declares which
159
- # call-site argument's source representation to look up
160
- # in a cross-plugin fact channel for the synthesised
161
- # method's return type.
140
+ # ADR-18 — per-call-site return-type DSL. Declares which call-site argument's source representation
141
+ # to look up in a cross-plugin fact channel for the synthesised method's return type.
162
142
  #
163
143
  # Authoring shape:
164
144
  #
@@ -167,16 +147,13 @@ module Rigor
167
147
  # lookup_via: { plugin_id: "dry-types", fact: :dry_type_aliases }
168
148
  # }
169
149
  #
170
- # Slice 1 (this file) ships the value class + validation
171
- # only. The scanner-side arg-position extraction +
172
- # fact-store lookup land in slice 2 / 3.
150
+ # Slice 1 (this file) ships the value class + validation only. The scanner-side arg-position
151
+ # extraction + fact-store lookup land in slice 2 / 3.
173
152
  class ReturnsFromArg
174
153
  attr_reader :position, :plugin_id, :fact
175
154
 
176
- # @return [ReturnsFromArg, nil] coerced value class
177
- # for a Hash / nil / ReturnsFromArg input. Raises on
178
- # any other shape so manifest authoring failures
179
- # surface at construction time.
155
+ # @return [ReturnsFromArg, nil] coerced value class for a Hash / nil / ReturnsFromArg input. Raises
156
+ # on any other shape so manifest authoring failures surface at construction time.
180
157
  def self.coerce(value)
181
158
  return nil if value.nil?
182
159
  return value if value.is_a?(ReturnsFromArg)