rigortype 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../type"
4
+ require_relative "../rbs_type_translator"
5
+
6
+ module Rigor
7
+ module Inference
8
+ module MethodDispatcher
9
+ module OverloadSelector
10
+ module_function
11
+
12
+ # Issue #2379 — every overload a supertype argument may select, when a nominal argument is wider than the
13
+ # parameter of the overload the call selected, `chosen`: `Integer#-` with a `Numeric` argument matched no
14
+ # overload and took the first, `(Integer) -> Integer`, although the runtime takes `(Float)`, `(Rational)` or
15
+ # `(Complex)` for those subclasses. Answers, in affinity order, every strictly typed overload whose every
16
+ # parameter takes its argument or is taken by a nominal one; nil when no argument is wider than `chosen`'s
17
+ # parameter or `chosen` is the only such overload.
18
+ # rubocop:disable-next Metrics/ParameterLists -- the selection inputs.
19
+ def contested_matches(method_definition, chosen, arg_types:, self_type:, instance_type:, type_vars:,
20
+ block_required:, environment:)
21
+ return nil unless arg_types.any?(Type::Nominal)
22
+
23
+ shared = { arg_types: arg_types, self_type: self_type, instance_type: instance_type,
24
+ type_vars: type_vars, block_required: block_required,
25
+ param_overrides: RbsExtended.param_type_override_map(method_definition, environment: environment),
26
+ environment: environment, keywords_last: false }
27
+ return nil unless wider_argument?(chosen, shared)
28
+
29
+ overloads = ReceiverAffinity.reorder(method_definition.method_types, self_type: self_type,
30
+ environment: environment)
31
+ matches = overloads.select do |method_type|
32
+ engages_block_shape?(method_type, block_required) &&
33
+ strictly_typed_params?(method_type, shared) && overlaps?(method_type, shared)
34
+ end
35
+ matches.empty? || (matches.size == 1 && matches.first.equal?(chosen)) ? nil : matches
36
+ end
37
+
38
+ class << self
39
+ private
40
+
41
+ # Whether a nominal argument is strictly wider than the parameter `method_type` binds it to: the parameter
42
+ # does not take the whole argument, and the argument takes the parameter (`Numeric` against `(Integer)`).
43
+ def wider_argument?(method_type, shared)
44
+ fun = method_type.type
45
+ arg_types = shared[:arg_types]
46
+ return false unless fun.respond_to?(:required_positionals) && arity_compatible?(fun, arg_types.size)
47
+
48
+ params = positional_params_for(fun, arg_types.size)
49
+ index = -1
50
+ params.any? do |param|
51
+ arg = arg_types[index += 1]
52
+ next false unless arg.is_a?(Type::Nominal)
53
+
54
+ param_type = translate_param(param, shared)
55
+ !param_type.accepts(arg, mode: :gradual).yes? && arg.accepts(param_type, mode: :gradual).yes?
56
+ end
57
+ end
58
+
59
+ # The strict pass's test with a nominal argument also counting where it takes the parameter: some value of
60
+ # a `Numeric` reaches `(Float)`.
61
+ def overlaps?(method_type, shared)
62
+ fun = method_type.type
63
+ ok = KeywordArguments.accepted_by?(fun, shared, true) { |*kv| accepts_param?(*kv, shared, true, true) }
64
+ return false unless ok
65
+
66
+ arg_types = shared[:arg_types]
67
+ return false unless arity_compatible?(fun, arg_types.size)
68
+
69
+ each_param_accepts?(positional_params_for(fun, arg_types.size), arg_types) do |param, arg|
70
+ accepts_param?(param, arg, shared, true) ||
71
+ (arg.is_a?(Type::Nominal) && arg.accepts(translate_param(param, shared), mode: :gradual).yes?)
72
+ end
73
+ end
74
+
75
+ # A parameter's type as the selector reads it: the `rigor:v1:param:` override, else the translated RBS type.
76
+ def translate_param(param, shared)
77
+ shared[:param_overrides][param.name] || RbsTypeTranslator.translate(
78
+ param.type,
79
+ self_type: shared[:self_type],
80
+ instance_type: shared[:instance_type],
81
+ type_vars: shared[:type_vars],
82
+ alias_expander: shared[:environment]&.rbs_loader
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../type"
4
+
5
+ module Rigor
6
+ module Inference
7
+ module MethodDispatcher
8
+ # Issue #3242 — whether a receiver's `find` / `detect` / `find_index` / `count` / `all?` / `any?` / `none?`
9
+ # may be other than core's. {BlockFolding}'s constant-block folds for those methods read no receiver shape, so
10
+ # they answer core's semantics, and they are sound only on a carrier known to be a core collection or
11
+ # enumerable. Anything else may define the method itself: a project class (`class Bag; def find = (yield; 8);
12
+ # end`), a subclass of a core class (its own `Nominal`), a class object (`def self.find`), a `Data.define` or
13
+ # `Struct.new` class, or a scalar that has no such method at all.
14
+ module CoreEnumerableReceiver
15
+ module_function
16
+
17
+ # The nominal receivers whose methods above are core's: Array's, Hash's, Set's, Range's own, or
18
+ # Enumerable's. `RBS::Unnamed::ENVClass` is `ENV`, which extends Enumerable.
19
+ NOMINALS = Ractor.make_shareable(
20
+ Set[
21
+ "Array", "Hash", "Set", "Range", "Enumerable", "Enumerator", "Enumerator::Lazy",
22
+ "Enumerator::ArithmeticSequence", "Enumerator::Chain", "Enumerator::Product", "Dir",
23
+ "RBS::Unnamed::ENVClass"
24
+ ].freeze
25
+ )
26
+
27
+ # False only for a carrier known to be a core collection or enumerable: a `Tuple` / `HashShape` literal
28
+ # shape, a `Constant` `Range` or `Set`, a `Nominal` in {NOMINALS}, `Top`, and a `Dynamic` / `Difference` /
29
+ # `Refined` / `Union` / `Intersection` over those. Every other receiver is true, so the folds decline.
30
+ def project_receiver?(receiver)
31
+ case receiver
32
+ when Type::Tuple, Type::HashShape, Type::Top then false
33
+ when Type::Constant then !(receiver.value.is_a?(Range) || receiver.value.is_a?(::Set))
34
+ when Type::Nominal then !NOMINALS.include?(receiver.class_name)
35
+ when Type::Difference, Type::Refined then project_receiver?(receiver.base)
36
+ when Type::Union, Type::Intersection then receiver.members.any? { |member| project_receiver?(member) }
37
+ when Type::Dynamic then project_receiver?(receiver.static_facet)
38
+ else true
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../reflection"
4
+ require_relative "../../type"
5
+ require_relative "argument_nodes"
6
+ require_relative "overload_selector"
7
+ require_relative "rbs_dispatch"
8
+
9
+ module Rigor
10
+ module Inference
11
+ module MethodDispatcher
12
+ # Issue #2884 — `X.new` through a `def self.new` that a project, gem or collection signature declares on `X` or
13
+ # on an ancestor of `X`. RBS models a constructor that answers another class this way (`email_address` declares
14
+ # `def self.new: (...) -> Address` on `EmailAddress`), and `MethodDispatcher#meta_new` answered `Nominal[X]` for
15
+ # every `Singleton[X].new` before any RBS tier ran. `meta_new` asks this module only after its curated lifts
16
+ # (`Array`, `Set`, `Hash`, `Struct`, `Class.new`, …) have declined, and keeps its `Nominal[X]` answer whenever
17
+ # this one is nil.
18
+ #
19
+ # The declaration counts only when it is an explicit `def self.new` (not the `new` RBS builds from `initialize`),
20
+ # every one of its definitions sits outside the `rbs` gem's core and stdlib trees
21
+ # ({Environment::RbsLoader#declared_outside_core_stdlib?}), and the class carrying it is not one core or stdlib
22
+ # declares. Core declares `Data.new: () -> bot` and `Ractor.new: … -> Ractor`, stdlib `Tempfile.new`; a project
23
+ # class inheriting one of those keeps the `Nominal` answer the curated tiers were written around.
24
+ #
25
+ # The declaration is found on `X`'s own RBS (which carries its RBS ancestors' singleton methods), or, for a
26
+ # Ruby-source class with no RBS of its own, on the first RBS-known class of its singleton
27
+ # {Scope::ResolutionChain}. A project entry ahead of it that defines `new` ends the walk with no answer: that
28
+ # method is the one Ruby runs. An inherited declaration that answers the instance
29
+ # ({Environment::RbsLoader#answers_instance_for_subclass?}) answers `Nominal[X]`; otherwise `instance`, and an
30
+ # overload returning the declaring class itself, read the receiver class, and `self` reads its class object.
31
+ #
32
+ # Nil (so `Nominal[X]`) when no overload takes the arguments (argument checking of `.new` is not this tier's
33
+ # job), and when the return translates to an untyped, `top` or `bot` answer, which would lose the precision
34
+ # `Nominal[X]` already has.
35
+ module DeclaredConstructor
36
+ # An entry of the singleton chain ahead of any declaring class defines `new` itself (in Ruby source, or as an
37
+ # `extend`ed module's RBS instance method), so that method is the one Ruby runs and the walk answers nothing.
38
+ OTHER_NEW = :other_new
39
+ private_constant :OTHER_NEW
40
+
41
+ module_function
42
+
43
+ def try_dispatch(class_name, context)
44
+ environment = context.environment
45
+ loader = environment&.rbs_loader
46
+ return nil if loader.nil? || !loader.respond_to?(:honoured_singleton_new?)
47
+
48
+ definition = declared_new(class_name, environment, loader, context.scope)
49
+ return nil if definition.nil? || !overload_matches?(definition, class_name, context)
50
+
51
+ owner = definition.defined_in.to_s.delete_prefix("::")
52
+ # The same test the walk's gate reads ({Environment::RbsLoader#singleton_new_departs_from_instance?}), so
53
+ # an inherited declaration that answers the instance answers `Nominal[receiver]` whether or not the gate let
54
+ # the walk run.
55
+ return nil if owner != class_name && loader.answers_instance_for_subclass?(definition)
56
+
57
+ answer = RbsDispatch.declared_singleton_return(context, definition, class_name)
58
+ answer = repoint_owner(answer, owner, class_name)
59
+ answer if usable?(answer)
60
+ end
61
+
62
+ class << self
63
+ private
64
+
65
+ def declared_new(class_name, environment, loader, scope)
66
+ owner = declaring_class(class_name, environment, loader, scope)
67
+ return nil if owner.nil?
68
+
69
+ definition = Rigor::Reflection.singleton_method_definition(owner, :new, environment: environment)
70
+ definition if definition && loader.honoured_singleton_new?(definition)
71
+ end
72
+
73
+ # `class_name` itself when RBS knows it; else the first RBS-known class on its singleton
74
+ # {Scope::ResolutionChain}, read in Ruby's order. Nil when a project entry ahead of it defines `new` (a
75
+ # `def self.new`, or an `extend`ed module's `def new`), when the chain reaches none, and when the chain does
76
+ # not stand (a fork or an unplaced mixin edge, where the tables cannot say which world ran).
77
+ #
78
+ # The walk runs only when some loaded declaration fails {Environment::RbsLoader#answers_instance_for_subclass?}
79
+ # ({Environment::RbsLoader#singleton_new_departs_from_instance?}); `try_dispatch` answers `Nominal[X]` for an
80
+ # inherited declaration that passes it, so without one the walk could change nothing. When it runs
81
+ # it files its reads whatever it answers: a later edit that puts such a declaration on the chain changes
82
+ # the answer, and the caller must be re-checked then.
83
+ def declaring_class(class_name, environment, loader, scope)
84
+ return class_name if Rigor::Reflection.rbs_class_known?(class_name, environment: environment)
85
+ return nil if scope.nil? || !loader.singleton_new_departs_from_instance?
86
+
87
+ chain = Scope::ResolutionChain.for(scope, class_name.to_s, :singleton, :methods)
88
+ found = first_new_owner(chain, scope, environment)
89
+ verdict = chain.settle(scope, found, owner: nil) { |retro| first_new_owner(retro, scope, environment) }
90
+ found unless found.nil? || found == OTHER_NEW || verdict != :chain
91
+ end
92
+
93
+ # The first entry on `chain` that answers `new`: an RBS-known class's name, or {OTHER_NEW} for an entry that
94
+ # defines it otherwise. An `extend`ed module is not a class object `new` is declared on, but one whose
95
+ # instance side declares `new` (in Ruby source or in RBS) puts that method ahead of the superclass's, so it
96
+ # stops the walk.
97
+ def first_new_owner(chain, scope, environment)
98
+ chain.search(scope) do |entry|
99
+ if entry.external?
100
+ extended_rbs_new = !entry.superclass_edge && rbs_instance_new?(entry.candidates, environment)
101
+ next OTHER_NEW if extended_rbs_new
102
+ next unless entry.superclass_edge
103
+
104
+ entry.candidates.find { |name| Rigor::Reflection.rbs_class_known?(name, environment: environment) }
105
+ elsif entry.side == :singleton && Rigor::Reflection.rbs_class_known?(entry.name, environment: environment)
106
+ entry.name
107
+ elsif scope.discovered_method?(entry.name, :new, entry.side) ||
108
+ (entry.side == :instance && rbs_instance_new?([entry.name], environment))
109
+ OTHER_NEW
110
+ end
111
+ end
112
+ end
113
+
114
+ def rbs_instance_new?(names, environment)
115
+ names.any? { |name| Rigor::Reflection.instance_method_definition(name, :new, environment: environment) }
116
+ end
117
+
118
+ # A genuine match only: the selector's first-overload fallback would answer a declaration the call does
119
+ # not take.
120
+ def overload_matches?(definition, class_name, context)
121
+ args = context.args || []
122
+ !OverloadSelector.select_declared(
123
+ definition, args, Type::Combinator.singleton_of(class_name), Type::Combinator.nominal_of(class_name),
124
+ {}, !context.block_type.nil?, context.environment, true,
125
+ ArgumentNodes.keyword_hash_last?(context.call_node, args), nil, true
126
+ ).empty?
127
+ end
128
+
129
+ # A return naming the declaring class itself (`def self.new: [T] (T) -> Box[T]`) is the constructor's own
130
+ # class, so a subclass inheriting it answers the subclass, as `instance` does; generic arguments name the
131
+ # owner's parameters and are dropped with it. A foreign return (`EmailAddress` -> `Address`) stays.
132
+ def repoint_owner(answer, owner, class_name)
133
+ return answer if owner == class_name
134
+
135
+ case answer
136
+ when Type::Nominal
137
+ answer.class_name == owner ? Type::Combinator.nominal_of(class_name) : answer
138
+ when Type::Union
139
+ Type::Combinator.union(*answer.members.map { |member| repoint_owner(member, owner, class_name) })
140
+ else answer
141
+ end
142
+ end
143
+
144
+ def usable?(answer)
145
+ !(answer.nil? || answer.is_a?(Type::Dynamic) || answer.is_a?(Type::Top) || answer.is_a?(Type::Bot))
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -119,7 +119,7 @@ module Rigor
119
119
  # `Dynamic`, the untyped carrier, a facet that is only `nil`, one wider than {MEMBER_LIMIT}, or one with a
120
120
  # member that is not sealed, an untyped member included.
121
121
  def facet_members(arg)
122
- return nil unless arg.is_a?(Type::Dynamic) && !arg.static_facet.is_a?(Type::Top)
122
+ return nil unless arg.is_a?(Type::Dynamic) && !Type::Combinator.untyped?(arg)
123
123
 
124
124
  facet = arg.static_facet
125
125
  members = (facet.is_a?(Type::Union) ? facet.members : [facet]).reject { |member| nil_value?(member) }
@@ -20,12 +20,12 @@ module Rigor
20
20
  module_function
21
21
 
22
22
  # The literal `untyped` carrier, `Dynamic[top]`.
23
- def untyped?(type) = type.is_a?(Type::Dynamic) && type.static_facet.is_a?(Type::Top)
23
+ def untyped?(type) = Type::Combinator.untyped?(type)
24
24
 
25
25
  # The untyped carrier itself, or a union or `Dynamic` holding it at any depth.
26
26
  def imprecise?(type)
27
27
  case type
28
- when Type::Dynamic then type.static_facet.is_a?(Type::Top) || imprecise?(type.static_facet)
28
+ when Type::Dynamic then Type::Combinator.untyped?(type) || imprecise?(type.static_facet)
29
29
  when Type::Union then type.members.any? { |member| imprecise?(member) }
30
30
  else false
31
31
  end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ require_relative "../../type"
6
+ require_relative "../../typing_state"
7
+ require_relative "../../memo_audit"
8
+ require_relative "argument_nodes"
9
+
10
+ module Rigor
11
+ module Inference
12
+ module MethodDispatcher
13
+ # ADR-130 WD3 — the overload-shape rule for a call on a `Rigor::Plugin` interface value. The argument rules
14
+ # never check a call on a `Dynamic` receiver, so nothing else would notice a call no overload of the member
15
+ # takes; without this rule dispatch took whichever overload the selector's fallback picked, and that
16
+ # overload's return made the next call wrong (`xs.each.with_index` read the block overload's
17
+ # `Array[Integer]`). A call resolves only through the overloads that take its shape:
18
+ #
19
+ # - a block when the call passes one, and no required block when it does not;
20
+ # - the positional count, within required plus optional unless the overload has a rest parameter;
21
+ # - no required keyword the call omits.
22
+ #
23
+ # A shape the AST cannot state, a splat (`*xs`), a keyword splat (`**opts`) or a forwarded `...`, is taken
24
+ # by no overload, so the call reads `untyped` as it does on master.
25
+ module InterfaceShape
26
+ # ADR-130 WD5 — the dispatch-off flag {.without_dispatch} installs (`RbsDispatch.without_interface_dispatch`).
27
+ DISPATCH_OFF = TypingState.frame(
28
+ :interface_dispatch_off, reason: "a flag installed around a dispatch-off run (sig-gen, the parity harness)",
29
+ witness: "spec/fixtures/typing_state_witnesses/interface_erasure.rb"
30
+ )
31
+ private_constant :DISPATCH_OFF
32
+
33
+ module_function
34
+
35
+ # Runs the block with interface dispatch switched off.
36
+ def without_dispatch(&)
37
+ MemoAudit.check_run_level_install(:interface_dispatch_off)
38
+ TypingState.current.with(DISPATCH_OFF, true, &)
39
+ end
40
+
41
+ # False inside {.without_dispatch}.
42
+ def dispatch? = TypingState.current[DISPATCH_OFF] != true
43
+
44
+ # `definition` narrowed to the overloads that take the call's shape (the definition itself when every
45
+ # overload does), or nil when none does.
46
+ #
47
+ # Nil too when more than one overload takes the shape and one of them has a parameter that mentions `self`
48
+ # (`(self other)`, `(Array[self] others)`): which of them the call reaches depends on whether an argument is
49
+ # the receiver's kind, and the interface names no class acceptance could decide that against. Read as a
50
+ # precise overload, `x.same(1)` against `(self) -> Integer | (Integer) -> String` answered the first one's
51
+ # `Integer`; the call reads `untyped` instead.
52
+ def accepting(definition, args, block:, call_node:)
53
+ shape = call_shape(args, call_node)
54
+ return nil if shape.nil?
55
+
56
+ positional, keywords = shape
57
+ defs = definition.defs.select { |type_def| takes?(type_def.type, positional, keywords, block) }
58
+ return nil if defs.empty?
59
+ return nil if defs.size > 1 && defs.any? { |type_def| self_parameter?(type_def.type.type) }
60
+
61
+ defs.size == definition.defs.size ? definition : definition.update(defs: defs)
62
+ end
63
+
64
+ # True when a parameter type of `function` mentions RBS `self`, at any depth.
65
+ def self_parameter?(function)
66
+ return false unless function.respond_to?(:required_positionals)
67
+
68
+ params = function.required_positionals + function.optional_positionals + function.trailing_positionals +
69
+ function.required_keywords.values + function.optional_keywords.values
70
+ params << function.rest_positionals if function.rest_positionals
71
+ params << function.rest_keywords if function.rest_keywords
72
+ params.any? { |param| mentions_self?(param.type) }
73
+ end
74
+
75
+ def mentions_self?(rbs_type)
76
+ return true if rbs_type.is_a?(RBS::Types::Bases::Self)
77
+
78
+ rbs_type.each_type { |inner| return true if mentions_self?(inner) }
79
+ false
80
+ end
81
+
82
+ # True when the call's block is a block-pass whose value may be nil at runtime, so the call may run
83
+ # block-less: `&blk` unless `blk` is provably not nil ({.never_nil?}), and an anonymous `&`, which forwards
84
+ # the enclosing method's own block. `&:sym` is never nil.
85
+ def nilable_block_pass?(call_node, scope)
86
+ block = call_node.respond_to?(:block) ? call_node.block : nil
87
+ return false unless block.is_a?(Prism::BlockArgumentNode)
88
+
89
+ expression = block.expression
90
+ return true if expression.nil?
91
+ return false if expression.is_a?(Prism::SymbolNode)
92
+ return true if scope.nil?
93
+
94
+ !never_nil?(scope.type_of(expression))
95
+ end
96
+
97
+ # The classes a value of which may be `nil`: `NilClass` and its ancestors.
98
+ NIL_ANCESTORS = %w[NilClass Object BasicObject Kernel].freeze
99
+ private_constant :NIL_ANCESTORS
100
+
101
+ # True only for a type that provably excludes `nil`: a non-nil literal, an instance of a class `nil` is not
102
+ # (a `Proc`, a `Method`), a class object, or a shape. `top`, `Dynamic`, `Object` and anything this does not
103
+ # list may be `nil` (ADR-130 WD3, as amended).
104
+ def never_nil?(type)
105
+ case type
106
+ when Type::Union then type.members.all? { |member| never_nil?(member) }
107
+ when Type::Constant then !type.value.nil?
108
+ when Type::Nominal then !NIL_ANCESTORS.include?(type.class_name)
109
+ when Type::Refined, Type::Difference then never_nil?(type.base)
110
+ when Type::Singleton, Type::Tuple, Type::HashShape, Type::BoundMethod, Type::IntegerRange, Type::FloatRange,
111
+ Type::DataInstance, Type::StructInstance, Type::DataClass, Type::StructClass then true
112
+ else false
113
+ end
114
+ end
115
+
116
+ # `[positional count, keyword names or nil]`, or nil for a shape the AST cannot state, which includes a keyword
117
+ # hash whose keys are not all literal symbols. The keyword names are nil when the call passes no keyword hash.
118
+ def call_shape(args, call_node)
119
+ arguments = call_node.respond_to?(:arguments) ? call_node.arguments&.arguments : nil
120
+ return [args.size, nil] if arguments.nil? || arguments.size != args.size
121
+ return nil if arguments.any? { |node| opaque_argument?(node) }
122
+ return [args.size, nil] unless ArgumentNodes.keyword_hash_last?(call_node, args)
123
+
124
+ names = keyword_names(arguments.last)
125
+ names && [args.size - 1, names]
126
+ end
127
+
128
+ def opaque_argument?(node)
129
+ case node
130
+ when Prism::SplatNode, Prism::ForwardingArgumentsNode then true
131
+ when Prism::KeywordHashNode then node.elements.any?(Prism::AssocSplatNode)
132
+ else false
133
+ end
134
+ end
135
+
136
+ # The literal keyword names of a keyword hash, or nil when one is not a literal symbol.
137
+ def keyword_names(hash_node)
138
+ hash_node.elements.map do |element|
139
+ key = element.key
140
+ return nil unless key.is_a?(Prism::SymbolNode)
141
+
142
+ key.unescaped.to_sym
143
+ end
144
+ end
145
+
146
+ def takes?(method_type, positional, keywords, block)
147
+ function = method_type.type
148
+ # `(...) -> T`: an untyped parameter list takes every call.
149
+ return block_taken?(method_type.block, block) unless function.respond_to?(:required_positionals)
150
+
151
+ block_taken?(method_type.block, block) && arguments_taken?(function, positional, keywords)
152
+ end
153
+
154
+ def block_taken?(declared, block)
155
+ block ? !declared.nil? : (declared.nil? || !declared.required)
156
+ end
157
+
158
+ def arguments_taken?(function, positional, keywords)
159
+ return positional_taken?(function, positional) && function.required_keywords.empty? if keywords.nil?
160
+ # A keyword hash an overload declares no keywords for is its last positional argument.
161
+ return positional_taken?(function, positional + 1) unless keywords?(function)
162
+
163
+ positional_taken?(function, positional) && function.required_keywords.keys.all? do |key|
164
+ keywords.include?(key)
165
+ end
166
+ end
167
+
168
+ def positional_taken?(function, count)
169
+ required = function.required_positionals.size + function.trailing_positionals.size
170
+ return false if count < required
171
+
172
+ !function.rest_positionals.nil? || count <= required + function.optional_positionals.size
173
+ end
174
+
175
+ def keywords?(function)
176
+ !function.required_keywords.empty? || !function.optional_keywords.empty? || !function.rest_keywords.nil?
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -105,7 +105,7 @@ module Rigor
105
105
  # throws)
106
106
  # - `Hash[K, V]` / `HashShape{...}` → Tuple[K, V]
107
107
  # (Ruby yields `[key, value]` pairs as the element)
108
- # - `Constant<Array>` / `Constant<Range>` / `Constant<Set>`
108
+ # - `Constant<Array>` / `Constant<Range>`
109
109
  # → corresponding Constant element
110
110
  #
111
111
  # Receivers we cannot project (Top, Dynamic, unknown nominals, IO, …) decline so the RBS tier still
@@ -11,7 +11,7 @@ module Rigor
11
11
  # Kernel intrinsic shape-folding — precision tier for the `Kernel` module-functions whose return type
12
12
  # is a function of the argument's *shape*, not just its class.
13
13
  #
14
- # Today the only catalogued intrinsic is `Kernel#Array`. The default RBS sig is
14
+ # `Kernel#Array` is the shape-sensitive fold. Its default RBS sig is
15
15
  # `Array(untyped) -> Array[untyped]`, which collapses to `Array[Dynamic[top]]` for every caller. This
16
16
  # tier short-circuits with a precise answer when the argument's type lattice tells us what the result
17
17
  # element type MUST be:
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ module Inference
5
+ module MethodDispatcher
6
+ module OverloadSelector
7
+ module_function
8
+
9
+ # The overload a call answers when no selection pass matched it. A call with a block, on a method that
10
+ # declares a block overload, reads the block overloads (`block_call_fallback`, #2429) unless `block_fallback`
11
+ # is false. Otherwise prefer an overload that does not REQUIRE a block over `overloads.first`: methods like
12
+ # `Array#filter` / `Enumerable#map` declare the block-bearing overload first (`() { ... } -> Array[Elem]`)
13
+ # and the bare-call overload second (`() -> Enumerator[...]`), and a no-block `[1, 2].filter` yields an
14
+ # `Enumerator`.
15
+ def no_match_fallback(overloads, shared, block_fallback)
16
+ if block_fallback && shared[:block_required] && overloads.any? { |mt| overload_has_block?(mt) }
17
+ return block_call_fallback(overloads, shared)
18
+ end
19
+
20
+ [overloads.find { |mt| !overload_requires_block?(mt) } || overloads.first]
21
+ end
22
+
23
+ class << self
24
+ private
25
+
26
+ # Issue #2429 — the block-less arm's return is what the method answers without a block, so
27
+ # `URI.open(url, read_timeout: 3) { |io| }` read `StringIO | Tempfile` instead of the block's value. The
28
+ # answer is the block overload whose positional arity admits the call when only one does, or the one every
29
+ # such overload agrees on; otherwise none (`[]`, which the return path reads as `Dynamic[top]`).
30
+ # `[3, 1, 2].max_by(2.0) { }` takes `(Integer n) { } -> Array[Elem]`, not `() { } -> Elem?`, and overloads of
31
+ # differing returns that all admit the call are not decided by position.
32
+ def block_call_fallback(overloads, shared)
33
+ admitted = overloads.select { |mt| overload_has_block?(mt) && arity_admits?(mt, shared) }
34
+ return [] if admitted.empty?
35
+
36
+ admitted.map { |mt| mt.type.return_type }.uniq.size == 1 ? [admitted.first] : []
37
+ end
38
+
39
+ # The call's positional count (less a keyword hash the overload takes as keywords) fits `method_type`'s
40
+ # arity; a splat fits when some count of its elements does (#1801).
41
+ def arity_admits?(method_type, shared)
42
+ fun = method_type.type
43
+ arg_types = KeywordArguments.selection_positional(method_type, shared)
44
+ splats = shared[:splats]
45
+ return arity_compatible?(fun, arg_types.size) unless splats
46
+
47
+ lists = SplatArity.expansions(fun, arg_types, splats)
48
+ lists.nil? || lists.any? { |list| arity_compatible?(fun, list.size) }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end