rigortype 0.2.7 → 0.2.9

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 (433) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/core_overlay/csv.rbs +28 -0
  4. data/data/core_overlay/psych.rbs +22 -0
  5. data/docs/handbook/01-getting-started.md +9 -1
  6. data/docs/handbook/02-everyday-types.md +4 -1
  7. data/docs/handbook/08-understanding-errors.md +3 -3
  8. data/docs/manual/01-installation.md +1 -0
  9. data/docs/manual/02-cli-reference.md +16 -7
  10. data/docs/manual/04-diagnostics.md +7 -4
  11. data/docs/manual/06-baseline.md +5 -2
  12. data/docs/manual/07-plugins.md +1 -1
  13. data/docs/manual/14-rails-quickstart.md +4 -2
  14. data/docs/manual/15-type-protection-coverage.md +27 -4
  15. data/docs/manual/plugins/rigor-actionpack.md +1 -1
  16. data/docs/manual/plugins/rigor-activerecord.md +12 -5
  17. data/docs/manual/plugins/rigor-rails-routes.md +11 -0
  18. data/lib/rigor/analysis/baseline.rb +56 -96
  19. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  20. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  21. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  22. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  23. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  24. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  25. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  26. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  27. data/lib/rigor/analysis/check_rules.rb +174 -274
  28. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  29. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  30. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  31. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  32. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  33. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  34. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  35. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  36. data/lib/rigor/analysis/diagnostic.rb +48 -75
  37. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  38. data/lib/rigor/analysis/fact_store.rb +12 -22
  39. data/lib/rigor/analysis/incremental.rb +47 -63
  40. data/lib/rigor/analysis/incremental_session.rb +74 -100
  41. data/lib/rigor/analysis/project_scan.rb +12 -23
  42. data/lib/rigor/analysis/result.rb +3 -5
  43. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  44. data/lib/rigor/analysis/run_stats.rb +38 -61
  45. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  46. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  47. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  48. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  49. data/lib/rigor/analysis/runner.rb +220 -333
  50. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  51. data/lib/rigor/analysis/worker_session.rb +64 -102
  52. data/lib/rigor/ast/type_node.rb +6 -8
  53. data/lib/rigor/ast.rb +6 -10
  54. data/lib/rigor/bleeding_edge.rb +27 -35
  55. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  56. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  57. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  58. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  59. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  60. data/lib/rigor/cache/descriptor.rb +58 -94
  61. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  62. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  63. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  64. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  65. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  66. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  67. data/lib/rigor/cache/rbs_environment.rb +11 -19
  68. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  69. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  70. data/lib/rigor/cache/store.rb +248 -186
  71. data/lib/rigor/cli/annotate_command.rb +51 -88
  72. data/lib/rigor/cli/baseline_command.rb +10 -17
  73. data/lib/rigor/cli/check_command.rb +121 -199
  74. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  75. data/lib/rigor/cli/ci_detector.rb +9 -14
  76. data/lib/rigor/cli/command.rb +8 -14
  77. data/lib/rigor/cli/coverage_command.rb +62 -48
  78. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  79. data/lib/rigor/cli/coverage_report.rb +0 -2
  80. data/lib/rigor/cli/coverage_scan.rb +7 -11
  81. data/lib/rigor/cli/diagnostic_formats.rb +46 -62
  82. data/lib/rigor/cli/diff_command.rb +10 -20
  83. data/lib/rigor/cli/docs_command.rb +26 -40
  84. data/lib/rigor/cli/doctor_command.rb +8 -9
  85. data/lib/rigor/cli/explain_command.rb +4 -7
  86. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  87. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  88. data/lib/rigor/cli/lsp_command.rb +11 -19
  89. data/lib/rigor/cli/mcp_command.rb +4 -6
  90. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  91. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  92. data/lib/rigor/cli/options.rb +11 -19
  93. data/lib/rigor/cli/plugin_command.rb +18 -30
  94. data/lib/rigor/cli/plugins_command.rb +29 -51
  95. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  96. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  97. data/lib/rigor/cli/protection_fork_scan.rb +55 -0
  98. data/lib/rigor/cli/protection_renderer.rb +5 -7
  99. data/lib/rigor/cli/protection_report.rb +34 -18
  100. data/lib/rigor/cli/renderable.rb +4 -6
  101. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  102. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  103. data/lib/rigor/cli/skill_command.rb +30 -47
  104. data/lib/rigor/cli/skill_describe.rb +40 -64
  105. data/lib/rigor/cli/trace_command.rb +9 -16
  106. data/lib/rigor/cli/trace_renderer.rb +35 -51
  107. data/lib/rigor/cli/triage_command.rb +6 -10
  108. data/lib/rigor/cli/triage_renderer.rb +1 -2
  109. data/lib/rigor/cli/type_of_command.rb +14 -23
  110. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  111. data/lib/rigor/cli/type_scan_command.rb +6 -10
  112. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  113. data/lib/rigor/cli/type_scan_report.rb +2 -3
  114. data/lib/rigor/cli/upgrade_command.rb +2 -3
  115. data/lib/rigor/cli.rb +16 -28
  116. data/lib/rigor/config_audit.rb +35 -44
  117. data/lib/rigor/configuration/dependencies.rb +41 -70
  118. data/lib/rigor/configuration/severity_profile.rb +28 -42
  119. data/lib/rigor/configuration.rb +150 -241
  120. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  121. data/lib/rigor/environment/class_registry.rb +19 -26
  122. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  123. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  124. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  125. data/lib/rigor/environment/missing_gem_constant_index.rb +128 -0
  126. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  127. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  128. data/lib/rigor/environment/rbs_loader.rb +420 -518
  129. data/lib/rigor/environment/reflection.rb +28 -53
  130. data/lib/rigor/environment/reporters.rb +13 -24
  131. data/lib/rigor/environment.rb +225 -304
  132. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  133. data/lib/rigor/flow_contribution/element.rb +7 -12
  134. data/lib/rigor/flow_contribution/fact.rb +33 -57
  135. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  136. data/lib/rigor/flow_contribution/merger.rb +32 -47
  137. data/lib/rigor/flow_contribution.rb +37 -55
  138. data/lib/rigor/inference/acceptance.rb +133 -219
  139. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  140. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  141. data/lib/rigor/inference/budget_trace.rb +43 -63
  142. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  143. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  144. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  145. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  146. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  147. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  148. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  149. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  150. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  151. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  152. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  153. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  154. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  155. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  156. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  157. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  158. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  159. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  160. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  161. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  162. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  163. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  164. data/lib/rigor/inference/def_return_typer.rb +8 -14
  165. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  166. data/lib/rigor/inference/expression_typer.rb +794 -1154
  167. data/lib/rigor/inference/fallback.rb +8 -12
  168. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  169. data/lib/rigor/inference/flow_tracer.rb +25 -36
  170. data/lib/rigor/inference/fork_map.rb +87 -0
  171. data/lib/rigor/inference/hkt_body.rb +45 -68
  172. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  173. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  174. data/lib/rigor/inference/hkt_registry.rb +50 -76
  175. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  176. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  177. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  178. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  179. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  180. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  181. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  182. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  183. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  184. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  185. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  186. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  187. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  188. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  189. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  190. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  191. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  192. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  193. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  194. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  195. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  196. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  197. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  198. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  199. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  200. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  201. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  202. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  203. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  204. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  205. data/lib/rigor/inference/mutation_widening.rb +115 -158
  206. data/lib/rigor/inference/narrowing.rb +627 -727
  207. data/lib/rigor/inference/origin_lookup.rb +38 -0
  208. data/lib/rigor/inference/parameter_inference_collector.rb +154 -109
  209. data/lib/rigor/inference/precision_scanner.rb +26 -35
  210. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  211. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  212. data/lib/rigor/inference/protection_scanner.rb +16 -18
  213. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  214. data/lib/rigor/inference/scope_indexer.rb +473 -821
  215. data/lib/rigor/inference/statement_evaluator.rb +754 -1114
  216. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  217. data/lib/rigor/inference/synthetic_method.rb +9 -16
  218. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  219. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  220. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  221. data/lib/rigor/language_server/buffer_table.rb +11 -18
  222. data/lib/rigor/language_server/completion_provider.rb +69 -116
  223. data/lib/rigor/language_server/debouncer.rb +16 -25
  224. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  225. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  226. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  227. data/lib/rigor/language_server/hover_provider.rb +8 -11
  228. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  229. data/lib/rigor/language_server/loop.rb +14 -19
  230. data/lib/rigor/language_server/project_context.rb +46 -74
  231. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  232. data/lib/rigor/language_server/server.rb +55 -83
  233. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  234. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  235. data/lib/rigor/language_server/uri.rb +8 -13
  236. data/lib/rigor/language_server.rb +4 -6
  237. data/lib/rigor/mcp/loop.rb +2 -3
  238. data/lib/rigor/mcp/server.rb +4 -7
  239. data/lib/rigor/mcp.rb +3 -6
  240. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  241. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  242. data/lib/rigor/plugin/base.rb +335 -518
  243. data/lib/rigor/plugin/blueprint.rb +14 -23
  244. data/lib/rigor/plugin/box.rb +18 -29
  245. data/lib/rigor/plugin/fact_store.rb +16 -26
  246. data/lib/rigor/plugin/inflector.rb +37 -53
  247. data/lib/rigor/plugin/io_boundary.rb +33 -56
  248. data/lib/rigor/plugin/isolation.rb +42 -55
  249. data/lib/rigor/plugin/load_error.rb +10 -15
  250. data/lib/rigor/plugin/loader.rb +30 -49
  251. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  252. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  253. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  254. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  255. data/lib/rigor/plugin/macro.rb +10 -15
  256. data/lib/rigor/plugin/manifest.rb +85 -144
  257. data/lib/rigor/plugin/node_context.rb +14 -22
  258. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  259. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  260. data/lib/rigor/plugin/registry.rb +132 -205
  261. data/lib/rigor/plugin/services.rb +21 -33
  262. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  263. data/lib/rigor/plugin/trust_policy.rb +24 -38
  264. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  265. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  266. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  267. data/lib/rigor/protection/mutator.rb +50 -70
  268. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  269. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  270. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  271. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  272. data/lib/rigor/rbs_extended.rb +107 -197
  273. data/lib/rigor/reflection.rb +68 -86
  274. data/lib/rigor/scope/discovery_index.rb +14 -19
  275. data/lib/rigor/scope.rb +255 -310
  276. data/lib/rigor/sig_gen/classification.rb +6 -10
  277. data/lib/rigor/sig_gen/generator.rb +197 -323
  278. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  279. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  280. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  281. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  282. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  283. data/lib/rigor/sig_gen/renderer.rb +7 -13
  284. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  285. data/lib/rigor/sig_gen/write_result.rb +8 -16
  286. data/lib/rigor/sig_gen/writer.rb +95 -174
  287. data/lib/rigor/sig_gen.rb +3 -6
  288. data/lib/rigor/signature_path_audit.rb +24 -30
  289. data/lib/rigor/source/constant_path.rb +10 -14
  290. data/lib/rigor/source/literals.rb +31 -45
  291. data/lib/rigor/source/node_locator.rb +9 -11
  292. data/lib/rigor/source/node_walker.rb +9 -13
  293. data/lib/rigor/source.rb +3 -4
  294. data/lib/rigor/testing.rb +16 -20
  295. data/lib/rigor/triage/catalogue.rb +38 -62
  296. data/lib/rigor/triage.rb +31 -52
  297. data/lib/rigor/trinary.rb +9 -13
  298. data/lib/rigor/type/acceptance_router.rb +4 -6
  299. data/lib/rigor/type/accepts_result.rb +10 -14
  300. data/lib/rigor/type/app.rb +19 -27
  301. data/lib/rigor/type/bot.rb +4 -6
  302. data/lib/rigor/type/bound_method.rb +10 -15
  303. data/lib/rigor/type/combinator.rb +165 -257
  304. data/lib/rigor/type/constant.rb +23 -34
  305. data/lib/rigor/type/data_class.rb +10 -15
  306. data/lib/rigor/type/data_instance.rb +14 -20
  307. data/lib/rigor/type/difference.rb +21 -32
  308. data/lib/rigor/type/dynamic.rb +3 -5
  309. data/lib/rigor/type/hash_shape.rb +32 -18
  310. data/lib/rigor/type/integer_range.rb +11 -16
  311. data/lib/rigor/type/intersection.rb +27 -42
  312. data/lib/rigor/type/nominal.rb +10 -15
  313. data/lib/rigor/type/plain_lattice.rb +9 -13
  314. data/lib/rigor/type/refined.rb +67 -114
  315. data/lib/rigor/type/singleton.rb +4 -6
  316. data/lib/rigor/type/struct_class.rb +11 -16
  317. data/lib/rigor/type/struct_instance.rb +15 -21
  318. data/lib/rigor/type/tuple.rb +14 -19
  319. data/lib/rigor/type/union.rb +30 -42
  320. data/lib/rigor/type_node/generic.rb +14 -26
  321. data/lib/rigor/type_node/identifier.rb +12 -19
  322. data/lib/rigor/type_node.rb +3 -12
  323. data/lib/rigor/value_semantics.rb +16 -21
  324. data/lib/rigor/version.rb +1 -1
  325. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  326. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  327. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  329. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  330. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  331. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  332. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  333. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +141 -184
  334. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  335. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +92 -108
  337. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  338. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  339. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  340. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  341. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +40 -48
  342. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +140 -129
  343. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +56 -59
  344. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  345. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  346. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +172 -0
  347. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +168 -241
  348. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  349. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  350. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  351. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  352. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  353. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  354. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  355. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  356. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
  357. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  358. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  359. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  361. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  363. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  365. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  366. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  367. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  368. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  369. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  372. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  373. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  376. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  377. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  378. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  379. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  380. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  381. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  385. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  386. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  389. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  390. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +189 -0
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +61 -75
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +319 -505
  398. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +91 -96
  399. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  400. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  406. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  408. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  409. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  411. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  416. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  417. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  426. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  427. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  428. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  429. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  430. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  431. data/sig/rigor/environment.rbs +1 -0
  432. data/sig/rigor/scope.rbs +6 -0
  433. metadata +9 -1
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Rigor
6
+ class Environment
7
+ # ADR-82 WD9 — maps a top-level constant name to the locked, RBS-less gem that declares it, so an
8
+ # unresolved constant read (`Faraday`, `Sidekiq`) can carry the `external_gem_without_rbs` provenance
9
+ # cause instead of the generic `unsupported_syntax`. For a gem with no RBS the constant read is where the
10
+ # class name is last visible — by dispatch time the receiver is already `Dynamic[top]` — so this index is
11
+ # what lets `coverage --protection` route those holes to `add_rbs` honestly.
12
+ #
13
+ # Ownership is established by READING, never by guessing: each gem's conventional entry file
14
+ # (`lib/<name>.rb`, with the dash → directory variant) is parsed with Prism and its top-level
15
+ # class / module / constant declarations recorded under their root name. No gem code runs — the same
16
+ # posture as ADR-72's "loads RBS data only" and the ADR-10 walker. A name-derivation heuristic
17
+ # (`faraday` → `Faraday` by camelizing) is deliberately NOT used: it breaks on irregular names
18
+ # (`activesupport` → `ActiveSupport`) and is exactly the guessing the ADR-82 honesty criterion forbids.
19
+ #
20
+ # Everything fails OPEN to "no owner": a gem that is not installed, an entry file that does not exist or
21
+ # does not parse, a root constant declared only in a deeper file. An unindexed constant keeps today's
22
+ # generic cause — the failure mode is a missing label, never a wrong one.
23
+ #
24
+ # The scan is bounded to entry files (one or two per gem) so building the index over a real app's
25
+ # hundreds of RBS-less gems stays sub-second; it is built lazily on the first unresolved constant, so a
26
+ # project whose constants all resolve never pays it.
27
+ #
28
+ # **Gem-directory resolution.** Rigor runs under its OWN bundle (`BUNDLE_GEMFILE=<rigor>/Gemfile`), so
29
+ # `Gem::Specification.find_by_name` sees rigor's gems, not the target project's — it would resolve only
30
+ # the handful of gems both bundles happen to share (i18n, rack) and miss every project-specific gem
31
+ # (the very ones a Rails app's holes root at). So the primary resolver is the **target's bundle install
32
+ # tree** (`<bundle>/ruby/*/gems/<name>-<version>/`, the same pure-filesystem layout
33
+ # {BundleSigDiscovery} walks — no `Bundler` API, no gem code). `Gem::Specification` remains a last-resort
34
+ # fallback for a project installed against system gems with no discoverable bundle; reading a gem's own
35
+ # entry file for its own top-level namespace constant is version-stable, so even a rigor-vs-target
36
+ # version skew on a shared gem yields the same constant name.
37
+ module MissingGemConstantIndex
38
+ module_function
39
+
40
+ # @param gems [Enumerable<Array(String, String)>] `[gem_name, version]` pairs (the `:missing` rows of
41
+ # {RbsCoverageReport}).
42
+ # @param bundle_path [String, Pathname, nil] the target's resolved bundler install root, or nil.
43
+ # @param spec_resolver [#call] `(name, version) -> String?` fallback dir resolver. Injectable for
44
+ # specs; the default is the RubyGems-metadata lookup (no code load).
45
+ # @return [Hash{String => String}] frozen `root constant name => gem name`. On a collision (two gems
46
+ # declaring the same top-level constant) the first gem wins — the CAUSE recorded downstream
47
+ # (external gem without RBS) is true under either owner.
48
+ def build(gems, bundle_path: nil, spec_resolver: method(:installed_gem_dir))
49
+ bundle_dirs = bundle_gem_dirs(bundle_path)
50
+ index = {}
51
+ gems.each do |gem_name, version|
52
+ dir = bundle_dirs["#{gem_name}-#{version}"] || spec_resolver.call(gem_name, version)
53
+ next unless dir
54
+
55
+ entry_files(dir, gem_name).each do |file|
56
+ top_level_root_constants(file).each { |name| index[name] ||= gem_name }
57
+ end
58
+ end
59
+ index.freeze
60
+ end
61
+
62
+ # `{"<name>-<version>" => gem_dir}` for the target's bundle, or `{}` when no bundle is resolvable. The
63
+ # glob mirrors {BundleSigDiscovery}: `<bundle>/ruby/X.Y.Z/gems/<name>-<version>/`. Keyed on the dir
64
+ # basename so a platform-tagged variant (`ffi-1.17.4-aarch64-linux-gnu`) simply doesn't match a
65
+ # `<name>-<version>` lookup — those gems ship native code, not the pure-Ruby constants this indexes.
66
+ def bundle_gem_dirs(bundle_path)
67
+ return {} if bundle_path.nil?
68
+
69
+ base = Pathname.new(bundle_path)
70
+ return {} unless base.directory?
71
+
72
+ Dir.glob(base.join("ruby", "*", "gems", "*")).each_with_object({}) do |dir, acc|
73
+ acc[File.basename(dir)] ||= dir
74
+ end
75
+ end
76
+
77
+ # RubyGems spec metadata lookup — the gem's on-disk source root, without loading any of its code.
78
+ # Exact-version first, any-version fallback. See the class note on why this is only a fallback.
79
+ def installed_gem_dir(name, version)
80
+ spec = begin
81
+ Gem::Specification.find_by_name(name, "= #{version}")
82
+ rescue Gem::LoadError
83
+ begin
84
+ Gem::Specification.find_by_name(name)
85
+ rescue Gem::LoadError
86
+ nil
87
+ end
88
+ end
89
+ spec&.full_gem_path
90
+ end
91
+
92
+ # The conventional require targets for a gem name: `lib/foo.rb`, and `lib/foo/bar.rb` for a dashed
93
+ # `foo-bar`. This is the require-name convention Bundler.require depends on — a filename convention,
94
+ # not a constant-name guess; the constants come from parsing whichever of these exists.
95
+ def entry_files(dir, gem_name)
96
+ candidates = ["lib/#{gem_name}.rb"]
97
+ candidates << "lib/#{gem_name.tr('-', '/')}.rb" if gem_name.include?("-")
98
+ candidates.filter_map do |relative|
99
+ path = File.join(dir, relative)
100
+ path if File.file?(path)
101
+ end
102
+ end
103
+
104
+ # Root names of the file's top-level declarations. Only direct children of the program are read — the
105
+ # root constant of a gem's namespace is declared at file top level by construction (`module Faraday`,
106
+ # `class Money::Error < …` roots at `Money`), so no recursion into bodies is needed.
107
+ def top_level_root_constants(path)
108
+ result = Prism.parse(File.read(path))
109
+ return [] unless result.errors.empty?
110
+
111
+ result.value.statements.body.filter_map do |node|
112
+ case node
113
+ when Prism::ClassNode, Prism::ModuleNode then root_segment(node.constant_path)
114
+ when Prism::ConstantWriteNode then node.name.to_s
115
+ end
116
+ end
117
+ rescue StandardError
118
+ []
119
+ end
120
+
121
+ def root_segment(node)
122
+ current = node
123
+ current = current.parent while current.is_a?(Prism::ConstantPathNode) && current.parent
124
+ current.respond_to?(:name) && current.name ? current.name.to_s : nil
125
+ end
126
+ end
127
+ end
128
+ end
@@ -6,74 +6,51 @@ module Rigor
6
6
  class Environment
7
7
  # `rbs collection install` awareness (O4 Layer 3 slice 2, implemented).
8
8
  #
9
- # When the target project has been set up with `rbs
10
- # collection install` (the standard RBS-ecosystem flow for
11
- # pulling community RBS from
12
- # https://github.com/ruby/gem_rbs_collection), a
13
- # `rbs_collection.lock.yaml` records the resolved (gem,
14
- # version, source) triples and `.gem_rbs_collection/<name>/
15
- # <version>/` carries the actual `.rbs` files. This module
16
- # parses the lockfile and returns the per-gem RBS directory
17
- # paths so they can be appended to `RbsLoader`'s
9
+ # When the target project has been set up with `rbs collection install` (the standard RBS-ecosystem flow
10
+ # for pulling community RBS from https://github.com/ruby/gem_rbs_collection), a
11
+ # `rbs_collection.lock.yaml` records the resolved (gem, version, source) triples and
12
+ # `.gem_rbs_collection/<name>/ <version>/` carries the actual `.rbs` files. This module parses the
13
+ # lockfile and returns the per-gem RBS directory paths so they can be appended to `RbsLoader`'s
18
14
  # `signature_paths:`.
19
15
  #
20
- # The discovery is intentionally a pure file-system + YAML
21
- # walk no Bundler API call, no network access. Failure
22
- # modes (missing lockfile, malformed YAML, missing
23
- # collection directory) silently degrade to an empty list.
16
+ # The discovery is intentionally a pure file-system + YAML walk — no Bundler API call, no network
17
+ # access. Failure modes (missing lockfile, malformed YAML, missing collection directory) silently
18
+ # degrade to an empty list.
24
19
  module RbsCollectionDiscovery
25
- # `stdlib`-typed entries in the lockfile are loaded into
26
- # the RBS environment by the standard library mechanism
27
- # (rigor's `Environment::DEFAULT_LIBRARIES` already covers
28
- # this surface). Including them as `signature_paths:`
29
- # entries would risk `RBS::DuplicatedDeclarationError`
30
- # (the same hazard O7's failure-memo handles). The other
31
- # documented source types — `git` (the gem_rbs_collection
32
- # repo), `rubygems` (sigs lifted from a gem's bundled
33
- # `sig/`), and `local` (a user-managed RBS dir) — all
34
- # produce a directory under the collection root and are
35
- # admitted.
20
+ # `stdlib`-typed entries in the lockfile are loaded into the RBS environment by the standard library
21
+ # mechanism (rigor's `Environment::DEFAULT_LIBRARIES` already covers this surface). Including them as
22
+ # `signature_paths:` entries would risk `RBS::DuplicatedDeclarationError` (the same hazard O7's
23
+ # failure-memo handles). The other documented source types — `git` (the gem_rbs_collection repo),
24
+ # `rubygems` (sigs lifted from a gem's bundled `sig/`), and `local` (a user-managed RBS dir) — all
25
+ # produce a directory under the collection root and are admitted.
36
26
  #
37
- # The `source.type` filter alone is NOT sufficient: gems
38
- # that were extracted from Ruby's stdlib into standalone
39
- # default gems (e.g. `cgi`, `logger`, `base64`, `csv`,
40
- # `bigdecimal`) are published in `ruby/gem_rbs_collection`
41
- # under a `git` source type, yet rigor ALSO loads them
42
- # from its bundled stdlib via `DEFAULT_LIBRARIES`. Loading
43
- # both copies triggers the very
44
- # `RBS::DuplicatedDeclarationError` this module exists to
45
- # avoid (observed on a Rails 8 app: `.gem_rbs_collection/
46
- # cgi/0.5/` vs the bundled `stdlib/cgi`). The
47
- # `skip_gem_names:` parameter lets the caller pass the set
48
- # of library names rigor already loads so those gems are
49
- # dropped regardless of `source.type`.
27
+ # The `source.type` filter alone is NOT sufficient: gems that were extracted from Ruby's stdlib into
28
+ # standalone default gems (e.g. `cgi`, `logger`, `base64`, `csv`, `bigdecimal`) are published in
29
+ # `ruby/gem_rbs_collection` under a `git` source type, yet rigor ALSO loads them from its bundled
30
+ # stdlib via `DEFAULT_LIBRARIES`. Loading both copies triggers the very
31
+ # `RBS::DuplicatedDeclarationError` this module exists to avoid (observed on a Rails 8 app:
32
+ # `.gem_rbs_collection/ cgi/0.5/` vs the bundled `stdlib/cgi`). The `skip_gem_names:` parameter lets
33
+ # the caller pass the set of library names rigor already loads so those gems are dropped regardless of
34
+ # `source.type`.
50
35
  SKIPPED_SOURCE_TYPES = Set["stdlib"].freeze
51
36
 
52
37
  DEFAULT_COLLECTION_PATH = ".gem_rbs_collection"
53
38
  private_constant :DEFAULT_COLLECTION_PATH
54
39
 
55
- # @param lockfile_path [String, Pathname, nil] explicit
56
- # path to `rbs_collection.lock.yaml`. When `nil`, falls
57
- # back to `auto_detect` if `auto_detect:` is true.
58
- # @param project_root [String] resolution base for
59
- # relative `lockfile_path:` and the auto-detect search.
60
- # @param auto_detect [Boolean] when true and
61
- # `lockfile_path:` is nil, look for
40
+ # @param lockfile_path [String, Pathname, nil] explicit path to `rbs_collection.lock.yaml`. When `nil`,
41
+ # falls back to `auto_detect` if `auto_detect:` is true.
42
+ # @param project_root [String] resolution base for relative `lockfile_path:` and the auto-detect
43
+ # search.
44
+ # @param auto_detect [Boolean] when true and `lockfile_path:` is nil, look for
62
45
  # `<project_root>/rbs_collection.lock.yaml`.
63
- # @param skip_gem_names [Array<String>, Set<String>] gem
64
- # names rigor already loads from its bundled stdlib (the
65
- # merged `DEFAULT_LIBRARIES + libraries:` set). Entries
66
- # whose `name` is in this set are dropped regardless of
67
- # `source.type` to avoid `RBS::DuplicatedDeclarationError`
68
- # on stdlib-extracted default gems. Defaults to empty.
69
- # @return [Array<Pathname>] every
70
- # `<collection_path>/<gem-name>/<gem-version>/`
71
- # directory listed in the lockfile whose entry has a
72
- # non-skipped source type, whose `name` is not in
73
- # `skip_gem_names:`, and whose directory exists on disk.
74
- # Returns `[]` when no lockfile is resolvable, when the
75
- # YAML is unreadable, or when the collection path
76
- # doesn't exist.
46
+ # @param skip_gem_names [Array<String>, Set<String>] gem names rigor already loads from its bundled
47
+ # stdlib (the merged `DEFAULT_LIBRARIES + libraries:` set). Entries whose `name` is in this set are
48
+ # dropped regardless of `source.type` to avoid `RBS::DuplicatedDeclarationError` on
49
+ # stdlib-extracted default gems. Defaults to empty.
50
+ # @return [Array<Pathname>] every `<collection_path>/<gem-name>/<gem-version>/` directory listed in the
51
+ # lockfile whose entry has a non-skipped source type, whose `name` is not in `skip_gem_names:`, and
52
+ # whose directory exists on disk. Returns `[]` when no lockfile is resolvable, when the YAML is
53
+ # unreadable, or when the collection path doesn't exist.
77
54
  def self.discover(lockfile_path:, project_root: Dir.pwd, auto_detect: true, skip_gem_names: [])
78
55
  resolved = resolve_lockfile_path(
79
56
  lockfile_path: lockfile_path,
@@ -91,9 +68,8 @@ module Rigor
91
68
  gem_paths_from(collection_root, data, skip_gem_names.to_set)
92
69
  end
93
70
 
94
- # Returns the resolved lockfile path (`Pathname`) or `nil`
95
- # when neither explicit nor auto-detect produces one.
96
- # Public so the stats banner can surface what rigor found.
71
+ # Returns the resolved lockfile path (`Pathname`) or `nil` when neither explicit nor auto-detect
72
+ # produces one. Public so the stats banner can surface what rigor found.
97
73
  def self.resolve_lockfile_path(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
98
74
  if lockfile_path
99
75
  path = Pathname.new(File.expand_path(lockfile_path.to_s, project_root))
@@ -119,8 +95,8 @@ module Rigor
119
95
  def self.resolve_collection_root(lockfile_pathname, data)
120
96
  rel = data["path"]
121
97
  rel = DEFAULT_COLLECTION_PATH if rel.nil? || rel.to_s.empty?
122
- # `path:` is documented as relative to the directory
123
- # holding the lockfile (RBS::Collection::Config::Lockfile#fullpath).
98
+ # `path:` is documented as relative to the directory holding the lockfile
99
+ # (RBS::Collection::Config::Lockfile#fullpath).
124
100
  lockfile_pathname.parent + Pathname.new(rel.to_s)
125
101
  end
126
102
  private_class_method :resolve_collection_root
@@ -2,29 +2,21 @@
2
2
 
3
3
  module Rigor
4
4
  class Environment
5
- # Open item O4 Layer 3 slice 3 — graceful-degradation
6
- # coverage report.
5
+ # Open item O4 Layer 3 slice 3 — graceful-degradation coverage report.
7
6
  #
8
- # When the user has a `Gemfile.lock` (via slice 1) and rigor
9
- # has resolved its target-project RBS sources (DEFAULT_LIBRARIES,
10
- # `data/vendored_gem_sigs/`, slice-1 bundle-shipped `sig/`,
11
- # slice-2 `rbs_collection.lock.yaml` paths), this module
12
- # classifies each locked gem by RBS provenance and surfaces
13
- # the "no RBS available" set so the run-start diagnostic in
14
- # {Rigor::Analysis::Runner} can suggest `rbs collection
15
- # install` or `dependencies.source_inference:` for the
16
- # uncovered gems.
7
+ # When the user has a `Gemfile.lock` (via slice 1) and rigor has resolved its target-project RBS sources
8
+ # (DEFAULT_LIBRARIES, `data/vendored_gem_sigs/`, slice-1 bundle-shipped `sig/`, slice-2
9
+ # `rbs_collection.lock.yaml` paths), this module classifies each locked gem by RBS provenance and
10
+ # surfaces the "no RBS available" set so the run-start diagnostic in {Rigor::Analysis::Runner} can
11
+ # suggest `rbs collection install` or `dependencies.source_inference:` for the uncovered gems.
17
12
  #
18
- # The classification is a pure function over the inputs
19
- # (`locked_gems`, two arrays of resolved sig paths). It does
20
- # NOT touch the filesystem on its own — the caller passes in
21
- # what discovery returned.
13
+ # The classification is a pure function over the inputs (`locked_gems`, two arrays of resolved sig
14
+ # paths). It does NOT touch the filesystem on its own — the caller passes in what discovery returned.
22
15
  module RbsCoverageReport
23
16
  # Frozen result row.
24
17
  #
25
- # `source` is a Symbol naming where RBS for this gem
26
- # resolves; `:missing` means none of the four resolution
27
- # paths covered it.
18
+ # `source` is a Symbol naming where RBS for this gem resolves; `:missing` means none of the four
19
+ # resolution paths covered it.
28
20
  Coverage = Data.define(:gem_name, :version, :source) do
29
21
  def initialize(gem_name:, version:, source:)
30
22
  super(
@@ -35,32 +27,24 @@ module Rigor
35
27
  end
36
28
  end
37
29
 
38
- # Names of gems whose RBS ships under
39
- # `data/vendored_gem_sigs/`. Kept in sync with the
40
- # vendored-stubs directory listing; when a new gem is
41
- # vendored, add its name here too. (The set is small
42
- # enough that hard-coding is acceptable; a directory walk
43
- # at every call would add stat-cost to no benefit.)
30
+ # Names of gems whose RBS ships under `data/vendored_gem_sigs/`. Kept in sync with the vendored-stubs
31
+ # directory listing; when a new gem is vendored, add its name here too. (The set is small enough that
32
+ # hard-coding is acceptable; a directory walk at every call would add stat-cost to no benefit.)
44
33
  VENDORED_GEM_NAMES = Set[
45
34
  "ast", "bcrypt", "bundler", "cgi", "did_you_mean",
46
35
  "idn-ruby", "mysql2", "nokogiri", "pg", "prism", "redis", "rubygems"
47
36
  ].freeze
48
37
 
49
- # @param locked_gems [Hash{String => LockfileResolver::LockedGem}]
50
- # The lockfile-resolved gem set. Empty hash → no
51
- # coverage analysis to do.
52
- # @param default_libraries [Array<String>] gem names rigor
53
- # auto-loads through `RBS::EnvironmentLoader#add(library:)`.
54
- # Pass `Rigor::Environment::DEFAULT_LIBRARIES` from callers
38
+ # @param locked_gems [Hash{String => LockfileResolver::LockedGem}] The lockfile-resolved gem set. Empty
39
+ # hash no coverage analysis to do.
40
+ # @param default_libraries [Array<String>] gem names rigor auto-loads through
41
+ # `RBS::EnvironmentLoader#add(library:)`. Pass `Rigor::Environment::DEFAULT_LIBRARIES` from callers
55
42
  # running in a project context.
56
- # @param bundle_sig_paths [Array<Pathname, String>] the
57
- # discovered `<bundle>/.../gems/<name>-<ver>/sig` paths
58
- # from {BundleSigDiscovery.discover}.
59
- # @param rbs_collection_paths [Array<Pathname, String>] the
60
- # discovered `<collection>/<name>/<version>/` paths from
61
- # {RbsCollectionDiscovery.discover}.
62
- # @return [Array<Coverage>] one row per locked gem; sorted
63
- # by gem name for deterministic output.
43
+ # @param bundle_sig_paths [Array<Pathname, String>] the discovered `<bundle>/.../gems/<name>-<ver>/sig`
44
+ # paths from {BundleSigDiscovery.discover}.
45
+ # @param rbs_collection_paths [Array<Pathname, String>] the discovered `<collection>/<name>/<version>/`
46
+ # paths from {RbsCollectionDiscovery.discover}.
47
+ # @return [Array<Coverage>] one row per locked gem; sorted by gem name for deterministic output.
64
48
  def self.classify(locked_gems:, default_libraries:,
65
49
  bundle_sig_paths:, rbs_collection_paths:)
66
50
  default_set = default_libraries.to_set
@@ -84,8 +68,7 @@ module Rigor
84
68
  end.sort_by(&:gem_name)
85
69
  end
86
70
 
87
- # Convenience accessor for the run-start diagnostic.
88
- # Filters {classify} down to `:missing` rows.
71
+ # Convenience accessor for the run-start diagnostic. Filters {classify} down to `:missing` rows.
89
72
  def self.missing(coverage_rows)
90
73
  coverage_rows.select { |row| row.source == :missing }
91
74
  end
@@ -99,8 +82,7 @@ module Rigor
99
82
  private_class_method :extract_gem_names_from_bundle_paths
100
83
 
101
84
  def self.extract_gem_names_from_collection_paths(paths)
102
- # `RbsCollectionDiscovery.discover` returns
103
- # `<collection_root>/<name>/<version>/` so the parent
85
+ # `RbsCollectionDiscovery.discover` returns `<collection_root>/<name>/<version>/` so the parent
104
86
  # basename is the gem name.
105
87
  paths.each_with_object(Set.new) do |path, set|
106
88
  pathname = path.is_a?(Pathname) ? path : Pathname.new(path)