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
@@ -10,32 +10,25 @@ module Rigor
10
10
  class Configuration # rubocop:disable Metrics/ClassLength
11
11
  # File-discovery order for `Configuration.load(nil)`.
12
12
  #
13
- # The first file present is loaded; the others are NOT
14
- # implicitly merged. To extend a base config explicitly the
15
- # winning file MUST list the base via `includes:`.
13
+ # The first file present is loaded; the others are NOT implicitly merged. To extend a base config
14
+ # explicitly the winning file MUST list the base via `includes:`.
16
15
  #
17
- # `.rigor.yml` is a developer-local override (typically
18
- # gitignored); `.rigor.dist.yml` is the project default
19
- # (committed to the repo). When both are present the
20
- # developer's local override wins outright — there is no
21
- # implicit auto-merge.
16
+ # `.rigor.yml` is a developer-local override (typically gitignored); `.rigor.dist.yml` is the project
17
+ # default (committed to the repo). When both are present the developer's local override wins outright —
18
+ # there is no implicit auto-merge.
22
19
  DISCOVERY_ORDER = %w[.rigor.yml .rigor.dist.yml].freeze
23
- # Back-compat alias. Keep here so external callers that read
24
- # `Configuration::DEFAULT_PATH` for help text / fixture paths
25
- # still work; the discovery list is the canonical source.
20
+ # Back-compat alias. Keep here so external callers that read `Configuration::DEFAULT_PATH` for help text /
21
+ # fixture paths still work; the discovery list is the canonical source.
26
22
  DEFAULT_PATH = DISCOVERY_ORDER.first
27
23
 
28
- # Built-in exclusion patterns appended to `exclude:` so vendored
29
- # dependencies, Bundler artefacts, and JavaScript node_modules are
30
- # never analysed by accident when a directory glob expands. Users
31
- # cannot disable these defaults; the trade-off is that analysing
32
- # any of these paths is essentially never what the user wants
33
- # (they're build outputs / external dependencies, not source).
24
+ # Built-in exclusion patterns appended to `exclude:` so vendored dependencies, Bundler artefacts, and
25
+ # JavaScript node_modules are never analysed by accident when a directory glob expands. Users cannot
26
+ # disable these defaults; the trade-off is that analysing any of these paths is essentially never what
27
+ # the user wants (they're build outputs / external dependencies, not source).
34
28
  #
35
- # We deliberately keep this list narrow. `tmp/` and similar
36
- # directories vary across project layouts (Rails has `tmp/`,
37
- # libraries usually don't); user-supplied `exclude:` entries
38
- # in `.rigor.yml` cover the project-specific cases.
29
+ # We deliberately keep this list narrow. `tmp/` and similar directories vary across project layouts
30
+ # (Rails has `tmp/`, libraries usually don't); user-supplied `exclude:` entries in `.rigor.yml` cover the
31
+ # project-specific cases.
39
32
  BUILTIN_EXCLUDES = %w[
40
33
  **/vendor/bundle/**
41
34
  **/.bundle/**
@@ -50,35 +43,26 @@ module Rigor
50
43
  "disable" => [],
51
44
  "libraries" => [],
52
45
  "signature_paths" => nil,
53
- # ADR-17 — project-side monkey-patch pre-evaluation.
54
- # Empty by default; users opt in by listing explicit files
55
- # that the analyzer walks before per-file inference so
56
- # patched-method declarations are visible across the
57
- # project (e.g. `lib/core_ext/string_extensions.rb`). Slice 1
58
- # plumbing only — listed files are validated at config-load
59
- # time (`pre-eval.file-not-found` on a missing path), but
60
- # the dispatcher tier consuming the registry lands in
61
- # slice 2.
46
+ # ADR-17 — project-side monkey-patch pre-evaluation. Empty by default; users opt in by listing explicit
47
+ # files that the analyzer walks before per-file inference so patched-method declarations are visible
48
+ # across the project (e.g. `lib/core_ext/string_extensions.rb`). Slice 1 plumbing only — listed files
49
+ # are validated at config-load time (`pre-eval.file-not-found` on a missing path), but the dispatcher
50
+ # tier consuming the registry lands in slice 2.
62
51
  "pre_eval" => [],
63
- # ADR-22 — baseline file path. nil (default) means no
64
- # baseline is loaded; the `false` literal is treated as
65
- # the explicit-disable form for `.rigor.yml`-side override
66
- # of an upstream `.rigor.dist.yml` `baseline:` declaration.
67
- # The presence of `.rigor-baseline.yml` on disk alone does
68
- # NOT activate filtering — the path must be named here
69
- # (WD2 (b) of ADR-22).
52
+ # ADR-22 — baseline file path. nil (default) means no baseline is loaded; the `false` literal is
53
+ # treated as the explicit-disable form for `.rigor.yml`-side override of an upstream `.rigor.dist.yml`
54
+ # `baseline:` declaration. The presence of `.rigor-baseline.yml` on disk alone does NOT activate
55
+ # filtering the path must be named here (WD2 (b) of ADR-22).
70
56
  "baseline" => nil,
71
57
  "fold_platform_specific_paths" => false,
72
58
  "cache" => {
73
59
  "path" => ".rigor/cache",
74
- # LRU eviction cap in bytes (ADR-54 WD3). The least-recently-used
75
- # entries are removed at the end of a run when the total exceeds
76
- # this limit. The 256 MB default exists to reap orphans entries
77
- # whose content key nothing references any more (an rbs gem bump,
78
- # a signature change) and that no run would otherwise ever delete;
79
- # a full active per-project set is ~2 MB, so the cap never touches
80
- # live entries. Set explicitly to `null` to disable eviction
81
- # (pre-WD3 behaviour: the cache grows until `--clear-cache`).
60
+ # LRU eviction cap in bytes (ADR-54 WD3). The least-recently-used entries are removed at the end of a
61
+ # run when the total exceeds this limit. The 256 MB default exists to reap orphans — entries whose
62
+ # content key nothing references any more (an rbs gem bump, a signature change) and that no run
63
+ # would otherwise ever delete; a full active per-project set is ~2 MB, so the cap never touches live
64
+ # entries. Set explicitly to `null` to disable eviction (pre-WD3 behaviour: the cache grows until
65
+ # `--clear-cache`).
82
66
  "max_bytes" => 268_435_456
83
67
  },
84
68
  "plugins_io" => {
@@ -88,100 +72,74 @@ module Rigor
88
72
  },
89
73
  "severity_profile" => "balanced",
90
74
  "severity_overrides" => {},
91
- # ADR-50 § WD2 — bleeding-edge overlay opt-in. Selects which of
92
- # the *next major's* queued changes ({Rigor::BleedingEdge}) this
93
- # project adopts early. Orthogonal to `severity_profile:`. Accepts
94
- # `false` (default adopt none), `true` (adopt the whole
95
- # overlay), a list of feature ids (adopt only those), or
96
- # `{ all: true, except: [ids] }` (adopt all but the named). The
97
- # overlay is empty today, so every form is currently a no-op; it
98
- # becomes live when the first discipline is queued for a major.
75
+ # ADR-50 § WD2 — bleeding-edge overlay opt-in. Selects which of the *next major's* queued changes
76
+ # ({Rigor::BleedingEdge}) this project adopts early. Orthogonal to `severity_profile:`. Accepts `false`
77
+ # (default adopt none), `true` (adopt the whole overlay), a list of feature ids (adopt only those),
78
+ # or `{ all: true, except: [ids] }` (adopt all but the named). The overlay is empty today, so every
79
+ # form is currently a no-op; it becomes live when the first discipline is queued for a major.
99
80
  "bleeding_edge" => false,
100
81
  "dependencies" => {
101
82
  "source_inference" => [],
102
83
  "budget_per_gem" => Configuration::Dependencies::DEFAULT_BUDGET_PER_GEM
103
84
  },
104
85
  "parallel" => {
105
- # ADR-15 Phase 4c — when greater than zero, `rigor check`
106
- # dispatches per-file analysis across N Ractor workers
107
- # built around {Rigor::Analysis::WorkerSession}.
108
- # `0` (default) keeps the sequential coordinator path
109
- # bit-for-bit unchanged. The CLI's `--workers=N` flag
110
- # and the `RIGOR_RACTOR_WORKERS` env var both override
111
- # this setting; precedence is CLI > env > config > 0.
86
+ # ADR-15 Phase 4c — when greater than zero, `rigor check` dispatches per-file analysis across N
87
+ # Ractor workers built around {Rigor::Analysis::WorkerSession}. `0` (default) keeps the sequential
88
+ # coordinator path bit-for-bit unchanged. The CLI's `--workers=N` flag and the `RIGOR_RACTOR_WORKERS`
89
+ # env var both override this setting; precedence is CLI > env > config > 0.
112
90
  "workers" => 0
113
91
  },
114
92
  "bundler" => {
115
- # Open item O4 — target-project Bundler awareness.
116
- # When `bundle_path:` is set (or auto-detected), Rigor
117
- # walks `<bundle_path>/ruby/*/gems/*/sig/` and adds each
118
- # gem-shipped sig directory to `signature_paths:`. With
119
- # O7's failure-memo in place, conflicts (a vendored sig
120
- # already declares the same constant) degrade gracefully
121
- # to "no RBS env" with a single-line warning naming the
122
- # offending file, rather than hanging.
93
+ # Open item O4 — target-project Bundler awareness. When `bundle_path:` is set (or auto-detected),
94
+ # Rigor walks `<bundle_path>/ruby/*/gems/*/sig/` and adds each gem-shipped sig directory to
95
+ # `signature_paths:`. With O7's failure-memo in place, conflicts (a vendored sig already declares the
96
+ # same constant) degrade gracefully to "no RBS env" with a single-line warning naming the offending
97
+ # file, rather than hanging.
123
98
  #
124
- # `bundle_path:` (String, optional): explicit path to the
125
- # bundler install root (e.g., "vendor/bundle" or an
126
- # absolute path). Resolved relative to the project root
127
- # (`paths:`'s base) when relative.
99
+ # `bundle_path:` (String, optional): explicit path to the bundler install root (e.g.,
100
+ # "vendor/bundle" or an absolute path). Resolved relative to the project root (`paths:`'s base) when
101
+ # relative.
128
102
  #
129
- # `auto_detect:` (Boolean, default true): when no
130
- # explicit `bundle_path:` is set, try `.bundle/config`'s
131
- # `BUNDLE_PATH:` first; fall back to `vendor/bundle/`
132
- # under the project root if it exists. When neither is
133
- # found, no extra sigs are added — the analyzer sees
134
- # only rigor's vendored RBS and the user's
135
- # `signature_paths:`.
103
+ # `auto_detect:` (Boolean, default true): when no explicit `bundle_path:` is set, try
104
+ # `.bundle/config`'s `BUNDLE_PATH:` first; fall back to `vendor/bundle/` under the project root if it
105
+ # exists. When neither is found, no extra sigs are added — the analyzer sees only rigor's vendored
106
+ # RBS and the user's `signature_paths:`.
136
107
  #
137
108
  # O4 Layer 3 keys:
138
109
  #
139
- # `lockfile:` (String, optional): explicit path to a
140
- # `Gemfile.lock`. Resolved relative to the project root
141
- # when relative. When set (or auto-detected via the
142
- # `auto_detect:` flag below) Rigor parses the lockfile
143
- # and uses it to FILTER the bundle-discovered `sig/`
144
- # directories: only gems whose `(name, version,
145
- # platform)` matches a lockfile entry are admitted to
146
- # `signature_paths:`. Stale or out-of-band gems sitting
147
- # in the bundle install tree are silently dropped.
110
+ # `lockfile:` (String, optional): explicit path to a `Gemfile.lock`. Resolved relative to the
111
+ # project root when relative. When set (or auto-detected via the `auto_detect:` flag below) Rigor
112
+ # parses the lockfile and uses it to FILTER the bundle-discovered `sig/` directories: only gems whose
113
+ # `(name, version, platform)` matches a lockfile entry are admitted to `signature_paths:`. Stale or
114
+ # out-of-band gems sitting in the bundle install tree are silently dropped.
148
115
  #
149
- # `auto_detect:` (Boolean, also gates the lockfile
150
- # search): when true and `lockfile:` is nil, look for
151
- # `<project_root>/Gemfile.lock`.
116
+ # `auto_detect:` (Boolean, also gates the lockfile search): when true and `lockfile:` is nil, look
117
+ # for `<project_root>/Gemfile.lock`.
152
118
  "bundle_path" => nil,
153
119
  "auto_detect" => true,
154
120
  "lockfile" => nil
155
121
  },
156
122
  "rbs_collection" => {
157
- # Open item O4 Layer 3 slice 2 — `rbs collection
158
- # install` awareness. When the target project has been
159
- # set up with `rbs collection install`, the resulting
160
- # `rbs_collection.lock.yaml` carries the resolved (gem,
161
- # version, source) triples and `.gem_rbs_collection/`
162
- # holds the downloaded `.rbs` files. Rigor parses the
163
- # lockfile and auto-feeds each gem's
164
- # `<collection_root>/<name>/<version>/` directory into
165
- # `RbsLoader`'s `signature_paths:`. Sources of type
166
- # `stdlib` are skipped because rigor's bundled
123
+ # Open item O4 Layer 3 slice 2 — `rbs collection install` awareness. When the target project has been
124
+ # set up with `rbs collection install`, the resulting `rbs_collection.lock.yaml` carries the resolved
125
+ # (gem, version, source) triples and `.gem_rbs_collection/` holds the downloaded `.rbs` files. Rigor
126
+ # parses the lockfile and auto-feeds each gem's `<collection_root>/<name>/<version>/` directory into
127
+ # `RbsLoader`'s `signature_paths:`. Sources of type `stdlib` are skipped because rigor's bundled
167
128
  # `DEFAULT_LIBRARIES` already covers that surface.
168
129
  #
169
- # `lockfile:` (String, optional): explicit path to
170
- # `rbs_collection.lock.yaml`. Resolved relative to the
171
- # project root when relative.
130
+ # `lockfile:` (String, optional): explicit path to `rbs_collection.lock.yaml`. Resolved relative to
131
+ # the project root when relative.
172
132
  #
173
- # `auto_detect:` (Boolean, default true): when no
174
- # explicit `lockfile:` is set, look for
133
+ # `auto_detect:` (Boolean, default true): when no explicit `lockfile:` is set, look for
175
134
  # `<project_root>/rbs_collection.lock.yaml`.
176
135
  "lockfile" => nil,
177
136
  "auto_detect" => true
178
137
  }
179
138
  }.freeze
180
139
 
181
- # Top-level keys whose values are file/directory paths that
182
- # MUST be resolved relative to the config file's directory.
183
- # `exclude:` is intentionally NOT in this list — its entries
184
- # are glob patterns (`**/vendor/**`), not paths.
140
+ # Top-level keys whose values are file/directory paths that MUST be resolved relative to the config
141
+ # file's directory. `exclude:` is intentionally NOT in this list — its entries are glob patterns
142
+ # (`**/vendor/**`), not paths.
185
143
  PATH_KEYS = %w[paths signature_paths pre_eval].freeze
186
144
  private_constant :PATH_KEYS
187
145
 
@@ -199,18 +157,14 @@ module Rigor
199
157
 
200
158
  # Loads a configuration file.
201
159
  #
202
- # `path == nil` triggers auto-discovery against
203
- # {DISCOVERY_ORDER}. The first present file in that list is
160
+ # `path == nil` triggers auto-discovery against {DISCOVERY_ORDER}. The first present file in that list is
204
161
  # loaded; if none exist the built-in {DEFAULTS} are used.
205
162
  #
206
- # When a path is supplied (whether by auto-discovery or by
207
- # the caller) the YAML body is processed for `includes:`
208
- # recursively, and every relative path inside path-bearing
209
- # keys (`paths:`, `signature_paths:`, `plugins_io.allowed_paths:`,
210
- # `includes:`) is resolved against THAT file's directory.
211
- # The resolution is per-file: an included file's relative
212
- # paths resolve against the included file's directory, not
213
- # the top-level file. Path resolution mirrors
163
+ # When a path is supplied (whether by auto-discovery or by the caller) the YAML body is processed for
164
+ # `includes:` recursively, and every relative path inside path-bearing keys (`paths:`,
165
+ # `signature_paths:`, `plugins_io.allowed_paths:`, `includes:`) is resolved against THAT file's
166
+ # directory. The resolution is per-file: an included file's relative paths resolve against the included
167
+ # file's directory, not the top-level file. Path resolution mirrors
214
168
  # [PHPStan](https://phpstan.org/config-reference#paths).
215
169
  def self.load(path = nil)
216
170
  resolved = path || discover
@@ -220,22 +174,17 @@ module Rigor
220
174
  new(DEFAULTS.merge(data))
221
175
  end
222
176
 
223
- # Returns the path to the config file Rigor would load
224
- # under auto-discovery, or `nil` when neither candidate
225
- # exists. Public so the CLI / spec drift checks can
226
- # introspect the resolved file.
177
+ # Returns the path to the config file Rigor would load under auto-discovery, or `nil` when neither
178
+ # candidate exists. Public so the CLI / spec drift checks can introspect the resolved file.
227
179
  def self.discover
228
180
  DISCOVERY_ORDER.find { |candidate| File.exist?(candidate) }
229
181
  end
230
182
 
231
- # Reads `path` (which MUST exist) plus every file listed in
232
- # its `includes:` chain, merging them under the order:
233
- # included files first (in declaration order), then the
234
- # current file's own keys override. Relative paths inside
235
- # each file are resolved against that file's directory.
236
- # Public so the CLI can run the include-aware load before
237
- # applying `--treat-all-as-inline-rbs`'s plugin injection
238
- # (see {CLI::CheckCommand#load_check_configuration}).
183
+ # Reads `path` (which MUST exist) plus every file listed in its `includes:` chain, merging them under the
184
+ # order: included files first (in declaration order), then the current file's own keys override.
185
+ # Relative paths inside each file are resolved against that file's directory. Public so the CLI can run
186
+ # the include-aware load before applying `--treat-all-as-inline-rbs`'s plugin injection (see
187
+ # {CLI::CheckCommand#load_check_configuration}).
239
188
  def self.load_with_includes(path, visited: Set.new)
240
189
  absolute = File.expand_path(path)
241
190
  raise ArgumentError, "circular include: #{absolute}" if visited.include?(absolute)
@@ -265,12 +214,10 @@ module Rigor
265
214
  deep_merge(accumulated, data)
266
215
  end
267
216
 
268
- # Per-file path resolution. Each path-bearing key listed in
269
- # {PATH_KEYS} plus the nested `plugins_io.allowed_paths:`
270
- # entries get their relative paths expanded against the
271
- # config file's directory. `cache.path:` is intentionally
272
- # left as-is so end-user messages (e.g. `--cache-stats`
273
- # output) keep the project-relative form the user wrote.
217
+ # Per-file path resolution. Each path-bearing key listed in {PATH_KEYS} plus the nested
218
+ # `plugins_io.allowed_paths:` entries get their relative paths expanded against the config file's
219
+ # directory. `cache.path:` is intentionally left as-is so end-user messages (e.g. `--cache-stats` output)
220
+ # keep the project-relative form the user wrote.
274
221
  def self.resolve_paths_in(data, base_dir)
275
222
  return data unless data.is_a?(Hash)
276
223
 
@@ -305,14 +252,11 @@ module Rigor
305
252
  merged
306
253
  end
307
254
 
308
- # Most keys are right-wins (override) or recursively
309
- # merged hashes. ADR-10 § "config-conflict diagnostic"
310
- # carves out `dependencies.source_inference[]`: the
311
- # per-gem merge across `includes:` chains needs union
312
- # behaviour with mode-conflict detection. The Hash itself
313
- # still merges deeply; only the inner array gets
314
- # concatenated so {Dependencies.from_h} sees every
315
- # contributor's entries and can dedupe them.
255
+ # Most keys are right-wins (override) or recursively merged hashes. ADR-10 § "config-conflict
256
+ # diagnostic" carves out `dependencies.source_inference[]`: the per-gem merge across `includes:` chains
257
+ # needs union behaviour with mode-conflict detection. The Hash itself still merges deeply; only the
258
+ # inner array gets concatenated so {Dependencies.from_h} sees every contributor's entries and can dedupe
259
+ # them.
316
260
  def self.merge_value(key, merged, value)
317
261
  if key == "dependencies" && merged[key].is_a?(Hash) && value.is_a?(Hash)
318
262
  merge_dependencies_hash(merged[key], value)
@@ -388,15 +332,10 @@ module Rigor
388
332
  rclf = rbs_collection.fetch("lockfile")
389
333
  @rbs_collection_lockfile = rclf.nil? ? nil : rclf.to_s.dup.freeze
390
334
  @rbs_collection_auto_detect = rbs_collection.fetch("auto_detect") == true
391
- # Ractor migration Phase 2a: deep-freeze the
392
- # Configuration so it is `Ractor.shareable?`. Every
393
- # ivar above is now either a frozen value (Symbol /
394
- # nil / Boolean) or an explicitly frozen
395
- # collection / value object; freezing `self` makes the
396
- # whole carrier safe to send across Ractor boundaries
397
- # (and catches accidental post-init mutation in any
398
- # caller). See
399
- # `docs/design/20260514-ractor-migration.md`.
335
+ # Ractor migration Phase 2a: deep-freeze the Configuration so it is `Ractor.shareable?`. Every ivar
336
+ # above is now either a frozen value (Symbol / nil / Boolean) or an explicitly frozen collection /
337
+ # value object; freezing `self` makes the whole carrier safe to send across Ractor boundaries (and
338
+ # catches accidental post-init mutation in any caller). See `docs/design/20260514-ractor-migration.md`.
400
339
  freeze
401
340
  end
402
341
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
@@ -440,23 +379,19 @@ module Rigor
440
379
  }
441
380
  end
442
381
 
443
- # ADR-50 § WD2 — returns a sibling Configuration whose bleeding-edge
444
- # selection (and the derived `bleeding_edge_severity_overrides` the two
445
- # {SeverityProfile.resolve} sites consult) is replaced by `value`,
446
- # leaving every other field shared with the receiver. `value` takes the
447
- # same forms as the `bleeding_edge:` config key `false` / `true` / a
448
- # feature-id Array / `{ "all" => true, "except" => [...] }` — and is
449
- # normalised through the same {#coerce_bleeding_edge} path, so an unknown
450
- # id stays inert.
382
+ # ADR-50 § WD2 — returns a sibling Configuration whose bleeding-edge selection (and the derived
383
+ # `bleeding_edge_severity_overrides` the two {SeverityProfile.resolve} sites consult) is replaced by
384
+ # `value`, leaving every other field shared with the receiver. `value` takes the same forms as the
385
+ # `bleeding_edge:` config key `false` / `true` / a feature-id Array / `{ "all" => true, "except" =>
386
+ # [...] }` and is normalised through the same {#coerce_bleeding_edge} path, so an unknown id stays
387
+ # inert.
451
388
  #
452
- # The CLI's `--bleeding-edge[=ids]` / `--no-bleeding-edge` flag uses this
453
- # to override the configured selection for a single run (the same
454
- # CLI-over-config precedence `--workers` and `--no-cache` follow). It is a
455
- # frozen `dup` with the two bleeding-edge ivars re-set: `dup` returns an
456
- # unfrozen shallow copy (every other ivar is the receiver's deeply-frozen
457
- # value, safe to share read-only), the two replacements are themselves
458
- # deeply frozen, and the re-`freeze` keeps the result `Ractor.shareable?`
459
- # for the worker path.
389
+ # The CLI's `--bleeding-edge[=ids]` / `--no-bleeding-edge` flag uses this to override the configured
390
+ # selection for a single run (the same CLI-over-config precedence `--workers` and `--no-cache` follow).
391
+ # It is a frozen `dup` with the two bleeding-edge ivars re-set: `dup` returns an unfrozen shallow copy
392
+ # (every other ivar is the receiver's deeply-frozen value, safe to share read-only), the two replacements
393
+ # are themselves deeply frozen, and the re-`freeze` keeps the result `Ractor.shareable?` for the worker
394
+ # path.
460
395
  def with_bleeding_edge(value)
461
396
  selector = coerce_bleeding_edge(value)
462
397
  copy = dup
@@ -468,17 +403,13 @@ module Rigor
468
403
 
469
404
  private
470
405
 
471
- # ADR-17 slice 4 — `pre_eval:` glob expansion. Each entry is
472
- # accepted as either a literal path (slice 1 contract) OR a
473
- # `File.fnmatch?`-shaped glob pattern (`lib/core_ext/**/*.rb`).
474
- # Glob meta characters (`*`, `?`, `[`) trigger `Dir.glob`
475
- # expansion; the resulting file list is folded into the
476
- # `pre_eval:` set with `uniq`. Literal entries that don't
477
- # exist on disk continue to surface as `pre-eval.file-not-found`
478
- # `:error` (slice 1 behaviour); glob entries that match
479
- # nothing degrade silently to "no contribution from this
480
- # entry" so a templated `**` pattern in a fresh project
481
- # doesn't generate an error per match-less pattern.
406
+ # ADR-17 slice 4 — `pre_eval:` glob expansion. Each entry is accepted as either a literal path (slice 1
407
+ # contract) OR a `File.fnmatch?`-shaped glob pattern (`lib/core_ext/**/*.rb`). Glob meta characters (`*`,
408
+ # `?`, `[`) trigger `Dir.glob` expansion; the resulting file list is folded into the `pre_eval:` set with
409
+ # `uniq`. Literal entries that don't exist on disk continue to surface as `pre-eval.file-not-found`
410
+ # `:error` (slice 1 behaviour); glob entries that match nothing degrade silently to "no contribution from
411
+ # this entry" so a templated `**` pattern in a fresh project doesn't generate an error per match-less
412
+ # pattern.
482
413
  def expand_pre_eval_entries(entries)
483
414
  entries.flat_map do |entry|
484
415
  glob_pattern?(entry) ? Dir.glob(entry, sort: true) : [entry]
@@ -489,10 +420,8 @@ module Rigor
489
420
  path.include?("*") || path.include?("?") || path.include?("[")
490
421
  end
491
422
 
492
- # Accepts either `"rigor-foo"` (gem-name shorthand) or
493
- # `{ "gem" => "rigor-foo", "id" => "foo", "config" => {...} }`
494
- # (full form). Returns the canonical hash form so the loader
495
- # works against a single shape.
423
+ # Accepts either `"rigor-foo"` (gem-name shorthand) or `{ "gem" => "rigor-foo", "id" => "foo", "config" =>
424
+ # {...} }` (full form). Returns the canonical hash form so the loader works against a single shape.
496
425
  def coerce_plugin_entry(entry)
497
426
  case entry
498
427
  when String
@@ -505,16 +434,12 @@ module Rigor
505
434
  end
506
435
  end
507
436
 
508
- # `target_ruby` is passed to `Prism.parse_file(path, version:)` at
509
- # the analyser's three parse sites (`Analysis::Runner`,
510
- # `CLI::TypeOfCommand`, `CLI::TypeScanCommand`) so projects that
511
- # target an older Ruby get parse errors for syntax their target
512
- # doesn't support. Format validation here is loose accepts
513
- # any `<major>.<minor>` or `<major>.<minor>.<patch>` form, plus
514
- # the literal `"latest"`. Prism itself enforces the supported
515
- # set and raises `ArgumentError` for versions it does not
516
- # recognise (e.g. `"1.0"`); the parse-time error message names
517
- # the version, so the user can correct the setting.
437
+ # `target_ruby` is passed to `Prism.parse_file(path, version:)` at the analyser's three parse sites
438
+ # (`Analysis::Runner`, `CLI::TypeOfCommand`, `CLI::TypeScanCommand`) so projects that target an older
439
+ # Ruby get parse errors for syntax their target doesn't support. Format validation here is loose —
440
+ # accepts any `<major>.<minor>` or `<major>.<minor>.<patch>` form, plus the literal `"latest"`. Prism
441
+ # itself enforces the supported set and raises `ArgumentError` for versions it does not recognise (e.g.
442
+ # `"1.0"`); the parse-time error message names the version, so the user can correct the setting.
518
443
  TARGET_RUBY_FORMAT = /\A(?:\d+\.\d+(?:\.\d+)?|latest)\z/
519
444
  private_constant :TARGET_RUBY_FORMAT
520
445
 
@@ -528,22 +453,17 @@ module Rigor
528
453
  s.dup.freeze
529
454
  end
530
455
 
531
- # YAML scalar may arrive as a String or already as a Symbol;
532
- # coerce to the canonical Symbol shape so the downstream
533
- # `TrustPolicy` constructor stays strict.
456
+ # YAML scalar may arrive as a String or already as a Symbol; coerce to the canonical Symbol shape so the
457
+ # downstream `TrustPolicy` constructor stays strict.
534
458
  #
535
- # The accepted set is duplicated from
536
- # {Rigor::Plugin::TrustPolicy::VALID_NETWORK_POLICIES} so
537
- # `Configuration` does not require the plugin namespace at
538
- # load time (Configuration is loaded before Plugin in
539
- # `lib/rigor.rb`); the two stay in lockstep via spec.
459
+ # The accepted set is duplicated from {Rigor::Plugin::TrustPolicy::VALID_NETWORK_POLICIES} so
460
+ # `Configuration` does not require the plugin namespace at load time (Configuration is loaded before
461
+ # Plugin in `lib/rigor.rb`); the two stay in lockstep via spec.
540
462
  VALID_NETWORK_POLICIES = %i[disabled allowlist].freeze
541
463
  private_constant :VALID_NETWORK_POLICIES
542
464
 
543
- # ADR-15 Phase 4c — accepts a non-negative Integer (or a
544
- # string-shaped one from YAML files that miss type
545
- # annotations). Negative / non-integer values raise so
546
- # typos / bad YAML fail loudly rather than silently
465
+ # ADR-15 Phase 4c — accepts a non-negative Integer (or a string-shaped one from YAML files that miss type
466
+ # annotations). Negative / non-integer values raise so typos / bad YAML fail loudly rather than silently
547
467
  # disabling parallelism.
548
468
  def coerce_parallel_workers(value)
549
469
  integer = Integer(value)
@@ -554,11 +474,9 @@ module Rigor
554
474
  raise ArgumentError, "parallel.workers must be a non-negative Integer, got #{value.inspect} (#{e.message})"
555
475
  end
556
476
 
557
- # ADR-22 WD2 (b) — `baseline: <path>` activates the file;
558
- # `baseline: false` is the explicit-disable form (useful in
559
- # `.rigor.yml` to override an upstream `.rigor.dist.yml`
560
- # that names a baseline). `nil` (default / absent key) is
561
- # also "no baseline".
477
+ # ADR-22 WD2 (b) — `baseline: <path>` activates the file; `baseline: false` is the explicit-disable form
478
+ # (useful in `.rigor.yml` to override an upstream `.rigor.dist.yml` that names a baseline). `nil`
479
+ # (default / absent key) is also "no baseline".
562
480
  def coerce_baseline_path(value)
563
481
  return nil if value.nil? || value == false
564
482
 
@@ -575,9 +493,8 @@ module Rigor
575
493
  sym
576
494
  end
577
495
 
578
- # ADR-8 § "Severity profile" — accepts the canonical Symbol
579
- # form or its String spelling; rejects unknown profile names
580
- # so typos fail loudly.
496
+ # ADR-8 § "Severity profile" — accepts the canonical Symbol form or its String spelling; rejects unknown
497
+ # profile names so typos fail loudly.
581
498
  def coerce_severity_profile(value)
582
499
  sym = value.to_sym
583
500
  unless SeverityProfile::VALID_PROFILES.include?(sym)
@@ -589,21 +506,17 @@ module Rigor
589
506
  sym
590
507
  end
591
508
 
592
- # ADR-8 § "Severity profile" — `severity_overrides:` is a
593
- # `{ rule => severity }` map. Keys are canonical rule ids
594
- # (`call.undefined-method`) or family wildcards (`call`).
595
- # Values are {SeverityProfile::VALID_SEVERITIES} symbols
596
- # (`:error` / `:warning` / `:info` / `:off`). Unknown
597
- # severities raise; unknown rule ids are silently kept (the
598
- # override is inert until the rule lands).
509
+ # ADR-8 § "Severity profile" — `severity_overrides:` is a `{ rule => severity }` map. Keys are canonical
510
+ # rule ids (`call.undefined-method`) or family wildcards (`call`). Values are
511
+ # {SeverityProfile::VALID_SEVERITIES} symbols (`:error` / `:warning` / `:info` / `:off`). Unknown
512
+ # severities raise; unknown rule ids are silently kept (the override is inert until the rule lands).
599
513
  def coerce_severity_overrides(value)
600
514
  raise ArgumentError, "severity_overrides must be a Hash, got #{value.inspect}" unless value.is_a?(Hash)
601
515
 
602
516
  value.to_h do |k, v|
603
- # YAML 1.1 parses bare `off`/`on`/`no`/`yes`/`true`/`false`
604
- # as booleans, so a user who wrote `off` (a valid severity)
605
- # without quotes hands us `false`. Catch the non-Symbol /
606
- # non-String case before `to_sym` blows up with a backtrace.
517
+ # YAML 1.1 parses bare `off`/`on`/`no`/`yes`/`true`/`false` as booleans, so a user who wrote `off` (a
518
+ # valid severity) without quotes hands us `false`. Catch the non-Symbol / non-String case before
519
+ # `to_sym` blows up with a backtrace.
607
520
  unless v.is_a?(String) || v.is_a?(Symbol)
608
521
  hint = v == false ? %( — did you mean the string "off"?) : ""
609
522
  raise ArgumentError,
@@ -623,12 +536,10 @@ module Rigor
623
536
  end.freeze
624
537
  end
625
538
 
626
- # ADR-50 § WD2 — normalizes the `bleeding_edge:` selector to a
627
- # canonical `{ "mode" => }` hash (interpreted by
628
- # {Rigor::BleedingEdge}). Validates *shape* only; membership against
629
- # the overlay is intentionally NOT checked here (an unknown id stays
630
- # inert, like an unknown `severity_overrides:` rule). Deep-frozen so
631
- # the Configuration stays `Ractor.shareable?`.
539
+ # ADR-50 § WD2 — normalizes the `bleeding_edge:` selector to a canonical `{ "mode" => … }` hash
540
+ # (interpreted by {Rigor::BleedingEdge}). Validates *shape* only; membership against the overlay is
541
+ # intentionally NOT checked here (an unknown id stays inert, like an unknown `severity_overrides:` rule).
542
+ # Deep-frozen so the Configuration stays `Ractor.shareable?`.
632
543
  def coerce_bleeding_edge(value)
633
544
  case value
634
545
  when nil, false then { "mode" => "none" }
@@ -651,18 +562,16 @@ module Rigor
651
562
  end
652
563
  end
653
564
 
654
- # Feature ids reach `coerce_bleeding_edge` from YAML, a `to_h` round-trip,
655
- # or the CLI's `--bleeding-edge=a,b` (all runtime-created, non-frozen
656
- # Strings). Each id String is frozen so the normalized selector — and thus
657
- # the whole Configuration stays deeply frozen and `Ractor.shareable?`
658
- # for the worker path (the same invariant `#initialize`'s final `freeze`
659
- # upholds for every other field).
565
+ # Feature ids reach `coerce_bleeding_edge` from YAML, a `to_h` round-trip, or the CLI's
566
+ # `--bleeding-edge=a,b` (all runtime-created, non-frozen Strings). Each id String is frozen so the
567
+ # normalized selector and thus the whole Configurationstays deeply frozen and `Ractor.shareable?`
568
+ # for the worker path (the same invariant `#initialize`'s final `freeze` upholds for every other field).
660
569
  def freeze_ids(ids)
661
570
  ids.map { |id| id.to_s.dup.freeze }.freeze
662
571
  end
663
572
 
664
- # Renders the normalized selector back into the user-facing
665
- # `bleeding_edge:` form for `#to_h` round-trips.
573
+ # Renders the normalized selector back into the user-facing `bleeding_edge:` form for `#to_h`
574
+ # round-trips.
666
575
  def bleeding_edge_to_h
667
576
  case bleeding_edge["mode"]
668
577
  when "all"