rigortype 0.3.7 → 0.3.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 (450) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +96 -15
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +39 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +12 -1
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +69 -13
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +100 -29
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
  78. data/lib/rigor/analysis/runner.rb +200 -43
  79. data/lib/rigor/analysis/worker_session.rb +36 -12
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +166 -30
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +70 -0
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
  97. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  98. data/lib/rigor/cache/store.rb +85 -33
  99. data/lib/rigor/cli/annotate_command.rb +5 -6
  100. data/lib/rigor/cli/check_command.rb +57 -10
  101. data/lib/rigor/cli/check_invocation.rb +6 -11
  102. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  103. data/lib/rigor/cli/coverage_command.rb +1 -1
  104. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  105. data/lib/rigor/cli/coverage_scan.rb +1 -5
  106. data/lib/rigor/cli/diff_command.rb +1 -1
  107. data/lib/rigor/cli/doc_links.rb +3 -3
  108. data/lib/rigor/cli/docs_command.rb +2 -2
  109. data/lib/rigor/cli/doctor_command.rb +85 -38
  110. data/lib/rigor/cli/effects_command.rb +2 -2
  111. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  112. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  113. data/lib/rigor/cli/explain_command.rb +34 -1
  114. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  115. data/lib/rigor/cli/lsp_command.rb +1 -1
  116. data/lib/rigor/cli/mcp_command.rb +1 -1
  117. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  118. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  119. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  120. data/lib/rigor/cli/plugin_command.rb +1 -1
  121. data/lib/rigor/cli/plugins_command.rb +2 -1
  122. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  123. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  124. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  125. data/lib/rigor/cli/sig_gen_command.rb +31 -2
  126. data/lib/rigor/cli/skill_command.rb +1 -1
  127. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  128. data/lib/rigor/cli/skill_describe.rb +36 -35
  129. data/lib/rigor/cli/trace_command.rb +4 -4
  130. data/lib/rigor/cli/trace_renderer.rb +5 -6
  131. data/lib/rigor/cli/triage_command.rb +1 -1
  132. data/lib/rigor/cli/type_of_command.rb +10 -6
  133. data/lib/rigor/cli/type_scan_command.rb +4 -4
  134. data/lib/rigor/cli/unused_command.rb +11 -3
  135. data/lib/rigor/cli/upgrade_command.rb +1 -1
  136. data/lib/rigor/cli.rb +68 -6
  137. data/lib/rigor/config_audit.rb +30 -5
  138. data/lib/rigor/configuration/severity_profile.rb +6 -6
  139. data/lib/rigor/configuration.rb +43 -4
  140. data/lib/rigor/effects/attribution.rb +1 -3
  141. data/lib/rigor/effects/config_envelopes.rb +6 -8
  142. data/lib/rigor/effects/definition_lines.rb +25 -6
  143. data/lib/rigor/effects/effect_table.rb +0 -1
  144. data/lib/rigor/effects/entry_points.rb +0 -2
  145. data/lib/rigor/effects/envelope_check.rb +8 -8
  146. data/lib/rigor/effects/envelope_index.rb +5 -8
  147. data/lib/rigor/effects/framework_units.rb +4 -6
  148. data/lib/rigor/effects/identity.rb +4 -6
  149. data/lib/rigor/effects/inline_anchor.rb +7 -7
  150. data/lib/rigor/effects/label_intent.rb +3 -4
  151. data/lib/rigor/effects/liskov_check.rb +8 -8
  152. data/lib/rigor/effects/method_key.rb +1 -1
  153. data/lib/rigor/effects/plugin_facts.rb +4 -6
  154. data/lib/rigor/effects/propagator.rb +2 -3
  155. data/lib/rigor/effects/scanner.rb +1 -1
  156. data/lib/rigor/effects/signature_sources.rb +3 -5
  157. data/lib/rigor/effects/snapshot.rb +10 -10
  158. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  159. data/lib/rigor/effects/unit_scan.rb +10 -10
  160. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  161. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  162. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  163. data/lib/rigor/environment/class_registry.rb +3 -1
  164. data/lib/rigor/environment/failure_slot.rb +28 -0
  165. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  166. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  167. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  168. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  169. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  170. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  171. data/lib/rigor/environment/rbs_loader.rb +613 -113
  172. data/lib/rigor/environment.rb +189 -27
  173. data/lib/rigor/flow_contribution/merger.rb +0 -2
  174. data/lib/rigor/flow_contribution.rb +11 -13
  175. data/lib/rigor/inference/acceptance.rb +153 -8
  176. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  177. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  178. data/lib/rigor/inference/budget_trace.rb +1 -5
  179. data/lib/rigor/inference/captured_locals.rb +2 -3
  180. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  181. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  182. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  183. data/lib/rigor/inference/element_read_widening.rb +168 -0
  184. data/lib/rigor/inference/expression_typer.rb +763 -143
  185. data/lib/rigor/inference/fork_map.rb +5 -7
  186. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  187. data/lib/rigor/inference/hkt_registry.rb +19 -10
  188. data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
  189. data/lib/rigor/inference/index_write_widening.rb +1 -5
  190. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  191. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  192. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  194. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  195. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  197. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  199. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  200. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  201. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  202. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  203. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  204. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  205. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  206. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  209. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  210. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  211. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  212. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  213. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  217. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  219. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  220. data/lib/rigor/inference/mutation_widening.rb +100 -51
  221. data/lib/rigor/inference/narrowing.rb +299 -46
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  223. data/lib/rigor/inference/origin_lookup.rb +2 -3
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  226. data/lib/rigor/inference/precision_scanner.rb +3 -4
  227. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  228. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  229. data/lib/rigor/inference/protection_scanner.rb +1 -2
  230. data/lib/rigor/inference/range_constant.rb +57 -0
  231. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  232. data/lib/rigor/inference/receiver_alias.rb +3 -3
  233. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  234. data/lib/rigor/inference/scope_indexer.rb +688 -151
  235. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  236. data/lib/rigor/inference/string_mutation.rb +60 -0
  237. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  238. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  239. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  240. data/lib/rigor/inference/version_guard.rb +28 -21
  241. data/lib/rigor/inference/void_origin.rb +3 -3
  242. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  243. data/lib/rigor/language_server/buffer_table.rb +4 -4
  244. data/lib/rigor/language_server/completion_provider.rb +1 -1
  245. data/lib/rigor/language_server/debouncer.rb +1 -1
  246. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  247. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  248. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  249. data/lib/rigor/language_server/hover_provider.rb +1 -1
  250. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  251. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  252. data/lib/rigor/language_server/project_context.rb +2 -5
  253. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  254. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  255. data/lib/rigor/language_server/server.rb +9 -9
  256. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  257. data/lib/rigor/language_server/uri.rb +1 -1
  258. data/lib/rigor/plugin/base.rb +90 -21
  259. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  260. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  261. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  262. data/lib/rigor/plugin/fact_store.rb +6 -6
  263. data/lib/rigor/plugin/io_boundary.rb +93 -8
  264. data/lib/rigor/plugin/isolation.rb +29 -6
  265. data/lib/rigor/plugin/loader.rb +31 -12
  266. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  267. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  268. data/lib/rigor/plugin/manifest.rb +23 -1
  269. data/lib/rigor/plugin/registry.rb +67 -14
  270. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  271. data/lib/rigor/plugin/trust_policy.rb +2 -4
  272. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  273. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  274. data/lib/rigor/project_environment.rb +138 -0
  275. data/lib/rigor/protection/analysis_guard.rb +93 -14
  276. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  277. data/lib/rigor/protection/dependency_closure.rb +5 -8
  278. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  279. data/lib/rigor/protection/discovery_seed.rb +11 -11
  280. data/lib/rigor/protection/kill_signature.rb +2 -4
  281. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  282. data/lib/rigor/protection/mutation_cache.rb +6 -9
  283. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  284. data/lib/rigor/protection/mutator.rb +2 -1
  285. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  286. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  287. data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
  288. data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
  289. data/lib/rigor/rbs_extended/reporter.rb +122 -16
  290. data/lib/rigor/rbs_extended.rb +67 -22
  291. data/lib/rigor/reflection/constant_path.rb +126 -0
  292. data/lib/rigor/reflection.rb +26 -10
  293. data/lib/rigor/runtime/jit.rb +6 -8
  294. data/lib/rigor/scope/discovery_index.rb +30 -0
  295. data/lib/rigor/scope.rb +203 -4
  296. data/lib/rigor/sig_gen/generator.rb +181 -102
  297. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  298. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  299. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  300. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  301. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  302. data/lib/rigor/sig_gen/renderer.rb +17 -11
  303. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  304. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  305. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  306. data/lib/rigor/sig_gen/writer.rb +23 -6
  307. data/lib/rigor/signature_path_audit.rb +153 -6
  308. data/lib/rigor/source/literals.rb +0 -23
  309. data/lib/rigor/source/node_children.rb +0 -2
  310. data/lib/rigor/source/node_locator.rb +5 -11
  311. data/lib/rigor/source/node_walker.rb +2 -5
  312. data/lib/rigor/triage/catalogue.rb +1 -3
  313. data/lib/rigor/triage.rb +3 -5
  314. data/lib/rigor/type/accepts_result.rb +24 -6
  315. data/lib/rigor/type/combinator.rb +68 -5
  316. data/lib/rigor/type/data_class.rb +2 -2
  317. data/lib/rigor/type/data_instance.rb +4 -4
  318. data/lib/rigor/type/float_range.rb +128 -0
  319. data/lib/rigor/type/hash_shape.rb +5 -5
  320. data/lib/rigor/type/integer_range.rb +13 -8
  321. data/lib/rigor/type/nominal.rb +8 -2
  322. data/lib/rigor/type/refined.rb +3 -3
  323. data/lib/rigor/type/struct_class.rb +3 -3
  324. data/lib/rigor/type/struct_instance.rb +4 -4
  325. data/lib/rigor/type.rb +1 -0
  326. data/lib/rigor/type_node/generic.rb +1 -1
  327. data/lib/rigor/type_node/range_literal.rb +25 -0
  328. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  329. data/lib/rigor/type_node.rb +1 -0
  330. data/lib/rigor/version.rb +1 -1
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  332. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  333. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  334. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  336. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  337. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  338. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  340. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  341. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  342. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  343. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  347. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  348. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  349. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  351. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  352. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  353. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  354. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  355. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  356. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  357. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  358. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  359. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  361. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  363. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  365. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  366. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  367. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  369. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  372. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  373. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  376. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  377. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  378. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  379. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  386. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  398. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  399. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  400. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  406. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  408. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  409. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  416. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  417. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  426. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  427. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  428. data/sig/rigor/cache.rbs +2 -3
  429. data/sig/rigor/environment.rbs +11 -0
  430. data/sig/rigor/inference.rbs +3 -10
  431. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  432. data/sig/rigor/plugin/manifest.rbs +2 -1
  433. data/sig/rigor/reflection.rbs +3 -1
  434. data/sig/rigor/scope.rbs +24 -3
  435. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  436. data/sig/rigor/type.rbs +27 -1
  437. data/sig/rigor.rbs +1 -0
  438. data/skills/rigor-ask/SKILL.md +8 -6
  439. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  440. data/skills/rigor-project-init/SKILL.md +6 -3
  441. data/skills/rigor-project-init/references/02-configure.md +10 -6
  442. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  443. data/skills/rigor-type-oracle/SKILL.md +228 -0
  444. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  445. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  446. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  447. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  448. metadata +28 -3
  449. data/lib/rigor/cli/probe_environment.rb +0 -85
  450. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -17,10 +17,9 @@ module Rigor
17
17
  # {.for} answers `nil` unless {LabelIntent} says the spelling is evidently a label — the whole
18
18
  # point of the diagnostic is that it fires where intent is evident and nowhere else.
19
19
  class UnknownLabelReport < Data.define(:token, :suggestion, :retirement)
20
- # @param token [String] the spelling as written.
21
- # @param registry [Rigor::Effects::Registry, nil] the vocabulary after plugin load.
22
- # @param siblings [Array<String>] the other tokens written alongside it.
23
- # @return [UnknownLabelReport, nil]
20
+ # @param token — the spelling as written.
21
+ # @param registry — the vocabulary after plugin load.
22
+ # @param siblings — the other tokens written alongside it.
24
23
  def self.for(token:, registry:, siblings: [])
25
24
  return nil unless LabelIntent.evident?(token, registry, siblings: siblings)
26
25
 
@@ -46,14 +46,14 @@ module Rigor
46
46
  "bundler", "rubygems"
47
47
  ].freeze
48
48
 
49
- # @param bundle_path [String, Pathname, nil] explicit path to the bundler install root. When `nil`,
49
+ # @param bundle_path — explicit path to the bundler install root. When `nil`,
50
50
  # falls back to `auto_detect` if `auto_detect:` is true.
51
- # @param project_root [String] resolution base for relative `bundle_path:` and the auto-detect search.
52
- # @param auto_detect [Boolean] when true and `bundle_path:` is nil, try `.bundle/config`'s
51
+ # @param project_root — resolution base for relative `bundle_path:` and the auto-detect search.
52
+ # @param auto_detect — when true and `bundle_path:` is nil, try `.bundle/config`'s
53
53
  # `BUNDLE_PATH:` and `vendor/bundle/` under `project_root`.
54
- # @param skip_gems [Set<String>] gem names to exclude from discovery. Defaults to
54
+ # @param skip_gems — gem names to exclude from discovery. Defaults to
55
55
  # {SKIPPED_GEMS_BY_DEFAULT}.
56
- # @param locked_gems [Hash{String => LockfileResolver::LockedGem}, nil] Optional O4-Layer-3 filter.
56
+ # @param locked_gems — Optional O4-Layer-3 filter.
57
57
  # When non-nil and non-empty, only `sig/` directories whose gem `(name, version, platform)` tuple
58
58
  # matches a lockfile entry are returned. Bundle entries absent from the lockfile (or at a drifted
59
59
  # version) are silently dropped — the lockfile is treated as the source of truth for "what gems this
@@ -63,7 +63,7 @@ module Rigor
63
63
  # source keeps its stale `bundler/gems/` directory under the SAME name until a `bundle clean`. Pass
64
64
  # `nil` (the default) to keep the pre-Layer-3 behaviour of returning every non-skipped `sig/` under
65
65
  # the bundle.
66
- # @return [Array<Pathname>] every `<gem-dir>/sig` directory under the resolved bundle path, minus any
66
+ # @return every `<gem-dir>/sig` directory under the resolved bundle path, minus any
67
67
  # whose gem name is in `skip_gems` and (when `locked_gems` is supplied) minus any whose `(name,
68
68
  # version, platform)` does not match a lockfile entry.
69
69
  def self.discover(bundle_path:, project_root: Dir.pwd, auto_detect: true,
@@ -136,8 +136,10 @@ module Rigor
136
136
 
137
137
  private
138
138
 
139
+ # Allocates only when there is a prefix to strip (see `Environment#normalize_class_name`).
139
140
  def normalize_name(name)
140
- name.to_s.delete_prefix("::")
141
+ name = name.to_s
142
+ name.start_with?("::") ? name.delete_prefix("::") : name
141
143
  end
142
144
  end
143
145
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ class Environment
5
+ # Issue #784 — a mutable, first-write-wins record of one shared-build failure, held by the otherwise
6
+ # frozen {Environment} the way {HktRegistryHolder} holds a memoised value. A lazy build that raises
7
+ # during per-file analysis records here at its seam instead of raising into every file's
8
+ # `analyze_body` rescue; the coordinator snapshots the slot after the file loop (the build is lazy, so
9
+ # a snapshot taken before the loop reads nothing) and the aggregator surfaces it once for the run.
10
+ #
11
+ # First-write-wins: the build is memoised, so a second failure can only be the same one re-observed.
12
+ #
13
+ # Concurrency: single-threaded use only, the same discipline as {HktRegistryHolder}.
14
+ class FailureSlot
15
+ def initialize
16
+ @value = nil
17
+ end
18
+
19
+ # @param value — a Marshal-clean tuple — the fork pool ships it back from the worker.
20
+ def record(value)
21
+ @value = value.freeze if @value.nil?
22
+ end
23
+
24
+ # @return the recorded tuple, or nil when the build never failed.
25
+ attr_reader :value
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lockfile_resolver"
4
+
5
+ module Rigor
6
+ class Environment
7
+ # Issue #530 — the gem set for a project that has NO `Gemfile.lock`.
8
+ #
9
+ # ADR-82 WD9's constant-ownership index is driven by the LOCKED gem set, so a project without a
10
+ # lockfile (a plain library, a `gemspec`-only gem, an app vendored outside Bundler) handed the index
11
+ # nothing at all: every constant reaching into a real, installed, RBS-less gem recorded the generic
12
+ # cause, and the entire gem boundary reported as `engine_gap` — "report this to Rigor" — where the
13
+ # honest story is `add_rbs`. Measured on slim, whose whole Temple boundary lands that way while haml,
14
+ # which has a lockfile, attributes the same gem correctly.
15
+ #
16
+ # Degrading to the gems that are actually INSTALLED keeps ADR-82's honesty criterion intact. A name
17
+ # here is a gem that exists on disk; the index still has to READ its entry file before claiming any
18
+ # constant, and its lack of RBS is established by the same {RbsCoverageReport.classify} pass the
19
+ # lock-driven path uses. The version is the one on disk rather than a declared one — which is the
20
+ # version a lockfile-less project would load anyway.
21
+ #
22
+ # Only the missing-gem provenance index consumes this. The ADR-72 gem overlays stay Gemfile.lock-gated:
23
+ # an overlay changes what type-checks, so it needs the project's own declaration, while this
24
+ # side-channel only changes a label.
25
+ module InstalledGemSet
26
+ module_function
27
+
28
+ # @param bundle_path — the target's resolved bundler install root, or nil.
29
+ # @return frozen `gem name => LockfileResolver::LockedGem`, the shape
30
+ # {RbsCoverageReport.classify} consumes. Empty when neither resolver sees anything, which leaves
31
+ # today's generic cause in place.
32
+ def gems(bundle_path: nil)
33
+ found = from_bundle_tree(bundle_path)
34
+ found = from_installed_specs if found.empty?
35
+ found.freeze
36
+ end
37
+
38
+ # `<bundle>/ruby/X.Y.Z/gems/<name>-<version>/` — the same pure-filesystem layout
39
+ # {MissingGemConstantIndex.bundle_gem_dirs} walks, preferred over the host's gems so a project that
40
+ # vendored its bundle without committing the lockfile is read from its own tree. A platform-tagged
41
+ # directory (`ffi-1.17.4-aarch64-linux-gnu`) does not split into a name/version pair and is skipped:
42
+ # those gems ship native code, not the pure-Ruby constants this feeds.
43
+ def from_bundle_tree(bundle_path)
44
+ return {} if bundle_path.nil?
45
+
46
+ base = Pathname.new(bundle_path)
47
+ return {} unless base.directory?
48
+
49
+ Dir.glob(base.join("ruby", "*", "gems", "*")).each_with_object({}) do |dir, acc|
50
+ name, version = split_name_version(File.basename(dir))
51
+ next unless name
52
+
53
+ acc[name] ||= locked_gem(name, version)
54
+ end
55
+ end
56
+
57
+ # The gems the RUNNING Ruby can load. For the shipped `gem install rigortype` distribution that IS a
58
+ # lockfile-less project's gem environment — the same set its own `require` would reach. Under
59
+ # `bundle exec` it is the analyzer's own bundle instead (the `Gem.paths`-no-ops-under-Bundler
60
+ # mechanism ADR-90 documents), which yields fewer names, never wrong ones. Highest version per name,
61
+ # because `stubs` promises no ordering.
62
+ def from_installed_specs
63
+ Gem::Specification.stubs.each_with_object({}) do |stub, acc|
64
+ previous = acc[stub.name]
65
+ next if previous && Gem::Version.new(previous.version) >= stub.version
66
+
67
+ acc[stub.name] = locked_gem(stub.name, stub.version.to_s)
68
+ end
69
+ rescue StandardError
70
+ {}
71
+ end
72
+
73
+ def locked_gem(name, version)
74
+ LockfileResolver::LockedGem.new(name: name, version: version, platform: "ruby")
75
+ end
76
+
77
+ # Splits a gem install directory's basename. The version tail is anchored on a leading digit and must
78
+ # carry no further hyphen, which is what excludes the platform-tagged variants above.
79
+ def split_name_version(basename)
80
+ match = /\A(.+)-(\d[^-]*)\z/.match(basename)
81
+ match ? [match[1], match[2]] : nil
82
+ end
83
+ end
84
+ end
85
+ end
@@ -39,13 +39,13 @@ module Rigor
39
39
  end
40
40
  end
41
41
 
42
- # @param lockfile_path [String, Pathname, nil] explicit path to the Gemfile.lock. When `nil`, falls
42
+ # @param lockfile_path — explicit path to the Gemfile.lock. When `nil`, falls
43
43
  # back to `auto_detect` if `auto_detect:` is true.
44
- # @param project_root [String] resolution base for a relative `lockfile_path:` and the auto-detect
44
+ # @param project_root — resolution base for a relative `lockfile_path:` and the auto-detect
45
45
  # search.
46
- # @param auto_detect [Boolean] when true and `lockfile_path:` is nil, look for
46
+ # @param auto_detect — when true and `lockfile_path:` is nil, look for
47
47
  # `<project_root>/Gemfile.lock`.
48
- # @return [Hash{String => LockedGem}] frozen map of gem name → locked entry. Returns the empty frozen
48
+ # @return frozen map of gem name → locked entry. Returns the empty frozen
49
49
  # hash when no lockfile is resolvable, when the file is unreadable, or when Bundler refuses to parse
50
50
  # it.
51
51
  def self.locked_gems(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
@@ -59,6 +59,53 @@ module Rigor
59
59
  parse(resolved)
60
60
  end
61
61
 
62
+ # The gems the project declares for itself — the lockfile's `DEPENDENCIES` section, which is what a
63
+ # `Gemfile` actually asked for, as opposed to {locked_gems}'s full resolved graph. A consumer that
64
+ # advises the user about their own stack has to read this one: `minitest`, `i18n`, `activesupport` and
65
+ # `ffi` sit in nearly every Rails lock as somebody else's dependency, so matching against the resolved
66
+ # graph advises about gems the project never chose.
67
+ #
68
+ # @param lockfile_path — explicit path to the Gemfile.lock; `nil` falls back to `auto_detect`.
69
+ # @param project_root — resolution base for a relative `lockfile_path:` and the auto-detect search.
70
+ # @param auto_detect — when true and `lockfile_path:` is nil, look for `<project_root>/Gemfile.lock`.
71
+ # @return frozen set of gem names. Empty when no lockfile is resolvable, when it is unreadable, when
72
+ # Bundler refuses to parse it, or when the lockfile has no `DEPENDENCIES` section at all.
73
+ def self.direct_dependency_names(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
74
+ resolved = resolve_lockfile_path(
75
+ lockfile_path: lockfile_path,
76
+ project_root: project_root,
77
+ auto_detect: auto_detect
78
+ )
79
+ return EMPTY_NAMES unless resolved
80
+
81
+ parse_dependency_names(resolved)
82
+ end
83
+
84
+ EMPTY_NAMES = Set.new.freeze
85
+ private_constant :EMPTY_NAMES
86
+
87
+ def self.parse_dependency_names(path)
88
+ require "bundler"
89
+ rescue LoadError => e
90
+ warn "rigor: cannot read #{path}: bundler is not available (#{e.message})"
91
+ EMPTY_NAMES
92
+ else
93
+ do_parse_dependency_names(path)
94
+ end
95
+ private_class_method :parse_dependency_names
96
+
97
+ def self.do_parse_dependency_names(path)
98
+ parser = Bundler::LockfileParser.new(File.read(path.to_s))
99
+ # `#dependencies` is the parsed `DEPENDENCIES` section keyed by gem name, so the version constraint
100
+ # and the pinned-source `!` suffix are already stripped for us. The upstream bundler RBS shim does
101
+ # not declare it.
102
+ parser.dependencies.keys.to_set { |name| -name.to_s }.freeze # rigor:disable undefined-method
103
+ rescue StandardError => e
104
+ warn "rigor: ignoring malformed #{path} (#{e.class}: #{e.message})"
105
+ EMPTY_NAMES
106
+ end
107
+ private_class_method :do_parse_dependency_names
108
+
62
109
  # Returns the resolved lockfile path (`Pathname`) or `nil` when neither explicit nor auto-detect
63
110
  # produces one. Public so the stats banner can show what rigor picked up.
64
111
  def self.resolve_lockfile_path(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
@@ -37,12 +37,12 @@ module Rigor
37
37
  module MissingGemConstantIndex
38
38
  module_function
39
39
 
40
- # @param gems [Enumerable<Array(String, String)>] `[gem_name, version]` pairs (the `:missing` rows of
40
+ # @param gems — `[gem_name, version]` pairs (the `:missing` rows of
41
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
42
+ # @param bundle_path — the target's resolved bundler install root, or nil.
43
+ # @param spec_resolver — `(name, version) -> String?` fallback dir resolver. Injectable for
44
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
45
+ # @return frozen `root constant name => gem name`. On a collision (two gems
46
46
  # declaring the same top-level constant) the first gem wins — the CAUSE recorded downstream
47
47
  # (external gem without RBS) is true under either owner.
48
48
  def build(gems, bundle_path: nil, spec_resolver: method(:installed_gem_dir))
@@ -37,17 +37,17 @@ module Rigor
37
37
  DEFAULT_COLLECTION_PATH = ".gem_rbs_collection"
38
38
  private_constant :DEFAULT_COLLECTION_PATH
39
39
 
40
- # @param lockfile_path [String, Pathname, nil] explicit path to `rbs_collection.lock.yaml`. When `nil`,
40
+ # @param lockfile_path — explicit path to `rbs_collection.lock.yaml`. When `nil`,
41
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
42
+ # @param project_root — resolution base for relative `lockfile_path:` and the auto-detect
43
43
  # search.
44
- # @param auto_detect [Boolean] when true and `lockfile_path:` is nil, look for
44
+ # @param auto_detect — when true and `lockfile_path:` is nil, look for
45
45
  # `<project_root>/rbs_collection.lock.yaml`.
46
- # @param skip_gem_names [Array<String>, Set<String>] gem names rigor already loads from its bundled
46
+ # @param skip_gem_names — gem names rigor already loads from its bundled
47
47
  # stdlib (the merged `DEFAULT_LIBRARIES + libraries:` set). Entries whose `name` is in this set are
48
48
  # dropped regardless of `source.type` to avoid `RBS::DuplicatedDeclarationError` on
49
49
  # stdlib-extracted default gems. Defaults to empty.
50
- # @return [Array<Pathname>] every `<collection_path>/<gem-name>/<gem-version>/` directory listed in the
50
+ # @return every `<collection_path>/<gem-name>/<gem-version>/` directory listed in the
51
51
  # lockfile whose entry has a non-skipped source type, whose `name` is not in `skip_gem_names:`, and
52
52
  # whose directory exists on disk. Returns `[]` when no lockfile is resolvable, when the YAML is
53
53
  # unreadable, or when the collection path doesn't exist.
@@ -35,16 +35,16 @@ module Rigor
35
35
  "idn-ruby", "mysql2", "nokogiri", "pg", "prism", "racc", "redis", "rubygems"
36
36
  ].freeze
37
37
 
38
- # @param locked_gems [Hash{String => LockfileResolver::LockedGem}] The lockfile-resolved gem set. Empty
38
+ # @param locked_gems — The lockfile-resolved gem set. Empty
39
39
  # hash → no coverage analysis to do.
40
- # @param default_libraries [Array<String>] gem names rigor auto-loads through
40
+ # @param default_libraries — gem names rigor auto-loads through
41
41
  # `RBS::EnvironmentLoader#add(library:)`. Pass `Rigor::Environment::DEFAULT_LIBRARIES` from callers
42
42
  # running in a project context.
43
- # @param bundle_sig_paths [Array<Pathname, String>] the discovered `<bundle>/.../gems/<name>-<ver>/sig`
43
+ # @param bundle_sig_paths — the discovered `<bundle>/.../gems/<name>-<ver>/sig`
44
44
  # paths from {BundleSigDiscovery.discover}.
45
- # @param rbs_collection_paths [Array<Pathname, String>] the discovered `<collection>/<name>/<version>/`
45
+ # @param rbs_collection_paths — the discovered `<collection>/<name>/<version>/`
46
46
  # paths from {RbsCollectionDiscovery.discover}.
47
- # @return [Array<Coverage>] one row per locked gem; sorted by gem name for deterministic output.
47
+ # @return one row per locked gem; sorted by gem name for deterministic output.
48
48
  def self.classify(locked_gems:, default_libraries:,
49
49
  bundle_sig_paths:, rbs_collection_paths:)
50
50
  default_set = default_libraries.to_set
@@ -63,8 +63,10 @@ module Rigor
63
63
  @ancestor_names_cache[key] = loader.ancestor_names_for(key)
64
64
  end
65
65
 
66
+ # Allocates only when there is a prefix to strip (see `Environment#normalize_class_name`).
66
67
  def normalize_name(name)
67
- name.to_s.delete_prefix("::")
68
+ name = name.to_s
69
+ name.start_with?("::") ? name.delete_prefix("::") : name
68
70
  end
69
71
  end
70
72
  end