rigortype 0.4.6 → 0.4.7

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 (417) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
  4. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  5. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  6. data/docs/handbook/01-getting-started.md +5 -2
  7. data/docs/handbook/11-sig-gen.md +50 -3
  8. data/docs/handbook/appendix-phpstan.md +4 -4
  9. data/docs/manual/02-cli-reference.md +152 -26
  10. data/docs/manual/03-configuration.md +36 -3
  11. data/docs/manual/06-baseline.md +6 -4
  12. data/docs/manual/09-editor-integration.md +13 -2
  13. data/docs/manual/10-mcp-server.md +38 -2
  14. data/docs/manual/12-caching.md +4 -3
  15. data/docs/manual/plugins/rigor-actionpack.md +19 -11
  16. data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
  17. data/lib/rigor/analysis/baseline.rb +24 -27
  18. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  19. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  20. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
  21. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  22. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  23. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
  24. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
  25. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  26. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
  27. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  28. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  29. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  30. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  31. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  32. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  33. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
  35. data/lib/rigor/analysis/check_rules.rb +124 -44
  36. data/lib/rigor/analysis/crash_signature.rb +2 -2
  37. data/lib/rigor/analysis/dependency_recorder.rb +4 -4
  38. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  39. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  40. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  41. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  42. data/lib/rigor/analysis/edit_replay.rb +93 -6
  43. data/lib/rigor/analysis/fact_store.rb +3 -3
  44. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
  45. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  46. data/lib/rigor/analysis/incremental_session.rb +27 -16
  47. data/lib/rigor/analysis/rule_catalog.rb +2 -2
  48. data/lib/rigor/analysis/run_stats.rb +2 -2
  49. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
  50. data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
  51. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
  52. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
  53. data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
  54. data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
  55. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  56. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  57. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  58. data/lib/rigor/analysis/runner.rb +77 -71
  59. data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
  60. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  61. data/lib/rigor/analysis/template_units.rb +9 -1
  62. data/lib/rigor/analysis/worker_session.rb +24 -31
  63. data/lib/rigor/bleeding_edge.rb +3 -6
  64. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  65. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  66. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  67. data/lib/rigor/cache/descriptor.rb +6 -5
  68. data/lib/rigor/cache/incremental_snapshot.rb +31 -12
  69. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  70. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  71. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  72. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  73. data/lib/rigor/cache/rbs_descriptor.rb +54 -3
  74. data/lib/rigor/cache/rbs_environment.rb +4 -1
  75. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  76. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  77. data/lib/rigor/cache/store.rb +11 -10
  78. data/lib/rigor/ci_detector.rb +1 -1
  79. data/lib/rigor/cli/annotate_command.rb +24 -19
  80. data/lib/rigor/cli/baseline_command.rb +145 -55
  81. data/lib/rigor/cli/catalogue.rb +256 -0
  82. data/lib/rigor/cli/check_command.rb +108 -102
  83. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  84. data/lib/rigor/cli/command.rb +15 -1
  85. data/lib/rigor/cli/coverage_command.rb +21 -8
  86. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  87. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  88. data/lib/rigor/cli/coverage_scan.rb +3 -5
  89. data/lib/rigor/cli/diff_command.rb +25 -20
  90. data/lib/rigor/cli/docs_command.rb +1 -2
  91. data/lib/rigor/cli/doctor_command.rb +12 -12
  92. data/lib/rigor/cli/effects_command.rb +50 -40
  93. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  94. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  95. data/lib/rigor/cli/effects_renderer.rb +2 -1
  96. data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
  97. data/lib/rigor/cli/explain_command.rb +18 -8
  98. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  99. data/lib/rigor/cli/json_document.rb +41 -0
  100. data/lib/rigor/cli/lens_command.rb +12 -8
  101. data/lib/rigor/cli/lsp_command.rb +250 -23
  102. data/lib/rigor/cli/mcp_command.rb +12 -7
  103. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  104. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  105. data/lib/rigor/cli/options.rb +40 -1
  106. data/lib/rigor/cli/plugin_command.rb +93 -46
  107. data/lib/rigor/cli/plugins_command.rb +17 -18
  108. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  109. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  110. data/lib/rigor/cli/protection_renderer.rb +2 -1
  111. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  112. data/lib/rigor/cli/sig_gen_command.rb +84 -27
  113. data/lib/rigor/cli/skill_command.rb +32 -3
  114. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  115. data/lib/rigor/cli/skill_describe.rb +1 -1
  116. data/lib/rigor/cli/trace_command.rb +14 -9
  117. data/lib/rigor/cli/triage_command.rb +10 -11
  118. data/lib/rigor/cli/triage_renderer.rb +2 -1
  119. data/lib/rigor/cli/type_of_command.rb +16 -11
  120. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  121. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  122. data/lib/rigor/cli/type_scan_command.rb +14 -9
  123. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  124. data/lib/rigor/cli/unused_command.rb +27 -17
  125. data/lib/rigor/cli/upgrade_command.rb +5 -0
  126. data/lib/rigor/cli.rb +209 -54
  127. data/lib/rigor/config_audit.rb +1 -1
  128. data/lib/rigor/configuration/severity_profile.rb +5 -5
  129. data/lib/rigor/configuration.rb +72 -30
  130. data/lib/rigor/effects/definition_context.rb +0 -2
  131. data/lib/rigor/effects/envelope.rb +1 -1
  132. data/lib/rigor/effects/framework_units.rb +1 -1
  133. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  134. data/lib/rigor/effects/propagator.rb +1 -1
  135. data/lib/rigor/effects/registry.rb +2 -2
  136. data/lib/rigor/effects/snapshot.rb +1 -1
  137. data/lib/rigor/effects/taint_cause.rb +0 -2
  138. data/lib/rigor/effects/unit_scan.rb +9 -13
  139. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
  140. data/lib/rigor/environment/class_registry.rb +3 -3
  141. data/lib/rigor/environment/core_root.rb +77 -0
  142. data/lib/rigor/environment/default_libraries.rb +1 -1
  143. data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
  144. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  145. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  146. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  147. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  148. data/lib/rigor/environment/member_consistency.rb +56 -7
  149. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  150. data/lib/rigor/environment/project_path_gems.rb +124 -0
  151. data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
  152. data/lib/rigor/environment/rbs_loader.rb +480 -67
  153. data/lib/rigor/environment/reflection.rb +7 -7
  154. data/lib/rigor/environment/required_features.rb +0 -3
  155. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  156. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  157. data/lib/rigor/environment.rb +41 -10
  158. data/lib/rigor/flow_contribution/merger.rb +4 -4
  159. data/lib/rigor/inference/acceptance.rb +93 -32
  160. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  161. data/lib/rigor/inference/block_run_count.rb +178 -0
  162. data/lib/rigor/inference/budget_trace.rb +8 -2
  163. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  164. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  165. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  166. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  167. data/lib/rigor/inference/callee_write_census.rb +815 -0
  168. data/lib/rigor/inference/construction_escape.rb +217 -0
  169. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  170. data/lib/rigor/inference/deep_erasure.rb +39 -7
  171. data/lib/rigor/inference/def_handle.rb +1 -1
  172. data/lib/rigor/inference/def_node_resolver.rb +1 -1
  173. data/lib/rigor/inference/def_return_typer.rb +46 -4
  174. data/lib/rigor/inference/definer_resolution.rb +4 -2
  175. data/lib/rigor/inference/definers_index.rb +106 -0
  176. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  177. data/lib/rigor/inference/element_read_widening.rb +49 -3
  178. data/lib/rigor/inference/expression_typer.rb +116 -49
  179. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  180. data/lib/rigor/inference/global_write_census.rb +76 -2
  181. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  182. data/lib/rigor/inference/hkt_body.rb +1 -2
  183. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  184. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  185. data/lib/rigor/inference/hkt_registry.rb +3 -5
  186. data/lib/rigor/inference/indexed_narrowing.rb +4 -7
  187. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  188. data/lib/rigor/inference/ivar_writer_census.rb +51 -0
  189. data/lib/rigor/inference/key_presence_guard.rb +2 -1
  190. data/lib/rigor/inference/loop_value_record.rb +8 -4
  191. data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
  192. data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
  193. data/lib/rigor/inference/match_rebinding.rb +11 -1
  194. data/lib/rigor/inference/memo_audit.rb +55 -0
  195. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  197. data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  199. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  200. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  201. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  202. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  203. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
  204. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  205. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
  206. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  208. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
  209. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  210. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
  211. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  212. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  213. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  214. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +64 -14
  217. data/lib/rigor/inference/module_function_state.rb +11 -1
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -1
  219. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  220. data/lib/rigor/inference/mutation_widening.rb +157 -27
  221. data/lib/rigor/inference/narrowing.rb +47 -33
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -1
  223. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  226. data/lib/rigor/inference/project_method_ownership.rb +6 -3
  227. data/lib/rigor/inference/project_yielder.rb +11 -17
  228. data/lib/rigor/inference/receiver_alias.rb +20 -0
  229. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
  230. data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
  231. data/lib/rigor/inference/remover_holder_scan.rb +233 -0
  232. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  233. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  234. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  235. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  236. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
  237. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  238. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  239. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
  240. data/lib/rigor/inference/scope_indexer.rb +137 -46
  241. data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
  242. data/lib/rigor/inference/set_mutation.rb +189 -0
  243. data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
  244. data/lib/rigor/inference/statement_evaluator.rb +2328 -138
  245. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  246. data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
  247. data/lib/rigor/inference/toplevel_def_census.rb +59 -0
  248. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  249. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  250. data/lib/rigor/inference/void_tail_summary.rb +2 -1
  251. data/lib/rigor/language_server/buffer_table.rb +3 -3
  252. data/lib/rigor/language_server/completion_provider.rb +26 -20
  253. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  254. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  255. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  256. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  257. data/lib/rigor/language_server/hover_provider.rb +1 -7
  258. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  259. data/lib/rigor/language_server/project_context.rb +39 -1
  260. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  261. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  262. data/lib/rigor/language_server/server.rb +7 -3
  263. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  264. data/lib/rigor/lens/plugins.rb +1 -1
  265. data/lib/rigor/mcp/server.rb +73 -2
  266. data/lib/rigor/mcp.rb +1 -1
  267. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  268. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  269. data/lib/rigor/plugin/io_boundary.rb +1 -1
  270. data/lib/rigor/plugin/loader.rb +25 -8
  271. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  272. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  273. data/lib/rigor/plugin/manifest.rb +9 -13
  274. data/lib/rigor/plugin/registry.rb +3 -3
  275. data/lib/rigor/plugin/services.rb +2 -3
  276. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  277. data/lib/rigor/plugin/trust_policy.rb +2 -2
  278. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  279. data/lib/rigor/plugin.rb +6 -8
  280. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  281. data/lib/rigor/project_environment.rb +16 -2
  282. data/lib/rigor/protection/analysis_guard.rb +1 -1
  283. data/lib/rigor/protection/discovery_seed.rb +44 -5
  284. data/lib/rigor/protection/mutator.rb +1 -1
  285. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  286. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  287. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  288. data/lib/rigor/rbs_extended.rb +2 -3
  289. data/lib/rigor/reflection/constant_ancestors.rb +5 -3
  290. data/lib/rigor/rooted_glob.rb +15 -1
  291. data/lib/rigor/run_scoped_slots.rb +38 -0
  292. data/lib/rigor/runtime/jit.rb +1 -1
  293. data/lib/rigor/scope/discovery_index.rb +19 -6
  294. data/lib/rigor/scope/resolution_chain.rb +3 -1
  295. data/lib/rigor/scope.rb +365 -31
  296. data/lib/rigor/sig_gen/classification.rb +4 -0
  297. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  298. data/lib/rigor/sig_gen/generator.rb +371 -58
  299. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  300. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  301. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  302. data/lib/rigor/sig_gen/method_candidate.rb +23 -4
  303. data/lib/rigor/sig_gen/observation_collector.rb +3 -5
  304. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  305. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  306. data/lib/rigor/sig_gen/renderer.rb +15 -9
  307. data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
  308. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  309. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  310. data/lib/rigor/sig_gen/write_result.rb +23 -12
  311. data/lib/rigor/sig_gen/writer.rb +217 -37
  312. data/lib/rigor/type/acceptance_router.rb +2 -2
  313. data/lib/rigor/type/accepts_result.rb +1 -1
  314. data/lib/rigor/type/app.rb +4 -6
  315. data/lib/rigor/type/combinator.rb +9 -13
  316. data/lib/rigor/type/constant.rb +4 -5
  317. data/lib/rigor/type/hash_shape.rb +21 -40
  318. data/lib/rigor/type/union.rb +1 -1
  319. data/lib/rigor/type_node/identifier.rb +1 -2
  320. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  321. data/lib/rigor/type_node/string_literal.rb +1 -2
  322. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  323. data/lib/rigor/type_node/union.rb +3 -6
  324. data/lib/rigor/value_semantics.rb +1 -10
  325. data/lib/rigor/version.rb +1 -1
  326. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  327. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  329. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  330. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  331. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  332. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  333. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  334. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  335. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
  337. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
  338. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  339. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  340. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  341. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  342. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  343. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  344. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
  345. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
  346. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
  347. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  348. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  349. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
  350. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  351. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  352. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  353. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
  354. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
  355. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  356. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  357. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  358. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  359. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  360. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  361. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  362. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  363. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  364. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  365. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  366. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  367. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
  368. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  369. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  370. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  371. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  372. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
  373. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  374. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  375. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  376. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  377. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  381. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  382. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
  383. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  386. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  387. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  388. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  389. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  390. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  391. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
  392. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  393. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
  394. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  395. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
  396. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  397. data/sig/rigor/analysis/baseline.rbs +0 -3
  398. data/sig/rigor/analysis/fact_store.rbs +1 -0
  399. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  400. data/sig/rigor/cli/diff_command.rbs +1 -1
  401. data/sig/rigor/cli/explain_command.rbs +1 -1
  402. data/sig/rigor/effects/registry.rbs +2 -2
  403. data/sig/rigor/inference.rbs +4 -1
  404. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  405. data/sig/rigor/plugin/base.rbs +1 -0
  406. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  407. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  408. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  409. data/sig/rigor/plugin/io_boundary.rbs +2 -1
  410. data/sig/rigor/plugin/manifest.rbs +9 -9
  411. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  412. data/sig/rigor/scope.rbs +23 -3
  413. data/skills/rigor-plugin-author/SKILL.md +4 -4
  414. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  415. data/skills/rigor-plugin-review/SKILL.md +3 -3
  416. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
  417. metadata +36 -3
@@ -14,7 +14,7 @@ module Rigor
14
14
  # declared types. See {MemberConsistency} for the rule; this class holds the type work.
15
15
  #
16
16
  # A contradiction is reported only where it is PROVEN: no value, and no call, satisfies both sides.
17
- # Everything short of a proof is undecided. Four places carry that burden:
17
+ # Everything short of a proof is undecided. Five places carry that burden:
18
18
  #
19
19
  # - {#disjoint?} proves two types share no value, from the RBS hierarchy of Ruby core and stdlib
20
20
  # classes ({RbsProof}) only: two such classes neither of which is an RBS ancestor of the other, or a
@@ -103,6 +103,7 @@ module Rigor
103
103
  # `:inline` (the inline side is more precise), `:contradiction` or `:undecided`; `detail` names the
104
104
  # deciding position for the two reported outcomes and is nil otherwise.
105
105
  def compare(signature_member, inline_member, method_name)
106
+ return compare_variables(signature_member, inline_member) if variable?(signature_member)
106
107
  return EQUAL if same_declaration?(signature_member, inline_member)
107
108
  return SIGNATURE_MORE_PRECISE if inferred_signature?(inline_member)
108
109
 
@@ -174,6 +175,22 @@ module Rigor
174
175
  member_annotation_strings(member).include?(INFERRED_SIGNATURE_DIRECTIVE)
175
176
  end
176
177
 
178
+ def variable?(member)
179
+ member.is_a?(::RBS::AST::Members::InstanceVariable) ||
180
+ member.is_a?(::RBS::AST::Members::ClassInstanceVariable)
181
+ end
182
+
183
+ # Issue #2440 — an instance variable (or class instance variable) declared on both sides. Two
184
+ # declarations of one type are equal and merge silently. Any other pair is undecided, never ranked
185
+ # and never a contradiction: `sig/` binds and the inline declaration is reported at `:info`. A
186
+ # variable has no call to satisfy, so "more precise" has no reader to favour, and the committed
187
+ # `.rbs` (typically `rbs-inline --output`'s own) is the declaration the project's other tools read.
188
+ def compare_variables(signature_member, inline_member)
189
+ return EQUAL if signature_member.type == inline_member.type
190
+
191
+ [:undecided, "the type is `#{signature_member.type}` in `sig/` and `#{inline_member.type}` inline"]
192
+ end
193
+
177
194
  def compare_aliases(signature_member, inline_member)
178
195
  both = signature_member.is_a?(::RBS::AST::Members::Alias) && inline_member.is_a?(::RBS::AST::Members::Alias)
179
196
  return EQUAL if both && signature_member.old_name == inline_member.old_name
@@ -59,6 +59,9 @@ module Rigor
59
59
  NO_RECORDS = [].freeze
60
60
  private_constant :NO_RECORDS
61
61
 
62
+ # The key kinds {.member_keys} gives a variable member.
63
+ VARIABLE_KINDS = %i[instance_variable class_instance_variable].freeze
64
+
62
65
  # What a disjointness proof may lean on (ADR-112 WD5, #1075): the RBS class hierarchy the analysis
63
66
  # itself uses, restricted to classes Ruby core and the stdlib declare. Never Ruby constants loaded in
64
67
  # the analyzer process — those depend on which libraries happen to be required, and rbs and Ruby
@@ -115,8 +118,8 @@ module Rigor
115
118
  class << self
116
119
  # @param signature_members — `{[class, method, kind] => [signature_path, member, visibility]}`, the
117
120
  # first project `.rbs` member declaring each key, with its effective visibility.
118
- # @param inline_members — `[[virtual_name, class_name, member, visibility], ...]`, every member the
119
- # inline sources declare.
121
+ # @param inline_members — `[[virtual_name, class_name, member, visibility, nesting], ...]`, every member
122
+ # the inline sources declare, with the lexical nesting {.member_keys} keys a mixin by.
120
123
  # @param shadowable — the first segment of every class / module name the project's inputs declare
121
124
  # ({Comparator#initialize}).
122
125
  # @param proof — an {RbsProof} for the reported rows, or nil for the environment build.
@@ -131,9 +134,9 @@ module Rigor
131
134
 
132
135
  state = { comparator: Comparator.new(shadowable, proof: proof), records: [], inline: Set.new,
133
136
  signature: Hash.new { |hash, file| hash[file] = Set.new }, checked: {}.compare_by_identity }
134
- inline_members.each do |virtual_name, class_name, member, visibility|
137
+ inline_members.each do |virtual_name, class_name, member, visibility, nesting|
135
138
  state[:records].concat(refinement_records(state, class_name, member, nil, virtual_name))
136
- resolve_member(state, signature_members, [virtual_name, class_name, member, visibility])
139
+ resolve_member(state, signature_members, [virtual_name, class_name, member, visibility, nesting])
137
140
  end
138
141
  Resolution.new(
139
142
  records: state[:records].uniq.sort_by { |r| record_sort_key(r) }.freeze,
@@ -159,11 +162,57 @@ module Rigor
159
162
  end
160
163
  end
161
164
 
165
+ # Issue #2440 — every key one RBS member contributes to a definition build: its method keys
166
+ # ({.member_method_keys}) and, for an instance variable (`@x: T`) or a class instance variable
167
+ # (`self.@x: T`), `[:@x, :instance_variable]` / `[:@x, :class_instance_variable]`. rbs 4.2 raises
168
+ # `InstanceVariableDuplicationError` / `ClassInstanceVariableDuplicationError` for two of those in one
169
+ # class even when the types agree, so a `sig/` and an inline declaration of one has to be reconciled
170
+ # like a method. A method name never starts with `@`, so the two key spaces cannot meet. A class
171
+ # variable (`@@x`) contributes nothing: rbs does not reject a duplicate. Neither does an attribute's
172
+ # implied instance variable, which rbs lets an explicit `@x` declaration override.
173
+ #
174
+ # A mixin (`include M`, `extend M`, `prepend M`) is keyed by the lexical nesting of the declaration
175
+ # it sits in and the module and type arguments exactly as written, `[:"A/C|M[String]", :include]`:
176
+ # rbs 4.2 counts every instance variable `M` declares once per declaration of the class that mixes it
177
+ # in, so the same `include M` in `sig/` and inline raises `InstanceVariableDuplicationError ... in ::M`
178
+ # for the class. A relative name resolves through the nesting it is written in, so `include M` inside
179
+ # `module A; class C` (`A::M` when that exists) and inside `class A::C` (`::M`) are different mixins,
180
+ # and so are `M` and `::M`; only an identical spelling in an identical nesting is the same member and
181
+ # stands down. `rbs-inline --output` mirrors the Ruby file's nesting, so its committed output still
182
+ # pairs with the inline buffer. A mixin with different arguments keys differently and is left alone.
183
+ # Two members sharing a mixin key declare no method types to compare, so they read as equal.
184
+ #
185
+ # `nesting` is the declaration names as written, outermost first, joined by `/`
186
+ # ({RbsLoader.each_declared_member}); nil keys a mixin by its spelling alone.
187
+ #
188
+ # Kept apart from {.member_method_keys} on purpose: the plugin-signature plans key collisions by
189
+ # method only, and this rule is the inline-vs-`sig/` one.
190
+ def member_keys(member, nesting = nil)
191
+ case member
192
+ when ::RBS::AST::Members::InstanceVariable then [[member.name, :instance_variable]]
193
+ when ::RBS::AST::Members::ClassInstanceVariable then [[member.name, :class_instance_variable]]
194
+ when ::RBS::AST::Members::Include then [[mixin_key(member, nesting), :include]]
195
+ when ::RBS::AST::Members::Extend then [[mixin_key(member, nesting), :extend]]
196
+ when ::RBS::AST::Members::Prepend then [[mixin_key(member, nesting), :prepend]]
197
+ else member_method_keys(member)
198
+ end
199
+ end
200
+
201
+ # Whether a key `kind` names a variable rather than a method.
202
+ def variable_kind?(kind)
203
+ VARIABLE_KINDS.include?(kind)
204
+ end
205
+
162
206
  private
163
207
 
208
+ def mixin_key(member, nesting)
209
+ args = member.args.empty? ? "" : "[#{member.args.join(', ')}]"
210
+ :"#{nesting}|#{member.name}#{args}"
211
+ end
212
+
164
213
  def resolve_member(state, signature_members, inline)
165
- _virtual_name, class_name, member, = inline
166
- overlapping = member_method_keys(member).filter_map do |method_name, kind|
214
+ _virtual_name, class_name, member, _visibility, nesting = inline
215
+ overlapping = member_keys(member, nesting).filter_map do |method_name, kind|
167
216
  key = [class_name, method_name, kind]
168
217
  owner = signature_members[key]
169
218
  owner && [key, *owner]
@@ -235,7 +284,7 @@ module Rigor
235
284
  end
236
285
 
237
286
  def full_keys(class_name, member)
238
- member_method_keys(member).map { |method_name, kind| [class_name, method_name, kind] }
287
+ member_keys(member).map { |method_name, kind| [class_name, method_name, kind] }
239
288
  end
240
289
 
241
290
  # The `.rbs` line is read only for an outcome that reports it: asking an `RBS::Buffer` for a line
@@ -107,8 +107,9 @@ module Rigor
107
107
  # difference is worth stating because the two look like the same problem. There, a stale
108
108
  # `bundler/gems/` directory left behind by a gem that moved to a released version made the loader
109
109
  # load the WRONG SIGNATURES, collapsing the RBS environment. Here the index's whole output is a gem
110
- # NAME, so reading a stale checkout of the same gem yields the same answer; and the rubygems lookup
111
- # is consulted FIRST in {.build}, so a gem now installed from RubyGems never reaches this map at all.
110
+ # NAME, so reading a stale checkout of the same gem yields the same answer; and the RubyGems-layout
111
+ # bundle directory is consulted FIRST in {.build}, so a gem installed from RubyGems at its locked
112
+ # version never reaches this map.
112
113
  def git_gem_dirs(bundle_path)
113
114
  return {} if bundle_path.nil?
114
115
 
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../analysis/path_expansion"
4
+
5
+ module Rigor
6
+ class Environment
7
+ # Issue #2441 — the locked gems that ARE the analysed project: a `PATH` stanza whose directory Rigor analyses
8
+ # as project source. `rbs collection install` still fetches community RBS for such a gem (its generator skips
9
+ # only a `gemspec` source), and the project's `sig/` declares the same classes, so the collection copy would
10
+ # double-declare every member. The same gem is not `:missing` RBS either, so it must not make an ADR-72
11
+ # overlay or a WD9 missing-gem index entry eligible.
12
+ #
13
+ # "Analysed as project source" means the gem's directory is at or under one of the configured `paths:`
14
+ # entries, or contains one: rails lists `actionpack/lib` in `paths:` and locks `actionpack` from
15
+ # `PATH remote: actionpack`. A `PATH` gem no `paths:` entry reaches — a vendored `vendor/gems/foo`, a sibling
16
+ # checkout — is a dependency and keeps its collection RBS.
17
+ #
18
+ # #2592 — a reached gem counts only when the file walker would analyse at least one of its Ruby files: some
19
+ # `**/*.rb` under the part of the gem a `paths:` entry covers survives `Analysis::PathExpansion.reject_excluded`.
20
+ # A gem whose Ruby the configuration's `exclude:` patterns all leave out — `vendor/**`, `vendor/**/*.rb`, or
21
+ # `vendor/gems/foo/lib/**` when `lib/` holds all of it — keeps its collection RBS, and so does a reached
22
+ # directory with no Ruby at all. A `paths:` entry naming one `.rb` file inside the gem counts that file. The
23
+ # overlap is found on real paths, so a symlinked spelling of the same directory matches, but the files are
24
+ # walked and matched against `exclude:` through the `paths:` entry as written, as the walker matches them.
25
+ module ProjectPathGems
26
+ module_function
27
+
28
+ # @param locked — `{name => LockfileResolver::LockedGem}`.
29
+ # @param paths — the configured `paths:` entries; relative ones resolve against `root`.
30
+ # @param exclude_patterns — the configuration's `exclude_patterns`, matched against `exclude_base`.
31
+ # @return frozen set of gem names. Empty when `paths` is nil or empty.
32
+ def names(locked, paths:, root: Dir.pwd, exclude_patterns: [], exclude_base: root)
33
+ entries = Array(paths).map do |path|
34
+ logical = File.expand_path(path.to_s, root.to_s)
35
+ [logical, real(logical)]
36
+ end.uniq
37
+ return Set.new.freeze if entries.empty?
38
+
39
+ locked.each_value.filter_map do |gem|
40
+ dir = gem.path_source
41
+ next unless dir
42
+
43
+ dir = real(dir)
44
+ covered = entries.filter_map { |logical, real_entry| covered_part(dir, logical, real_entry) }
45
+ gem.name if covered.uniq.any? { |part| analysed_ruby?(part, Array(exclude_patterns), exclude_base.to_s) }
46
+ end.to_set.freeze
47
+ end
48
+
49
+ # The part of the gem at real directory `dir` that the `paths:` entry covers, spelled through the entry as
50
+ # written (`logical`), as the file walker spells the files it matches `exclude:` against: the entry itself
51
+ # when it lies at or under the gem, or the gem's directory reached through the entry when the gem lies under
52
+ # it. Nil when they do not overlap.
53
+ def covered_part(dir, logical, real_entry)
54
+ return logical if at_or_under?(real_entry, dir)
55
+ return unless at_or_under?(dir, real_entry)
56
+
57
+ rest = dir.byteslice(real_entry.bytesize..).delete_prefix("/")
58
+ rest.empty? ? logical : File.join(logical, rest)
59
+ end
60
+
61
+ # True when `path` is `dir` or lies under it. Joined with a `/` only when `dir` does not already end in one,
62
+ # so `/` reaches everything without spelling `//`, and `/proj/lib2` is not under `/proj/lib`.
63
+ def at_or_under?(path, dir)
64
+ return true if path == dir
65
+
66
+ path.start_with?(dir.end_with?("/") ? dir : "#{dir}/")
67
+ end
68
+
69
+ # Files are matched in slices as the walk finds them, so a large gem stops at the first slice holding a
70
+ # surviving file instead of listing its whole tree first (`Dir.glob` with a block still builds the list).
71
+ EXCLUDE_SLICE = 64
72
+ private_constant :EXCLUDE_SLICE
73
+
74
+ # True when the file walker ({Analysis::PathExpansion.ruby_files}) would analyse some `.rb` file of `part`:
75
+ # the `.rb` file itself when `part` names one, else a `**/*.rb` under the directory, that survives `patterns`.
76
+ def analysed_ruby?(part, patterns, base)
77
+ return surviving?([part], patterns, base) if File.file?(part)
78
+ return false unless File.directory?(part)
79
+
80
+ slice = []
81
+ found = each_ruby_file(part) do |file|
82
+ slice << file
83
+ next false if slice.size < EXCLUDE_SLICE
84
+
85
+ hit = surviving?(slice, patterns, base)
86
+ slice = []
87
+ hit
88
+ end
89
+ found || surviving?(slice, patterns, base)
90
+ end
91
+
92
+ # Walks `dir` as `Dir.glob("dir/**/*.rb")` matches: sorted, no dot-named entries, no descent through a
93
+ # symlinked directory. Stops and returns true as soon as the block returns true.
94
+ def each_ruby_file(dir, &)
95
+ Dir.children(dir).sort.each do |name|
96
+ next if name.start_with?(".")
97
+
98
+ path = File.join(dir, name)
99
+ if name.end_with?(".rb") && !File.directory?(path)
100
+ return true if File.exist?(path) && yield(path)
101
+ elsif File.directory?(path) && !File.symlink?(path)
102
+ return true if each_ruby_file(path, &)
103
+ end
104
+ end
105
+ false
106
+ rescue SystemCallError
107
+ false
108
+ end
109
+
110
+ def surviving?(files, patterns, base)
111
+ return false if files.empty?
112
+ return files.any? { |file| file.end_with?(".rb") } if patterns.empty?
113
+
114
+ Analysis::PathExpansion.reject_excluded(files, patterns, base: base).any? { |file| file.end_with?(".rb") }
115
+ end
116
+
117
+ def real(path)
118
+ File.realpath(path)
119
+ rescue SystemCallError
120
+ File.expand_path(path)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -41,21 +41,12 @@ module Rigor
41
41
  end
42
42
  end
43
43
 
44
- # Issue #696 review, second pass — the ancestry lookup moved to {RbsLoader#ancestor_names_for}, and
45
- # what moved with it is the reason.
46
- #
47
- # This method used to fetch `Cache::RbsClassAncestorTable` DIRECTLY, bypassing the loader's own
48
- # accessor, and branch on `loader.cache_store`: a whole-universe table build with a store, a
49
- # single-class demand without one. Same project, same question, three different answers to "which
50
- # classes failed to build" — 504 on a cold store, 0 on a warm one, 2 with no store. On the DEFAULT
51
- # `--workers=0` path nothing pre-warms the store, so the cold answer was the one written into the
52
- # run-result cache and replayed until a file changed.
53
- #
54
- # The cache-state branch still exists — it is worth 50x on the warm path — but it lives behind the
55
- # loader now, where both of its sides are marked as RIGOR'S OWN demand. Ordering two classes is not
56
- # the analysis asking whether either one's methods resolve, so neither side reaches the diagnostic,
57
- # and the answer is identical on both (the table's producer computes exactly this, keyed the same
58
- # way). What this method keeps is its own per-name memo, which is about repeated comparisons.
44
+ # Issue #696 review, second pass — the ancestry lookup lives in {RbsLoader#ancestor_names_for}.
45
+ # Read directly from `Cache::RbsClassAncestorTable`, the same question gave 504 failed classes on a
46
+ # cold store, 0 on a warm one and 2 with no store, and the cold answer was cached into the run result.
47
+ # Through the loader, both cache states are marked as Rigor's own demand, so ordering two classes
48
+ # never reaches the "which classes failed to build" diagnostic. The cache-state branch stays behind
49
+ # the loader (it is worth 50x on the warm path); this method keeps only its per-name memo.
59
50
  def ancestor_names(class_name)
60
51
  key = normalize_name(class_name)
61
52
  return @ancestor_names_cache[key] if @ancestor_names_cache.key?(key)