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
@@ -5,18 +5,14 @@ require_relative "../cache/store"
5
5
 
6
6
  module Rigor
7
7
  class CLI
8
- # Shared factory for building an {Analysis::Runner} from CLI option
9
- # hashes + a loaded {Configuration}.
8
+ # Shared factory for building an {Analysis::Runner} from CLI option hashes + a loaded {Configuration}.
10
9
  #
11
- # Extracted from {CheckCommand} so that `rigor doctor` and future
12
- # `describe --deep` can reuse the exact same runner-setup logic
13
- # (cache, workers, buffer, explain) without diverging from the
14
- # primary check path (ADR-77 WD3).
10
+ # Extracted from {CheckCommand} so that `rigor doctor` and future `describe --deep` can reuse the exact same
11
+ # runner-setup logic (cache, workers, buffer, explain) without diverging from the primary check path (ADR-77 WD3).
15
12
  module CheckRunnerFactory
16
13
  module_function
17
14
 
18
- # Builds a runner matching the configuration/plugin/cache resolution
19
- # that `rigor check` itself uses.
15
+ # Builds a runner matching the configuration/plugin/cache resolution that `rigor check` itself uses.
20
16
  #
21
17
  # @param configuration [Rigor::Configuration]
22
18
  # @param options [Hash] parsed CLI options (must contain at least
@@ -43,12 +39,10 @@ module Rigor
43
39
  )
44
40
  end
45
41
 
46
- # Resolves the worker count by precedence: CLI `--workers=N`
47
- # (most explicit) > env `RIGOR_RACTOR_WORKERS` > config
48
- # `.rigor.yml` `parallel.workers:` > 0 (sequential default).
49
- # Returns an Integer; non-numeric values raise so typos fail
50
- # loudly. CLI / env may pass a negative value — clamped to 0
51
- # (sequential) so a stray `-1` doesn't crash the pool spawn loop.
42
+ # Resolves the worker count by precedence: CLI `--workers=N` (most explicit) > env `RIGOR_RACTOR_WORKERS` > config
43
+ # `.rigor.yml` `parallel.workers:` > 0 (sequential default). Returns an Integer; non-numeric values raise so typos
44
+ # fail loudly. CLI / env may pass a negative value — clamped to 0 (sequential) so a stray `-1` doesn't crash the
45
+ # pool spawn loop.
52
46
  def resolve_workers(options, configuration)
53
47
  cli_value = options[:workers]
54
48
  return [Integer(cli_value), 0].max if cli_value
@@ -2,11 +2,9 @@
2
2
 
3
3
  module Rigor
4
4
  class CLI
5
- # Runtime CI-environment detection (ADR-51 WD7), modelled on
6
- # `OndraM/ci-detector` (the library PHPStan uses). Reads the well-known
7
- # environment variables a CI provider sets and returns the matching
8
- # {Platform}, classifying it into a **tier** that decides how
9
- # `rigor check` surfaces diagnostics there:
5
+ # Runtime CI-environment detection (ADR-51 WD7), modelled on `OndraM/ci-detector` (the library PHPStan uses). Reads
6
+ # the well-known environment variables a CI provider sets and returns the matching {Platform}, classifying it into a
7
+ # **tier** that decides how `rigor check` surfaces diagnostics there:
10
8
  #
11
9
  # :native_stdout — Rigor has a native format that renders purely from
12
10
  # stdout, so it is auto-emitted on top of the human
@@ -19,9 +17,8 @@ module Rigor
19
17
  # reviewdog (`checkstyle`/`sarif`) or `junit`. Hint
20
18
  # only.
21
19
  #
22
- # Detection is a pure function of the environment hash, so it is fully
23
- # testable; the CLI passes `ENV`. `RIGOR_CI_DETECT=0` (or `false`/`no`)
24
- # disables it globally — the seam the spec suite uses for determinism.
20
+ # Detection is a pure function of the environment hash, so it is fully testable; the CLI passes `ENV`.
21
+ # `RIGOR_CI_DETECT=0` (or `false`/`no`) disables it globally — the seam the spec suite uses for determinism.
25
22
  module CiDetector
26
23
  Platform = Struct.new(:id, :name, :format, :tier, keyword_init: true) do
27
24
  def native_stdout? = tier == :native_stdout
@@ -29,10 +26,9 @@ module Rigor
29
26
  def reviewdog? = tier == :reviewdog
30
27
  end
31
28
 
32
- # The detection table, ordered most-specific first so the generic
33
- # `CI=true` catch-all is last (a provider that also sets `CI` is still
34
- # recognised by its own variable). `match` is `:truthy` (value in
35
- # 1/true/yes/on), `:present` (variable set non-empty), or `:equals`.
29
+ # The detection table, ordered most-specific first so the generic `CI=true` catch-all is last (a provider that
30
+ # also sets `CI` is still recognised by its own variable). `match` is `:truthy` (value in 1/true/yes/on),
31
+ # `:present` (variable set non-empty), or `:equals`.
36
32
  PROVIDERS = [
37
33
  { id: "github-actions", name: "GitHub Actions", format: "github", tier: :native_stdout,
38
34
  var: "GITHUB_ACTIONS", match: :truthy },
@@ -66,8 +62,7 @@ module Rigor
66
62
 
67
63
  module_function
68
64
 
69
- # Returns the detected {Platform}, or nil when no CI is recognised or
70
- # detection is disabled via `RIGOR_CI_DETECT`.
65
+ # Returns the detected {Platform}, or nil when no CI is recognised or detection is disabled via `RIGOR_CI_DETECT`.
71
66
  def detect(env = ENV)
72
67
  return nil if disabled?(env)
73
68
 
@@ -4,16 +4,12 @@ module Rigor
4
4
  class CLI
5
5
  # Base class for the `rigor <subcommand>` command objects.
6
6
  #
7
- # Every subcommand captured the same invariant wiring — the argument
8
- # vector plus the output and error streams in an identical
9
- # `initialize(argv:, out:, err:)`, and defaulted the streams
10
- # inconsistently (some to `$stdout` / `$stderr`, most not at all).
11
- # Centralising it here gives one consistent shape and lets a test
12
- # instantiate a command with just `argv:` (the streams default so a
13
- # spec can pass a `StringIO` for one and ignore the other).
7
+ # Every subcommand captured the same invariant wiring — the argument vector plus the output and error streams — in
8
+ # an identical `initialize(argv:, out:, err:)`, and defaulted the streams inconsistently (some to `$stdout` /
9
+ # `$stderr`, most not at all). Centralising it here gives one consistent shape and lets a test instantiate a command
10
+ # with just `argv:` (the streams default so a spec can pass a `StringIO` for one and ignore the other).
14
11
  #
15
- # Subclasses read the `@argv` / `@out` / `@err` ivars directly, as
16
- # they did before.
12
+ # Subclasses read the `@argv` / `@out` / `@err` ivars directly, as they did before.
17
13
  class Command
18
14
  def initialize(argv:, out: $stdout, err: $stderr)
19
15
  @argv = argv
@@ -23,11 +19,9 @@ module Rigor
23
19
 
24
20
  private
25
21
 
26
- # Expands `args` (a mix of files and directories) into a unique
27
- # list of `.rb` paths, recursing into directories. Returns nil
28
- # after writing `<command_name>: not a file or directory: <arg>` to
29
- # `@err` — on the first arg that is neither. Shared by the
30
- # path-walking commands (`type-scan`, `coverage`).
22
+ # Expands `args` (a mix of files and directories) into a unique list of `.rb` paths, recursing into directories.
23
+ # Returns nil after writing `<command_name>: not a file or directory: <arg>` to `@err` on the first arg that
24
+ # is neither. Shared by the path-walking commands (`type-scan`, `coverage`).
31
25
  def collect_paths(args, command_name:)
32
26
  paths = []
33
27
  args.each do |arg|
@@ -31,12 +31,11 @@ module Rigor
31
31
  class CLI
32
32
  # Executes the `rigor coverage` command.
33
33
  #
34
- # Walks every Prism node in one or more files, infers its type via
35
- # `Rigor::Scope#type_of`, and classifies the result into precision tiers
36
- # (constant / nominal / shaped / refined / bot / dynamic_specific /
34
+ # Walks every Prism node in one or more files, infers its type via `Rigor::Scope#type_of`, and classifies the result
35
+ # into precision tiers (constant / nominal / shaped / refined / bot / dynamic_specific /
37
36
  # dynamic_top / top). Reports aggregate and per-file statistics so
38
- # maintainers can track type-precision trends and SKILL pipelines can
39
- # measure the impact of adding new constant-fold or shape-dispatch rules.
37
+ # maintainers can track type-precision trends and SKILL pipelines can measure the impact of adding new constant-fold
38
+ # or shape-dispatch rules.
40
39
  #
41
40
  # Exit codes:
42
41
  # 0 — scan complete, precision ratio ≥ threshold (or no threshold given)
@@ -47,8 +46,8 @@ module Rigor
47
46
 
48
47
  USAGE = "Usage: rigor coverage [options] PATH..."
49
48
 
50
- # ADR-70 — the default test runner hook for `--with-tests`. The
51
- # conventional Ruby test task; override with `--test-command`.
49
+ # ADR-70 — the default test runner hook for `--with-tests`. The conventional Ruby test task; override with
50
+ # `--test-command`.
52
51
  DEFAULT_TEST_COMMAND = %w[bundle exec rake].freeze
53
52
 
54
53
  # @return [Integer] CLI exit status.
@@ -153,9 +152,8 @@ module Rigor
153
152
  accumulator.to_report
154
153
  end
155
154
 
156
- # Seed the protection scan's scope with the same cross-file facts
157
- # `rigor check` resolves against, so a receiver reads the type it
158
- # actually has rather than a stripped-scope `Dynamic`:
155
+ # Seed the protection scan's scope with the same cross-file facts `rigor check` resolves against, so a receiver
156
+ # reads the type it actually has rather than a stripped-scope `Dynamic`:
159
157
  #
160
158
  # - `discovered_classes` — a project constant referring to a class
161
159
  # defined in a *sibling* file (`Account`, `User`) types as
@@ -168,8 +166,8 @@ module Rigor
168
166
  # parameter) counts as protected when its call sites resolve to
169
167
  # concrete argument types.
170
168
  #
171
- # Both span the scanned `paths` only (no whole-project pre-pass) —
172
- # a site that gains neither is classified exactly as before.
169
+ # Both span the scanned `paths` only (no whole-project pre-pass) — a site that gains neither is classified exactly
170
+ # as before.
173
171
  def scope_with_inferred_params(paths, configuration, environment)
174
172
  base = Scope.empty(environment: environment)
175
173
  seed = {}
@@ -206,24 +204,19 @@ module Rigor
206
204
  CoverageScan.precision_report(files: paths, configuration: Configuration.load(options.fetch(:config)))
207
205
  end
208
206
 
209
- # Delegated to the shared scan module (see {CoverageScan}); the
210
- # protection path below reuses both, and `rigor check --coverage`
211
- # reuses `precision_report` over the same machinery.
207
+ # Delegated to the shared scan module (see {CoverageScan}); the protection path below reuses both, and `rigor
208
+ # check --coverage` reuses `precision_report` over the same machinery.
212
209
  def project_environment(configuration)
213
210
  CoverageScan.project_environment(configuration)
214
211
  end
215
212
 
216
- # The protection scan must see the same receiver types `rigor check`
217
- # does including plugin-contributed `dynamic_return` types (a
218
- # controller's `params` `ActionController::Parameters`, a
219
- # `Model.where` `ActiveRecord::Relation[Model]`). The bare
220
- # `project_environment` carries only the RBS environment (no plugin
221
- # registry), so every plugin-typed receiver reads `Dynamic` and its
222
- # dispatch site is miscounted as *unprotected* a systematic
223
- # undercount of what Rigor actually types on a plugin-using project.
224
- # `ProjectContext` builds the plugin-aware environment (registry
225
- # materialised + the per-run prepare pass that primes producers like
226
- # the controller / model index) exactly as the LSP and the runner do.
213
+ # The protection scan must see the same receiver types `rigor check` does — including plugin-contributed
214
+ # `dynamic_return` types (a controller's `params` `ActionController::Parameters`, a `Model.where` →
215
+ # `ActiveRecord::Relation[Model]`). The bare `project_environment` carries only the RBS environment (no plugin
216
+ # registry), so every plugin-typed receiver reads `Dynamic` and its dispatch site is miscounted as *unprotected* —
217
+ # a systematic undercount of what Rigor actually types on a plugin-using project. `ProjectContext` builds the
218
+ # plugin-aware environment (registry materialised + the per-run prepare pass that primes producers like the
219
+ # controller / model index) exactly as the LSP and the runner do.
227
220
  def plugin_aware_environment(configuration)
228
221
  LanguageServer::ProjectContext.new(configuration: configuration).environment
229
222
  end
@@ -5,19 +5,16 @@ require "prism"
5
5
 
6
6
  module Rigor
7
7
  class CLI
8
- # ADR-63 Tier 2 + ADR-70 — the mutation-effectiveness and fused static∪dynamic
9
- # protection paths, factored out of {CoverageCommand} to keep that command
10
- # focused on dispatch. Mixed in, so each method runs in the command instance
11
- # (using `@out` / `@err` / `@argv` / `collect_paths` / `determine_protection_exit`
12
- # and the Protection + LanguageServer collaborators the command requires).
8
+ # ADR-63 Tier 2 + ADR-70 — the mutation-effectiveness and fused static∪dynamic protection paths, factored out of
9
+ # {CoverageCommand} to keep that command focused on dispatch. Mixed in, so each method runs in the command instance
10
+ # (using `@out` / `@err` / `@argv` / `collect_paths` / `determine_protection_exit` and the Protection +
11
+ # LanguageServer collaborators the command requires).
13
12
  module CoverageMutation
14
13
  private
15
14
 
16
- # ADR-63 Tier 2 — the mutation-effectiveness deep dive. Builds the RBS
17
- # environment + project pre-pass once (the warm loop), then re-analyses
18
- # each target file's mutants against its clean baseline. Defaults to the
19
- # git-changed `.rb` files; explicit paths override (and enable the
20
- # whole-project opt-in, which is minutes).
15
+ # ADR-63 Tier 2 — the mutation-effectiveness deep dive. Builds the RBS environment + project pre-pass once (the
16
+ # warm loop), then re-analyses each target file's mutants against its clean baseline. Defaults to the git-changed
17
+ # `.rb` files; explicit paths override (and enable the whole-project opt-in, which is minutes).
21
18
  def run_mutation_protection(options)
22
19
  explicit = collect_paths(@argv, command_name: "coverage")
23
20
  return CLI::EXIT_USAGE if explicit.nil?
@@ -36,8 +33,8 @@ module Rigor
36
33
  determine_protection_exit(report, options)
37
34
  end
38
35
 
39
- # A `--limit` sample makes the report an estimate (per-file ratios over a
40
- # random N of the mutations). Say so on stderr — stdout stays clean for JSON.
36
+ # A `--limit` sample makes the report an estimate (per-file ratios over a random N of the mutations). Say so on
37
+ # stderr — stdout stays clean for JSON.
41
38
  def note_sampling(options)
42
39
  return unless options[:limit]
43
40
 
@@ -47,10 +44,9 @@ module Rigor
47
44
  )
48
45
  end
49
46
 
50
- # ADR-70 — the fused static∪dynamic deep dive. The type pass is the ADR-63
51
- # Tier 2 warm loop; each type-survivor is then run against the project's
52
- # test suite (the runner hook). The suite MUST pass on clean code first, or
53
- # "a mutant survived" is meaningless — abort with a clear message if not.
47
+ # ADR-70 — the fused static∪dynamic deep dive. The type pass is the ADR-63 Tier 2 warm loop; each type-survivor is
48
+ # then run against the project's test suite (the runner hook). The suite MUST pass on clean code first, or "a
49
+ # mutant survived" is meaningless abort with a clear message if not.
54
50
  def run_fused_protection(paths, options)
55
51
  configuration = Configuration.load(options.fetch(:config))
56
52
  test_oracle = Protection::TestSuiteOracle.new(command: options.fetch(:test_command))
@@ -117,9 +113,8 @@ module Rigor
117
113
  accumulator.absorb(scanner.scan_file(path, source: source))
118
114
  end
119
115
 
120
- # The git-changed (modified / added / untracked) `.rb` files that exist on
121
- # disk — the default Tier 2 scope. Returns [] outside a git work tree or
122
- # when git is unavailable; the caller then reports "nothing to measure".
116
+ # The git-changed (modified / added / untracked) `.rb` files that exist on disk — the default Tier 2 scope.
117
+ # Returns [] outside a git work tree or when git is unavailable; the caller then reports "nothing to measure".
123
118
  def changed_ruby_files
124
119
  output = git_status_porcelain
125
120
  return [] if output.nil?
@@ -11,7 +11,6 @@ module Rigor
11
11
  :per_file,
12
12
  :total
13
13
  )
14
- # Sum of all per-file totals.
15
14
  def grand_total
16
15
  total.total
17
16
  end
@@ -44,7 +43,6 @@ module Rigor
44
43
  def initialize
45
44
  @per_file = []
46
45
  @parse_errors = []
47
- # Accumulated totals across all files.
48
46
  @total_total = 0
49
47
  @total_tier_counts = Inference::PrecisionScanner::TIERS.to_h { |t| [t, 0] }
50
48
  end
@@ -10,12 +10,10 @@ require_relative "coverage_report"
10
10
 
11
11
  module Rigor
12
12
  class CLI
13
- # Shared type-precision scan behind both `rigor coverage` (the
14
- # dedicated command) and `rigor check --coverage` (the in-run
15
- # coverage block). Walks each file's AST, types every expression via
16
- # `Scope#type_of`, and accumulates the precision-tier breakdown into a
17
- # `CoverageReport`. Extracted so the two surfaces stay byte-identical
18
- # on the same file set.
13
+ # Shared type-precision scan behind both `rigor coverage` (the dedicated command) and `rigor check --coverage` (the
14
+ # in-run coverage block). Walks each file's AST, types every expression via `Scope#type_of`, and accumulates the
15
+ # precision-tier breakdown into a `CoverageReport`. Extracted so the two surfaces stay byte-identical on the same
16
+ # file set.
19
17
  module CoverageScan
20
18
  module_function
21
19
 
@@ -37,11 +35,9 @@ module Rigor
37
35
  )
38
36
  end
39
37
 
40
- # Parses one file and feeds the scan result (or a parse-error
41
- # record) into `accumulator`. `scanner` / `accumulator` are a
42
- # matched pair a `PrecisionScanner` + `CoverageAccumulator`, or a
43
- # `ProtectionScanner` + `ProtectionAccumulator` — both of which
44
- # respond to `scan(node)` and `absorb(path, result)`.
38
+ # Parses one file and feeds the scan result (or a parse-error record) into `accumulator`. `scanner` /
39
+ # `accumulator` are a matched pair — a `PrecisionScanner` + `CoverageAccumulator`, or a `ProtectionScanner` +
40
+ # `ProtectionAccumulator`both of which respond to `scan(node)` and `absorb(path, result)`.
45
41
  def scan_into(path, scanner, accumulator, configuration)
46
42
  source = File.read(path)
47
43
  parse_result = Prism.parse(source, filepath: path, version: configuration.target_ruby)
@@ -6,11 +6,9 @@ require_relative "../version"
6
6
 
7
7
  module Rigor
8
8
  class CLI
9
- # CI-native diagnostic output formats (ADR-51). Each renders an
10
- # `Analysis::Result` to a string a CI platform consumes to surface
11
- # diagnostics inline in a pull / merge request, rather than only in the
12
- # job log. They read the same `Diagnostic` fields as `--format json`
13
- # (path / line / column / severity / qualified rule / message) and add
9
+ # CI-native diagnostic output formats (ADR-51). Each renders an `Analysis::Result` to a string a CI platform
10
+ # consumes to surface diagnostics inline in a pull / merge request, rather than only in the job log. They read the
11
+ # same `Diagnostic` fields as `--format json` (path / line / column / severity / qualified rule / message) and add
14
12
  # no new information — only a platform-native rendering of it.
15
13
  #
16
14
  # sarif — SARIF 2.1.0 (cross-platform; GitHub code-scanning, any
@@ -23,11 +21,9 @@ module Rigor
23
21
  # reviewdog (`-f=checkstyle`) and Jenkins/etc. consume
24
22
  # junit — JUnit XML, the test-report format many CI systems render
25
23
  #
26
- # Severity maps once per format from Rigor's `:error` / `:warning` /
27
- # `:info`; the qualified rule (`<source_family>.<rule>` or the bare rule
28
- # for the builtin family) is the stable identifier, nil only for the
29
- # ruleless producers (parse / internal errors), which each format
30
- # degrades gracefully.
24
+ # Severity maps once per format from Rigor's `:error` / `:warning` / `:info`; the qualified rule
25
+ # (`<source_family>.<rule>` or the bare rule for the builtin family) is the stable identifier, nil only for the
26
+ # ruleless producers (parse / internal errors), which each format degrades gracefully.
31
27
  module DiagnosticFormats
32
28
  FORMATS = %w[sarif github gitlab checkstyle junit teamcity].freeze
33
29
 
@@ -37,8 +33,7 @@ module Rigor
37
33
  FORMATS.include?(format)
38
34
  end
39
35
 
40
- # Renders `result` in the named CI format. Callers gate on
41
- # {.supports?} first; an unrecognised format returns nil.
36
+ # Renders `result` in the named CI format. Callers gate on {.supports?} first; an unrecognised format returns nil.
42
37
  def render(result, format)
43
38
  case format
44
39
  when "sarif" then Sarif.new(result).render
@@ -50,9 +45,8 @@ module Rigor
50
45
  end
51
46
  end
52
47
 
53
- # XML attribute / text escaping shared by the Checkstyle and JUnit
54
- # formatters. Covers the five predefined XML entities so a diagnostic
55
- # message carrying `<`, `&`, or a quote can't break the document.
48
+ # XML attribute / text escaping shared by the Checkstyle and JUnit formatters. Covers the five predefined XML
49
+ # entities so a diagnostic message carrying `<`, `&`, or a quote can't break the document.
56
50
  module XmlEscaping
57
51
  ENTITIES = { "&" => "&amp;", "<" => "&lt;", ">" => "&gt;",
58
52
  '"' => "&quot;", "'" => "&apos;" }.freeze
@@ -62,15 +56,14 @@ module Rigor
62
56
  end
63
57
  end
64
58
 
65
- # SARIF 2.1.0 — the OASIS static-analysis interchange format. GitHub's
66
- # `codeql-action/upload-sarif` ingests it to render findings on the PR
67
- # diff and in the Security tab; it is the cross-platform anchor format.
59
+ # SARIF 2.1.0 — the OASIS static-analysis interchange format. GitHub's `codeql-action/upload-sarif` ingests it to
60
+ # render findings on the PR diff and in the Security tab; it is the cross-platform anchor format.
68
61
  class Sarif
69
62
  SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json"
70
63
  INFORMATION_URI = "https://github.com/rigortype/rigor"
71
64
 
72
- # SARIF defines exactly three result levels; Rigor's `:info` is a
73
- # `note` (the SARIF spelling for advisory findings).
65
+ # SARIF defines exactly three result levels; Rigor's `:info` is a `note` (the SARIF spelling for advisory
66
+ # findings).
74
67
  LEVELS = { error: "error", warning: "warning", info: "note" }.freeze
75
68
 
76
69
  def initialize(result)
@@ -103,9 +96,8 @@ module Rigor
103
96
  }
104
97
  end
105
98
 
106
- # The distinct rule ids seen in this run, declared so consumers can
107
- # cross-reference each result's `ruleId`. Id-only is a valid minimal
108
- # SARIF rule object; richer per-rule metadata is a later enrichment.
99
+ # The distinct rule ids seen in this run, declared so consumers can cross-reference each result's `ruleId`.
100
+ # Id-only is a valid minimal SARIF rule object; richer per-rule metadata is a later enrichment.
109
101
  def rules
110
102
  @result.diagnostics.filter_map(&:qualified_rule).uniq.map { |id| { "id" => id } }
111
103
  end
@@ -121,9 +113,8 @@ module Rigor
121
113
  entry
122
114
  end
123
115
 
124
- # Rigor lines / columns are already 1-based, matching SARIF's
125
- # 1-based `startLine` / `startColumn`. Paths are project-relative;
126
- # SARIF URIs use forward slashes on every platform.
116
+ # Rigor lines / columns are already 1-based, matching SARIF's 1-based `startLine` / `startColumn`. Paths are
117
+ # project-relative; SARIF URIs use forward slashes on every platform.
127
118
  def location_for(diagnostic)
128
119
  {
129
120
  "physicalLocation" => {
@@ -134,9 +125,8 @@ module Rigor
134
125
  end
135
126
  end
136
127
 
137
- # GitHub Actions workflow commands — `::<level> file=…,line=…,col=…,
138
- # title=…::<message>` lines the runner parses out of stdout and turns
139
- # into inline PR annotations, with no separate upload step.
128
+ # GitHub Actions workflow commands — `::<level> file=…,line=…,col=…, title=…::<message>` lines the runner parses
129
+ # out of stdout and turns into inline PR annotations, with no separate upload step.
140
130
  class GithubActions
141
131
  # GitHub's annotation levels; Rigor's `:info` is a `notice`.
142
132
  LEVELS = { error: "error", warning: "warning", info: "notice" }.freeze
@@ -160,26 +150,24 @@ module Rigor
160
150
  "::#{level} #{props.join(',')}::#{escape_data(diagnostic.message)}"
161
151
  end
162
152
 
163
- # GitHub's documented workflow-command escaping: `%` and the CR / LF
164
- # that would otherwise terminate the command line, for message data.
153
+ # GitHub's documented workflow-command escaping: `%` and the CR / LF that would otherwise terminate the command
154
+ # line, for message data.
165
155
  def escape_data(value)
166
156
  value.to_s.gsub("%", "%25").gsub("\r", "%0D").gsub("\n", "%0A")
167
157
  end
168
158
 
169
- # Property values additionally escape the `,` (property separator)
170
- # and `:` (command terminator) so a path or rule id can carry them.
159
+ # Property values additionally escape the `,` (property separator) and `:` (command terminator) so a path or
160
+ # rule id can carry them.
171
161
  def escape_property(value)
172
162
  escape_data(value).gsub(",", "%2C").gsub(":", "%3A")
173
163
  end
174
164
  end
175
165
 
176
- # GitLab Code Quality report — the CodeClimate-subset JSON array GitLab
177
- # reads from a `codequality` CI artifact to populate the merge-request
178
- # Code Quality widget.
166
+ # GitLab Code Quality report — the CodeClimate-subset JSON array GitLab reads from a `codequality` CI artifact to
167
+ # populate the merge-request Code Quality widget.
179
168
  class GitlabCodeQuality
180
- # GitLab's severity vocabulary (a CodeClimate subset). Rigor maps
181
- # error major, warning minor, info info; `critical` / `blocker`
182
- # are left for a louder future tier.
169
+ # GitLab's severity vocabulary (a CodeClimate subset). Rigor maps error → major, warning → minor, info → info;
170
+ # `critical` / `blocker` are left for a louder future tier.
183
171
  SEVERITIES = { error: "major", warning: "minor", info: "info" }.freeze
184
172
 
185
173
  def initialize(result)
@@ -205,17 +193,16 @@ module Rigor
205
193
  }
206
194
  end
207
195
 
208
- # The rule id is folded into the description (the widget shows it)
209
- # because Code Quality has no dedicated rule field.
196
+ # The rule id is folded into the description (the widget shows it) because Code Quality has no dedicated rule
197
+ # field.
210
198
  def description(diagnostic)
211
199
  rule_id = diagnostic.qualified_rule
212
200
  rule_id ? "#{diagnostic.message} [#{rule_id}]" : diagnostic.message
213
201
  end
214
202
 
215
- # GitLab dedups findings by fingerprint and tracks them across runs
216
- # by it, so it must be stable for an unchanged finding and unique per
217
- # finding. Hashing the locating tuple (path, rule, line, column,
218
- # message) satisfies both — order-independent, no run-volatile input.
203
+ # GitLab dedups findings by fingerprint and tracks them across runs by it, so it must be stable for an unchanged
204
+ # finding and unique per finding. Hashing the locating tuple (path, rule, line, column, message) satisfies both
205
+ # order-independent, no run-volatile input.
219
206
  def fingerprint(diagnostic)
220
207
  payload = [diagnostic.path, diagnostic.qualified_rule, diagnostic.line,
221
208
  diagnostic.column, diagnostic.message].join("")
@@ -223,12 +210,10 @@ module Rigor
223
210
  end
224
211
  end
225
212
 
226
- # Checkstyle XML — the lint-interchange format a wide range of tools
227
- # read, most usefully reviewdog (`-f=checkstyle`), which then posts to
228
- # any of its reporters (GitHub PR review, GitLab MR, Gerrit, ). Errors
229
- # are grouped by file; the qualified rule rides in `source` (the rule
230
- # code reviewdog surfaces). Checkstyle's native severities are
231
- # `error` / `warning` / `info`, so Rigor's map through unchanged.
213
+ # Checkstyle XML — the lint-interchange format a wide range of tools read, most usefully reviewdog
214
+ # (`-f=checkstyle`), which then posts to any of its reporters (GitHub PR review, GitLab MR, Gerrit, …). Errors are
215
+ # grouped by file; the qualified rule rides in `source` (the rule code reviewdog surfaces). Checkstyle's native
216
+ # severities are `error` / `warning` / `info`, so Rigor's map through unchanged.
232
217
  class Checkstyle
233
218
  include XmlEscaping
234
219
 
@@ -258,12 +243,10 @@ module Rigor
258
243
  end
259
244
  end
260
245
 
261
- # JUnit XML — the test-report format GitHub's test reporting, GitLab,
262
- # Jenkins, and CircleCI render. Following the established linter
263
- # convention (rubocop / eslint / PHPStan): every diagnostic is a
264
- # `testcase` carrying a `failure` typed by its severity, so all of them
265
- # are visible in the report. The exit code (errors only) remains the
266
- # gate; this view is for surfacing, not gating.
246
+ # JUnit XML — the test-report format GitHub's test reporting, GitLab, Jenkins, and CircleCI render. Following the
247
+ # established linter convention (rubocop / eslint / PHPStan): every diagnostic is a `testcase` carrying a
248
+ # `failure` typed by its severity, so all of them are visible in the report. The exit code (errors only) remains
249
+ # the gate; this view is for surfacing, not gating.
267
250
  class Junit
268
251
  include XmlEscaping
269
252
 
@@ -299,11 +282,9 @@ module Rigor
299
282
  end
300
283
  end
301
284
 
302
- # TeamCity inspection service messages — the `##teamcity[…]` lines a
303
- # TeamCity build agent parses out of the build log into its Inspections
304
- # view. The one stdout-native format (besides `github`) that
305
- # CI-detection auto-emits. One `inspectionType` declares the category;
306
- # each diagnostic is an `inspection` typed by severity.
285
+ # TeamCity inspection service messages — the `##teamcity[…]` lines a TeamCity build agent parses out of the build
286
+ # log into its Inspections view. The one stdout-native format (besides `github`) that CI-detection auto-emits. One
287
+ # `inspectionType` declares the category; each diagnostic is an `inspection` typed by severity.
307
288
  class Teamcity
308
289
  SEVERITIES = { error: "ERROR", warning: "WARNING", info: "INFO" }.freeze
309
290
 
@@ -7,10 +7,8 @@ require "optionparser"
7
7
 
8
8
  module Rigor
9
9
  class CLI
10
- # Executes `rigor diff <baseline.json> [paths...]`. Compares
11
- # the current `rigor check` diagnostics against a saved
12
- # baseline JSON (the output of a previous `rigor check
13
- # --format=json` run) and prints the delta:
10
+ # Executes `rigor diff <baseline.json> [paths...]`. Compares the current `rigor check` diagnostics against a saved
11
+ # baseline JSON (the output of a previous `rigor check --format=json` run) and prints the delta:
14
12
  #
15
13
  # - **new** — diagnostics in the current run that were not
16
14
  # in the baseline (typically a regression introduced in
@@ -18,19 +16,13 @@ module Rigor
18
16
  # - **fixed** — diagnostics in the baseline that no longer
19
17
  # appear in the current run (typically progress).
20
18
  #
21
- # Identity for matching is the tuple
22
- # `(path, line, column, rule, source_family, message)`.
23
- # An edit that moves a diagnostic to a new line surfaces as
24
- # one fixed + one new pair, which lines up with the user's
25
- # mental model of "you changed the line, the analyzer's
26
- # position changed too."
19
+ # Identity for matching is the tuple `(path, line, column, rule, source_family, message)`. An edit that moves a
20
+ # diagnostic to a new line surfaces as one fixed + one new pair, which lines up with the user's mental model of "you
21
+ # changed the line, the analyzer's position changed too."
27
22
  #
28
- # CI usage: commit a `rigor.baseline.json` produced once
29
- # with `rigor check --format=json > rigor.baseline.json`,
30
- # then run `rigor diff rigor.baseline.json` in CI. Exit code
31
- # is `1` when any new diagnostic appears, `0` otherwise —
32
- # so adding new errors fails CI but legacy errors recorded
33
- # in the baseline don't.
23
+ # CI usage: commit a `rigor.baseline.json` produced once with `rigor check --format=json > rigor.baseline.json`,
24
+ # then run `rigor diff rigor.baseline.json` in CI. Exit code is `1` when any new diagnostic appears, `0` otherwise —
25
+ # so adding new errors fails CI but legacy errors recorded in the baseline don't.
34
26
  class DiffCommand < Command
35
27
  USAGE = "Usage: rigor diff [options] <baseline.json> [paths...]"
36
28
 
@@ -77,10 +69,8 @@ module Rigor
77
69
  options
78
70
  end
79
71
 
80
- # Runs `rigor check` against the remaining argv (or the
81
- # configured paths) and returns the diagnostics array.
82
- # Reuses the analyzer + configuration plumbing the
83
- # check-command path uses.
72
+ # Runs `rigor check` against the remaining argv (or the configured paths) and returns the diagnostics array.
73
+ # Reuses the analyzer + configuration plumbing the check-command path uses.
84
74
  def run_current(options)
85
75
  require_relative "../analysis/runner"
86
76
  require_relative "../configuration"