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
@@ -2,50 +2,40 @@
2
2
 
3
3
  module Rigor
4
4
  module Analysis
5
- # ADR-24 slice 4a — records every *implicit-self* method call the
6
- # inference engine could not resolve to any method (RBS tier miss +
7
- # user-class ancestor-walk miss + not a `Dynamic` receiver), captured
8
- # at the single engine choke-point where such a call falls through to
9
- # `Dynamic[top]` (`ExpressionTyper#call_type_for` → `fallback_for`).
5
+ # ADR-24 slice 4a — records every *implicit-self* method call the inference engine could not resolve to any
6
+ # method (RBS tier miss + user-class ancestor-walk miss + not a `Dynamic` receiver), captured at the single
7
+ # engine choke-point where such a call falls through to `Dynamic[top]` (`ExpressionTyper#call_type_for`
8
+ # `fallback_for`).
10
9
  #
11
10
  # ## Why a recorder, not a check-rule
12
11
  #
13
- # Slice 4 attempt 1 reimplemented self-call resolution inside
14
- # `CheckRules` and produced 135 false positives on Rigor's own `lib`
15
- # ([ADR-24](../../../docs/adr/24-self-method-call-resolution.md)
16
- # § "Slice 4"): the second, weaker resolution path could not see
17
- # `module_function` siblings, `Data.define` / `Struct` accessors, or
18
- # mixin-contributed methods that the *engine's* real resolution
19
- # handles. Recording at the engine's own miss point reuses that real
20
- # resolution — those methods resolve before the miss, so they never
21
- # reach the recorder. This module is the ADR-46 / ADR-47 "collect at
22
- # evaluation time, never recompute" lesson applied to self-calls.
12
+ # Slice 4 attempt 1 reimplemented self-call resolution inside `CheckRules` and produced 135 false
13
+ # positives on Rigor's own `lib` ([ADR-24](../../../docs/adr/24-self-method-call-resolution.md) § "Slice
14
+ # 4"): the second, weaker resolution path could not see `module_function` siblings, `Data.define` /
15
+ # `Struct` accessors, or mixin-contributed methods that the *engine's* real resolution handles. Recording
16
+ # at the engine's own miss point reuses that real resolution — those methods resolve before the miss, so
17
+ # they never reach the recorder. This module is the ADR-46 / ADR-47 "collect at evaluation time, never
18
+ # recompute" lesson applied to self-calls.
23
19
  #
24
- # ADR-24 slice 4 (`call.self-undefined-method`) consumes the recorded
25
- # misses behind a confidently-closed-class gate (see `CheckRules` L775).
26
- # The rule ships `:off` by default {active?} is false on a normal run, so the
27
- # instrumented choke-point pays a single integer read and records
28
- # nothing. Recording is purely observational; it never changes a
29
- # diagnostic.
20
+ # ADR-24 slice 4 (`call.self-undefined-method`) consumes the recorded misses behind a confidently-closed-class
21
+ # gate (see `CheckRules` L775). The rule ships `:off` by default — {active?} is false on a normal run, so
22
+ # the instrumented choke-point pays a single integer read and records nothing. Recording is purely
23
+ # observational; it never changes a diagnostic.
30
24
  #
31
- # Modelled on {DependencyRecorder}: process-thread-local accumulator,
32
- # a cheap disabled fast path, and a frozen snapshot for consumers.
25
+ # Modelled on {DependencyRecorder}: process-thread-local accumulator, a cheap disabled fast path, and a
26
+ # frozen snapshot for consumers.
33
27
  module SelfCallResolutionRecorder
34
28
  KEY = :__rigor_self_call_resolution_recorder__
35
29
  private_constant :KEY
36
30
 
37
- # One unresolved implicit-self call. `class_name` is the receiver's
38
- # statically known class (the enclosing `self` type); `method_name`
39
- # the called name; `node` the Prism `CallNode` (held in-memory so the
40
- # `call.self-undefined-method` collector can resolve its scope from the
41
- # scope index and apply the closed-class gate); `path` / `line` /
42
- # `column` locate the call site for the diagnostic.
31
+ # One unresolved implicit-self call. `class_name` is the receiver's statically known class (the
32
+ # enclosing `self` type); `method_name` the called name; `node` the Prism `CallNode` (held in-memory so
33
+ # the `call.self-undefined-method` collector can resolve its scope from the scope index and apply the
34
+ # closed-class gate); `path` / `line` / `column` locate the call site for the diagnostic.
43
35
  UnresolvedSelfCall = Data.define(:class_name, :method_name, :node, :path, :line, :column)
44
36
 
45
- # Mutable per-consumer accumulator, frozen into a {Record} snapshot
46
- # when {record_for} returns. Dedupes by the full call tuple so a
47
- # method body re-typed under several call-site signatures records the
48
- # miss once.
37
+ # Mutable per-consumer accumulator, frozen into a {Record} snapshot when {record_for} returns. Dedupes by
38
+ # the full call tuple so a method body re-typed under several call-site signatures records the miss once.
49
39
  class Accumulator
50
40
  attr_reader :consumer, :calls
51
41
 
@@ -68,18 +58,16 @@ module Rigor
68
58
 
69
59
  Record = Data.define(:consumer, :calls)
70
60
 
71
- # Module-level activation count so the disabled fast path ({active?})
72
- # is a plain integer read rather than a `Thread.current` hash lookup —
73
- # the instrumented choke-point is on the dispatch miss path, so a
61
+ # Module-level activation count so the disabled fast path ({active?}) is a plain integer read rather
62
+ # than a `Thread.current` hash lookup — the instrumented choke-point is on the dispatch miss path, so a
74
63
  # normal (non-recording) run must pay as little as possible.
75
64
  @active_count = 0
76
65
  @mutex = Mutex.new
77
66
 
78
67
  module_function
79
68
 
80
- # Activates recording for `consumer` (the path being analyzed) for the
81
- # duration of the block and returns the frozen {Record}. Nests safely;
82
- # restores the previous recorder on exit.
69
+ # Activates recording for `consumer` (the path being analyzed) for the duration of the block and returns
70
+ # the frozen {Record}. Nests safely; restores the previous recorder on exit.
83
71
  def record_for(consumer)
84
72
  previous = Thread.current[KEY]
85
73
  accumulator = Accumulator.new(consumer.to_s)
@@ -92,14 +80,13 @@ module Rigor
92
80
  @mutex.synchronize { @active_count -= 1 }
93
81
  end
94
82
 
95
- # Plain integer read (GVL-atomic) — no `Thread.current` lookup on the
96
- # disabled fast path.
83
+ # Plain integer read (GVL-atomic) — no `Thread.current` lookup on the disabled fast path.
97
84
  def active?
98
85
  @active_count.positive?
99
86
  end
100
87
 
101
- # Records one unresolved implicit-self call. No-op when no consumer is
102
- # active on this thread (another thread may have flipped {active?}).
88
+ # Records one unresolved implicit-self call. No-op when no consumer is active on this thread (another
89
+ # thread may have flipped {active?}).
103
90
  def record(class_name:, method_name:, node:, path:, line:, column:)
104
91
  accumulator = Thread.current[KEY]
105
92
  return if accumulator.nil?
@@ -19,69 +19,49 @@ require_relative "erb_template_detector"
19
19
 
20
20
  module Rigor
21
21
  module Analysis
22
- # ADR-15 Phase 4a — per-worker analysis substrate.
23
- # [ADR-15](../../../docs/adr/15-ractor-concurrency.md)
24
- # § Phase 4 carves the Ractor-isolated worker pool into sub-phases;
25
- # 4a/4b/4c all landed, but the Ractor pool (4b) is blocked by Ruby
26
- # Bug #22075 (UAF) — the active pool backend is fork (ADR-15 Amendment).
27
- # This class exists so the per-worker ownership boundary is testable
28
- # independently of any pool coordinator.
22
+ # ADR-15 Phase 4a — per-worker analysis substrate. [ADR-15](../../../docs/adr/15-ractor-concurrency.md) §
23
+ # Phase 4 carves the Ractor-isolated worker pool into sub-phases; 4a/4b/4c all landed, but the Ractor
24
+ # pool (4b) is blocked by Ruby Bug #22075 (UAF) — the active pool backend is fork (ADR-15 Amendment).
25
+ # This class exists so the per-worker ownership boundary is testable independently of any pool
26
+ # coordinator.
29
27
  #
30
28
  # The constructor takes only `Ractor.shareable?` inputs:
31
29
  #
32
- # - `configuration` — Phase 2a ({Rigor::Configuration} is
33
- # `Ractor.shareable?`).
34
- # - `cache_store` the fork backend passes the parent runner's
35
- # pre-built Store (`cache_store: @cache_store` in PoolCoordinator);
36
- # workers share it rather than building their own at `cache_root`.
37
- # - `plugin_blueprints` — Phase 3a
38
- # (`Array<Plugin::Blueprint>` is `Ractor.shareable?`).
30
+ # - `configuration` — Phase 2a ({Rigor::Configuration} is `Ractor.shareable?`).
31
+ # - `cache_store` — the fork backend passes the parent runner's pre-built Store (`cache_store:
32
+ # @cache_store` in PoolCoordinator); workers share it rather than building their own at `cache_root`.
33
+ # - `plugin_blueprints` — Phase 3a (`Array<Plugin::Blueprint>` is `Ractor.shareable?`).
39
34
  # - `explain` — Boolean.
40
- # - `synthetic_method_index` / `project_patched_methods` /
41
- # `project_scope_seed` optional (default `nil` / `{}`). NOT
42
- # `Ractor.shareable?` (the seed tables carry Prism def nodes),
43
- # so the Ractor pool path leaves them unset; the fork backend
44
- # (ADR-15 Amendment), which builds the session pre-fork on the
45
- # parent, threads the runner's project-scan results through so
46
- # per-file inference matches the sequential path exactly.
47
- # `project_scope_seed` is `Runner#project_scope_seed_tables`
48
- # the cross-file discovery tables `seed_project_scope` applies
49
- # to every per-file scope on the sequential path; without it a
50
- # worker cannot resolve calls to methods defined in OTHER
51
- # project files and emits `call.undefined-method` false
52
- # positives the sequential path does not.
35
+ # - `synthetic_method_index` / `project_patched_methods` / `project_scope_seed` — optional (default `nil`
36
+ # / `{}`). NOT `Ractor.shareable?` (the seed tables carry Prism def nodes), so the Ractor pool path
37
+ # leaves them unset; the fork backend (ADR-15 Amendment), which builds the session pre-fork on the
38
+ # parent, threads the runner's project-scan results through so per-file inference matches the
39
+ # sequential path exactly. `project_scope_seed` is `Runner#project_scope_seed_tables` the cross-file
40
+ # discovery tables `seed_project_scope` applies to every per-file scope on the sequential path;
41
+ # without it a worker cannot resolve calls to methods defined in OTHER project files and emits
42
+ # `call.undefined-method` false positives the sequential path does not.
53
43
  #
54
44
  # Internally the session OWNS (and never shares):
55
45
  #
56
46
  # - {Rigor::Plugin::Services} bound to the per-worker Store.
57
- # - {Rigor::Plugin::Registry} materialised from the blueprints
58
- # via {Rigor::Plugin::Registry.materialize}; each plugin
59
- # instance, with its mutable per-run accumulators
60
- # (`@reachable_absurd_nodes`, `*_index`, …) lives entirely
61
- # inside this session.
62
- # - {Rigor::RbsExtended::Reporter} +
63
- # {Rigor::Analysis::DependencySourceInference::BoundaryCrossReporter}
64
- # (Mutex-bearing; intentionally per-worker the runner
65
- # merges entries post-pool via {#drain_reporters}).
66
- # - {Rigor::Environment} threaded with the per-worker reporters
67
- # so reporter writes from inference / dispatcher accumulate
68
- # into the worker's own state.
47
+ # - {Rigor::Plugin::Registry} materialised from the blueprints via {Rigor::Plugin::Registry.materialize};
48
+ # each plugin instance, with its mutable per-run accumulators (`@reachable_absurd_nodes`, `*_index`,
49
+ # …) lives entirely inside this session.
50
+ # - {Rigor::RbsExtended::Reporter} + {Rigor::Analysis::DependencySourceInference::BoundaryCrossReporter}
51
+ # (Mutex-bearing; intentionally per-worker — the runner merges entries post-pool via
52
+ # {#drain_reporters}).
53
+ # - {Rigor::Environment} threaded with the per-worker reporters so reporter writes from inference /
54
+ # dispatcher accumulate into the worker's own state.
69
55
  #
70
- # Plugin `prepare` runs ONCE at construction time so each
71
- # worker is "warm" by the time `#analyze` is first called. Any
72
- # raise from `prepare` is captured into {#prepare_diagnostics}
73
- # so the runner can surface them alongside the per-file
74
- # diagnostic stream.
56
+ # Plugin `prepare` runs ONCE at construction time so each worker is "warm" by the time `#analyze` is
57
+ # first called. Any raise from `prepare` is captured into {#prepare_diagnostics} so the runner can
58
+ # surface them alongside the per-file diagnostic stream.
75
59
  #
76
- # Equivalence contract (proven by spec): given identical
77
- # `(configuration, cache_store, plugin_blueprints)`, the
78
- # multiset of diagnostics from
79
- # `paths.flat_map { |p| session.analyze(p) }` plus
80
- # {#prepare_diagnostics} plus reporter drains MUST equal the
81
- # corresponding subset of {Rigor::Analysis::Runner#run}'s
82
- # output (modulo severity-profile re-stamping, which the
83
- # session leaves to the caller because it is a per-run
84
- # aggregate concern).
60
+ # Equivalence contract (proven by spec): given identical `(configuration, cache_store,
61
+ # plugin_blueprints)`, the multiset of diagnostics from `paths.flat_map { |p| session.analyze(p) }` plus
62
+ # {#prepare_diagnostics} plus reporter drains MUST equal the corresponding subset of
63
+ # {Rigor::Analysis::Runner#run}'s output (modulo severity-profile re-stamping, which the session leaves
64
+ # to the caller because it is a per-run aggregate concern).
85
65
  class WorkerSession # rubocop:disable Metrics/ClassLength
86
66
  attr_reader :configuration, :cache_store, :services, :plugin_registry,
87
67
  :dependency_source_index, :environment,
@@ -89,16 +69,12 @@ module Rigor
89
69
  :prepare_diagnostics
90
70
 
91
71
  # @param configuration [Rigor::Configuration]
92
- # @param cache_store [Rigor::Cache::Store, nil] persistent
93
- # cache the session exposes to plugin-side producers and
94
- # the RBS loader. Pass `nil` to disable caching.
95
- # @param plugin_blueprints [Array<Rigor::Plugin::Blueprint>]
96
- # replay descriptors. Empty array yields a session with
97
- # no plugin contributions.
98
- # @param explain [Boolean] when true, `#analyze` additionally
99
- # emits one `:info` `fallback` diagnostic per
100
- # directly-unrecognised node, mirroring
101
- # {Rigor::Analysis::Runner#explain_diagnostics}.
72
+ # @param cache_store [Rigor::Cache::Store, nil] persistent cache the session exposes to plugin-side
73
+ # producers and the RBS loader. Pass `nil` to disable caching.
74
+ # @param plugin_blueprints [Array<Rigor::Plugin::Blueprint>] replay descriptors. Empty array yields a
75
+ # session with no plugin contributions.
76
+ # @param explain [Boolean] when true, `#analyze` additionally emits one `:info` `fallback` diagnostic
77
+ # per directly-unrecognised node, mirroring {Rigor::Analysis::Runner#explain_diagnostics}.
102
78
  def initialize(configuration:, cache_store: nil, # rubocop:disable Metrics/MethodLength,Metrics/ParameterLists
103
79
  plugin_blueprints: [], explain: false, buffer: nil,
104
80
  synthetic_method_index: nil, project_patched_methods: nil,
@@ -110,18 +86,14 @@ module Rigor
110
86
  @synthetic_method_index = synthetic_method_index
111
87
  @project_patched_methods = project_patched_methods
112
88
  @project_scope_seed = project_scope_seed || {}
113
- # ADR-32 WD4 — full project file list (frozen
114
- # Array<String>) for env-build-time invocation of any
89
+ # ADR-32 WD4 — full project file list (frozen Array<String>) for env-build-time invocation of any
115
90
  # loaded plugin's `source_rbs_synthesizer` callable.
116
91
  @source_files = source_files
117
92
 
118
- # NOTE: `Inference::MethodDispatcher::FileFolding.fold_platform_specific_paths`
119
- # is process-global state. Writing it from a non-main
120
- # Ractor would raise `Ractor::IsolationError`, so the
121
- # session does NOT touch it the CALLER (typically
122
- # {Rigor::Analysis::Runner#run}) is responsible for
123
- # setting it on the main Ractor before spawning the
124
- # pool. The substrate stays Ractor-safe by construction.
93
+ # NOTE: `Inference::MethodDispatcher::FileFolding.fold_platform_specific_paths` is process-global
94
+ # state. Writing it from a non-main Ractor would raise `Ractor::IsolationError`, so the session does
95
+ # NOT touch it — the CALLER (typically {Rigor::Analysis::Runner#run}) is responsible for setting it
96
+ # on the main Ractor before spawning the pool. The substrate stays Ractor-safe by construction.
125
97
  @rbs_extended_reporter = RbsExtended::Reporter.new
126
98
  @boundary_cross_reporter = DependencySourceInference::BoundaryCrossReporter.new
127
99
  @source_rbs_synthesis_reporter = Plugin::SourceRbsSynthesisReporter.new
@@ -158,11 +130,9 @@ module Rigor
158
130
  @prepare_diagnostics = run_plugin_prepare.freeze
159
131
  end
160
132
 
161
- # Equivalent of {Rigor::Analysis::Runner#analyze_file} +
162
- # `plugin_emitted_diagnostics` + `explain_diagnostics`.
163
- # Returns a flat `Array<Diagnostic>` for the file. Severity
164
- # profile re-stamping is intentionally NOT applied — that
165
- # is a per-run aggregate concern handled by the caller.
133
+ # Equivalent of {Rigor::Analysis::Runner#analyze_file} + `plugin_emitted_diagnostics` +
134
+ # `explain_diagnostics`. Returns a flat `Array<Diagnostic>` for the file. Severity profile re-stamping
135
+ # is intentionally NOT applied — that is a per-run aggregate concern handled by the caller.
166
136
  def analyze(path)
167
137
  parse_result = parse_source(path)
168
138
  unless parse_result.errors.empty?
@@ -192,11 +162,9 @@ module Rigor
192
162
  [analyzer_error(path, "internal analyzer error: #{e.class}: #{e.message}")]
193
163
  end
194
164
 
195
- # Read-once snapshot of the per-worker reporters so the
196
- # caller (or the eventual Phase 4b pool aggregator) can
197
- # merge into a single coordinator-side reporter. Both
198
- # reporters dedupe at write time, so a post-hoc concat +
199
- # de-dup at the entry-key level is sound.
165
+ # Read-once snapshot of the per-worker reporters so the caller (or the eventual Phase 4b pool
166
+ # aggregator) can merge into a single coordinator-side reporter. Both reporters dedupe at write time,
167
+ # so a post-hoc concat + de-dup at the entry-key level is sound.
200
168
  def drain_reporters
201
169
  {
202
170
  rbs_extended: {
@@ -210,21 +178,18 @@ module Rigor
210
178
 
211
179
  private
212
180
 
213
- # Mirrors {Runner#seed_project_scope}: applies the cross-file
214
- # pre-pass discovery tables the constructor received (fork
215
- # backend only see the class comment) to a fresh per-file
216
- # scope, so worker-side inference resolves project-internal
217
- # cross-file calls exactly like the sequential path.
181
+ # Mirrors {Runner#seed_project_scope}: applies the cross-file pre-pass discovery tables the
182
+ # constructor received (fork backend only — see the class comment) to a fresh per-file scope, so
183
+ # worker-side inference resolves project-internal cross-file calls exactly like the sequential path.
218
184
  def seed_project_scope(scope)
219
185
  return scope if @project_scope_seed.empty?
220
186
 
221
187
  scope.with_discovery(scope.discovery.with(**@project_scope_seed))
222
188
  end
223
189
 
224
- # See {Runner#parse_source}. Same contract: if `@buffer`
225
- # binds `path` to a physical file, read the physical bytes
226
- # but stamp the parse buffer's `filepath:` as the LOGICAL
227
- # path so downstream diagnostics carry the logical path.
190
+ # See {Runner#parse_source}. Same contract: if `@buffer` binds `path` to a physical file, read the
191
+ # physical bytes but stamp the parse buffer's `filepath:` as the LOGICAL path so downstream
192
+ # diagnostics carry the logical path.
228
193
  def parse_source(path)
229
194
  physical = @buffer ? @buffer.resolve(path) : path
230
195
  return Prism.parse_file(physical, version: @configuration.target_ruby) if physical == path
@@ -232,9 +197,8 @@ module Rigor
232
197
  Prism.parse(File.read(physical), filepath: path, version: @configuration.target_ruby)
233
198
  end
234
199
 
235
- # Mirrors {Runner#build_trust_policy}. Deriving trust inside
236
- # the session keeps the substrate decoupled from the
237
- # coordinator; configuration is already Ractor-shareable.
200
+ # Mirrors {Runner#build_trust_policy}. Deriving trust inside the session keeps the substrate decoupled
201
+ # from the coordinator; configuration is already Ractor-shareable.
238
202
  def build_trust_policy
239
203
  trusted_gems = @configuration.plugins.map { |entry| trusted_gem_name(entry) }.uniq
240
204
  roots = [Dir.pwd]
@@ -302,11 +266,10 @@ module Rigor
302
266
  end
303
267
  end
304
268
 
305
- # ADR-52 WD4 + ADR-53 B4 — single engine-owned walk per file drives
306
- # both the plugin node rules (bucketed per plugin in registry order,
307
- # plugin-major emission) and the built-in node collectors
308
- # (`node_collectors`, populated in place). Runs even with no node-rule
309
- # plugins so the collectors still get driven (converged path).
269
+ # ADR-52 WD4 + ADR-53 B4 — single engine-owned walk per file drives both the plugin node rules
270
+ # (bucketed per plugin in registry order, plugin-major emission) and the built-in node collectors
271
+ # (`node_collectors`, populated in place). Runs even with no node-rule plugins so the collectors still
272
+ # get driven (converged path).
310
273
  def node_rule_results_by_plugin(path, root, scope, node_collectors, scope_index)
311
274
  walk = @plugin_registry.node_rule_walk
312
275
  driver = node_collectors && CheckRules.node_collector_driver(node_collectors)
@@ -325,9 +288,8 @@ module Rigor
325
288
 
326
289
  def collect_plugin_diagnostics(plugin, path, root, scope, node_result)
327
290
  raw = Array(plugin.diagnostics_for_file(path: path, scope: scope, root: root))
328
- # A node-rule context/rule raise isolates the whole plugin's
329
- # node-rule contribution, matching the old combined per-plugin
330
- # rescue (which discarded `diagnostics_for_file` output too).
291
+ # A node-rule context/rule raise isolates the whole plugin's node-rule contribution, matching the
292
+ # old combined per-plugin rescue (which discarded `diagnostics_for_file` output too).
331
293
  raise node_result.error if node_result&.error
332
294
 
333
295
  raw += node_result.diagnostics if node_result
@@ -2,16 +2,14 @@
2
2
 
3
3
  module Rigor
4
4
  module AST
5
- # A virtual node that wraps a Rigor::Type. Allows callers to ask
6
- # "what would the analyzer infer at this position if the value's type
7
- # were T?" without constructing a real Prism expression.
5
+ # A virtual node that wraps a Rigor::Type. Allows callers to ask "what would the analyzer infer at this
6
+ # position if the value's type were T?" without constructing a real Prism expression.
8
7
  #
9
- # Rigor::Scope#type_of(TypeNode.new(t)) MUST return a structurally-
10
- # equal t. The engine MUST NOT modify or annotate the wrapped type.
8
+ # Rigor::Scope#type_of(TypeNode.new(t)) MUST return a structurally-equal t. The engine MUST NOT modify or
9
+ # annotate the wrapped type.
11
10
  #
12
- # Inspired by PHPStan's TypeExpr (a synthetic Expr that returns a
13
- # specific Type from $scope->getType). The Rigor counterpart is
14
- # spelled "TypeNode" to align with Prism's "Node" suffix convention.
11
+ # Inspired by PHPStan's TypeExpr (a synthetic Expr that returns a specific Type from $scope->getType). The
12
+ # Rigor counterpart is spelled "TypeNode" to align with Prism's "Node" suffix convention.
15
13
  class TypeNode
16
14
  include Node
17
15
 
data/lib/rigor/ast.rb CHANGED
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- # Synthetic AST nodes accepted by Rigor::Scope#type_of alongside real
5
- # Prism nodes. Rigor::AST::Node is a documentation-only marker module
6
- # that production code uses to detect virtual-node arguments. Concrete
7
- # virtual node classes include Rigor::AST::Node and provide whatever
8
- # node-specific data the engine needs to translate them into a
9
- # Rigor::Type.
4
+ # Synthetic AST nodes accepted by Rigor::Scope#type_of alongside real Prism nodes. Rigor::AST::Node is a
5
+ # documentation-only marker module that production code uses to detect virtual-node arguments. Concrete virtual
6
+ # node classes include Rigor::AST::Node and provide whatever node-specific data the engine needs to translate
7
+ # them into a Rigor::Type.
10
8
  #
11
- # The contract for virtual nodes lives in
12
- # docs/internal-spec/inference-engine.md; the rationale and the rejected
13
- # alternative of specialising type classes for operator-method dispatch
14
- # live in docs/adr/4-type-inference-engine.md.
9
+ # The contract for virtual nodes lives in docs/internal-spec/inference-engine.md; the rationale and the rejected
10
+ # alternative of specialising type classes for operator-method dispatch live in docs/adr/4-type-inference-engine.md.
15
11
  module AST
16
12
  # Marker module included by every synthetic node. Carries no behaviour.
17
13
  module Node
@@ -3,26 +3,21 @@
3
3
  module Rigor
4
4
  # ADR-50 § WD2 — the bleeding-edge overlay.
5
5
  #
6
- # A Rigor-maintained set of the *next major's* queued changes —
7
- # severity-map promotions and new-discipline rule enablements — that a
8
- # user can adopt early, before they become default-on at a major
9
- # (ADR-50 § WD7). It is orthogonal to `severity_profile:` (how loud
10
- # *today's* rules are) and is versioned with the gem, NOT a
11
- # user-supplied file: the inspectable counterpart to PHPStan's
12
- # `bleedingEdge` include.
6
+ # A Rigor-maintained set of the *next major's* queued changes — severity-map promotions and
7
+ # new-discipline rule enablements — that a user can adopt early, before they become
8
+ # default-on at a major (ADR-50 § WD7). It is orthogonal to `severity_profile:` (how loud
9
+ # *today's* rules are) and is versioned with the gem, NOT a user-supplied file: the
10
+ # inspectable counterpart to PHPStan's `bleedingEdge` include.
13
11
  #
14
- # The overlay is **empty today** — no discipline has yet been queued
15
- # for the next major. This module is the WD2 *foundation* (the v0.1.19
16
- # slice): the surface (`bleeding_edge:` config, the
17
- # `rigor show-bleedingedge` command, the severity-composition hook in
18
- # {Configuration::SeverityProfile.resolve}) exists and is wired
19
- # end-to-end, so the first real feature lands as a single {FEATURES}
20
- # entry with no engine plumbing.
12
+ # The overlay is **empty today** — no discipline has yet been queued for the next major.
13
+ # This module is the WD2 *foundation* (the v0.1.19 slice): the surface (`bleeding_edge:`
14
+ # config, the `rigor show-bleedingedge` command, the severity-composition hook in
15
+ # {Configuration::SeverityProfile.resolve}) exists and is wired end-to-end, so the first
16
+ # real feature lands as a single {FEATURES} entry with no engine plumbing.
21
17
  #
22
- # Each feature carries a **stable feature id** — part of the ADR-50
23
- # WD1 contract vocabulary: the config, the `show` command, and the
24
- # eventual CHANGELOG migration note all name the same id, and a
25
- # feature graduates to default-on at a major by being removed from
18
+ # Each feature carries a **stable feature id** — part of the ADR-50 WD1 contract
19
+ # vocabulary: the config, the `show` command, and the eventual CHANGELOG migration note all
20
+ # name the same id, and a feature graduates to default-on at a major by being removed from
26
21
  # {FEATURES}.
27
22
  module BleedingEdge
28
23
  # One queued change.
@@ -32,10 +27,9 @@ module Rigor
32
27
  # @!attribute summary
33
28
  # @return [String] a one-line description of what it changes.
34
29
  # @!attribute severity_overrides
35
- # @return [Hash{String => Symbol}] canonical rule id → the
36
- # severity this feature imposes. Composed *below* the user's own
37
- # `severity_overrides:` and *above* the active `severity_profile`
38
- # (see {Configuration::SeverityProfile.resolve}).
30
+ # @return [Hash{String => Symbol}] canonical rule id → the severity this feature
31
+ # imposes. Composed *below* the user's own `severity_overrides:` and *above* the
32
+ # active `severity_profile` (see {Configuration::SeverityProfile.resolve}).
39
33
  Feature = Data.define(:id, :summary, :severity_overrides) do
40
34
  def to_h
41
35
  {
@@ -46,8 +40,8 @@ module Rigor
46
40
  end
47
41
  end
48
42
 
49
- # The overlay. Empty until the first next-major discipline is
50
- # queued; add a {Feature} here (with a stable id) when one is.
43
+ # The overlay. Empty until the first next-major discipline is queued; add a {Feature}
44
+ # here (with a stable id) when one is.
51
45
  FEATURES = [].freeze
52
46
 
53
47
  module_function
@@ -68,12 +62,11 @@ module Rigor
68
62
  FEATURES.find { |f| f.id == id }
69
63
  end
70
64
 
71
- # Resolves a normalized `bleeding_edge:` selector (see
72
- # {Configuration#bleeding_edge}) to the active {Feature} list.
73
- # Unknown ids in a `list` / `except` selector are simply absent from
74
- # the overlay and contribute nothing symmetric with how
75
- # `severity_overrides:` keeps an unknown rule id inert until it
76
- # lands (robust across gem versions).
65
+ # Resolves a normalized `bleeding_edge:` selector (see {Configuration#bleeding_edge}) to
66
+ # the active {Feature} list. Unknown ids in a `list` / `except` selector are simply
67
+ # absent from the overlay and contribute nothing symmetric with how
68
+ # `severity_overrides:` keeps an unknown rule id inert until it lands (robust across gem
69
+ # versions).
77
70
  #
78
71
  # @param selector [Hash] `{ "mode" => "none" }`,
79
72
  # `{ "mode" => "all" }`, `{ "mode" => "all", "except" => [ids] }`,
@@ -92,8 +85,8 @@ module Rigor
92
85
  end
93
86
  end
94
87
 
95
- # The merged severity-override map the active features impose for a
96
- # selector. Frozen so the result is `Ractor.shareable?`.
88
+ # The merged severity-override map the active features impose for a selector. Frozen so
89
+ # the result is `Ractor.shareable?`.
97
90
  #
98
91
  # @param selector [Hash] see {#active_features}.
99
92
  # @return [Hash{String => Symbol}]
@@ -103,9 +96,8 @@ module Rigor
103
96
  end.freeze
104
97
  end
105
98
 
106
- # Feature ids named by a selector that are NOT in the overlay
107
- # (typo / graduated / from a newer gem). Surfaced by
108
- # `rigor show-bleedingedge` as a hint; never an error.
99
+ # Feature ids named by a selector that are NOT in the overlay (typo / graduated / from a
100
+ # newer gem). Surfaced by `rigor show-bleedingedge` as a hint; never an error.
109
101
  #
110
102
  # @param selector [Hash] see {#active_features}.
111
103
  # @return [Array<String>]
@@ -6,28 +6,21 @@ require_relative "../inference/hkt_body_parser"
6
6
 
7
7
  module Rigor
8
8
  module Builtins
9
- # ADR-20 slices 2c + 3 — Rigor-bundled Lightweight HKT
10
- # registrations that ship with every analyzer instance.
11
- # The set is intentionally small at v0.1.x: only the URIs
12
- # whose payoff justifies hardcoded definitions. Plugin
13
- # authors register more URIs through their manifests; user
14
- # `.rbs` overlays register through the
15
- # `%a{rigor:v1:hkt_register}` /
16
- # `%a{rigor:v1:hkt_define}` annotations Slice 1 ships.
9
+ # ADR-20 slices 2c + 3 — Rigor-bundled Lightweight HKT registrations that ship with every analyzer instance.
10
+ # The set is intentionally small at v0.1.x: only the URIs whose payoff justifies hardcoded definitions. Plugin
11
+ # authors register more URIs through their manifests; user `.rbs` overlays register through the
12
+ # `%a{rigor:v1:hkt_register}` / `%a{rigor:v1:hkt_define}` annotations Slice 1 ships.
17
13
  #
18
14
  # Today's contents:
19
15
  #
20
- # - `json::value[K]` — the recursive sum stdlib's
21
- # `JSON.parse` returns. Body:
16
+ # - `json::value[K]` — the recursive sum stdlib's `JSON.parse` returns. Body:
22
17
  #
23
18
  # nil | true | false | Integer | Float | String
24
19
  # | Array[App[json::value, K]]
25
20
  # | Hash[K, App[json::value, K]]
26
21
  #
27
- # The reducer handles the self-recursive `App` nodes via
28
- # lazy "tying-the-knot" (see {HktReducer}). `K = String`
29
- # matches stdlib's default key handling; `K = Symbol`
30
- # matches `symbolize_names: true`.
22
+ # The reducer handles the self-recursive `App` nodes via lazy "tying-the-knot" (see {HktReducer}). `K = String`
23
+ # matches stdlib's default key handling; `K = Symbol` matches `symbolize_names: true`.
31
24
  module HktBuiltins
32
25
  module_function
33
26