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
@@ -18,14 +18,11 @@ module Rigor
18
18
  class CLI
19
19
  # Executes the `rigor type-of` command.
20
20
  #
21
- # The command is a thin probe over `Rigor::Scope#type_of`: it locates the
22
- # deepest expression at a `(file, line, column)` triple and prints the
23
- # inferred type, RBS erasure, and (optionally) the recorded fail-soft
24
- # fallbacks.
21
+ # The command is a thin probe over `Rigor::Scope#type_of`: it locates the deepest expression at a `(file, line,
22
+ # column)` triple and prints the inferred type, RBS erasure, and (optionally) the recorded fail-soft fallbacks.
25
23
  #
26
- # Encapsulating the command in its own class keeps `Rigor::CLI` focused on
27
- # dispatching and lets us evolve the type-of UX (extra flags, watch mode,
28
- # streaming output) without bloating the CLI shell. Output formatting is
24
+ # Encapsulating the command in its own class keeps `Rigor::CLI` focused on dispatching and lets us evolve the
25
+ # type-of UX (extra flags, watch mode, streaming output) without bloating the CLI shell. Output formatting is
29
26
  # delegated to {TypeOfRenderer}.
30
27
  class TypeOfCommand < Command
31
28
  USAGE = "Usage: rigor type-of [options] FILE:LINE:COL"
@@ -63,11 +60,9 @@ module Rigor
63
60
 
64
61
  def execute(target:, options:, buffer: nil)
65
62
  file, line, column = target
66
- # Under editor mode the logical `file` may not exist on disk
67
- # (user editing a new file); the runtime check is only that
68
- # the BUFFER is readable, which `resolve_buffer_binding`
69
- # has already enforced. For non-editor mode `file` must
70
- # exist.
63
+ # Under editor mode the logical `file` may not exist on disk (user editing a new file); the runtime check is
64
+ # only that the BUFFER is readable, which `resolve_buffer_binding` has already enforced. For non-editor mode
65
+ # `file` must exist.
71
66
  physical = buffer ? buffer.resolve(file) : file
72
67
  return 1 unless file_exists?(buffer ? physical : file)
73
68
 
@@ -83,12 +78,10 @@ module Rigor
83
78
  tracer = options[:trace] ? Inference::FallbackTracer.new : nil
84
79
  base_scope = Scope.empty(environment: project_environment(file, configuration))
85
80
 
86
- # Build a per-node scope index so locals bound earlier in the
87
- # file flow into the scope used to type the queried node. We
88
- # build the index with no tracer attached (it would otherwise
89
- # double-record fallback events with the second-pass type_of
90
- # call below), then look up the scope visible at the queried
91
- # node and run the actual probe under it.
81
+ # Build a per-node scope index so locals bound earlier in the file flow into the scope used to type the queried
82
+ # node. We build the index with no tracer attached (it would otherwise double-record fallback events with the
83
+ # second-pass type_of call below), then look up the scope visible at the queried node and run the actual probe
84
+ # under it.
92
85
  scope_index = Inference::ScopeIndexer.index(parse_result.value, default_scope: base_scope)
93
86
  node_scope = scope_index[node]
94
87
 
@@ -99,11 +92,9 @@ module Rigor
99
92
  0
100
93
  end
101
94
 
102
- # Builds a project-aware environment relative to the probed file.
103
- # Project-RBS auto-detection roots at CWD today; future work will
104
- # walk parent directories to find the enclosing `Gemfile`/`*.gemspec`
105
- # so probes against files outside the current process's CWD still
106
- # see the right `sig/` tree.
95
+ # Builds a project-aware environment relative to the probed file. Project-RBS auto-detection roots at CWD today;
96
+ # future work will walk parent directories to find the enclosing `Gemfile`/`*.gemspec` so probes against files
97
+ # outside the current process's CWD still see the right `sig/` tree.
107
98
  def project_environment(_file, configuration)
108
99
  Environment.for_project(
109
100
  libraries: configuration.libraries,
@@ -7,12 +7,10 @@ require_relative "renderable"
7
7
 
8
8
  module Rigor
9
9
  class CLI
10
- # Renders a `TypeOfCommand::Result` as either human-readable text or a
11
- # machine-readable JSON document.
10
+ # Renders a `TypeOfCommand::Result` as either human-readable text or a machine-readable JSON document.
12
11
  #
13
- # The renderer is a separate concern from the command itself so that future
14
- # output formats (sexp, lsp-style hover payloads, color decoration) can
15
- # plug in without disturbing argument parsing or the inference call site.
12
+ # The renderer is a separate concern from the command itself so that future output formats (sexp, lsp-style hover
13
+ # payloads, color decoration) can plug in without disturbing argument parsing or the inference call site.
16
14
  class TypeOfRenderer
17
15
  include Renderable
18
16
 
@@ -16,11 +16,9 @@ module Rigor
16
16
  class CLI
17
17
  # Executes the `rigor type-scan` command.
18
18
  #
19
- # The command walks every Prism node in one or more files, runs
20
- # `Rigor::Scope#type_of` on each, and reports per-node-class coverage of
21
- # the inference engine's directly recognized classes. It is the project's
22
- # primary CI gate for tracking how much of an input source the engine can
23
- # name without falling back to `Dynamic[Top]`.
19
+ # The command walks every Prism node in one or more files, runs `Rigor::Scope#type_of` on each, and reports
20
+ # per-node-class coverage of the inference engine's directly recognized classes. It is the project's primary CI gate
21
+ # for tracking how much of an input source the engine can name without falling back to `Dynamic[Top]`.
24
22
  class TypeScanCommand < Command
25
23
  USAGE = "Usage: rigor type-scan [options] PATH..."
26
24
 
@@ -78,9 +76,8 @@ module Rigor
78
76
  accumulator.to_report(paths, options)
79
77
  end
80
78
 
81
- # Builds a project-aware environment that auto-detects `<cwd>/sig`
82
- # by default and honours the configuration's `libraries:` /
83
- # `signature_paths:` keys when present.
79
+ # Builds a project-aware environment that auto-detects `<cwd>/sig` by default and honours the configuration's
80
+ # `libraries:` / `signature_paths:` keys when present.
84
81
  def project_environment(configuration)
85
82
  Environment.for_project(
86
83
  libraries: configuration.libraries,
@@ -108,8 +105,7 @@ module Rigor
108
105
  report.unrecognized_ratio > threshold ? 1 : 0
109
106
  end
110
107
 
111
- # Internal helper that accumulates per-file scan results into the
112
- # totals carried by `Report`.
108
+ # Internal helper that accumulates per-file scan results into the totals carried by `Report`.
113
109
  class ScanAccumulator
114
110
  def initialize
115
111
  @visits = Hash.new(0)
@@ -7,11 +7,9 @@ require_relative "renderable"
7
7
 
8
8
  module Rigor
9
9
  class CLI
10
- # Renders a `TypeScanCommand::Report` as either a terminal-friendly text
11
- # summary or a JSON document suitable for CI ingestion. Text and JSON
12
- # branches share a single source of truth (the `Report` value object) so
13
- # the two formats stay in lockstep; that pairing is why this class is a
14
- # bit longer than the default class-length budget.
10
+ # Renders a `TypeScanCommand::Report` as either a terminal-friendly text summary or a JSON document suitable for CI
11
+ # ingestion. Text and JSON branches share a single source of truth (the `Report` value object) so the two formats
12
+ # stay in lockstep; that pairing is why this class is a bit longer than the default class-length budget.
15
13
  class TypeScanRenderer
16
14
  include Renderable
17
15
 
@@ -2,9 +2,8 @@
2
2
 
3
3
  module Rigor
4
4
  class CLI
5
- # Aggregated report assembled by `TypeScanCommand` and consumed by
6
- # `TypeScanRenderer`. The struct holds per-file paths, accumulated
7
- # per-class counts, located fallback events, and any parse errors.
5
+ # Aggregated report assembled by `TypeScanCommand` and consumed by `TypeScanRenderer`. The struct holds per-file
6
+ # paths, accumulated per-class counts, located fallback events, and any parse errors.
8
7
  class Report < Data.define(
9
8
  :files,
10
9
  :parse_errors,
@@ -6,9 +6,8 @@ module Rigor
6
6
  class CLI
7
7
  # `rigor upgrade` — the ADR-50 WD7 migration command skeleton.
8
8
  #
9
- # The real body lands when a concrete backwards-compatibility break
10
- # gives it a target (e.g. re-running `baseline regenerate` against a
11
- # strengthened default profile, surfacing renamed suppression ids,
9
+ # The real body lands when a concrete backwards-compatibility break gives it a target (e.g. re-running `baseline
10
+ # regenerate` against a strengthened default profile, surfacing renamed suppression ids,
12
11
  # reporting `bleeding_edge:` graduations). Until then it prints the
13
12
  # current version and notes that upgrade is queued.
14
13
  class UpgradeCommand < Command
data/lib/rigor/cli.rb CHANGED
@@ -14,11 +14,9 @@ require_relative "cli/diagnostic_formats"
14
14
  require_relative "cli/ci_detector"
15
15
 
16
16
  module Rigor
17
- # The CLI class is a dispatcher: each `run_*` method delegates to a
18
- # command-specific class once the command grows beyond a few lines (see
19
- # {CLI::TypeOfCommand} and {CLI::CheckCommand}). It necessarily grows by
20
- # one delegator + one help line per command, so — like the command
21
- # classes it fans out to — it carries an explicit ClassLength exemption.
17
+ # The CLI class is a dispatcher: each `run_*` method delegates to a command-specific class once the command grows
18
+ # beyond a few lines (see {CLI::TypeOfCommand} and {CLI::CheckCommand}). It necessarily grows by one delegator + one
19
+ # help line per command, so like the command classes it fans out to — it carries an explicit ClassLength exemption.
22
20
  class CLI # rubocop:disable Metrics/ClassLength
23
21
  EXIT_USAGE = 64
24
22
 
@@ -94,11 +92,9 @@ module Rigor
94
92
  end
95
93
 
96
94
  def run_init
97
- # Default destination is `.rigor.dist.yml` — the
98
- # project-default config that gets committed. Developers
99
- # who want a personal override layer create `.rigor.yml`
100
- # alongside it (auto-discovery prefers `.rigor.yml` when
101
- # both are present; no implicit merge).
95
+ # Default destination is `.rigor.dist.yml` — the project-default config that gets committed. Developers who want a
96
+ # personal override layer create `.rigor.yml` alongside it (auto-discovery prefers `.rigor.yml` when both are
97
+ # present; no implicit merge).
102
98
  options = {
103
99
  force: false,
104
100
  path: ".rigor.dist.yml"
@@ -123,16 +119,11 @@ module Rigor
123
119
  0
124
120
  end
125
121
 
126
- # `rigor init`'s template ships empty `plugins:` so a fresh
127
- # init has nothing to validate but the moment the user adds
128
- # any plugin entry, the activation-failure surfaces enumerated
129
- # in `rigor plugins`'s docstring become real. Point them at
130
- # the verification command + the canonical readiness flow so
131
- # silent failures (the cwd / Gemfile / signature_paths
132
- # mismatches that surfaced during the Mastodon trial) get
133
- # caught the first time the user wires a plugin, not the first
134
- # time `rigor check` reports false positives that should have
135
- # been covered.
122
+ # `rigor init`'s template ships empty `plugins:` so a fresh init has nothing to validate — but the moment the user
123
+ # adds any plugin entry, the activation-failure surfaces enumerated in `rigor plugins`'s docstring become real.
124
+ # Point them at the verification command + the canonical readiness flow so silent failures (the cwd / Gemfile /
125
+ # signature_paths mismatches that surfaced during the Mastodon trial) get caught the first time the user wires a
126
+ # plugin, not the first time `rigor check` reports false positives that should have been covered.
136
127
  def print_init_next_steps(path)
137
128
  @out.puts ""
138
129
  @out.puts "Next steps:"
@@ -142,10 +133,8 @@ module Rigor
142
133
  @out.puts " 3. Run `rigor check` to analyse your code."
143
134
  end
144
135
 
145
- # Renders the starter `.rigor.yml` body. The template
146
- # serialises `Configuration::DEFAULTS` (so the on-disk file
147
- # round-trips through `Configuration.load`) and prepends a
148
- # short header that points the user at the keys they are
136
+ # Renders the starter `.rigor.yml` body. The template serialises `Configuration::DEFAULTS` (so the on-disk file
137
+ # round-trips through `Configuration.load`) and prepends a short header that points the user at the keys they are
149
138
  # most likely to want to edit.
150
139
  def init_template
151
140
  <<~YAML
@@ -291,10 +280,9 @@ module Rigor
291
280
  CLI::SkillCommand.new(argv: @argv, out: @out, err: @err).run
292
281
  end
293
282
 
294
- # `rigor describe` — a top-level alias for `rigor skill describe`,
295
- # the entry point most users reach for first. Surfaced because a
296
- # bare `rigor describe` is the intuitive guess (the onboarding field
297
- # trial saw it tried and met "Unknown command").
283
+ # `rigor describe` — a top-level alias for `rigor skill describe`, the entry point most users reach for first.
284
+ # Surfaced because a bare `rigor describe` is the intuitive guess (the onboarding field trial saw it tried and met
285
+ # "Unknown command").
298
286
  def run_describe
299
287
  require_relative "cli/skill_command"
300
288
 
@@ -4,30 +4,26 @@ require_relative "signature_path_audit"
4
4
  require_relative "analysis/check_rules"
5
5
 
6
6
  module Rigor
7
- # Audits a loaded {Configuration} for the class of mistake where a
8
- # configured value silently resolves to nothing — a typo'd or moved
9
- # path, an unknown library name, an inert rule id. The shared failure
10
- # mode is that the loader filters the bad entry without a word, so the
11
- # only symptom is downstream and confusing: missing signatures turn
12
- # every call into the types they were meant to cover into a
13
- # high-confidence `call.undefined-method`, and an unrecognised
14
- # suppression token leaves the rule firing as if the `disable:` line
15
- # were never written. This module surfaces each such entry up front so
16
- # the cause is visible instead of inferred.
7
+ # Audits a loaded {Configuration} for the class of mistake where a configured value
8
+ # silently resolves to nothing — a typo'd or moved path, an unknown library name, an inert
9
+ # rule id. The shared failure mode is that the loader filters the bad entry without a word,
10
+ # so the only symptom is downstream and confusing: missing signatures turn every call into
11
+ # the types they were meant to cover into a high-confidence `call.undefined-method`, and an
12
+ # unrecognised suppression token leaves the rule firing as if the `disable:` line were
13
+ # never written. This module surfaces each such entry up front so the cause is visible
14
+ # instead of inferred.
17
15
  #
18
- # Every check is held to the same bar that {SignaturePathAudit} set:
19
- # it mirrors the loader's own acceptance test, so a warning means the
20
- # loader really did load nothing, and it never fires on a setup that
21
- # works. In particular the rule-token check only flags a token under a
22
- # built-in family (`call`, `flow`, …) a plugin- or `rbs_extended.*`
23
- # rule id (whose family Rigor cannot statically enumerate, since a
24
- # `node_rule` block emits any `rule:` string it likes) is left alone, so
25
- # disabling a plugin rule is never mistaken for a typo.
16
+ # Every check is held to the same bar that {SignaturePathAudit} set: it mirrors the
17
+ # loader's own acceptance test, so a warning means the loader really did load nothing, and
18
+ # it never fires on a setup that works. In particular the rule-token check only flags a
19
+ # token under a built-in family (`call`, `flow`, …) — a plugin- or `rbs_extended.*` rule id
20
+ # (whose family Rigor cannot statically enumerate, since a `node_rule` block emits any
21
+ # `rule:` string it likes) is left alone, so disabling a plugin rule is never mistaken for
22
+ # a typo.
26
23
  module ConfigAudit
27
- # One config-level finding. `kind` discriminates the source key
28
- # (`:signature_path`, `:library`, `:disabled_rule`,
29
- # `:severity_override`, `:bundler_bundle_path`, `:bundler_lockfile`,
30
- # `:rbs_collection_lockfile`); `fields` carries the kind-specific
24
+ # One config-level finding. `kind` discriminates the source key (`:signature_path`,
25
+ # `:library`, `:disabled_rule`, `:severity_override`, `:bundler_bundle_path`,
26
+ # `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
31
27
  # structured data merged into {#to_h} for JSON consumers.
32
28
  Warning = Data.define(:kind, :message, :fields) do
33
29
  def to_h
@@ -36,9 +32,8 @@ module Rigor
36
32
  end
37
33
 
38
34
  # @param configuration [Rigor::Configuration]
39
- # @param project_root [String] the directory the run's relative
40
- # bundler / collection paths resolve against (the CLI's CWD), used
41
- # only by the explicit-path checks.
35
+ # @param project_root [String] the directory the run's relative bundler / collection
36
+ # paths resolve against (the CLI's CWD), used only by the explicit-path checks.
42
37
  # @return [Array<Warning>]
43
38
  def self.warnings(configuration, project_root: Dir.pwd)
44
39
  signature_path_warnings(configuration) +
@@ -47,9 +42,8 @@ module Rigor
47
42
  explicit_path_warnings(configuration, project_root)
48
43
  end
49
44
 
50
- # `signature_paths:` entries that resolve to nothing — delegated to
51
- # {SignaturePathAudit}, which mirrors the loader's `directory?` +
52
- # recursive `**/*.rbs` acceptance test.
45
+ # `signature_paths:` entries that resolve to nothing — delegated to {SignaturePathAudit},
46
+ # which mirrors the loader's `directory?` + recursive `**/*.rbs` acceptance test.
53
47
  def self.signature_path_warnings(configuration)
54
48
  SignaturePathAudit.warnings(configuration.signature_paths).map do |entry|
55
49
  Warning.new(
@@ -61,10 +55,9 @@ module Rigor
61
55
  end
62
56
 
63
57
  # `libraries:` entries RBS does not recognise. Uses the same
64
- # `RBS::EnvironmentLoader#has_library?` guard the loader filters
65
- # through ({Environment::RbsLoader} `build_env_for`), so a flagged
66
- # name is exactly one the loader skipped. Fails soft: if RBS itself
67
- # raises, no warning rather than a crash.
58
+ # `RBS::EnvironmentLoader#has_library?` guard the loader filters through
59
+ # ({Environment::RbsLoader} `build_env_for`), so a flagged name is exactly one the loader
60
+ # skipped. Fails soft: if RBS itself raises, no warning rather than a crash.
68
61
  def self.library_warnings(configuration)
69
62
  configured = Array(configuration.libraries)
70
63
  return [] if configured.empty?
@@ -81,9 +74,9 @@ module Rigor
81
74
  []
82
75
  end
83
76
 
84
- # `disable:` tokens and `severity_overrides:` keys that name no rule.
85
- # Restricted to tokens under a built-in family so a plugin rule id is
86
- # never mis-flagged (see the module docstring).
77
+ # `disable:` tokens and `severity_overrides:` keys that name no rule. Restricted to
78
+ # tokens under a built-in family so a plugin rule id is never mis-flagged (see the module
79
+ # docstring).
87
80
  def self.rule_token_warnings(configuration)
88
81
  disable = Array(configuration.disabled_rules).filter_map do |token|
89
82
  next unless inert_builtin_token?(token.to_s)
@@ -106,11 +99,10 @@ module Rigor
106
99
  disable + overrides
107
100
  end
108
101
 
109
- # True when `token` looks like a built-in rule id but matches none —
110
- # its first segment is a built-in family yet it is neither a bare
111
- # family wildcard nor a known canonical id. A token whose family is
112
- # not built-in (a plugin / `rbs_extended.*` rule, or a bare legacy
113
- # alias) is deliberately NOT flagged: it may resolve at run time, so
102
+ # True when `token` looks like a built-in rule id but matches none — its first segment
103
+ # is a built-in family yet it is neither a bare family wildcard nor a known canonical id.
104
+ # A token whose family is not built-in (a plugin / `rbs_extended.*` rule, or a bare
105
+ # legacy alias) is deliberately NOT flagged: it may resolve at run time, so
114
106
  # under-warning is the FP-safe choice.
115
107
  def self.inert_builtin_token?(token)
116
108
  family = token.split(".").first
@@ -121,10 +113,9 @@ module Rigor
121
113
  true
122
114
  end
123
115
 
124
- # Explicitly-configured bundler / rbs-collection paths that do not
125
- # exist. Only the explicit form is audited (a nil value means
126
- # auto-detection, which finding nothing is normal); messages stay
127
- # factual about the path rather than guessing the fallback.
116
+ # Explicitly-configured bundler / rbs-collection paths that do not exist. Only the
117
+ # explicit form is audited (a nil value means auto-detection, which finding nothing is
118
+ # normal); messages stay factual about the path rather than guessing the fallback.
128
119
  def self.explicit_path_warnings(configuration, project_root)
129
120
  warnings = []
130
121
  add_missing_dir(warnings, configuration.bundler_bundle_path, project_root,
@@ -3,62 +3,47 @@
3
3
  module Rigor
4
4
  class Configuration
5
5
  # Parsed `dependencies:` section of `.rigor.yml`. Per
6
- # [ADR-10](../../../docs/adr/10-dependency-source-inference.md),
7
- # the only nested key today is `source_inference:`, listing
8
- # gems whose Ruby implementation Rigor MAY walk during
9
- # inference instead of degrading to `Dynamic[top]` at the
10
- # dependency boundary.
6
+ # [ADR-10](../../../docs/adr/10-dependency-source-inference.md), the only nested key today is
7
+ # `source_inference:`, listing gems whose Ruby implementation Rigor MAY walk during inference instead of
8
+ # degrading to `Dynamic[top]` at the dependency boundary.
11
9
  #
12
- # Parser for the `dependencies:` YAML section; consumed by
13
- # `Analysis::DependencySourceInference` (ADR-10).
10
+ # Parser for the `dependencies:` YAML section; consumed by `Analysis::DependencySourceInference` (ADR-10).
14
11
  class Dependencies
15
- # Walking modes per
16
- # [ADR-10 § "Decision"](../../../docs/adr/10-dependency-source-inference.md#decision).
12
+ # Walking modes per [ADR-10 §
13
+ # "Decision"](../../../docs/adr/10-dependency-source-inference.md#decision).
17
14
  VALID_MODES = %i[disabled when_missing full].freeze
18
15
 
19
- # Default `roots:` for an entry that does not supply one.
20
- # The hard-excluded directories (`spec/` / `test/` / `bin/`
21
- # / C extensions) are enforced by the walker, not the
22
- # parser — see ADR-10 § "Hard exclusions".
16
+ # Default `roots:` for an entry that does not supply one. The hard-excluded directories (`spec/` /
17
+ # `test/` / `bin/` / C extensions) are enforced by the walker, not the parser — see ADR-10 § "Hard
18
+ # exclusions".
23
19
  DEFAULT_ROOTS = %w[lib].freeze
24
20
 
25
- # Default per-gem catalog cap. ADR-10 slice 4 picks
26
- # 5000 method definitions: it covers Rack (~1500),
27
- # Faraday (~500), Sidekiq (~800) and other realistic
28
- # opt-in targets, while still surfacing a diagnostic for
29
- # ActiveSupport-class libraries (~10 000+ methods) where
30
- # the user should ship RBS or de-list the gem instead.
21
+ # Default per-gem catalog cap. ADR-10 slice 4 picks 5000 method definitions: it covers Rack (~1500),
22
+ # Faraday (~500), Sidekiq (~800) and other realistic opt-in targets, while still surfacing a diagnostic
23
+ # for ActiveSupport-class libraries (~10 000+ methods) where the user should ship RBS or de-list the gem
24
+ # instead.
31
25
  DEFAULT_BUDGET_PER_GEM = 5000
32
26
 
33
- # Range bounds per ADR-10 § "Budget interaction"
34
- # ("range 0.25× – 4×"). Configured against the default,
27
+ # Range bounds per ADR-10 § "Budget interaction" ("range 0.25× – 4×"). Configured against the default,
35
28
  # this lands at 1250 – 20 000.
36
29
  MIN_BUDGET_PER_GEM = (DEFAULT_BUDGET_PER_GEM * 0.25).to_i
37
30
  MAX_BUDGET_PER_GEM = (DEFAULT_BUDGET_PER_GEM * 4).to_i
38
31
 
39
32
  # ADR-10 5b — budget-overrun strategy enum.
40
33
  #
41
- # - `:walker_cap` (default): the (α) semantics. The
42
- # walker stops harvesting at the cap; methods past the
43
- # cap fall through to the existing user-class fallback
44
- # path. Existing v0.1.3 behaviour.
45
- # - `:dependency_silence`: the (β) semantics. Same
46
- # walker behaviour, but the dispatcher additionally
47
- # consults `Index#class_to_gem` after a catalog miss.
48
- # When the receiver's class belongs to a budget-
49
- # exceeded gem, the call resolves to `Dynamic[top]`
50
- # rather than falling through to user-class fallback.
51
- # This silences `call.undefined-method` for unrecorded
52
- # methods at the cost of weaker static checking on
53
- # that gem's surface.
34
+ # - `:walker_cap` (default): the (α) semantics. The walker stops harvesting at the cap; methods past
35
+ # the cap fall through to the existing user-class fallback path. Existing v0.1.3 behaviour.
36
+ # - `:dependency_silence`: the (β) semantics. Same walker behaviour, but the dispatcher additionally
37
+ # consults `Index#class_to_gem` after a catalog miss. When the receiver's class belongs to a
38
+ # budget-exceeded gem, the call resolves to `Dynamic[top]` rather than falling through to user-class
39
+ # fallback. This silences `call.undefined-method` for unrecorded methods at the cost of weaker static
40
+ # checking on that gem's surface.
54
41
  VALID_BUDGET_OVERRUN_STRATEGIES = %i[walker_cap dependency_silence].freeze
55
42
  DEFAULT_BUDGET_OVERRUN_STRATEGY = :walker_cap
56
43
 
57
- # Frozen value object describing a single per-gem opt-in.
58
- # `gem:` is the gem name (matched against the bundle at
59
- # walk time); `mode:` is one of {VALID_MODES}; `roots:` is
60
- # the list of subdirectories within the gem's installation
61
- # directory to walk (defaults to `["lib"]`).
44
+ # Frozen value object describing a single per-gem opt-in. `gem:` is the gem name (matched against the
45
+ # bundle at walk time); `mode:` is one of {VALID_MODES}; `roots:` is the list of subdirectories within
46
+ # the gem's installation directory to walk (defaults to `["lib"]`).
62
47
  class Entry < Data.define(:gem, :mode, :roots)
63
48
  def disabled? = mode == :disabled
64
49
  def when_missing? = mode == :when_missing
@@ -67,10 +52,8 @@ module Rigor
67
52
 
68
53
  attr_reader :source_inference, :budget_per_gem, :budget_overrun_strategy, :warnings
69
54
 
70
- # Parse the YAML-shaped `dependencies:` value into a
71
- # frozen {Dependencies}. Accepts `nil` / `{}` / a Hash with
72
- # `source_inference:` and / or `budget_per_gem:` /
73
- # `budget_overrun_strategy:` present.
55
+ # Parse the YAML-shaped `dependencies:` value into a frozen {Dependencies}. Accepts `nil` / `{}` / a
56
+ # Hash with `source_inference:` and / or `budget_per_gem:` / `budget_overrun_strategy:` present.
74
57
  def self.from_h(data)
75
58
  return new([]) if data.nil?
76
59
  raise ArgumentError, "dependencies: must be a Hash, got #{data.inspect}" unless data.is_a?(Hash)
@@ -110,25 +93,17 @@ module Rigor
110
93
  def empty? = @source_inference.empty?
111
94
 
112
95
  class << self
113
- # ADR-10 § "config-conflict diagnostic" — merges a
114
- # potentially-duplicated entry list (the `includes:`
115
- # chain produces concatenated arrays via
116
- # `Configuration.deep_merge`'s special-case for
117
- # `dependencies.source_inference`) into a single
118
- # canonical entry per gem name. The merge rules:
96
+ # ADR-10 § "config-conflict diagnostic" — merges a potentially-duplicated entry list (the `includes:`
97
+ # chain produces concatenated arrays via `Configuration.deep_merge`'s special-case for
98
+ # `dependencies.source_inference`) into a single canonical entry per gem name. The merge rules:
119
99
  #
120
- # - Same gem, same all fields → idempotent collapse
121
- # (no warning).
122
- # - Same gem, different `mode:` keep the LAST entry
123
- # (matches existing right-wins semantics elsewhere)
124
- # AND emit a `:warning` so the user knows their
125
- # `includes:` chain is ambiguous.
126
- # - Same gem, different `roots:` → union the roots
127
- # silently (no warning). The walker is happy to
100
+ # - Same gem, same all fields → idempotent collapse (no warning).
101
+ # - Same gem, different `mode:` → keep the LAST entry (matches existing right-wins semantics
102
+ # elsewhere) AND emit a `:warning` so the user knows their `includes:` chain is ambiguous.
103
+ # - Same gem, different `roots:` → union the roots silently (no warning). The walker is happy to
128
104
  # visit the union.
129
105
  #
130
- # Returns `[entries, warnings]` so the caller can
131
- # plumb the warning list through to the Runner for
106
+ # Returns `[entries, warnings]` so the caller can plumb the warning list through to the Runner for
132
107
  # diagnostic emission.
133
108
  def dedupe_entries(entries)
134
109
  warnings = []
@@ -156,11 +131,10 @@ module Rigor
156
131
 
157
132
  private
158
133
 
159
- # `source_inference:` is a list of per-gem entries, or `false` /
160
- # omitted to disable it (the default). Guard the Ruby
161
- # `Array(false) == [false]` quirk that would otherwise feed `false`
162
- # straight into coerce_entry and crash the whole run on a perfectly
163
- # reasonable "off" config (`dependencies: { source_inference: false }`).
134
+ # `source_inference:` is a list of per-gem entries, or `false` / omitted to disable it (the default).
135
+ # Guard the Ruby `Array(false) == [false]` quirk that would otherwise feed `false` straight into
136
+ # coerce_entry and crash the whole run on a perfectly reasonable "off" config
137
+ # (`dependencies: { source_inference: false }`).
164
138
  def coerce_source_inference(value)
165
139
  return [] if value.nil? || value == false
166
140
 
@@ -222,12 +196,9 @@ module Rigor
222
196
  "#{VALID_BUDGET_OVERRUN_STRATEGIES.inspect}, got #{value.inspect}"
223
197
  end
224
198
 
225
- # ADR-10 slice 4. Per-gem catalog cap is mandatory
226
- # (the parser supplies the default before this is
227
- # called, so `nil` only reaches here on an explicit
228
- # `budget_per_gem: ~`). Range bounds match
229
- # MIN_BUDGET_PER_GEM .. MAX_BUDGET_PER_GEM
230
- # (i.e. 0.25× – 4× of the default).
199
+ # ADR-10 slice 4. Per-gem catalog cap is mandatory (the parser supplies the default before this is
200
+ # called, so `nil` only reaches here on an explicit `budget_per_gem: ~`). Range bounds match
201
+ # MIN_BUDGET_PER_GEM .. MAX_BUDGET_PER_GEM (i.e. 0.25× of the default).
231
202
  def coerce_budget_per_gem(value)
232
203
  unless value.is_a?(Integer)
233
204
  raise ArgumentError,