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
@@ -8,64 +8,47 @@ require_relative "write_result"
8
8
 
9
9
  module Rigor
10
10
  module SigGen
11
- # Applies a per-source-file group of {MethodCandidate}s to
12
- # the target `.rbs` file under the project signature tree.
11
+ # Applies a per-source-file group of {MethodCandidate}s to the target `.rbs` file under the project
12
+ # signature tree.
13
13
  #
14
- # ADR-14 slice 2: the writer parses the target with
15
- # `RBS::Parser` to find the matching class declaration and
16
- # inserts new method declarations just before the class's
17
- # closing `end` keyword. Existing declarations are NEVER
18
- # touched unless `--overwrite` is set AND the candidate's
19
- # classification is `tighter-return`.
14
+ # ADR-14 slice 2: the writer parses the target with `RBS::Parser` to find the matching class declaration and
15
+ # inserts new method declarations just before the class's closing `end` keyword. Existing declarations are
16
+ # NEVER touched unless `--overwrite` is set AND the candidate's classification is `tighter-return`.
20
17
  #
21
- # The slice does NOT re-render the whole file through
22
- # `RBS::Writer`. That would lose comments / blank-line
23
- # formatting per upstream design; the byte-range insertion
24
- # approach taken here preserves untouched declarations
25
- # verbatim. Mixed hand-written + generated output inside
26
- # the *same* class declaration may still lose trailing
27
- # blank lines on the touched ranges; the `--diff` review
28
- # surface from slice 1 is the user's escape hatch.
18
+ # The slice does NOT re-render the whole file through `RBS::Writer`. That would lose comments / blank-line
19
+ # formatting per upstream design; the byte-range insertion approach taken here preserves untouched
20
+ # declarations verbatim. Mixed hand-written + generated output inside the *same* class declaration may still
21
+ # lose trailing blank lines on the touched ranges; the `--diff` review surface from slice 1 is the user's
22
+ # escape hatch.
29
23
  #
30
- # Safety boundary: the writer ASSERTS the target lives
31
- # inside the configured signature tree before touching the
32
- # disk. Files outside that tree route through
33
- # `WriteResult(action: :skipped_outside_sig_root)`; the
24
+ # Safety boundary: the writer ASSERTS the target lives inside the configured signature tree before touching
25
+ # the disk. Files outside that tree route through `WriteResult(action: :skipped_outside_sig_root)`; the
34
26
  # caller decides whether to warn or fail.
35
27
  class Writer # rubocop:disable Metrics/ClassLength
36
28
  INDENT = " "
37
29
  private_constant :INDENT
38
30
 
39
- # Per-`update_existing` accumulator. The merge_class
40
- # helper mutates `source` / `decls` / `applied` /
41
- # `skipped` in place as each class is processed so the
42
- # next class sees the latest byte positions.
31
+ # Per-`update_existing` accumulator. The merge_class helper mutates `source` / `decls` / `applied` /
32
+ # `skipped` in place as each class is processed so the next class sees the latest byte positions.
43
33
  MergeState = Struct.new(:source, :decls, :applied, :skipped, keyword_init: true)
44
34
  private_constant :MergeState
45
35
 
46
36
  def initialize(path_mapper:, overwrite: false)
47
37
  @path_mapper = path_mapper
48
38
  @overwrite = overwrite
49
- # Run-level (cross-file) namespace-kind view, populated
50
- # per `#write_all` from every candidate's per-file map.
51
- # Empty until then so the single-target `#write` path
52
- # falls back to per-candidate kinds only.
39
+ # Run-level (cross-file) namespace-kind view, populated per `#write_all` from every candidate's per-file
40
+ # map. Empty until then so the single-target `#write` path falls back to per-candidate kinds only.
53
41
  @global_namespace_kinds = {}
54
- # Run-level qualified-class-name => superclass-token view,
55
- # same lifecycle as `@global_namespace_kinds`.
42
+ # Run-level qualified-class-name => superclass-token view, same lifecycle as `@global_namespace_kinds`.
56
43
  @global_superclasses = {}
57
44
  end
58
45
 
59
- # Process the full candidate list by resolving each
60
- # candidate's target sig file via the path mapper (which
61
- # may route consolidated-layout classes to existing
62
- # files) and grouping candidates that share a target
46
+ # Process the full candidate list by resolving each candidate's target sig file via the path mapper (which
47
+ # may route consolidated-layout classes to existing files) and grouping candidates that share a target
63
48
  # before writing.
64
49
  #
65
- # ADR-14 follow-up: this is the consolidated-layout
66
- # entry point. The legacy `write(source_path, candidates)`
67
- # below assumes all candidates share a target and
68
- # remains for spec convenience.
50
+ # ADR-14 follow-up: this is the consolidated-layout entry point. The legacy `write(source_path,
51
+ # candidates)` below assumes all candidates share a target and remains for spec convenience.
69
52
  #
70
53
  # @param candidates [Array<MethodCandidate>]
71
54
  # @return [Array<WriteResult>] one per target sig file.
@@ -95,10 +78,8 @@ module Rigor
95
78
 
96
79
  private
97
80
 
98
- # Shared per-target write path used by both `#write` and
99
- # `#write_all`. Picks a representative `source_path` for
100
- # the {WriteResult} when multiple candidates merge into
101
- # one target.
81
+ # Shared per-target write path used by both `#write` and `#write_all`. Picks a representative
82
+ # `source_path` for the {WriteResult} when multiple candidates merge into one target.
102
83
  def write_target(target, candidates, source_path: nil)
103
84
  source_path ||= candidates.first&.path
104
85
  unless inside_sig_root?(target)
@@ -116,8 +97,7 @@ module Rigor
116
97
  root = @path_mapper.sig_root_dir.realpath
117
98
  target.expand_path.ascend.any? { |ancestor| realpath_or_nil(ancestor) == root }
118
99
  rescue Errno::ENOENT
119
- # The sig root doesn't exist yet; we'll create it
120
- # alongside the target file. Allow this case.
100
+ # The sig root doesn't exist yet; we'll create it alongside the target file. Allow this case.
121
101
  target.expand_path.to_s.start_with?(@path_mapper.sig_root_dir.expand_path.to_s)
122
102
  end
123
103
 
@@ -134,26 +114,17 @@ module Rigor
134
114
  action: :created, applied: candidates)
135
115
  end
136
116
 
137
- # ADR-14 gap-#3 follow-up (c): when one candidate's
138
- # `class_name` is a strict prefix of another's, emit a
139
- # single nested tree instead of two flat sibling
140
- # blocks. The third-round self-dogfood surfaced
141
- # `Analysis::DependencySourceInference::GemResolver`
142
- # containing `class Resolved < Data.define(...)` — the
143
- # earlier `group_by(&:class_name)` flattened that into
144
- # two top-level wraps (one for GemResolver's own
145
- # methods, one for GemResolver::Resolved's), which
146
- # Steep accepted but is not the canonical layout in
147
- # this project's `sig/`.
117
+ # ADR-14 gap-#3 follow-up (c): when one candidate's `class_name` is a strict prefix of another's, emit a
118
+ # single nested tree instead of two flat sibling blocks. The third-round self-dogfood surfaced
119
+ # `Analysis::DependencySourceInference::GemResolver` containing `class Resolved < Data.define(...)` the
120
+ # earlier `group_by(&:class_name)` flattened that into two top-level wraps (one for GemResolver's own
121
+ # methods, one for GemResolver::Resolved's), which Steep accepted but is not the canonical layout in this
122
+ # project's `sig/`.
148
123
  #
149
- # The writer now builds a tree keyed by qualified-name
150
- # segments. Each tree node carries (qualified_name,
151
- # methods, shells, children); rendering walks the tree
152
- # so every nested class appears inside its parent's
153
- # block. Empty class shells (gap-#3 follow-up (e), e.g.
154
- # `Unresolvable = Data.define(...)`) participate as
155
- # zero-method tree nodes — they emit `class Foo\nend`
156
- # at their position.
124
+ # The writer now builds a tree keyed by qualified-name segments. Each tree node carries (qualified_name,
125
+ # methods, shells, children); rendering walks the tree so every nested class appears inside its parent's
126
+ # block. Empty class shells (gap-#3 follow-up (e), e.g. `Unresolvable = Data.define(...)`) participate as
127
+ # zero-method tree nodes — they emit `class Foo\nend` at their position.
157
128
  def render_new_file(candidates)
158
129
  shells = collect_class_shells(candidates)
159
130
  tree = build_namespace_tree(candidates, shells)
@@ -162,8 +133,7 @@ module Rigor
162
133
  render_tree_nodes(tree, kinds, supers, 0)
163
134
  end
164
135
 
165
- # Drains `class_shells` from every candidate; the
166
- # generator's walker populates the same set on every
136
+ # Drains `class_shells` from every candidate; the generator's walker populates the same set on every
167
137
  # candidate produced from a given file (gap-#3 (e)).
168
138
  def collect_class_shells(candidates)
169
139
  shells = Set.new
@@ -179,29 +149,22 @@ module Rigor
179
149
  merged
180
150
  end
181
151
 
182
- # Folds every candidate's per-file namespace-kind map
183
- # into one run-level view so a `class Foo` recorded
184
- # while scanning `foo.rb` governs the wrapper keyword
185
- # emitted for `Foo` in a *sibling* file's target e.g.
186
- # `foo/bar.rb` declaring `class Foo::Bar`, whose compact
187
- # constant path never names `Foo`, so the walker records
188
- # no kind for it. Without this view that sibling target
189
- # wraps the nested class in `module Foo` while `foo.rbs`
190
- # declares `class Foo`; loading both raises
191
- # `RBS::DuplicatedDeclarationError`, aborting the whole
192
- # RBS env build.
152
+ # Folds every candidate's per-file namespace-kind map into one run-level view so a `class Foo` recorded
153
+ # while scanning `foo.rb` governs the wrapper keyword emitted for `Foo` in a *sibling* file's target — e.g.
154
+ # `foo/bar.rb` declaring `class Foo::Bar`, whose compact constant path never names `Foo`, so the walker
155
+ # records no kind for it. Without this view that sibling target wraps the nested class in `module Foo`
156
+ # while `foo.rbs` declares `class Foo`; loading both raises `RBS::DuplicatedDeclarationError`, aborting the
157
+ # whole RBS env build.
193
158
  def build_namespace_kinds(candidates)
194
159
  candidates.each_with_object({}) do |candidate, acc|
195
160
  (candidate.namespace_kinds || {}).each { |name, kind| apply_namespace_kind(acc, name, kind) }
196
161
  end
197
162
  end
198
163
 
199
- # Superclass twins of {#merged_namespace_kinds} /
200
- # {#build_namespace_kinds}: fold every candidate's
201
- # per-file `class_superclasses` map into one view keyed by
202
- # qualified class name. Only plain-constant superclasses are
203
- # ever recorded (the generator skips computed ones), so a
204
- # missing key means "emit no superclass", never "unknown".
164
+ # Superclass twins of {#merged_namespace_kinds} / {#build_namespace_kinds}: fold every candidate's
165
+ # per-file `class_superclasses` map into one view keyed by qualified class name. Only plain-constant
166
+ # superclasses are ever recorded (the generator skips computed ones), so a missing key means "emit no
167
+ # superclass", never "unknown".
205
168
  def merged_superclasses(candidates)
206
169
  merged = @global_superclasses.dup
207
170
  candidates.each do |c|
@@ -220,13 +183,10 @@ module Rigor
220
183
  end
221
184
  end
222
185
 
223
- # A `class` declaration is authoritative and MUST win
224
- # over the `:module` wrapper default: a compact
225
- # `class Foo::Bar` never names `Foo`, so the only signal
226
- # for `Foo`'s kind is an actual `class Foo` (or
227
- # `Const = Data.define(...)` shell) seen elsewhere. This
228
- # guarantees the generated tree never mixes `class` /
229
- # `module` for the same constant.
186
+ # A `class` declaration is authoritative and MUST win over the `:module` wrapper default: a compact
187
+ # `class Foo::Bar` never names `Foo`, so the only signal for `Foo`'s kind is an actual `class Foo` (or
188
+ # `Const = Data.define(...)` shell) seen elsewhere. This guarantees the generated tree never mixes
189
+ # `class` / `module` for the same constant.
230
190
  def apply_namespace_kind(map, key, kind)
231
191
  if kind == :class
232
192
  map[key] = :class
@@ -235,12 +195,9 @@ module Rigor
235
195
  end
236
196
  end
237
197
 
238
- # Tree node: { name:, children: Hash{String => node},
239
- # methods: Array<MethodCandidate>, shell: Boolean }.
240
- # `shell` flags nodes that came in via `class_shells`
241
- # only (no methods of their own); rendering uses it to
242
- # default the keyword to `:class` for the `class Const`
243
- # = `Data.define(...)` case.
198
+ # Tree node: { name:, children: Hash{String => node}, methods: Array<MethodCandidate>, shell: Boolean }.
199
+ # `shell` flags nodes that came in via `class_shells` only (no methods of their own); rendering uses it to
200
+ # default the keyword to `:class` for the `class Const` = `Data.define(...)` case.
244
201
  def build_namespace_tree(candidates, shells)
245
202
  root = { name: nil, children: {}, methods: [], shell: false }
246
203
  candidates.group_by(&:class_name).each do |class_name, methods|
@@ -287,12 +244,9 @@ module Rigor
287
244
  method_lines + child_blocks
288
245
  end
289
246
 
290
- # ` < Super` for a `class` node whose qualified name has a
291
- # recorded superclass, else the empty string. A `module`
292
- # never takes a superclass (RBS forbids it), so the keyword
293
- # gates emission — a name coincidentally recorded as both
294
- # (impossible from one source class, but cheap to guard)
295
- # stays a bare module.
247
+ # ` < Super` for a `class` node whose qualified name has a recorded superclass, else the empty string. A
248
+ # `module` never takes a superclass (RBS forbids it), so the keyword gates emission — a name coincidentally
249
+ # recorded as both (impossible from one source class, but cheap to guard) stays a bare module.
296
250
  def superclass_suffix(keyword, supers, qualified)
297
251
  return "" unless keyword == :class
298
252
 
@@ -300,14 +254,10 @@ module Rigor
300
254
  superclass ? " < #{superclass}" : ""
301
255
  end
302
256
 
303
- # Per ADR-14 gap-#3 (a) the keyword for a segment comes
304
- # from `namespace_kinds` when known. The default for an
305
- # explicit class shell (gap-#3 (e), `Const =
306
- # Data.define(...)`) is `:class`; otherwise default to
307
- # `:class` for a method-bearing leaf node and `:module`
308
- # for an intermediate (children-only) segment. Defaulting
309
- # intermediates to `:module` matches RBS's "multiple
310
- # `module Foo` declarations merge" rule.
257
+ # Per ADR-14 gap-#3 (a) the keyword for a segment comes from `namespace_kinds` when known. The default for
258
+ # an explicit class shell (gap-#3 (e), `Const = Data.define(...)`) is `:class`; otherwise default to
259
+ # `:class` for a method-bearing leaf node and `:module` for an intermediate (children-only) segment.
260
+ # Defaulting intermediates to `:module` matches RBS's "multiple `module Foo` declarations merge" rule.
311
261
  def node_keyword(node, kinds, qualified)
312
262
  return kinds.fetch(qualified) if kinds.key?(qualified)
313
263
  return :class if node[:shell]
@@ -332,15 +282,11 @@ module Rigor
332
282
  action: action, applied: state.applied, skipped: state.skipped)
333
283
  end
334
284
 
335
- # ADR-14 gap-#3 (e): for every requested class shell
336
- # that isn't already declared in the target file,
337
- # insert an empty `class Const\nend` block inside the
338
- # nearest existing ancestor. Shells already covered by
339
- # an existing declaration are silently a no-op. The
340
- # `applied` accumulator does NOT grow — shells are
341
- # structural declarations, not methods, so the
342
- # action-count surface (`updated +N`) keeps
343
- # reflecting method changes only.
285
+ # ADR-14 gap-#3 (e): for every requested class shell that isn't already declared in the target file,
286
+ # insert an empty `class Const\nend` block inside the nearest existing ancestor. Shells already covered by
287
+ # an existing declaration are silently a no-op. The `applied` accumulator does NOT grow — shells are
288
+ # structural declarations, not methods, so the action-count surface (`updated +N`) keeps reflecting method
289
+ # changes only.
344
290
  def merge_class_shells(state, shells, kinds)
345
291
  shells.each do |qualified|
346
292
  next if find_class_decl(state.decls, qualified)
@@ -371,11 +317,9 @@ module Rigor
371
317
  [[], segments]
372
318
  end
373
319
 
374
- # Pulls the indent depth (in `INDENT` units) one level
375
- # deeper than the anchor decl's own column. Pre-
376
- # existing members might be missing (an empty
377
- # `class Foo; end`) so the keyword column is the
378
- # robust signal.
320
+ # Pulls the indent depth (in `INDENT` units) one level deeper than the anchor decl's own column.
321
+ # Pre-existing members might be missing (an empty `class Foo; end`) so the keyword column is the robust
322
+ # signal.
379
323
  def anchor_decl_indent_depth(decl)
380
324
  decl_column = decl.location[:keyword].start_column
381
325
  (decl_column / INDENT.size) + 1
@@ -423,12 +367,9 @@ module Rigor
423
367
  state.decls = parse_signature(state.source) || state.decls
424
368
  end
425
369
 
426
- # Walks the parsed decl tree recursively, tracking the
427
- # enclosing module/class prefix, and returns the
428
- # declaration whose fully-qualified name matches
429
- # `qualified_name`. Recursing into modules lets us
430
- # match `Rigor::Type::Nominal` against the
431
- # `class Nominal` declaration nested inside
370
+ # Walks the parsed decl tree recursively, tracking the enclosing module/class prefix, and returns the
371
+ # declaration whose fully-qualified name matches `qualified_name`. Recursing into modules lets us match
372
+ # `Rigor::Type::Nominal` against the `class Nominal` declaration nested inside
432
373
  # `module Rigor; module Type; … end; end`.
433
374
  def find_class_decl(decls, qualified_name)
434
375
  find_class_decl_in(decls, [], qualified_name)
@@ -448,8 +389,7 @@ module Rigor
448
389
  nil
449
390
  end
450
391
 
451
- # Appends an entirely new `class Foo … end` block at the
452
- # end of the file (with a leading blank line as
392
+ # Appends an entirely new `class Foo … end` block at the end of the file (with a leading blank line as
453
393
  # separator).
454
394
  def append_new_class(source, class_name, methods, applied, superclass = nil)
455
395
  body = methods.map { |c| "#{INDENT}#{c.rbs}" }.join("\n")
@@ -481,13 +421,10 @@ module Rigor
481
421
  source
482
422
  end
483
423
 
484
- # Returns a list of `[method_name (Symbol), kind (Symbol)]`
485
- # pairs for every method-like member in the declaration.
486
- # ADR-14 slice 4 recognises `MethodDefinition`'s
487
- # `:instance` / `:singleton` kind plus the three
488
- # `attr_*` declaration kinds so a source-side
489
- # `attr_reader :name` and an RBS-side `attr_reader name: T`
490
- # are treated as the same member (i.e. user-authored).
424
+ # Returns a list of `[method_name (Symbol), kind (Symbol)]` pairs for every method-like member in the
425
+ # declaration. ADR-14 slice 4 recognises `MethodDefinition`'s `:instance` / `:singleton` kind plus the
426
+ # three `attr_*` declaration kinds so a source-side `attr_reader :name` and an RBS-side
427
+ # `attr_reader name: T` are treated as the same member (i.e. user-authored).
491
428
  def collect_member_pairs(decl)
492
429
  pairs = []
493
430
  decl.members.each { |m| collect_pairs_for_member(m, pairs) }
@@ -512,10 +449,9 @@ module Rigor
512
449
  methods.partition { |c| !existing_pairs.include?([c.method_name, c.kind]) }
513
450
  end
514
451
 
515
- # Inserts each new method line one column before the
516
- # class declaration's `end` keyword. The insertion text
517
- # carries its own leading indent + trailing newline so
518
- # the surrounding source's whitespace stays intact.
452
+ # Inserts each new method line one column before the class declaration's `end` keyword. The insertion
453
+ # text carries its own leading indent + trailing newline so the surrounding source's whitespace stays
454
+ # intact.
519
455
  def insert_into_class(source, decl, new_methods)
520
456
  return source if new_methods.empty?
521
457
 
@@ -524,34 +460,25 @@ module Rigor
524
460
  source[0...end_pos] + addition + source[end_pos..]
525
461
  end
526
462
 
527
- # Walks the class's existing method declarations; for
528
- # each replaceable candidate that matches a member
529
- # name, slices out the old declaration's source range
530
- # and substitutes the new RBS one-liner. Members that
463
+ # Walks the class's existing method declarations; for each replaceable candidate that matches a member
464
+ # name, slices out the old declaration's source range and substitutes the new RBS one-liner. Members that
531
465
  # are not `MethodDefinition`s are left alone.
532
466
  #
533
- # Two candidate classifications are eligible for
534
- # replacement under `--overwrite`:
467
+ # Two candidate classifications are eligible for replacement under `--overwrite`:
535
468
  #
536
- # 1. `TIGHTER_RETURN` — the classifier already proved the
537
- # new return type is a strict subtype of the declared
538
- # one (with lenience guards passed).
539
- # 2. `NEW_METHOD` whose new RBS strictly tightens an
540
- # `untyped` position in the existing declaration. The
541
- # canonical case is `initialize_stub_candidate`, which
542
- # bypasses the existing-RBS comparison and always
543
- # classifies as `NEW_METHOD` — when sig-gen's
544
- # `--params=observed` upgrades a `(path: untyped) -> void`
545
- # declaration to `(path: String) -> void` we want
546
- # `--overwrite` to apply it.
469
+ # 1. `TIGHTER_RETURN` — the classifier already proved the new return type is a strict subtype of the
470
+ # declared one (with lenience guards passed).
471
+ # 2. `NEW_METHOD` whose new RBS strictly tightens an `untyped` position in the existing declaration. The
472
+ # canonical case is `initialize_stub_candidate`, which bypasses the existing-RBS comparison and always
473
+ # classifies as `NEW_METHOD` when sig-gen's `--params=observed` upgrades a `(path: untyped) -> void`
474
+ # declaration to `(path: String) -> void` we want `--overwrite` to apply it.
547
475
  def replace_eligible_conflicts(source, decl, candidates)
548
476
  eligible = candidates.select { |c| eligible_for_replacement?(c, decl, source) }
549
477
  return [source, []] if eligible.empty?
550
478
 
551
479
  replaced = []
552
- # Apply replacements from highest byte position downward
553
- # so earlier byte offsets remain valid as the source
554
- # grows or shrinks.
480
+ # Apply replacements from highest byte position downward so earlier byte offsets remain valid as the
481
+ # source grows or shrinks.
555
482
  sorted = eligible.sort_by { |c| -member_position(decl, c.method_name, c.kind) }
556
483
  sorted.each do |candidate|
557
484
  source = apply_replacement(source, decl, candidate) and replaced << candidate
@@ -567,14 +494,10 @@ module Rigor
567
494
  end
568
495
  end
569
496
 
570
- # Compares the existing member's source-side RBS text
571
- # against the candidate's proposed RBS text. Returns
572
- # true when the new spelling has STRICTLY FEWER bare
573
- # `untyped` tokens than the existing one i.e. at
574
- # least one `untyped` slot becomes a concrete type AND
575
- # no concrete slot becomes `untyped`. Word-boundary
576
- # matching ensures we count `untyped` only as a type
577
- # token, not as a substring inside identifiers.
497
+ # Compares the existing member's source-side RBS text against the candidate's proposed RBS text. Returns
498
+ # true when the new spelling has STRICTLY FEWER bare `untyped` tokens than the existing one — i.e. at
499
+ # least one `untyped` slot becomes a concrete type AND no concrete slot becomes `untyped`. Word-boundary
500
+ # matching ensures we count `untyped` only as a type token, not as a substring inside identifiers.
578
501
  def tightens_untyped?(candidate, decl, source)
579
502
  member = find_method_member(decl, candidate.method_name, candidate.kind)
580
503
  return false if member.nil?
@@ -598,10 +521,8 @@ module Rigor
598
521
  end
599
522
  end
600
523
 
601
- # Splices the new RBS one-liner over the existing
602
- # declaration's byte range. `RBS::Parser`'s location
603
- # starts at the `def` keyword, NOT at the column zero of
604
- # the line, so the leading whitespace stays inside
524
+ # Splices the new RBS one-liner over the existing declaration's byte range. `RBS::Parser`'s location
525
+ # starts at the `def` keyword, NOT at the column zero of the line, so the leading whitespace stays inside
605
526
  # `source[0...start_pos]` and we do not re-emit it.
606
527
  def apply_replacement(source, decl, candidate)
607
528
  member = find_method_member(decl, candidate.method_name, candidate.kind)
data/lib/rigor/sig_gen.rb CHANGED
@@ -13,13 +13,10 @@ require_relative "sig_gen/write_result"
13
13
  require_relative "sig_gen/writer"
14
14
 
15
15
  module Rigor
16
- # Namespace for the RBS signature generator that powers
17
- # `rigor sig-gen` (ADR-14).
16
+ # Namespace for the RBS signature generator that powers `rigor sig-gen` (ADR-14).
18
17
  #
19
- # The generator emits RBS from Rigor's inference results so
20
- # users close RBS coverage gaps without freehand authorship.
21
- # See `docs/adr/14-rbs-sig-generation.md` for the design
22
- # rationale and the slicing plan.
18
+ # The generator emits RBS from Rigor's inference results so users close RBS coverage gaps without freehand
19
+ # authorship. See `docs/adr/14-rbs-sig-generation.md` for the design rationale and the slicing plan.
23
20
  module SigGen
24
21
  end
25
22
  end
@@ -1,33 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- # Classifies each configured `signature_paths:` entry by what it
5
- # actually contributes to the RBS environment, so a caller can warn
6
- # when a configured path resolves to nothing.
4
+ # Classifies each configured `signature_paths:` entry by what it actually contributes to
5
+ # the RBS environment, so a caller can warn when a configured path resolves to nothing.
7
6
  #
8
- # The failure this guards against is silent. {Environment::RbsLoader}
9
- # `add`s a `signature_paths:` entry only when `path.directory?`, and
10
- # only the `.rbs` files under it carry signatures — so a typo'd or
11
- # moved path (or a directory holding no `.rbs`) loads zero signatures
12
- # with no trace on stderr or in the run summary. The downstream symptom
13
- # is the most authoritative diagnostics: every call into the extensions
14
- # the missing RBS was meant to describe fires `call.undefined-method` at
15
- # `evidence_tier: high`. A one-character path typo can manufacture
16
- # hundreds of plausible-looking false positives; surfacing the empty
17
- # entry makes the real cause visible.
7
+ # The failure this guards against is silent. {Environment::RbsLoader} `add`s a
8
+ # `signature_paths:` entry only when `path.directory?`, and only the `.rbs` files under it
9
+ # carry signatures — so a typo'd or moved path (or a directory holding no `.rbs`) loads
10
+ # zero signatures with no trace on stderr or in the run summary. The downstream symptom is
11
+ # the most authoritative diagnostics: every call into the extensions the missing RBS was
12
+ # meant to describe fires `call.undefined-method` at `evidence_tier: high`. A one-character
13
+ # path typo can manufacture hundreds of plausible-looking false positives; surfacing the
14
+ # empty entry makes the real cause visible.
18
15
  #
19
- # The audit deliberately mirrors the loader's own acceptance test
20
- # (`path.directory?` + a recursive `**/*.rbs` glob) so a `:ok` verdict
21
- # means the loader did load from it and a warning means it did not.
16
+ # The audit deliberately mirrors the loader's own acceptance test (`path.directory?` + a
17
+ # recursive `**/*.rbs` glob) so a `:ok` verdict means the loader did load from it and a
18
+ # warning means it did not.
22
19
  module SignaturePathAudit
23
20
  # One configured `signature_paths:` entry's resolution status.
24
21
  #
25
22
  # `status` is one of:
26
23
  # - `:ok` — a directory containing at least one `.rbs`.
27
24
  # - `:missing` — the path does not exist.
28
- # - `:not_directory` — the path exists but is not a directory (the
29
- # loader only `add`s directories, so a `.rbs` file passed directly
30
- # is silently ignored).
25
+ # - `:not_directory` — the path exists but is not a directory (the loader only `add`s
26
+ # directories, so a `.rbs` file passed directly is silently ignored).
31
27
  # - `:empty` — a directory with no `.rbs` file (recursive).
32
28
  Entry = Data.define(:path, :status, :rbs_file_count) do
33
29
  def ok?
@@ -38,9 +34,9 @@ module Rigor
38
34
  !ok?
39
35
  end
40
36
 
41
- # One-line, human-facing reason. The wording matches the loader's
42
- # actual behaviour ("loaded nothing from it") rather than the
43
- # filesystem error, so the message points at the consequence.
37
+ # One-line, human-facing reason. The wording matches the loader's actual behaviour
38
+ # ("loaded nothing from it") rather than the filesystem error, so the message points
39
+ # at the consequence.
44
40
  def message
45
41
  case status
46
42
  when :missing
@@ -59,12 +55,11 @@ module Rigor
59
55
  end
60
56
  end
61
57
 
62
- # Audits each configured entry. `signature_paths` is the
63
- # {Configuration#signature_paths} array (absolute paths, already
64
- # resolved against the config file's directory). Pass `nil`the
65
- # unset default, where Rigor auto-detects `<root>/sig` to get an
66
- # empty result: an absent auto-detected `sig/` is a normal setup, not
67
- # a misconfiguration, so it is never audited.
58
+ # Audits each configured entry. `signature_paths` is the {Configuration#signature_paths}
59
+ # array (absolute paths, already resolved against the config file's directory). Pass
60
+ # `nil` the unset default, where Rigor auto-detects `<root>/sig` — to get an empty
61
+ # result: an absent auto-detected `sig/` is a normal setup, not a misconfiguration, so it
62
+ # is never audited.
68
63
  #
69
64
  # @param signature_paths [Array<String, Pathname>, nil]
70
65
  # @return [Array<Entry>]
@@ -72,8 +67,7 @@ module Rigor
72
67
  Array(signature_paths).map { |path| classify(path.to_s) }
73
68
  end
74
69
 
75
- # The subset of {audit} that resolved to nothing — the entries worth
76
- # warning about.
70
+ # The subset of {audit} that resolved to nothing — the entries worth warning about.
77
71
  #
78
72
  # @param signature_paths [Array<String, Pathname>, nil]
79
73
  # @return [Array<Entry>]
@@ -2,24 +2,20 @@
2
2
 
3
3
  module Rigor
4
4
  module Source
5
- # Flattens a Prism constant-reference node (`ConstantReadNode` /
6
- # `ConstantPathNode`) to its source-qualified `"A::B::C"` string.
5
+ # Flattens a Prism constant-reference node (`ConstantReadNode` / `ConstantPathNode`) to its
6
+ # source-qualified `"A::B::C"` string.
7
7
  #
8
- # Two nil policies for the one edge case that distinguishes the call
9
- # sites — a constant path rooted in a *dynamic* base (`expr::Bar`, where
10
- # the left side is a runtime expression rather than a constant):
8
+ # Two nil policies for the one edge case that distinguishes the call sites — a constant path rooted in a
9
+ # *dynamic* base (`expr::Bar`, where the left side is a runtime expression rather than a constant):
11
10
  #
12
- # * {.qualified_name} / {.render} are LENIENT — they drop the dynamic
13
- # segment and render the trailing constant names (`expr::Bar` => "Bar").
14
- # The scope indexer and statement evaluator feed only genuine
11
+ # * {.qualified_name} / {.render} are LENIENT — they drop the dynamic segment and render the trailing
12
+ # constant names (`expr::Bar` => "Bar"). The scope indexer and statement evaluator feed only genuine
15
13
  # class/module path nodes and want a best-effort name.
16
- # * {.qualified_name_or_nil} is STRICT — a dynamic base anywhere in the
17
- # chain yields `nil`, so a caller that statically names constants can
18
- # treat the path as opaque rather than guessing.
14
+ # * {.qualified_name_or_nil} is STRICT — a dynamic base anywhere in the chain yields `nil`, so a caller
15
+ # that statically names constants can treat the path as opaque rather than guessing.
19
16
  #
20
- # A leading `::` (absolute root, `::Foo`) renders as `"Foo"` under both
21
- # policies. A node that is neither a `ConstantReadNode` nor a
22
- # `ConstantPathNode` yields `nil` under both.
17
+ # A leading `::` (absolute root, `::Foo`) renders as `"Foo"` under both policies. A node that is neither
18
+ # a `ConstantReadNode` nor a `ConstantPathNode` yields `nil` under both.
23
19
  module ConstantPath
24
20
  module_function
25
21