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,34 +2,28 @@
2
2
 
3
3
  module Rigor
4
4
  module Analysis
5
- # ADR-46 slice 2 — the pure set-algebra core of the incremental step,
6
- # kept side-effect-free and Runner-independent so the soundness
7
- # property it encodes is unit-testable without the analysis machinery.
5
+ # ADR-46 slice 2 — the pure set-algebra core of the incremental step, kept side-effect-free and
6
+ # Runner-independent so the soundness property it encodes is unit-testable without the analysis machinery.
8
7
  #
9
- # Given the files that changed since the baseline run and the
10
- # baseline's `dependents` index ({Runner#file_dependents}), the
11
- # **affected closure** the body tier must re-analyse is the changed set
12
- # plus every file that read a declaration or method body from a changed
13
- # file. Every other file is served from the per-file diagnostic cache.
8
+ # Given the files that changed since the baseline run and the baseline's `dependents` index
9
+ # ({Runner#file_dependents}), the **affected closure** the body tier must re-analyse is the changed set
10
+ # plus every file that read a declaration or method body from a changed file. Every other file is served
11
+ # from the per-file diagnostic cache.
14
12
  #
15
- # The soundness invariant (the {Runner}-driven `--verify-incremental`
16
- # gate and the spec assert it): for an edit whose declaration-structure
17
- # fingerprint is unchanged (a method-body edit no symbol created,
18
- # destroyed, moved, or re-parented), the set of files whose diagnostics
19
- # actually change is a SUBSET of {affected}. A file outside the closure
20
- # whose diagnostics changed would be served stalea manufactured
21
- # false positive/negative, the failure mode this design exists to
22
- # prevent. Structural edits (fingerprint changed) are out of this
23
- # tier's scope — they widen via the negative-dependency / full fallback
24
- # path (slice 3).
13
+ # The soundness invariant (the {Runner}-driven `--verify-incremental` gate and the spec assert it): for an
14
+ # edit whose declaration-structure fingerprint is unchanged (a method-body edit no symbol created,
15
+ # destroyed, moved, or re-parented), the set of files whose diagnostics actually change is a SUBSET of
16
+ # {affected}. A file outside the closure whose diagnostics changed would be served stale — a manufactured
17
+ # false positive/negative, the failure mode this design exists to prevent. Structural edits (fingerprint
18
+ # changed) are out of this tier's scopethey widen via the negative-dependency / full fallback path
19
+ # (slice 3).
25
20
  module Incremental
26
21
  module_function
27
22
 
28
- # Inverts a per-consumer source map (`consumer → enumerable of source
29
- # files it read from`) into the `dependents` index (`source → Set of
30
- # consumers that read from it`). The reverse edge the incremental step
31
- # walks. Returns a frozen hash of frozen Sets; a missing key reads as
32
- # nil (the default proc is dropped before freezing).
23
+ # Inverts a per-consumer source map (`consumer → enumerable of source files it read from`) into the
24
+ # `dependents` index (`source → Set of consumers that read from it`). The reverse edge the incremental
25
+ # step walks. Returns a frozen hash of frozen Sets; a missing key reads as nil (the default proc is
26
+ # dropped before freezing).
33
27
  def invert(sources_by_consumer)
34
28
  index = Hash.new { |hash, key| hash[key] = Set.new }
35
29
  sources_by_consumer.each do |consumer, sources|
@@ -40,20 +34,18 @@ module Rigor
40
34
  index.freeze
41
35
  end
42
36
 
43
- # The closure the body tier re-analyses. `changed` is any Enumerable
44
- # of paths; `dependents` maps a source path to the Set of files that
45
- # read from it (missing key → no dependents). Returns a frozen Set.
37
+ # The closure the body tier re-analyses. `changed` is any Enumerable of paths; `dependents` maps a
38
+ # source path to the Set of files that read from it (missing key → no dependents). Returns a frozen Set.
46
39
  def affected(changed, dependents)
47
40
  closure = changed.to_set
48
41
  changed.each { |file| closure.merge(dependents[file] || []) }
49
42
  closure.freeze
50
43
  end
51
44
 
52
- # ADR-46 slice 4 — inverts a per-consumer symbol-sources map
53
- # (`consumer → { source_path → Set<"ClassName#method"> }`) into the
54
- # symbol-level dependents index: `[source_path, symbol] Set<consumer>`.
55
- # Used by {affected_with_symbols} to limit fan-out to callers of
56
- # symbols that actually changed rather than all callers of the file.
45
+ # ADR-46 slice 4 — inverts a per-consumer symbol-sources map (`consumer → { source_path →
46
+ # Set<"ClassName#method"> }`) into the symbol-level dependents index: `[source_path, symbol] →
47
+ # Set<consumer>`. Used by {affected_with_symbols} to limit fan-out to callers of symbols that actually
48
+ # changed rather than all callers of the file.
57
49
  def invert_symbols(symbol_sources_by_consumer)
58
50
  index = Hash.new { |h, k| h[k] = Set.new }
59
51
  symbol_sources_by_consumer.each do |consumer, sources_by_file|
@@ -66,10 +58,9 @@ module Rigor
66
58
  index.freeze
67
59
  end
68
60
 
69
- # ADR-46 slice 4 — given a set of changed file paths and two per-file
70
- # symbol fingerprint maps (before and after), returns the frozen Set of
71
- # `[path, symbol]` pairs whose fingerprints differ (added, removed, or
72
- # body-changed).
61
+ # ADR-46 slice 4 — given a set of changed file paths and two per-file symbol fingerprint maps (before
62
+ # and after), returns the frozen Set of `[path, symbol]` pairs whose fingerprints differ (added,
63
+ # removed, or body-changed).
73
64
  def changed_symbol_pairs(changed_files, fingerprints_before, fingerprints_after)
74
65
  pairs = Set.new
75
66
  changed_files.each do |path|
@@ -89,8 +80,8 @@ module Rigor
89
80
  # (b) it has an ancestry dep on a changed file (always re-checked — file-level), or
90
81
  # (c) it has a symbol dep on a `[file, symbol]` pair that changed.
91
82
  #
92
- # Consumers that only have symbol deps on a changed file, and none of
93
- # their tracked symbols changed, are NOT included — the slice 4 precision win.
83
+ # Consumers that only have symbol deps on a changed file, and none of their tracked symbols changed,
84
+ # are NOT included — the slice 4 precision win.
94
85
  def affected_with_symbols(changed_files, changed_pairs, symbol_dependents, ancestry_dependents)
95
86
  closure = changed_files.to_set
96
87
  changed_files.each { |file| closure.merge(ancestry_dependents[file] || []) }
@@ -98,13 +89,11 @@ module Rigor
98
89
  closure.freeze
99
90
  end
100
91
 
101
- # ADR-46 slice 3 — the symbol keys (`"ClassName#method"`) that are
102
- # present in a changed file's after-fingerprints but were absent from
103
- # its before-fingerprints: a symbol that *appeared* in this edit. A
104
- # symbol that merely moved between files still appears here for the
105
- # destination file, but its negative-dependents set is empty (nobody
106
- # missed a name that already resolved elsewhere), so the over-report
107
- # costs nothing. Returns a frozen Set of symbol-key Strings.
92
+ # ADR-46 slice 3 — the symbol keys (`"ClassName#method"`) that are present in a changed file's
93
+ # after-fingerprints but were absent from its before-fingerprints: a symbol that *appeared* in this
94
+ # edit. A symbol that merely moved between files still appears here for the destination file, but its
95
+ # negative-dependents set is empty (nobody missed a name that already resolved elsewhere), so the
96
+ # over-report costs nothing. Returns a frozen Set of symbol-key Strings.
108
97
  def appeared_symbols(changed_files, fingerprints_before, fingerprints_after)
109
98
  appeared = Set.new
110
99
  changed_files.each do |path|
@@ -115,14 +104,12 @@ module Rigor
115
104
  appeared.freeze
116
105
  end
117
106
 
118
- # ADR-46 slice 3 — the qualified class/module names *declared* in a
119
- # changed file's after-state that were absent from its before-state: a
120
- # class that appeared in this edit. (For an added file the before-set
121
- # is empty, so every class it declares appears.) A class that merely
122
- # moved files still appears here, but its negative-dependents are empty,
123
- # so the over-report costs nothing. Returns a frozen Set of qualified
124
- # class-name Strings. `decls_before` / `decls_after` map a path to its
125
- # Set of declared class names.
107
+ # ADR-46 slice 3 — the qualified class/module names *declared* in a changed file's after-state that
108
+ # were absent from its before-state: a class that appeared in this edit. (For an added file the
109
+ # before-set is empty, so every class it declares appears.) A class that merely moved files still
110
+ # appears here, but its negative-dependents are empty, so the over-report costs nothing. Returns a
111
+ # frozen Set of qualified class-name Strings. `decls_before` / `decls_after` map a path to its Set of
112
+ # declared class names.
126
113
  def appeared_classes(changed_files, decls_before, decls_after)
127
114
  appeared = Set.new
128
115
  changed_files.each do |path|
@@ -133,23 +120,20 @@ module Rigor
133
120
  appeared.freeze
134
121
  end
135
122
 
136
- # ADR-46 slice 3 — the consumers to re-check because a name they
137
- # looked up and *missed* (a negative dependency) now resolves. `keys`
138
- # is the set of negative-dependency keys (`"toplevel:foo"` /
139
- # `"method:C#m"`) the appeared symbols would satisfy; `negative_dependents`
140
- # maps each key to the Set of consumers that recorded the miss. Returns
141
- # a frozen Set of consumer paths.
123
+ # ADR-46 slice 3 — the consumers to re-check because a name they looked up and *missed* (a negative
124
+ # dependency) now resolves. `keys` is the set of negative-dependency keys (`"toplevel:foo"` /
125
+ # `"method:C#m"`) the appeared symbols would satisfy; `negative_dependents` maps each key to the Set of
126
+ # consumers that recorded the miss. Returns a frozen Set of consumer paths.
142
127
  def negative_closure(keys, negative_dependents)
143
128
  closure = Set.new
144
129
  keys.each { |key| closure.merge(negative_dependents[key] || []) }
145
130
  closure.freeze
146
131
  end
147
132
 
148
- # The files whose per-file diagnostics differ between two runs.
149
- # Each argument maps a path to its diagnostic list; diagnostics are
150
- # compared structurally via {Diagnostic#to_h} so identity / ordering
151
- # of the objects themselves does not matter. A file present in one
152
- # run and absent (zero diagnostics) in the other counts as changed.
133
+ # The files whose per-file diagnostics differ between two runs. Each argument maps a path to its
134
+ # diagnostic list; diagnostics are compared structurally via {Diagnostic#to_h} so identity / ordering
135
+ # of the objects themselves does not matter. A file present in one run and absent (zero diagnostics) in
136
+ # the other counts as changed.
153
137
  def changed_files(before_by_file, after_by_file)
154
138
  (before_by_file.keys | after_by_file.keys).each_with_object(Set.new) do |path, changed|
155
139
  before = (before_by_file[path] || []).map(&:to_h)
@@ -7,38 +7,30 @@ require_relative "../inference/scope_indexer"
7
7
 
8
8
  module Rigor
9
9
  module Analysis
10
- # ADR-46 slice 2 — the in-memory incremental orchestrator that composes
11
- # the recorded dependency graph ({Runner#file_dependents}), the affected
12
- # closure ({Incremental.affected}), and the subset-analysis hook
10
+ # ADR-46 slice 2 — the in-memory incremental orchestrator that composes the recorded dependency graph
11
+ # ({Runner#file_dependents}), the affected closure ({Incremental.affected}), and the subset-analysis hook
13
12
  # ({Runner} `analyze_only:`) into a working incremental re-check.
14
13
  #
15
- # `#baseline` runs a full analysis with dependency recording and keeps,
16
- # per analyzed file, its diagnostics (the cache), its content digest,
17
- # and the per-file source set (to maintain the dependents index across
18
- # rounds). `#recheck` digests the files again, computes the changed set
19
- # ΔF, re-analyzes only `ΔF ∪ dependents[ΔF]`, and serves every other
20
- # analyzed file from the cache — the body tier.
14
+ # `#baseline` runs a full analysis with dependency recording and keeps, per analyzed file, its
15
+ # diagnostics (the cache), its content digest, and the per-file source set (to maintain the dependents
16
+ # index across rounds). `#recheck` digests the files again, computes the changed set ΔF, re-analyzes
17
+ # only `ΔF dependents[ΔF]`, and serves every other analyzed file from the cache — the body tier.
21
18
  #
22
- # The invariant the verify harness (and the spec) assert: `#recheck`'s
23
- # merged diagnostics are byte-identical (as a sorted set) to a full
24
- # `--no-cache` re-analysis of the edited tree. This is the
25
- # `--verify-incremental` acceptance gate, here without disk persistence
26
- # or CLI wiring (the cache is in-process). It models the body tier only:
27
- # an edit that adds / removes / moves a *file* is outside the analyzed
28
- # set it maintains and falls to a fresh {#baseline} (the structural tier
29
- # is a later slice).
19
+ # The invariant the verify harness (and the spec) assert: `#recheck`'s merged diagnostics are
20
+ # byte-identical (as a sorted set) to a full `--no-cache` re-analysis of the edited tree. This is the
21
+ # `--verify-incremental` acceptance gate, here without disk persistence or CLI wiring (the cache is
22
+ # in-process). It models the body tier only: an edit that adds / removes / moves a *file* is outside
23
+ # the analyzed set it maintains and falls to a fresh {#baseline} (the structural tier is a later slice).
30
24
  class IncrementalSession
31
- # The outcome of a {#recheck}: the merged diagnostics plus the file
32
- # sets, so a caller (or the verify gate) can report what was
33
- # re-analyzed versus served from cache.
25
+ # The outcome of a {#recheck}: the merged diagnostics plus the file sets, so a caller (or the verify
26
+ # gate) can report what was re-analyzed versus served from cache.
34
27
  Recheck = Data.define(:diagnostics, :changed, :affected, :reused)
35
28
 
36
- # @param paths [Array<String>, nil] explicit analysis roots; nil
37
- # (the default) uses the configuration's `paths:`.
38
- # @param environment [Rigor::Environment, nil] optional shared
39
- # environment to thread into each internal Runner. Long-lived
40
- # callers and specs can use this to avoid rebuilding the same
41
- # RBS universe for every baseline / recheck / oracle run.
29
+ # @param paths [Array<String>, nil] explicit analysis roots; nil (the default) uses the configuration's
30
+ # `paths:`.
31
+ # @param environment [Rigor::Environment, nil] optional shared environment to thread into each internal
32
+ # Runner. Long-lived callers and specs can use this to avoid rebuilding the same RBS universe for
33
+ # every baseline / recheck / oracle run.
42
34
  def initialize(configuration:, paths: nil, environment: nil)
43
35
  @configuration = configuration
44
36
  @paths = paths
@@ -63,15 +55,14 @@ module Rigor
63
55
  @class_decls = {} # path => Set<qualified class name declared in the file>
64
56
  end
65
57
 
66
- # The project files analyzed at the last baseline / recheck — the set
67
- # a verify pass partitions and the merge subtracts the affected
68
- # closure from.
58
+ # The project files analyzed at the last baseline / recheck — the set a verify pass partitions and the
59
+ # merge subtracts the affected closure from.
69
60
  def analyzed_files
70
61
  @analyzed
71
62
  end
72
63
 
73
- # Full baseline analysis with recording. Returns the run's
74
- # diagnostics; populates the in-process cache + dependency state.
64
+ # Full baseline analysis with recording. Returns the run's diagnostics; populates the in-process cache
65
+ # + dependency state.
75
66
  def baseline
76
67
  runner = build_runner(record_dependencies: true)
77
68
  diagnostics = run_runner(runner).diagnostics
@@ -82,9 +73,8 @@ module Rigor
82
73
  diagnostics
83
74
  end
84
75
 
85
- # Re-check after on-disk edits, including files added or removed since
86
- # the last run (the structural tier). Re-analyzes only the affected
87
- # closure and serves the rest from cache; refreshes the cache +
76
+ # Re-check after on-disk edits, including files added or removed since the last run (the structural
77
+ # tier). Re-analyzes only the affected closure and serves the rest from cache; refreshes the cache +
88
78
  # dependency state so a subsequent #recheck sees the new world.
89
79
  def recheck
90
80
  previous = @analyzed
@@ -102,13 +92,11 @@ module Rigor
102
92
  Recheck.new(diagnostics: merged, changed: changed.to_set, affected: affected, reused: reused.to_set)
103
93
  end
104
94
 
105
- # The frozen set of files a #recheck must re-analyse: the
106
- # symbol/ancestry-granularity closure of the changed files (slice 4),
107
- # the added files themselves, the consumers of any symbol / class that
108
- # *appeared* in a changed OR added file (slice 3 a now-defined
109
- # `call.unresolved-toplevel` target or `def.override-*` ancestor), and
110
- # the consumers of every removed file (which now miss what it provided).
111
- # An added file has no before-state, so all its symbols / classes appear.
95
+ # The frozen set of files a #recheck must re-analyse: the symbol/ancestry-granularity closure of the
96
+ # changed files (slice 4), the added files themselves, the consumers of any symbol / class that
97
+ # *appeared* in a changed OR added file (slice 3 a now-defined `call.unresolved-toplevel` target or
98
+ # `def.override-*` ancestor), and the consumers of every removed file (which now miss what it
99
+ # provided). An added file has no before-state, so all its symbols / classes appear.
112
100
  def affected_closure(changed, added, removed)
113
101
  scan = changed + added
114
102
  new_fps = symbol_fingerprints_for(scan)
@@ -124,21 +112,18 @@ module Rigor
124
112
  closure.freeze
125
113
  end
126
114
 
127
- # The current project file set (cheap directory expansion, no analysis),
128
- # used to detect files added / removed since the last run.
115
+ # The current project file set (cheap directory expansion, no analysis), used to detect files added /
116
+ # removed since the last run.
129
117
  def current_files
130
118
  runner = build_runner
131
119
  @paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
132
120
  end
133
121
 
134
- # Verification engine (the `--verify-incremental` gate): with NO
135
- # source edit, re-analyze `subset` fresh and serve every other
136
- # analyzed file from the baseline cache. Because nothing on disk
137
- # changed, the merged result MUST equal a full analysis so this
138
- # exercises the subset-analysis and cache-merge paths against a
139
- # known-good oracle (a full `--no-cache` run) for an arbitrary
140
- # partition, without mutating session state. Returns the merged
141
- # diagnostics.
122
+ # Verification engine (the `--verify-incremental` gate): with NO source edit, re-analyze `subset` fresh
123
+ # and serve every other analyzed file from the baseline cache. Because nothing on disk changed, the
124
+ # merged result MUST equal a full analysis so this exercises the subset-analysis and cache-merge
125
+ # paths against a known-good oracle (a full `--no-cache` run) for an arbitrary partition, without
126
+ # mutating session state. Returns the merged diagnostics.
142
127
  def reanalyze_subset(subset)
143
128
  affected = subset.to_set
144
129
  runner = build_runner(analyze_only: affected)
@@ -147,14 +132,12 @@ module Rigor
147
132
  fresh + reused.flat_map { |path| @cache[path] || [] }
148
133
  end
149
134
 
150
- # Cross-process incremental run (the `--incremental` flag's engine).
151
- # With a disk `snapshot` whose `fingerprint` matches, restore the
152
- # prior per-file state and `#recheck` (re-analyze only the changed
153
- # closure, serve the rest from the restored cache); otherwise run a
154
- # full `#baseline`. Either way, persist the updated snapshot for the
155
- # next process. Returns `[diagnostics, warm]` — `warm` is true when a
156
- # snapshot was restored. A nil `fingerprint` (uncomputable inputs)
157
- # disables persistence: a plain full run.
135
+ # Cross-process incremental run (the `--incremental` flag's engine). With a disk `snapshot` whose
136
+ # `fingerprint` matches, restore the prior per-file state and `#recheck` (re-analyze only the changed
137
+ # closure, serve the rest from the restored cache); otherwise run a full `#baseline`. Either way,
138
+ # persist the updated snapshot for the next process. Returns `[diagnostics, warm]` — `warm` is true
139
+ # when a snapshot was restored. A nil `fingerprint` (uncomputable inputs) disables persistence: a
140
+ # plain full run.
158
141
  def run_incremental(snapshot:, fingerprint:)
159
142
  restored = fingerprint && snapshot.load(fingerprint: fingerprint)
160
143
  if restored
@@ -171,26 +154,22 @@ module Rigor
171
154
 
172
155
  private
173
156
 
174
- # Adopt a persisted snapshot's per-file state as this session's
175
- # baseline (the warm-start path).
157
+ # Adopt a persisted snapshot's per-file state as this session's baseline (the warm-start path).
176
158
  def restore(payload)
177
159
  @analyzed = payload.analyzed
178
160
  @cache = payload.cache
179
161
  @sources = payload.sources
180
162
  @digests = payload.digests
181
163
  @dependents = Incremental.invert(@sources)
182
- # ADR-46 slice 4 — restore symbol-granularity state if present in the
183
- # payload (absent in snapshots written before slice 4 → fall back to
184
- # file-level dependents, which is always sound).
164
+ # ADR-46 slice 4 — restore symbol-granularity state if present in the payload (absent in snapshots
165
+ # written before slice 4 → fall back to file-level dependents, which is always sound).
185
166
  @symbol_sources = payload.symbol_sources || {}
186
167
  @ancestry_sources = payload.ancestry_sources || {}
187
168
  @symbol_fingerprints = payload.symbol_fingerprints || {}
188
- # ADR-46 slice 3 — restore negative edges if present (absent in
189
- # pre-slice-3 snapshots empty, which only loses the appeared-symbol
190
- # re-check refinement; such a snapshot is never loaded by a slice-3+
191
- # engine because the engine version + schema is part of the snapshot
192
- # fingerprint, and `--verify-incremental` backstops any residual
193
- # under-capture, so it is never unsound).
169
+ # ADR-46 slice 3 — restore negative edges if present (absent in pre-slice-3 snapshots → empty, which
170
+ # only loses the appeared-symbol re-check refinement; such a snapshot is never loaded by a slice-3+
171
+ # engine because the engine version + schema is part of the snapshot fingerprint, and
172
+ # `--verify-incremental` backstops any residual under-capture, so it is never unsound).
194
173
  @missing = payload.missing || {}
195
174
  @class_decls = payload.class_decls || {}
196
175
  @symbol_dependents = Incremental.invert_symbols(@symbol_sources)
@@ -207,10 +186,9 @@ module Rigor
207
186
  )
208
187
  end
209
188
 
210
- # Fold a #recheck's fresh results back into the cache + graph so the
211
- # session is correct across multiple edits: the analyzed set gets fresh
212
- # diagnostics + digests + dependency edges, removed files are evicted
213
- # from every map, and the analyzed-file list advances to `current`.
189
+ # Fold a #recheck's fresh results back into the cache + graph so the session is correct across
190
+ # multiple edits: the analyzed set gets fresh diagnostics + digests + dependency edges, removed files
191
+ # are evicted from every map, and the analyzed-file list advances to `current`.
214
192
  def absorb(runner, fresh, current, analyze_set, removed)
215
193
  removed.each { |path| forget(path) }
216
194
  @analyzed = current
@@ -222,8 +200,8 @@ module Rigor
222
200
  absorb_dependency_graph(runner)
223
201
  end
224
202
 
225
- # Evict a removed file from every per-file map so its stale diagnostics
226
- # are never served and it drops out of the inverted dependency indexes.
203
+ # Evict a removed file from every per-file map so its stale diagnostics are never served and it drops
204
+ # out of the inverted dependency indexes.
227
205
  def forget(path)
228
206
  @cache.delete(path)
229
207
  @digests.delete(path)
@@ -236,8 +214,8 @@ module Rigor
236
214
  # pre-pass in absorb_dependency_graph, and is frozen, so no delete.
237
215
  end
238
216
 
239
- # Fold a runner's dependency recording (file-level and symbol-level) back
240
- # into the session's graph state. Rebuilds all derived indexes.
217
+ # Fold a runner's dependency recording (file-level and symbol-level) back into the session's graph
218
+ # state. Rebuilds all derived indexes.
241
219
  def absorb_dependency_graph(runner)
242
220
  runner.file_dependencies.each do |path, record|
243
221
  @sources[path] = record.sources.dup
@@ -250,17 +228,15 @@ module Rigor
250
228
  @ancestry_dependents = Incremental.invert(@ancestry_sources)
251
229
  @negative_dependents = Incremental.invert(@missing)
252
230
  @symbol_fingerprints.merge!(runner.symbol_fingerprints)
253
- # Wholesale replace (the subset runner's pre-pass is complete): a file
254
- # that lost its last class must drop out of the map so a later re-add
255
- # registers as an appearance.
231
+ # Wholesale replace (the subset runner's pre-pass is complete): a file that lost its last class must
232
+ # drop out of the map so a later re-add registers as an appearance.
256
233
  @class_decls = runner.class_declarations
257
234
  end
258
235
 
259
- # Compute per-symbol body fingerprints for `paths` via a quick indexing
260
- # re-pass (Prism parse + def extraction, no type inference). Returns a
261
- # hash of the form `{ path => { "ClassName#method" => sha256_hex } }`.
262
- # Used by {#recheck} to detect which symbols in a changed file actually
263
- # changed, so only their callers are added to the affected closure.
236
+ # Compute per-symbol body fingerprints for `paths` via a quick indexing re-pass (Prism parse + def
237
+ # extraction, no type inference). Returns a hash of the form `{ path => { "ClassName#method" =>
238
+ # sha256_hex } }`. Used by {#recheck} to detect which symbols in a changed file actually changed, so
239
+ # only their callers are added to the affected closure.
264
240
  def symbol_fingerprints_for(paths)
265
241
  return {} if paths.empty?
266
242
 
@@ -281,11 +257,10 @@ module Rigor
281
257
  result.transform_values(&:freeze).freeze
282
258
  end
283
259
 
284
- # ADR-46 slice 3 — the consumers to re-check because a symbol that
285
- # appeared in a changed file resolves a prior missed lookup. Maps each
286
- # appeared `"ClassName#method"` to the negative-dependency key it would
287
- # satisfy (`toplevel:foo` for a top-level def, `method:C#m` otherwise),
288
- # then unions the recorded negative-dependents of those keys.
260
+ # ADR-46 slice 3 — the consumers to re-check because a symbol that appeared in a changed file resolves
261
+ # a prior missed lookup. Maps each appeared `"ClassName#method"` to the negative-dependency key it
262
+ # would satisfy (`toplevel:foo` for a top-level def, `method:C#m` otherwise), then unions the recorded
263
+ # negative-dependents of those keys.
289
264
  def negative_affected(changed, new_fingerprints, new_class_decls)
290
265
  appeared_methods = Incremental.appeared_symbols(changed, @symbol_fingerprints, new_fingerprints)
291
266
  appeared_classes = Incremental.appeared_classes(changed, @class_decls, new_class_decls)
@@ -294,9 +269,9 @@ module Rigor
294
269
  Incremental.negative_closure(keys, @negative_dependents)
295
270
  end
296
271
 
297
- # The qualified class/module names declared in `paths`, via the same
298
- # quick indexing re-pass {#symbol_fingerprints_for} uses (Prism parse +
299
- # declaration extraction, no inference). `{ path => Set<class name> }`.
272
+ # The qualified class/module names declared in `paths`, via the same quick indexing re-pass
273
+ # {#symbol_fingerprints_for} uses (Prism parse + declaration extraction, no inference). `{ path =>
274
+ # Set<class name> }`.
300
275
  def class_declarations_for(paths)
301
276
  return {} if paths.empty?
302
277
 
@@ -320,16 +295,15 @@ module Rigor
320
295
  Runner.new(configuration: @configuration, cache_store: nil, environment: @environment, **)
321
296
  end
322
297
 
323
- # Run the runner over the session's explicit paths (or, when none were
324
- # given, the configuration's `paths:` via `Runner#run`'s default).
298
+ # Run the runner over the session's explicit paths (or, when none were given, the configuration's
299
+ # `paths:` via `Runner#run`'s default).
325
300
  def run_runner(runner)
326
301
  @paths ? runner.run(@paths) : runner.run
327
302
  end
328
303
 
329
- # Group diagnostics by their file path, keeping only those whose path
330
- # is an analyzed project file — run-level streams (the gem-RBS info
331
- # diagnostic, keyed on `.rigor.yml`) are recomputed fresh every run
332
- # and must not be served from the per-file cache.
304
+ # Group diagnostics by their file path, keeping only those whose path is an analyzed project file —
305
+ # run-level streams (the gem-RBS info diagnostic, keyed on `.rigor.yml`) are recomputed fresh every
306
+ # run and must not be served from the per-file cache.
333
307
  def per_file(diagnostics)
334
308
  diagnostics.group_by(&:path).slice(*@analyzed)
335
309
  end
@@ -2,31 +2,20 @@
2
2
 
3
3
  module Rigor
4
4
  module Analysis
5
- # Frozen snapshot of the project-wide state {Runner} consumes
6
- # before per-file analysis fires: the loaded plugin registry
7
- # (with `#prepare` already invoked), the dependency-source
8
- # index, the synthetic-method and project-patched indexes
9
- # produced by the pre-pass scanners, and the diagnostics
10
- # those passes emitted.
5
+ # Frozen snapshot of the project-wide state {Runner} consumes before per-file analysis fires: the loaded
6
+ # plugin registry (with `#prepare` already invoked), the dependency-source index, the synthetic-method and
7
+ # project-patched indexes produced by the pre-pass scanners, and the diagnostics those passes emitted.
11
8
  #
12
- # Owners (`Rigor::LanguageServer::ProjectContext`, future
13
- # editor / sig-gen integrations) build a ProjectScan once
14
- # per project-state generation via
15
- # `Runner#prepare_project_scan` and pass it to
16
- # `Runner.new(prebuilt: ...)` so per-buffer publishes skip
17
- # the scanner walks and `#prepare` re-runs. When watched
18
- # project files change, the owner discards the ProjectScan
19
- # and a fresh one builds on next read.
9
+ # Owners (`Rigor::LanguageServer::ProjectContext`, future editor / sig-gen integrations) build a ProjectScan
10
+ # once per project-state generation via `Runner#prepare_project_scan` and pass it to
11
+ # `Runner.new(prebuilt: ...)` so per-buffer publishes skip the scanner walks and `#prepare` re-runs. When
12
+ # watched project files change, the owner discards the ProjectScan and a fresh one builds on next read.
20
13
  #
21
- # Editor mode v1 contract reminder: scanners observe the
22
- # bytes that were on disk at scan time, NOT the in-flight
23
- # buffer. Edits to a file that itself declares synthetic
24
- # methods (or `pre_eval:`-listed patches) are NOT visible
25
- # until the owner invalidates the scan — typically via
26
- # `workspace/didChangeWatchedFiles`. This is the same
27
- # trade-off the LSP made when slice 7 cached only the
28
- # `Environment`; extending the cache to the pre-pass
29
- # outputs preserves the contract.
14
+ # Editor mode v1 contract reminder: scanners observe the bytes that were on disk at scan time, NOT the
15
+ # in-flight buffer. Edits to a file that itself declares synthetic methods (or `pre_eval:`-listed patches)
16
+ # are NOT visible until the owner invalidates the scan — typically via `workspace/didChangeWatchedFiles`.
17
+ # This is the same trade-off the LSP made when slice 7 cached only the `Environment`; extending the cache
18
+ # to the pre-pass outputs preserves the contract.
30
19
  ProjectScan = Data.define(
31
20
  :plugin_registry,
32
21
  :dependency_source_index,
@@ -5,11 +5,9 @@ module Rigor
5
5
  class Result
6
6
  attr_reader :diagnostics, :stats
7
7
 
8
- # @param stats [Rigor::Analysis::RunStats, nil] end-of-run
9
- # telemetry (target file count, RBS class breakdown,
10
- # wall + RSS) collected by the Runner. Nil when stats
11
- # collection wasn't requested or wasn't applicable
12
- # (early-exit paths like `validate_target_ruby` failure).
8
+ # @param stats [Rigor::Analysis::RunStats, nil] end-of-run telemetry (target file count, RBS class
9
+ # breakdown, wall + RSS) collected by the Runner. Nil when stats collection wasn't requested or wasn't
10
+ # applicable (early-exit paths like `validate_target_ruby` failure).
13
11
  def initialize(diagnostics: [], stats: nil)
14
12
  @diagnostics = diagnostics
15
13
  @stats = stats