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
@@ -14,16 +14,12 @@ require_relative "command"
14
14
 
15
15
  module Rigor
16
16
  class CLI
17
- # `rigor plugins` — reports the activation status of every
18
- # plugin entry in `.rigor.yml` so users (and the
19
- # `rigor-project-init` SKILL, CI gates, `rigor init`) can
20
- # verify their plugin configuration is actually doing what
17
+ # `rigor plugins` — reports the activation status of every plugin entry in `.rigor.yml` so users (and the
18
+ # `rigor-project-init` SKILL, CI gates, `rigor init`) can verify their plugin configuration is actually doing what
21
19
  # they think.
22
20
  #
23
- # The command is read-only and idempotent: it loads the
24
- # project's `.rigor.yml` (same discovery as `rigor check`),
25
- # runs `Plugin::Loader.load` to attempt instantiation, then
26
- # prints a table of:
21
+ # The command is read-only and idempotent: it loads the project's `.rigor.yml` (same discovery as `rigor check`),
22
+ # runs `Plugin::Loader.load` to attempt instantiation, then prints a table of:
27
23
  #
28
24
  # - load status (`loaded` / `load-error` with reason);
29
25
  # - resolved manifest id, version, description;
@@ -39,13 +35,12 @@ module Rigor
39
35
  # class — `node_rule` node types, `dynamic_return` receivers,
40
36
  # `narrowing_facts` methods.
41
37
  #
42
- # `--capabilities` switches to a focused catalogue of just the
43
- # narrow-protocol gate values + produced/consumed facts (ADR-37
44
- # § "Machine-readable capability catalogue") — the AI-legibility
45
- # surface that lets an agent enumerate what every plugin does.
38
+ # `--capabilities` switches to a focused catalogue of just the narrow-protocol gate values + produced/consumed facts
39
+ # (ADR-37 § "Machine-readable capability catalogue") the AI-legibility surface that lets an agent enumerate what
40
+ # every plugin does.
46
41
  #
47
- # Output formats: `text` (default, human-readable table) and
48
- # `json` (for tooling — SKILLs, CI gates, editor integrations).
42
+ # Output formats: `text` (default, human-readable table) and `json` (for tooling — SKILLs, CI gates, editor
43
+ # integrations).
49
44
  #
50
45
  # Exit codes:
51
46
  # - `0` — every configured plugin loaded.
@@ -83,13 +78,10 @@ module Rigor
83
78
 
84
79
  private
85
80
 
86
- # Picks the renderer view. `--capabilities` switches to the
87
- # focused extension-protocol catalogue (ADR-37 § "Machine-readable
88
- # capability catalogue") per plugin, only the gate values that
89
- # tell a reader (or an AI agent) exactly what the plugin
90
- # contributes: the node-rule node types, the dynamic-return
91
- # receivers, the type-specifier methods, and the produced /
92
- # consumed facts. The default view stays the full activation report.
81
+ # Picks the renderer view. `--capabilities` switches to the focused extension-protocol catalogue (ADR-37 §
82
+ # "Machine-readable capability catalogue") — per plugin, only the gate values that tell a reader (or an AI agent)
83
+ # exactly what the plugin contributes: the node-rule node types, the dynamic-return receivers, the type-specifier
84
+ # methods, and the produced / consumed facts. The default view stays the full activation report.
93
85
  def render(renderer, options)
94
86
  json = options.fetch(:format) == "json"
95
87
  if options.fetch(:capabilities)
@@ -120,22 +112,18 @@ module Rigor
120
112
  raise OptionParser::InvalidArgument, "unsupported format: #{options.fetch(:format)}"
121
113
  end
122
114
 
123
- # Runs the plugin loader against the project configuration
124
- # and returns a row per declared plugin entry. Each row is
125
- # a plain Hash with the fields enumerated in the class
126
- # docstring so the renderer (text + JSON) reads from a
115
+ # Runs the plugin loader against the project configuration and returns a row per declared plugin entry. Each row
116
+ # is a plain Hash with the fields enumerated in the class docstring so the renderer (text + JSON) reads from a
127
117
  # single shape.
128
118
  #
129
- # The loader catches require / init failures and surfaces
130
- # them through `registry.load_errors`; we merge those back
119
+ # The loader catches require / init failures and surfaces them through `registry.load_errors`; we merge those back
131
120
  # into the per-entry rows by matching on `plugin_ref`.
132
121
  def build_rows(configuration)
133
122
  services = build_services(configuration)
134
123
  registry = Plugin::Loader.load(configuration: configuration, services: services)
135
124
 
136
125
  rows = configuration.plugins.map { |entry| row_for_entry(entry, registry) }
137
- # Surface registry-level errors that did not bind to an
138
- # entry (e.g. dependency-cycle errors that name multiple
126
+ # Surface registry-level errors that did not bind to an entry (e.g. dependency-cycle errors that name multiple
139
127
  # plugins). The renderer treats these as "orphan" errors.
140
128
  orphan_errors = orphan_load_errors(registry, rows)
141
129
  rows + orphan_errors
@@ -154,18 +142,15 @@ module Rigor
154
142
  gem_name = entry_gem_name(entry)
155
143
  config = entry_config(entry)
156
144
  plugin = registry.plugins.find do |p|
157
- # The loader has already matched the entry to a plugin
158
- # class; we can identify it by either the gem name (when
159
- # the entry was a String) or the explicit id (when the
160
- # entry was a Hash with `id:`).
145
+ # The loader has already matched the entry to a plugin class; we can identify it by either the gem name (when
146
+ # the entry was a String) or the explicit id (when the entry was a Hash with `id:`).
161
147
  plugin_matches_entry?(p, gem_name, entry_id(entry))
162
148
  end
163
149
 
164
150
  if plugin
165
151
  loaded_row(plugin, gem_name, config)
166
152
  else
167
- # Find the load error whose plugin_ref names this entry
168
- # (the ref is set by Loader to the gem name on require
153
+ # Find the load error whose plugin_ref names this entry (the ref is set by Loader to the gem name on require
169
154
  # failures and to the manifest id on later failures).
170
155
  error = registry.load_errors.find do |e|
171
156
  ref = e.plugin_ref.to_s
@@ -178,8 +163,7 @@ module Rigor
178
163
  def plugin_matches_entry?(plugin, gem_name, entry_id)
179
164
  return true if entry_id && plugin.manifest.id == entry_id
180
165
 
181
- # A bare gem entry conventionally has manifest.id equal to
182
- # the gem name with the `rigor-` prefix stripped.
166
+ # A bare gem entry conventionally has manifest.id equal to the gem name with the `rigor-` prefix stripped.
183
167
  derived_id = gem_name.delete_prefix("rigor-")
184
168
  [derived_id, gem_name].include?(plugin.manifest.id)
185
169
  end
@@ -192,12 +176,10 @@ module Rigor
192
176
  .merge(load_error: nil)
193
177
  end
194
178
 
195
- # ADR-37 narrow extension protocols. Unlike the 10 declarative
196
- # manifest fields, these are class-level DSLs (`node_rule` /
197
- # `dynamic_return` / `narrowing_facts`), so they are read off the
198
- # plugin class rather than the manifest. The gate values — node
199
- # types, receiver class names, specified method names — are the
200
- # greppable, enumerable surface the capability catalogue exposes.
179
+ # ADR-37 narrow extension protocols. Unlike the 10 declarative manifest fields, these are class-level DSLs
180
+ # (`node_rule` / `dynamic_return` / `narrowing_facts`), so they are read off the plugin class rather than the
181
+ # manifest. The gate values — node types, receiver class names, specified method names — are the greppable,
182
+ # enumerable surface the capability catalogue exposes.
201
183
  def narrow_protocol_fields(plugin)
202
184
  klass = plugin.class
203
185
  {
@@ -269,10 +251,8 @@ module Rigor
269
251
  }
270
252
  end
271
253
 
272
- # For each `signature_paths:` directory the plugin resolves
273
- # against its gem root, report the absolute path and a
274
- # quick `.rbs`-file count. The count is a sanity signal:
275
- # an empty bundle directory loads silently today but
254
+ # For each `signature_paths:` directory the plugin resolves against its gem root, report the absolute path and a
255
+ # quick `.rbs`-file count. The count is a sanity signal: an empty bundle directory loads silently today but
276
256
  # contributes nothing.
277
257
  def signature_path_rows(plugin)
278
258
  plugin.signature_paths.map do |abs|
@@ -317,10 +297,8 @@ module Rigor
317
297
  end
318
298
  end
319
299
 
320
- # Build orphan-error rows for load errors whose `plugin_ref`
321
- # did not bind to any configured plugin entry (e.g. a
322
- # dependency-cycle naming a plugin we already accounted for
323
- # but reported alongside another). De-duplicates against
300
+ # Build orphan-error rows for load errors whose `plugin_ref` did not bind to any configured plugin entry (e.g. a
301
+ # dependency-cycle naming a plugin we already accounted for but reported alongside another). De-duplicates against
324
302
  # errors we already attached.
325
303
  def orphan_load_errors(registry, rows)
326
304
  attached_refs = rows.compact.flat_map { |row| [row[:gem], row[:id]].compact }.to_set
@@ -4,15 +4,11 @@ require "json"
4
4
 
5
5
  module Rigor
6
6
  class CLI
7
- # Renderer for `rigor plugins`. Produces a human-readable
8
- # text table and a JSON representation from the same row
9
- # shape (the Hash documented on
10
- # {Rigor::CLI::PluginsCommand#loaded_row}).
7
+ # Renderer for `rigor plugins`. Produces a human-readable text table and a JSON representation from the same row
8
+ # shape (the Hash documented on {Rigor::CLI::PluginsCommand#loaded_row}).
11
9
  #
12
- # The two formats carry the same content; JSON is meant for
13
- # tooling (SKILLs, CI, editor integrations) while text is
14
- # for interactive inspection. Rows are printed in the order
15
- # the loader resolved them.
10
+ # The two formats carry the same content; JSON is meant for tooling (SKILLs, CI, editor integrations) while text is
11
+ # for interactive inspection. Rows are printed in the order the loader resolved them.
16
12
  class PluginsRenderer # rubocop:disable Metrics/ClassLength
17
13
  def initialize(rows:, configuration_path:)
18
14
  @rows = rows
@@ -42,12 +38,10 @@ module Rigor
42
38
  )
43
39
  end
44
40
 
45
- # ADR-37 § "Machine-readable capability catalogue" — the focused
46
- # per-plugin extension-protocol dump. Only loaded plugins appear
47
- # (a plugin that failed to load contributes no capabilities), and
48
- # each carries only the gate values an agent enumerates to learn
49
- # what the plugin does: node-rule node types, dynamic-return
50
- # receivers, type-specifier methods, and produced / consumed facts.
41
+ # ADR-37 § "Machine-readable capability catalogue" — the focused per-plugin extension-protocol dump. Only loaded
42
+ # plugins appear (a plugin that failed to load contributes no capabilities), and each carries only the gate values
43
+ # an agent enumerates to learn what the plugin does: node-rule node types, dynamic-return receivers,
44
+ # type-specifier methods, and produced / consumed facts.
51
45
  def capabilities_json
52
46
  JSON.pretty_generate(
53
47
  {
@@ -84,9 +78,8 @@ module Rigor
84
78
  lines
85
79
  end
86
80
 
87
- # The non-empty capability surfaces for a plugin, each as a
88
- # `label: a, b, c` string. Data-driven so the catalogue stays a
89
- # single source of truth shared between the text and JSON views.
81
+ # The non-empty capability surfaces for a plugin, each as a `label: a, b, c` string. Data-driven so the catalogue
82
+ # stays a single source of truth shared between the text and JSON views.
90
83
  def capability_surfaces(row)
91
84
  [
92
85
  ["node_rule", row[:node_rule_types]],
@@ -169,9 +162,8 @@ module Rigor
169
162
  lines
170
163
  end
171
164
 
172
- # ADR-37 narrow extension protocols (node_rule / dynamic_return /
173
- # narrowing_facts). Surfaced in the full report alongside the
174
- # declarative surfaces; `--capabilities` is the focused view.
165
+ # ADR-37 narrow extension protocols (node_rule / dynamic_return / narrowing_facts). Surfaced in the full report
166
+ # alongside the declarative surfaces; `--capabilities` is the focused view.
175
167
  def narrow_protocol_lines(row)
176
168
  lines = []
177
169
  lines << " node_rule: #{row[:node_rule_types].join(', ')}" if row[:node_rule_types].any?
@@ -4,14 +4,12 @@ require "prism"
4
4
 
5
5
  module Rigor
6
6
  class CLI
7
- # Re-lexes Ruby source with Prism and wraps each token in an
8
- # ANSI colour escape, producing IRB-style syntax highlighting.
7
+ # Re-lexes Ruby source with Prism and wraps each token in an ANSI colour escape, producing IRB-style syntax
8
+ # highlighting.
9
9
  #
10
- # Rigor ships no runtime dependencies (ADR-0), so the `irb`
11
- # gem's `IRB::Color` is not available; this module reproduces
12
- # the same effect from Prism's own token stream. `rigor
13
- # annotate` re-parses its annotated output through here before
14
- # printing.
10
+ # Rigor ships no runtime dependencies (ADR-0), so the `irb` gem's `IRB::Color` is not available; this module
11
+ # reproduces the same effect from Prism's own token stream. `rigor annotate` re-parses its annotated output through
12
+ # here before printing.
15
13
  module PrismColorizer
16
14
  module_function
17
15
 
@@ -41,9 +39,8 @@ module Rigor
41
39
  # @return [String] the source with ANSI colour escapes, or
42
40
  # the input unchanged when lexing surfaces an error.
43
41
  def colorize(source)
44
- # Sources read under a POSIX locale arrive tagged US-ASCII even
45
- # when they carry UTF-8 bytes; retag so the token regexes below
46
- # do not raise on multibyte comments.
42
+ # Sources read under a POSIX locale arrive tagged US-ASCII even when they carry UTF-8 bytes; retag so the token
43
+ # regexes below do not raise on multibyte comments.
47
44
  source = source.dup.force_encoding(Encoding::UTF_8) unless source.encoding == Encoding::UTF_8
48
45
  result = Prism.lex(source)
49
46
  return source unless result.errors.empty?
@@ -51,9 +48,8 @@ module Rigor
51
48
  render(source, result.value)
52
49
  end
53
50
 
54
- # Prism token offsets are BYTE offsets — slice with byteslice, or
55
- # any multibyte character earlier in the source shifts every
56
- # subsequent token boundary.
51
+ # Prism token offsets are BYTE offsets — slice with byteslice, or any multibyte character earlier in the source
52
+ # shifts every subsequent token boundary.
57
53
  def render(source, lexed)
58
54
  out = +""
59
55
  offset = 0
@@ -73,10 +69,8 @@ module Rigor
73
69
  out
74
70
  end
75
71
 
76
- # The token after a `SYMBOL_BEGIN` (`:`) carries the symbol
77
- # nameand Prism lexes `:then` / `:class` etc. as a
78
- # keyword token — so it is painted with the symbol colour
79
- # regardless of its own token type.
72
+ # The token after a `SYMBOL_BEGIN` (`:`) carries the symbol name — and Prism lexes `:then` / `:class` etc. as a
73
+ # keyword token so it is painted with the symbol colour regardless of its own token type.
80
74
  def effective_category(token_type, previous_type)
81
75
  return :symbol if previous_type == :SYMBOL_BEGIN
82
76
 
@@ -87,8 +81,8 @@ module Rigor
87
81
  sgr = CATEGORY_SGR.fetch(category)
88
82
  return text if sgr.nil? || text.empty?
89
83
 
90
- # Keep a trailing newline outside the colour span so the
91
- # reset sits on the token's own line (comments include it).
84
+ # Keep a trailing newline outside the colour span so the reset sits on the token's own line (comments include
85
+ # it).
92
86
  trailing = text[/\s*\z/] || ""
93
87
  body = trailing.empty? ? text : text[0...-trailing.length]
94
88
  return text if body.empty?
@@ -6,17 +6,15 @@ require_relative "../inference/dynamic_origin"
6
6
 
7
7
  module Rigor
8
8
  class CLI
9
- # Renders an {ProtectionReport} (ADR-63 Tier 1) as text or JSON. The text
10
- # form leads with the protected ratio, then the highest-traffic untyped
11
- # dispatches ("add a type here"), then the lowest-protected files. The
12
- # framing is always *where to add a type*, never "your code is broken".
9
+ # Renders an {ProtectionReport} (ADR-63 Tier 1) as text or JSON. The text form leads with the protected ratio, then
10
+ # the highest-traffic untyped dispatches ("add a type here"), then the lowest-protected files. The framing is always
11
+ # *where to add a type*, never "your code is broken".
13
12
  class ProtectionRenderer
14
13
  TOP_CALLS = 15
15
14
  TOP_FILES = 10
16
15
 
17
- # ADR-73 P6 / ADR-75 WD2 — the actionable axis for each tractability
18
- # category, shown next to a hole's origin so a user knows whether (and
19
- # how) a type can close it.
16
+ # ADR-73 P6 / ADR-75 WD2 — the actionable axis for each tractability category, shown next to a hole's origin so a
17
+ # user knows whether (and how) a type can close it.
20
18
  TRACTABILITY_HINTS = {
21
19
  Inference::DynamicOrigin::ADD_RBS => "add RBS",
22
20
  Inference::DynamicOrigin::ENABLE_PLUGIN => "enable a plugin / pre_eval",
@@ -4,31 +4,37 @@ require_relative "../inference/dynamic_origin"
4
4
 
5
5
  module Rigor
6
6
  class CLI
7
- # ADR-63 Tier 1 — aggregates per-file {Inference::ProtectionScanner}
8
- # results into a project-level protection report: the protected ratio, the
9
- # per-file breakdown, and a ranked "add a type here" list keyed by the
10
- # method called on an unprotected (`Dynamic`) receiver — the highest-traffic
11
- # untyped dispatches, where a receiver annotation buys the most catching
12
- # power.
7
+ # ADR-63 Tier 1 — aggregates per-file {Inference::ProtectionScanner} results into a project-level protection report:
8
+ # the protected ratio, the per-file breakdown, and a ranked "add a type here" list keyed by the method called on an
9
+ # unprotected (`Dynamic`) receiver the highest-traffic untyped dispatches, where a receiver annotation buys the
10
+ # most catching power.
13
11
  FileProtection = Data.define(:path, :protected_count, :unprotected_count, :ratio)
14
12
  UntypedCall = Data.define(:method_name, :count, :examples, :dynamic_origin)
15
13
 
16
- ProtectionReport = Data.define(:files, :untyped_calls, :parse_errors) do
14
+ ProtectionReport = Data.define(:files, :untyped_calls, :parse_errors, :cause_site_counts) do
17
15
  def total_protected = files.sum(&:protected_count)
18
16
  def total_unprotected = files.sum(&:unprotected_count)
19
17
  def grand_total = total_protected + total_unprotected
20
18
  def ratio = grand_total.zero? ? 1.0 : total_protected.to_f / grand_total
21
19
 
22
- # ADR-73 P6 — total dispatch-site count per tractability axis across
23
- # the classified holes (those with a recorded `dynamic_origin`), so a
24
- # user sees at a glance how much of the untyped surface a type can
25
- # actually close (`add_rbs`) vs. needs a plugin (`enable_plugin`) vs.
26
- # is an engine gap. Keys are omitted when zero; an all-unclassified
27
- # run yields `{}`.
20
+ # ADR-73 P6 / ADR-82 — total dispatch-site count per tractability axis across the classified holes (those with a
21
+ # recorded `dynamic_origin`), so a user sees at a glance how much of the untyped surface a type can actually close
22
+ # (`add_rbs`) vs. needs a plugin (`enable_plugin`) vs. is an engine gap. Keys are omitted when zero; an
23
+ # all-unclassified run yields `{}`. Computed per-site from `cause_site_counts` (not per method-group-dominant
24
+ # cause) so a group with mixed causes is attributed accurately.
28
25
  def tractability_summary
29
- untyped_calls.each_with_object(Hash.new(0)) do |c, acc|
30
- axis = c.dynamic_origin && Inference::DynamicOrigin.tractability(c.dynamic_origin)
31
- acc[axis] += c.count if axis
26
+ cause_site_counts.each_with_object(Hash.new(0)) do |(cause, count), acc|
27
+ axis = cause && Inference::DynamicOrigin.tractability(cause)
28
+ acc[axis] += count if axis
29
+ end
30
+ end
31
+
32
+ # ADR-82 — per-site count of every dynamic-origin cause across all holes, including the causeless sites
33
+ # (keyed `"none"`). Unlike `add_a_type_here`'s per-method-group dominant cause, this is an exact
34
+ # per-site tally, so it is the faithful measure of provenance coverage (how many holes carry any cause).
35
+ def cause_site_totals
36
+ cause_site_counts.each_with_object({}) do |(cause, count), acc|
37
+ acc[(cause || :none).to_s] = count
32
38
  end
33
39
  end
34
40
 
@@ -38,6 +44,7 @@ module Rigor
38
44
  "unprotected" => total_unprotected,
39
45
  "protection_ratio" => ratio.round(4),
40
46
  "tractability_summary" => tractability_summary.transform_keys(&:to_s),
47
+ "cause_site_counts" => cause_site_totals,
41
48
  "files" => files.map do |f|
42
49
  { "path" => f.path, "protected" => f.protected_count,
43
50
  "unprotected" => f.unprotected_count, "ratio" => f.ratio.round(4) }
@@ -60,6 +67,7 @@ module Rigor
60
67
  def initialize
61
68
  @files = []
62
69
  @calls = Hash.new { |h, k| h[k] = { count: 0, examples: [], origins: Hash.new(0) } }
70
+ @cause_site_counts = Hash.new(0)
63
71
  @parse_errors = []
64
72
  end
65
73
 
@@ -73,6 +81,7 @@ module Rigor
73
81
  bucket[:count] += 1
74
82
  bucket[:examples] << "#{path}:#{site.line}" if bucket[:examples].size < 3
75
83
  bucket[:origins][site.dynamic_origin] += 1 if site.dynamic_origin
84
+ @cause_site_counts[site.dynamic_origin] += 1
76
85
  end
77
86
  end
78
87
 
@@ -87,7 +96,8 @@ module Rigor
87
96
  dynamic_origin: origin)
88
97
  end
89
98
  untyped = calls.sort_by { |c| [-c.count, c.method_name] }
90
- ProtectionReport.new(files: @files, untyped_calls: untyped, parse_errors: @parse_errors)
99
+ ProtectionReport.new(files: @files, untyped_calls: untyped, parse_errors: @parse_errors,
100
+ cause_site_counts: @cause_site_counts)
91
101
  end
92
102
  end
93
103
  end
@@ -6,12 +6,10 @@ module Rigor
6
6
  class CLI
7
7
  # Output-format dispatch shared by the `--format text|json` renderers.
8
8
  #
9
- # Each renderer included this and then implemented `render_text` /
10
- # `render_json`; the `render(data, format:)` entry pointroute by
11
- # the format string, raise one consistent `OptionParser::InvalidArgument`
12
- # on anything else was copied verbatim into every one. Centralising
13
- # it keeps the unsupported-format wording and the text/json contract
14
- # in a single place as new renderers and formats are added.
9
+ # Each renderer included this and then implemented `render_text` / `render_json`; the `render(data, format:)` entry
10
+ # point — route by the format string, raise one consistent `OptionParser::InvalidArgument` on anything else was
11
+ # copied verbatim into every one. Centralising it keeps the unsupported-format wording and the text/json contract in
12
+ # a single place as new renderers and formats are added.
15
13
  module Renderable
16
14
  def render(data, format:)
17
15
  case format
@@ -11,15 +11,12 @@ module Rigor
11
11
  class CLI
12
12
  # Executes `rigor show-bleedingedge` (ADR-50 § WD2).
13
13
  #
14
- # Prints the bleeding-edge overlay — the Rigor-maintained set of the
15
- # next major's queued changes ({Rigor::BleedingEdge}) — as an
16
- # explicit list, and reports which of them the project's
17
- # `bleeding_edge:` configuration adopts. The overlay is empty in this
18
- # release, so the command currently reports an empty set; it becomes
19
- # the inspection surface ADR-50 describes once a feature is queued.
14
+ # Prints the bleeding-edge overlay — the Rigor-maintained set of the next major's queued changes
15
+ # ({Rigor::BleedingEdge}) — as an explicit list, and reports which of them the project's `bleeding_edge:`
16
+ # configuration adopts. The overlay is empty in this release, so the command currently reports an empty set; it
17
+ # becomes the inspection surface ADR-50 describes once a feature is queued.
20
18
  #
21
- # Read-only: it loads `.rigor.yml` to resolve the active selection
22
- # but runs no analysis.
19
+ # Read-only: it loads `.rigor.yml` to resolve the active selection but runs no analysis.
23
20
  class ShowBleedingedgeCommand < Command
24
21
  USAGE = "Usage: rigor show-bleedingedge [options]"
25
22
 
@@ -11,30 +11,20 @@ module Rigor
11
11
  class CLI
12
12
  # Executes the `rigor sig-gen` command — ADR-14 slices 1–3.
13
13
  #
14
- # Walks the given paths (or `configuration.paths` when none
15
- # are supplied), classifies every reachable instance method
16
- # via {Rigor::SigGen::Generator}, and either prints the
17
- # resulting RBS skeletons / unified-style diffs (`--print`,
18
- # `--diff`; slice 1) or writes them to the project signature
19
- # tree via {Rigor::SigGen::Writer} (`--write`; slice 2).
14
+ # Walks the given paths (or `configuration.paths` when none are supplied), classifies every reachable instance
15
+ # method via {Rigor::SigGen::Generator}, and either prints the resulting RBS skeletons / unified-style diffs
16
+ # (`--print`, `--diff`; slice 1) or writes them to the project signature tree via {Rigor::SigGen::Writer}
17
+ # (`--write`; slice 2).
20
18
  #
21
- # `--write` follows the established Ruby community
22
- # convention: `lib/foo/bar.rb` `sig/foo/bar.rbs`. New
23
- # methods are inserted into the matching class declaration
24
- # just before its closing `end`; new classes are appended
25
- # to the file; non-existent target files are created. User-
26
- # authored declarations are NEVER replaced unless
27
- # `--overwrite` is set AND the candidate is a
28
- # `tighter-return`.
19
+ # `--write` follows the established Ruby community convention: `lib/foo/bar.rb` → `sig/foo/bar.rbs`. New methods are
20
+ # inserted into the matching class declaration just before its closing `end`; new classes are appended to the file;
21
+ # non-existent target files are created. User-authored declarations are NEVER replaced unless `--overwrite` is set
22
+ # AND the candidate is a `tighter-return`.
29
23
  #
30
- # Parameter policy defaults to `untyped`. `--params=observed`
31
- # (slice 3) opts in to caller-side observation harvesting:
32
- # the `ObservationCollector` walks `--observe=PATH...`
33
- # (default `spec/` when no flag is given AND a `spec/`
34
- # directory exists), unions per-position arg types, and the
35
- # generator emits the union per ADR-5 clause 2.
36
- # `--params=observed-strict` stays reserved-but-inert until
37
- # the capability-role catalog ships (rejected with a usage
24
+ # Parameter policy defaults to `untyped`. `--params=observed` (slice 3) opts in to caller-side observation
25
+ # harvesting: the `ObservationCollector` walks `--observe=PATH...` (default `spec/` when no flag is given AND a
26
+ # `spec/` directory exists), unions per-position arg types, and the generator emits the union per ADR-5 clause 2.
27
+ # `--params=observed-strict` stays reserved-but-inert until the capability-role catalog ships (rejected with a usage
38
28
  # error so the surface stays stable).
39
29
  class SigGenCommand < Command
40
30
  USAGE = "Usage: rigor sig-gen [options] [paths]"
@@ -86,10 +76,8 @@ module Rigor
86
76
  SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format))
87
77
  end
88
78
 
89
- # Slice 3 — collect call-site argument observations when
90
- # `--params=observed` is set. When `--observe=PATH` is
91
- # not specified, default to `spec/` (skipped silently
92
- # when the directory is absent).
79
+ # Slice 3 — collect call-site argument observations when `--params=observed` is set. When `--observe=PATH` is not
80
+ # specified, default to `spec/` (skipped silently when the directory is absent).
93
81
  def collect_observations(configuration, options)
94
82
  return {} if options.fetch(:params) != "observed"
95
83