rigortype 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +72 -1
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +32 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +11 -0
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +8 -24
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +81 -22
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  78. data/lib/rigor/analysis/runner.rb +133 -35
  79. data/lib/rigor/analysis/worker_session.rb +9 -10
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +155 -21
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +2 -4
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  97. data/lib/rigor/cache/store.rb +77 -32
  98. data/lib/rigor/cli/annotate_command.rb +5 -6
  99. data/lib/rigor/cli/check_command.rb +57 -10
  100. data/lib/rigor/cli/check_invocation.rb +6 -11
  101. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  102. data/lib/rigor/cli/coverage_command.rb +1 -1
  103. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  104. data/lib/rigor/cli/coverage_scan.rb +1 -5
  105. data/lib/rigor/cli/diff_command.rb +1 -1
  106. data/lib/rigor/cli/doc_links.rb +3 -3
  107. data/lib/rigor/cli/docs_command.rb +2 -2
  108. data/lib/rigor/cli/doctor_command.rb +85 -38
  109. data/lib/rigor/cli/effects_command.rb +2 -2
  110. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  111. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  112. data/lib/rigor/cli/explain_command.rb +34 -1
  113. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  114. data/lib/rigor/cli/lsp_command.rb +1 -1
  115. data/lib/rigor/cli/mcp_command.rb +1 -1
  116. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  117. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  118. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  119. data/lib/rigor/cli/plugin_command.rb +1 -1
  120. data/lib/rigor/cli/plugins_command.rb +2 -1
  121. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  122. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  123. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  124. data/lib/rigor/cli/sig_gen_command.rb +2 -2
  125. data/lib/rigor/cli/skill_command.rb +1 -1
  126. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  127. data/lib/rigor/cli/skill_describe.rb +36 -35
  128. data/lib/rigor/cli/trace_command.rb +4 -4
  129. data/lib/rigor/cli/trace_renderer.rb +5 -6
  130. data/lib/rigor/cli/triage_command.rb +1 -1
  131. data/lib/rigor/cli/type_of_command.rb +10 -6
  132. data/lib/rigor/cli/type_scan_command.rb +4 -4
  133. data/lib/rigor/cli/unused_command.rb +11 -3
  134. data/lib/rigor/cli/upgrade_command.rb +1 -1
  135. data/lib/rigor/cli.rb +68 -6
  136. data/lib/rigor/config_audit.rb +30 -5
  137. data/lib/rigor/configuration/severity_profile.rb +6 -6
  138. data/lib/rigor/configuration.rb +43 -4
  139. data/lib/rigor/effects/attribution.rb +1 -3
  140. data/lib/rigor/effects/config_envelopes.rb +6 -8
  141. data/lib/rigor/effects/definition_lines.rb +25 -6
  142. data/lib/rigor/effects/effect_table.rb +0 -1
  143. data/lib/rigor/effects/entry_points.rb +0 -2
  144. data/lib/rigor/effects/envelope_check.rb +8 -8
  145. data/lib/rigor/effects/envelope_index.rb +5 -8
  146. data/lib/rigor/effects/framework_units.rb +4 -6
  147. data/lib/rigor/effects/identity.rb +4 -6
  148. data/lib/rigor/effects/inline_anchor.rb +7 -7
  149. data/lib/rigor/effects/label_intent.rb +3 -4
  150. data/lib/rigor/effects/liskov_check.rb +8 -8
  151. data/lib/rigor/effects/method_key.rb +1 -1
  152. data/lib/rigor/effects/plugin_facts.rb +4 -6
  153. data/lib/rigor/effects/propagator.rb +2 -3
  154. data/lib/rigor/effects/scanner.rb +1 -1
  155. data/lib/rigor/effects/signature_sources.rb +3 -5
  156. data/lib/rigor/effects/snapshot.rb +10 -10
  157. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  158. data/lib/rigor/effects/unit_scan.rb +10 -10
  159. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  160. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  161. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  162. data/lib/rigor/environment/class_registry.rb +3 -1
  163. data/lib/rigor/environment/failure_slot.rb +2 -2
  164. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  165. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  166. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  167. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  168. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  169. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  170. data/lib/rigor/environment/rbs_loader.rb +388 -59
  171. data/lib/rigor/environment.rb +105 -20
  172. data/lib/rigor/flow_contribution/merger.rb +0 -2
  173. data/lib/rigor/flow_contribution.rb +11 -13
  174. data/lib/rigor/inference/acceptance.rb +153 -8
  175. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  176. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  177. data/lib/rigor/inference/budget_trace.rb +1 -5
  178. data/lib/rigor/inference/captured_locals.rb +2 -3
  179. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  180. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  181. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  182. data/lib/rigor/inference/element_read_widening.rb +168 -0
  183. data/lib/rigor/inference/expression_typer.rb +763 -143
  184. data/lib/rigor/inference/fork_map.rb +5 -7
  185. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  186. data/lib/rigor/inference/hkt_registry.rb +4 -7
  187. data/lib/rigor/inference/index_write_widening.rb +1 -5
  188. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  189. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  190. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  191. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  192. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  194. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  195. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  197. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  198. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  199. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  200. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  201. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  202. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  203. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  204. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  205. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  206. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  209. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  210. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  211. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  212. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  213. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  215. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  216. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  217. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  218. data/lib/rigor/inference/mutation_widening.rb +100 -51
  219. data/lib/rigor/inference/narrowing.rb +299 -46
  220. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  221. data/lib/rigor/inference/origin_lookup.rb +2 -3
  222. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  223. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  224. data/lib/rigor/inference/precision_scanner.rb +3 -4
  225. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  226. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  227. data/lib/rigor/inference/protection_scanner.rb +1 -2
  228. data/lib/rigor/inference/range_constant.rb +57 -0
  229. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  230. data/lib/rigor/inference/receiver_alias.rb +3 -3
  231. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  232. data/lib/rigor/inference/scope_indexer.rb +688 -151
  233. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  234. data/lib/rigor/inference/string_mutation.rb +60 -0
  235. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  236. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  237. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  238. data/lib/rigor/inference/version_guard.rb +28 -21
  239. data/lib/rigor/inference/void_origin.rb +3 -3
  240. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  241. data/lib/rigor/language_server/buffer_table.rb +4 -4
  242. data/lib/rigor/language_server/completion_provider.rb +1 -1
  243. data/lib/rigor/language_server/debouncer.rb +1 -1
  244. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  245. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  246. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  247. data/lib/rigor/language_server/hover_provider.rb +1 -1
  248. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  249. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  250. data/lib/rigor/language_server/project_context.rb +2 -5
  251. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  252. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  253. data/lib/rigor/language_server/server.rb +9 -9
  254. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  255. data/lib/rigor/language_server/uri.rb +1 -1
  256. data/lib/rigor/plugin/base.rb +90 -21
  257. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  258. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  259. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  260. data/lib/rigor/plugin/fact_store.rb +6 -6
  261. data/lib/rigor/plugin/io_boundary.rb +93 -8
  262. data/lib/rigor/plugin/isolation.rb +29 -6
  263. data/lib/rigor/plugin/loader.rb +31 -12
  264. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  265. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  266. data/lib/rigor/plugin/manifest.rb +23 -1
  267. data/lib/rigor/plugin/registry.rb +42 -12
  268. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  269. data/lib/rigor/plugin/trust_policy.rb +2 -4
  270. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  271. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  272. data/lib/rigor/project_environment.rb +138 -0
  273. data/lib/rigor/protection/analysis_guard.rb +93 -14
  274. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  275. data/lib/rigor/protection/dependency_closure.rb +5 -8
  276. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  277. data/lib/rigor/protection/discovery_seed.rb +11 -11
  278. data/lib/rigor/protection/kill_signature.rb +2 -4
  279. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  280. data/lib/rigor/protection/mutation_cache.rb +6 -9
  281. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  282. data/lib/rigor/protection/mutator.rb +2 -1
  283. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  284. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  285. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  286. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  287. data/lib/rigor/rbs_extended.rb +61 -21
  288. data/lib/rigor/reflection/constant_path.rb +126 -0
  289. data/lib/rigor/reflection.rb +26 -10
  290. data/lib/rigor/runtime/jit.rb +6 -8
  291. data/lib/rigor/scope/discovery_index.rb +30 -0
  292. data/lib/rigor/scope.rb +203 -4
  293. data/lib/rigor/sig_gen/generator.rb +115 -22
  294. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  295. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  296. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  297. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  298. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  299. data/lib/rigor/sig_gen/renderer.rb +4 -5
  300. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  301. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  302. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  303. data/lib/rigor/sig_gen/writer.rb +23 -6
  304. data/lib/rigor/signature_path_audit.rb +153 -6
  305. data/lib/rigor/source/literals.rb +0 -23
  306. data/lib/rigor/source/node_children.rb +0 -2
  307. data/lib/rigor/source/node_locator.rb +5 -11
  308. data/lib/rigor/source/node_walker.rb +2 -5
  309. data/lib/rigor/triage/catalogue.rb +1 -3
  310. data/lib/rigor/triage.rb +3 -5
  311. data/lib/rigor/type/accepts_result.rb +24 -6
  312. data/lib/rigor/type/combinator.rb +68 -5
  313. data/lib/rigor/type/data_class.rb +2 -2
  314. data/lib/rigor/type/data_instance.rb +4 -4
  315. data/lib/rigor/type/float_range.rb +128 -0
  316. data/lib/rigor/type/hash_shape.rb +5 -5
  317. data/lib/rigor/type/integer_range.rb +13 -8
  318. data/lib/rigor/type/nominal.rb +8 -2
  319. data/lib/rigor/type/refined.rb +3 -3
  320. data/lib/rigor/type/struct_class.rb +3 -3
  321. data/lib/rigor/type/struct_instance.rb +4 -4
  322. data/lib/rigor/type.rb +1 -0
  323. data/lib/rigor/type_node/generic.rb +1 -1
  324. data/lib/rigor/type_node/range_literal.rb +25 -0
  325. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  326. data/lib/rigor/type_node.rb +1 -0
  327. data/lib/rigor/version.rb +1 -1
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  329. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  330. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  332. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  333. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  334. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  337. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  338. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  340. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  341. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  342. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  343. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  347. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  348. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  349. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  351. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  352. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  353. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  354. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  355. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  356. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  357. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  358. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  359. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  360. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  361. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  362. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  363. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  364. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  365. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  366. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  367. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  369. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  370. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  371. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  372. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  373. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  374. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  375. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  376. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  377. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  378. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  379. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  382. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  383. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  384. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  386. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  387. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  388. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  395. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  396. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  397. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  398. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  399. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  400. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  403. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  404. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  405. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  406. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  407. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  408. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  409. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  410. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  414. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  415. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  416. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  417. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  423. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  424. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  425. data/sig/rigor/cache.rbs +2 -3
  426. data/sig/rigor/environment.rbs +5 -0
  427. data/sig/rigor/inference.rbs +3 -10
  428. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  429. data/sig/rigor/plugin/manifest.rbs +2 -1
  430. data/sig/rigor/reflection.rbs +3 -1
  431. data/sig/rigor/scope.rbs +24 -3
  432. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  433. data/sig/rigor/type.rbs +27 -1
  434. data/sig/rigor.rbs +1 -0
  435. data/skills/rigor-ask/SKILL.md +8 -6
  436. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  437. data/skills/rigor-project-init/SKILL.md +6 -3
  438. data/skills/rigor-project-init/references/02-configure.md +10 -6
  439. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  440. data/skills/rigor-type-oracle/SKILL.md +228 -0
  441. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  442. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  443. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  444. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  445. metadata +26 -3
  446. data/lib/rigor/cli/probe_environment.rb +0 -85
  447. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -13,6 +13,7 @@ require_relative "environment/constant_type_cache_holder"
13
13
  require_relative "environment/missing_gem_constant_index"
14
14
  require_relative "environment/bundle_sig_discovery"
15
15
  require_relative "environment/lockfile_resolver"
16
+ require_relative "environment/installed_gem_set"
16
17
  require_relative "environment/rbs_collection_discovery"
17
18
  require_relative "plugin/isolation"
18
19
  require_relative "environment/rbs_coverage_report"
@@ -48,16 +49,15 @@ module Rigor
48
49
  :reporters, :name_scope,
49
50
  :synthetic_method_index, :project_patched_methods
50
51
 
51
- # @param class_registry [Rigor::Environment::ClassRegistry]
52
- # @param rbs_loader [Rigor::Environment::RbsLoader, nil] when nil the environment is "RBS-blind"; useful
52
+ # @param rbs_loader — when nil the environment is "RBS-blind"; useful
53
53
  # in tests that want to assert how the engine behaves without RBS data. The default Environment wires
54
54
  # the shared core loader, which is itself lazy: requesting an environment instance does NOT load RBS
55
55
  # until a method or class query actually consults the loader.
56
- # @param plugin_registry [Rigor::Plugin::Registry, nil] v0.1.1 Track 2 slice 7. The per-run plugin
56
+ # @param plugin_registry v0.1.1 Track 2 slice 7. The per-run plugin
57
57
  # registry the inference engine consults at call sites for plugin `dynamic_return` rules. When nil
58
58
  # (the default), no plugin-level return-type contribution participates — useful for tests, the
59
59
  # `Environment.default` facade, and analyses that don't load plugins.
60
- # @param dependency_source_index [Rigor::Analysis::DependencySourceInference::Index, nil] ADR-10 slice
60
+ # @param dependency_source_index ADR-10 slice
61
61
  # 2b-ii. The per-run index of opt-in gem sources the dispatcher consults BELOW RBS dispatch. When nil
62
62
  # (the default), no dep-source contribution participates and the dispatcher tier is a no-op.
63
63
  def initialize(class_registry: ClassRegistry.default, rbs_loader: nil, # rubocop:disable Metrics/ParameterLists
@@ -151,7 +151,7 @@ module Rigor
151
151
  # defect. First-write-wins on the slot means a run whose overlay AND scan both raise reports the
152
152
  # overlay — the actionable one, since the plugin is what a user can remove.
153
153
  #
154
- # @return [Rigor::Inference::HktRegistry] the registry the RBS scan is layered on top of.
154
+ # @return the registry the RBS scan is layered on top of.
155
155
  def pre_scan_hkt_registry
156
156
  return @hkt_registry_base unless @plugin_registry.respond_to?(:hkt_overlay_registry)
157
157
 
@@ -171,8 +171,6 @@ module Rigor
171
171
  # worker (the coordinator never analyses a file under the pool, so the drain is how its snapshot learns
172
172
  # the outcome: the #696 lesson), and from a resolved environment when the analyze set is empty — so the
173
173
  # row never depends on which files happened to be analysed.
174
- #
175
- # @return [Array(String, String, String, Symbol), Array(String, String, nil, Symbol), nil]
176
174
  def hkt_scan_failure
177
175
  @hkt_scan_failure.value
178
176
  end
@@ -255,18 +253,17 @@ module Rigor
255
253
  # Builds an Environment that consults the project's local signatures and any opt-in stdlib libraries on
256
254
  # top of RBS core.
257
255
  #
258
- # @param root [String, Pathname] project root used to auto-detect the default signature path. Defaults
256
+ # @param root project root used to auto-detect the default signature path. Defaults
259
257
  # to the current working directory.
260
- # @param libraries [Array<String, Symbol>] additional stdlib libraries to load on top of
258
+ # @param libraries additional stdlib libraries to load on top of
261
259
  # {DEFAULT_LIBRARIES}. The final list is the union of the two, de-duplicated while preserving order.
262
260
  # Pass an empty array (the default) to load only the defaults.
263
- # @param signature_paths [Array<String, Pathname>, nil] explicit list of `sig/`-style directories. When
261
+ # @param signature_paths explicit list of `sig/`-style directories. When
264
262
  # `nil` (the default), the canonical project layout `<root>/sig` is used if it exists, otherwise no
265
263
  # signature path is loaded.
266
- # @param cache_store [Rigor::Cache::Store, nil] persistent cache threaded into the underlying
264
+ # @param cache_store persistent cache threaded into the underlying
267
265
  # {Environment::RbsLoader} so constant lookups (and, in later v0.0.9 slices, other reflection
268
266
  # artefacts) consult the cache. Pass `nil` (the default) to skip caching for this environment.
269
- # @return [Rigor::Environment]
270
267
  # rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
271
268
  def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil,
272
269
  plugin_registry: nil, dependency_source_index: nil,
@@ -336,7 +333,7 @@ module Rigor
336
333
  missing_gems, overlay_paths = missing_gems_and_overlay_paths(
337
334
  locked: locked, default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
338
335
  rbs_collection_paths: collection_paths, plugin_registry: plugin_registry,
339
- signature_paths: resolved_paths
336
+ signature_paths: resolved_paths, bundle_root: bundle_root
340
337
  )
341
338
  loader_signature_paths = resolved_paths + plugin_sig_paths + gem_sig_paths +
342
339
  collection_paths + overlay_paths
@@ -361,6 +358,13 @@ module Rigor
361
358
  # the bundle walk and `rbs collection install` all stay authoritative.
362
359
  deferred_signature_paths: plugin_sig_paths
363
360
  )
361
+ # Issue #824 / ADR-32 WD13 — one `:info` per inline member the loader stood down against the
362
+ # project's own `sig/`. Recorded HERE because this is where the reporter is: the run's
363
+ # `source_rbs_synthesis` stream already carries WD12's "parsed but not honoured" rows, drains out of
364
+ # pool workers, and is regenerated (never cached) on every run, so the member-level stand-down needs
365
+ # no plumbing of its own. Costs nothing for a project with no inline RBS: the loader's reader
366
+ # short-circuits on an empty `virtual_rbs`.
367
+ record_inline_member_standdowns(loader, source_rbs_synthesis_reporter, root)
364
368
  # ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
365
369
  # `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
366
370
  # don't consult HKT (e.g. `rigor check --cache-stats --no-stats`) don't pay the eager env-build cost
@@ -412,13 +416,13 @@ module Rigor
412
416
  # and consumed twice: as `[gem_name, version]` pairs for the ADR-82 WD9 constant-ownership index, and
413
417
  # as the eligible-gem set for the ADR-72 overlay resolution. Returns `[pairs, overlay_paths]`.
414
418
  def missing_gems_and_overlay_paths(locked:, default_libraries:, bundle_sig_paths:,
415
- rbs_collection_paths:, plugin_registry:, signature_paths: [])
416
- return [[], []] if locked.empty?
419
+ rbs_collection_paths:, plugin_registry:, signature_paths: [],
420
+ bundle_root: nil)
421
+ if locked.empty?
422
+ return unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
423
+ end
417
424
 
418
- rows = RbsCoverageReport.classify(
419
- locked_gems: locked, default_libraries: default_libraries,
420
- bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
421
- ).select { |row| row.source == :missing }
425
+ rows = missing_rows(locked, default_libraries, bundle_sig_paths, rbs_collection_paths)
422
426
  overlays = gem_overlay_paths(
423
427
  missing_gem_names: rows.map(&:gem_name), plugin_registry: plugin_registry,
424
428
  signature_paths: signature_paths
@@ -426,6 +430,27 @@ module Rigor
426
430
  [rows.map { |row| [row.gem_name, row.version] }, overlays]
427
431
  end
428
432
 
433
+ # Issue #530 — a project with no `Gemfile.lock` still has gems on disk, and before this it handed the
434
+ # ADR-82 WD9 index nothing, so every constant reaching into an RBS-less gem kept the generic cause and
435
+ # the whole boundary reported as `engine_gap` rather than `add_rbs`. The installed set stands in for
436
+ # the locked one ({InstalledGemSet} says why that stays honest), and only for the provenance index:
437
+ # the second element is always empty because the ADR-72 overlays change what type-checks and so remain
438
+ # gated on the project's own declaration.
439
+ def unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
440
+ installed = InstalledGemSet.gems(bundle_path: bundle_root)
441
+ return [[], []] if installed.empty?
442
+
443
+ rows = missing_rows(installed, default_libraries, bundle_sig_paths, rbs_collection_paths)
444
+ [rows.map { |row| [row.gem_name, row.version] }, []]
445
+ end
446
+
447
+ def missing_rows(gems, default_libraries, bundle_sig_paths, rbs_collection_paths)
448
+ RbsCoverageReport.classify(
449
+ locked_gems: gems, default_libraries: default_libraries,
450
+ bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
451
+ ).select { |row| row.source == :missing }
452
+ end
453
+
429
454
  # ADR-72 — resolve the bundled RBS overlay directories to load for this project. A gem is eligible when
430
455
  # it is classified `:missing` (see {missing_rbs_gem_rows}) and its conflicting opt-in plugin twin (if
431
456
  # any) is NOT already reachable. Returns `[Pathname]`, deterministically ordered, or `[]` when no
@@ -540,6 +565,54 @@ module Rigor
540
565
  reporter&.record(plugin_id: plugin.manifest.id, path: path, message: message.to_s, kind: kind)
541
566
  end
542
567
 
568
+ # ADR-32 WD13 — turn each {Environment::RbsLoader#inline_member_standdowns} record into a WD12
569
+ # `:not_honoured` entry, so the run's `plugin.rbs-inline.source-rbs-annotation-not-honoured` row names
570
+ # BOTH sources and which one won. WD12's rule is what makes this necessary rather than optional: an
571
+ # annotation Rigor parses and does not honour is reported, never swallowed — and a silent per-member
572
+ # strip is exactly the shape that rule forbids.
573
+ #
574
+ # The row is positioned at the annotated `.rb` (line 1, as every entry on this stream is): the member's
575
+ # own position lives in the synthesized RBS buffer, which describes a document nobody has, and the
576
+ # ADR-54 environment cache drops positions anyway, so a line read off it would differ warm and cold.
577
+ def record_inline_member_standdowns(loader, reporter, root)
578
+ return if reporter.nil?
579
+
580
+ loader.inline_member_standdowns.each do |class_name, method_name, kind, signature_path, virtual_name|
581
+ plugin_id, path = split_virtual_source_name(virtual_name)
582
+ next if path.nil?
583
+
584
+ reporter.record(
585
+ plugin_id: plugin_id, path: path, kind: :not_honoured,
586
+ message: inline_member_standdown_message(class_name, method_name, kind, signature_path, root)
587
+ )
588
+ end
589
+ end
590
+
591
+ # Splits a virtual buffer name back into the pair {.collect_virtual_rbs} composed it from
592
+ # (`virtual:<plugin id>:<source path>`). Limited to three fields so a path carrying a colon survives.
593
+ # Returns `[nil, nil]` for a name in any other shape — a synthesizer channel this method does not own.
594
+ def split_virtual_source_name(virtual_name)
595
+ prefix, plugin_id, path = virtual_name.to_s.split(":", 3)
596
+ return [nil, nil] unless prefix == "virtual" && plugin_id && !plugin_id.empty? && path && !path.empty?
597
+
598
+ [plugin_id, path]
599
+ end
600
+
601
+ def inline_member_standdown_message(class_name, method_name, kind, signature_path, root)
602
+ separator = kind == :singleton ? "." : "#"
603
+ "`#{class_name}#{separator}#{method_name}` is also declared in " \
604
+ "`#{path_relative_to(signature_path, root)}`, and an explicit `.rbs` declaration wins over an " \
605
+ "inline annotation for the same member, so the inline signature was dropped. Left to collide the " \
606
+ "two would fail the class's definition build, and every call on it — real methods and typos " \
607
+ "alike — would read `Dynamic[top]`. Remove one of the two declarations to make the inline " \
608
+ "annotation bind."
609
+ end
610
+
611
+ def path_relative_to(path, root)
612
+ prefix = "#{root}#{File::SEPARATOR}"
613
+ path.to_s.start_with?(prefix) ? path.to_s.delete_prefix(prefix) : path.to_s
614
+ end
615
+
543
616
  SYNTHESIZER_CACHE_PRODUCER_ID = "plugin.source_rbs_synthesizer"
544
617
  private_constant :SYNTHESIZER_CACHE_PRODUCER_ID
545
618
 
@@ -653,6 +726,15 @@ module Rigor
653
726
  rbs_loader.rbs_module?(name)
654
727
  end
655
728
 
729
+ # Issue #915 — the module names RBS puts in `name`'s SINGLETON ancestry, the declaration-side twin of
730
+ # `Scope#singleton_extends_of`. `Inference::Narrowing` reads it the same one-directional way: presence
731
+ # withholds a `Bot`, absence asserts nothing (a class the environment does not know answers `[]`).
732
+ def singleton_extended_modules(name)
733
+ return [] unless rbs_loader
734
+
735
+ rbs_loader.singleton_extended_module_names(normalize_class_name(name))
736
+ end
737
+
656
738
  # Compares two class/module names using analyzer-owned class data. Returns `:equal`, `:subclass`,
657
739
  # `:superclass`, `:disjoint`, or `:unknown`. The static registry handles built-ins cheaply; the RBS
658
740
  # loader handles project/stdlib classes without relying on host Ruby constants being loaded.
@@ -677,8 +759,11 @@ module Rigor
677
759
  rbs_loader.class_known?(name)
678
760
  end
679
761
 
762
+ # Allocates only when there is a prefix to strip: `String#delete_prefix` copies unconditionally, and
763
+ # this runs once per class-registry / hierarchy probe (~320k times on the lib self-check).
680
764
  def normalize_class_name(name)
681
- name.to_s.delete_prefix("::")
765
+ name = name.to_s
766
+ name.start_with?("::") ? name.delete_prefix("::") : name
682
767
  end
683
768
 
684
769
  # ADR-13 slice 3b — composes the per-run plugin-supplied {Rigor::TypeNode::ResolverChain} into a single
@@ -53,8 +53,6 @@ module Rigor
53
53
 
54
54
  module_function
55
55
 
56
- # @param contributions [Array<FlowContribution>]
57
- # @return [MergeResult]
58
56
  def merge(contributions)
59
57
  contributions = Array(contributions)
60
58
  return MergeResult.new if contributions.empty?
@@ -49,24 +49,24 @@ module Rigor
49
49
 
50
50
  attr_reader(*SLOT_NAMES, :provenance)
51
51
 
52
- # @param return_type [Object, nil] normal-edge return type. Use `nil` when the contribution does not
52
+ # @param return_type normal-edge return type. Use `nil` when the contribution does not
53
53
  # refine the return type selected from the RBS contract.
54
- # @param truthy_facts [Array, nil] facts that hold only on the truthy control-flow edge. Edge-local: a
54
+ # @param truthy_facts facts that hold only on the truthy control-flow edge. Edge-local: a
55
55
  # truthy-edge fact does NOT imply its falsey-edge complement (ADR-2 § "Plugin Contribution Merging").
56
- # @param falsey_facts [Array, nil] dual of `truthy_facts`.
57
- # @param post_return_facts [Array, nil] facts that hold after the call returns normally on every edge —
56
+ # @param falsey_facts dual of `truthy_facts`.
57
+ # @param post_return_facts facts that hold after the call returns normally on every edge —
58
58
  # the carrier for assertion-style contributions.
59
- # @param mutations [Array, nil] receiver and argument mutation effects.
60
- # @param invalidations [Array, nil] targeted fact invalidations beyond what mutation effects already
59
+ # @param mutations receiver and argument mutation effects.
60
+ # @param invalidations targeted fact invalidations beyond what mutation effects already
61
61
  # imply.
62
- # @param exceptional [Object, nil] non-returning, raising, or unreachable effect.
63
- # @param role_conformance [Array, nil] capability-role conformance facts the contribution provides.
64
- # @param effects [Rigor::Effects::LabelSet, nil] ADR-103 WD5 — the effect labels this call edge
62
+ # @param exceptional non-returning, raising, or unreachable effect.
63
+ # @param role_conformance capability-role conformance facts the contribution provides.
64
+ # @param effects ADR-103 WD5 — the effect labels this call edge
65
65
  # attributes to its callee, as an upper bound. `nil` means "says nothing about effects", which is
66
66
  # NOT the same as the empty set (which asserts the call performs none). Merged by union, the
67
67
  # conservative direction: two sources that each name part of a call's footprint together name
68
68
  # more of it, and neither can shrink the other's claim.
69
- # @param provenance [Provenance] source-family, plugin-id, node, and cache-descriptor metadata. Defaults
69
+ # @param provenance source-family, plugin-id, node, and cache-descriptor metadata. Defaults
70
70
  # to `Provenance.builtin`.
71
71
  # rubocop:disable Metrics/ParameterLists
72
72
  def initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil,
@@ -87,7 +87,7 @@ module Rigor
87
87
  freeze
88
88
  end
89
89
 
90
- # @return [Boolean] true when every content slot is unset (nil or an empty collection). Provenance does
90
+ # @return true when every content slot is unset (nil or an empty collection). Provenance does
91
91
  # not count toward emptiness — an empty bundle still carries source attribution.
92
92
  def empty?
93
93
  SLOT_NAMES.all? { |slot| slot_empty?(slot, public_send(slot)) }
@@ -116,8 +116,6 @@ module Rigor
116
116
  # | exceptional | exceptional | exception | :raise |
117
117
  # | role_conformance | normal | role | (per-role target) |
118
118
  # | effects | normal | effects | :effects |
119
- #
120
- # @return [Array<Element>]
121
119
  def to_element_list # rubocop:disable Metrics/AbcSize
122
120
  elements = []
123
121
  elements << element_for(:return, :normal, :return_type, return_type) unless return_type.nil?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../type"
4
+ require_relative "range_constant"
4
5
 
5
6
  module Rigor
6
7
  module Inference
@@ -27,10 +28,7 @@ module Rigor
27
28
  module Acceptance
28
29
  module_function
29
30
 
30
- # @param self_type [Rigor::Type]
31
- # @param other_type [Rigor::Type]
32
- # @param mode [Symbol] `:gradual` (default) or `:strict`.
33
- # @return [Rigor::Type::AcceptsResult]
31
+ # @param mode — `:gradual` (default) or `:strict`.
34
32
  def accepts(self_type, other_type, mode: :gradual)
35
33
  raise ArgumentError, "Acceptance mode #{mode.inspect} is not implemented yet" unless mode == :gradual
36
34
 
@@ -63,6 +61,7 @@ module Rigor
63
61
  Type::Nominal => :accepts_nominal,
64
62
  Type::Constant => :accepts_constant,
65
63
  Type::IntegerRange => :accepts_integer_range,
64
+ Type::FloatRange => :accepts_float_range,
66
65
  Type::Difference => :accepts_difference,
67
66
  Type::Refined => :accepts_refined,
68
67
  Type::Intersection => :accepts_intersection,
@@ -201,7 +200,8 @@ module Rigor
201
200
  # {#accepts_nominal_args});
202
201
  # - Constant[v] when v.is_a?(klass(C)). The type_args of self are ignored here because a Constant
203
202
  # carries a concrete value, not a generic instantiation, and the analyzer has no way to refute the
204
- # args from a literal alone.
203
+ # args from a literal alone. A Range value is the one exception — see
204
+ # {#accepts_range_constant_type_arg}.
205
205
  # - Tuple[*] when self is the Array (or a supertype) family. The Tuple is projected to
206
206
  # `Nominal[Array, [union(elements)]]` so the existing generic-arg machinery handles it.
207
207
  # - HashShape{*} when self is the Hash (or a supertype) family, projected to
@@ -215,6 +215,7 @@ module Rigor
215
215
  when Type::Singleton then accepts_nominal_from_singleton(self_type, other_type, mode)
216
216
  when Type::StructClass, Type::DataClass then accepts_nominal_from_class_factory(self_type, other_type, mode)
217
217
  when Type::IntegerRange then accepts_nominal_from_integer_range(self_type, other_type, mode)
218
+ when Type::FloatRange then accepts_nominal_from_float_range(self_type, other_type, mode)
218
219
  else accepts_nominal_from_shape(self_type, other_type, mode)
219
220
  end
220
221
  end
@@ -311,6 +312,32 @@ module Rigor
311
312
  end
312
313
  end
313
314
 
315
+ # ADR-109 WD4 — the Float twin of the IntegerRange rule: `Nominal[Float]` and everything Float
316
+ # is-a accept any `FloatRange`; nothing else does.
317
+ FLOAT_NOMINAL_ANCESTORS = %w[Float Numeric Comparable Object BasicObject].freeze
318
+ private_constant :FLOAT_NOMINAL_ANCESTORS
319
+
320
+ def accepts_nominal_from_float_range(self_type, _other_type, mode)
321
+ unless self_type.type_args.empty?
322
+ return Type::AcceptsResult.no(
323
+ mode: mode,
324
+ reasons: "Nominal[#{self_type.class_name}] with type args rejects FloatRange"
325
+ )
326
+ end
327
+
328
+ if FLOAT_NOMINAL_ANCESTORS.include?(self_type.class_name)
329
+ Type::AcceptsResult.yes(
330
+ mode: mode,
331
+ reasons: "FloatRange is-a #{self_type.class_name}"
332
+ )
333
+ else
334
+ Type::AcceptsResult.no(
335
+ mode: mode,
336
+ reasons: "Nominal[#{self_type.class_name}] rejects FloatRange"
337
+ )
338
+ end
339
+ end
340
+
314
341
  # v0.0.2 — meta-type rule. A `Singleton[T]` is the class object for `T`, so it is an instance of
315
342
  # `Class` (when `T` is a class) and always an instance of `Module`. Without this rule a method whose
316
343
  # parameter is typed `Class | Module` would reject every `is_a?(SomeClass)` call and similar
@@ -492,7 +519,7 @@ module Rigor
492
519
 
493
520
  def accepts_nominal_from_constant(self_type, constant, mode)
494
521
  ruby_class = resolve_class(self_type.class_name)
495
- return constant_is_a_result(ruby_class, constant, self_type, mode) if ruby_class
522
+ return accepts_loaded_class_from_constant(ruby_class, constant, self_type, mode) if ruby_class
496
523
 
497
524
  # The host process may not have required the constant's declared self_type (e.g. `BigDecimal`
498
525
  # since Ruby 3.4 is no longer a default gem). Fall back to inspecting the value's own class
@@ -527,6 +554,13 @@ module Rigor
527
554
  end
528
555
  end
529
556
 
557
+ def accepts_loaded_class_from_constant(ruby_class, constant, self_type, mode)
558
+ result = constant_is_a_result(ruby_class, constant, self_type, mode)
559
+ return result unless result.yes?
560
+
561
+ accepts_range_constant_type_arg(self_type, constant, mode) || result
562
+ end
563
+
530
564
  def constant_is_a_result(ruby_class, constant, self_type, mode)
531
565
  if constant.value.is_a?(ruby_class)
532
566
  Type::AcceptsResult.yes(mode: mode, reasons: "Constant value is_a?(#{self_type.class_name})")
@@ -538,10 +572,53 @@ module Rigor
538
572
  end
539
573
  end
540
574
 
575
+ # Issue #833 — the single exception to "the type_args of self are ignored for a Constant".
576
+ #
577
+ # A `Constant<Range>` is the one literal that does pin a generic instantiation: the engine
578
+ # builds it only when both endpoints are static, so `Range[T]` CAN be refuted from the literal.
579
+ # Without this, `Nominal[Range, [Integer]]` accepted `0.0...1.0` and `OverloadSelector` pass 1
580
+ # pinned whichever `Range[…]` arm came first in declaration order — for `Kernel#rand` that is
581
+ # `(::Range[Integer]) -> Integer?`, ahead of every Float arm.
582
+ #
583
+ # Returns nil — "no opinion, keep the class-level answer" — outside the envelope: a raw `Range`
584
+ # or any other arity of type_args, and a range the shared reader cannot read endpoints from.
585
+ # A missing endpoint contributes nothing, because a beginless or endless literal inhabits
586
+ # `Range[T]` for every T (`Range[Integer?]` is the common core spelling of the slicing param).
587
+ #
588
+ # Known coarse spot, deliberately not special-cased: a mixed-endpoint literal (`1.0..2`) is
589
+ # rejected by `Range[Integer]` and by `Range[Float]` alike, so overload selection falls through
590
+ # to its gradual pass and then to the first-overload fallback exactly as it did before.
591
+ def accepts_range_constant_type_arg(self_type, constant, mode)
592
+ return nil unless self_type.type_args.size == 1
593
+
594
+ endpoints = RangeConstant.literal_endpoints(constant)
595
+ return nil if endpoints.nil?
596
+
597
+ element = self_type.type_args.first
598
+ combine_range_endpoint_results(
599
+ endpoints.map { |endpoint| accepts(element, Type::Combinator.constant_of(endpoint), mode: mode) },
600
+ element,
601
+ mode
602
+ )
603
+ end
604
+
605
+ def combine_range_endpoint_results(per_endpoint, element, mode)
606
+ if per_endpoint.any?(&:no?)
607
+ Type::AcceptsResult.no(mode: mode, reasons: "a Range endpoint is rejected by #{element.describe}")
608
+ elsif per_endpoint.any?(&:maybe?)
609
+ Type::AcceptsResult.maybe(
610
+ mode: mode,
611
+ reasons: "a Range endpoint could not be proven accepted by #{element.describe}"
612
+ )
613
+ else
614
+ Type::AcceptsResult.yes(mode: mode, reasons: "every Range endpoint accepted by #{element.describe}")
615
+ end
616
+ end
617
+
541
618
  # IntegerRange[a..b] accepts:
542
619
  # - Constant[n] where n is an Integer covered by [a..b];
543
620
  # - IntegerRange[c..d] where [c..d] ⊆ [a..b];
544
- # - Nominal[Integer] only when self is the universal range (`int<min, max>`), since otherwise an
621
+ # - Nominal[Integer] only when self is the universal range (`Integer[min..max]`), since otherwise an
545
622
  # arbitrary Integer could fall outside the bound.
546
623
  # Anything else is rejected.
547
624
  def accepts_integer_range(self_type, other_type, mode)
@@ -616,6 +693,65 @@ module Rigor
616
693
  end
617
694
  end
618
695
 
696
+ # ADR-109 WD4 — `FloatRange` accepts:
697
+ # - Constant[f] where f is a Float the range covers (never NaN: `cover?` compares);
698
+ # - FloatRange[c..d] whose canonical closed bounds sit inside self's.
699
+ # `Nominal[Float]` is never accepted, not even by the universal range: an arbitrary Float may be
700
+ # NaN, and the universal range is exactly "every Float except NaN".
701
+ def accepts_float_range(self_type, other_type, mode)
702
+ case other_type
703
+ when Type::Constant
704
+ accepts_float_range_from_constant(self_type, other_type, mode)
705
+ when Type::FloatRange
706
+ accepts_float_range_from_float_range(self_type, other_type, mode)
707
+ when Type::Nominal
708
+ Type::AcceptsResult.no(
709
+ mode: mode,
710
+ reasons: "#{self_type.describe} rejects Nominal[#{other_type.class_name}] (could be NaN or fall outside)"
711
+ )
712
+ else
713
+ Type::AcceptsResult.no(
714
+ mode: mode,
715
+ reasons: "FloatRange rejects #{other_type.class}"
716
+ )
717
+ end
718
+ end
719
+
720
+ def accepts_float_range_from_constant(self_type, constant, mode)
721
+ unless constant.value.is_a?(Float)
722
+ return Type::AcceptsResult.no(
723
+ mode: mode,
724
+ reasons: "FloatRange rejects non-Float Constant"
725
+ )
726
+ end
727
+
728
+ if self_type.covers?(constant.value)
729
+ Type::AcceptsResult.yes(
730
+ mode: mode,
731
+ reasons: "Constant[#{constant.value}] is in #{self_type.describe}"
732
+ )
733
+ else
734
+ Type::AcceptsResult.no(
735
+ mode: mode,
736
+ reasons: "Constant[#{constant.value}] outside #{self_type.describe}"
737
+ )
738
+ end
739
+ end
740
+
741
+ def accepts_float_range_from_float_range(self_type, other_range, mode)
742
+ if self_type.min <= other_range.min && other_range.canonical_max <= self_type.canonical_max
743
+ Type::AcceptsResult.yes(
744
+ mode: mode,
745
+ reasons: "#{other_range.describe} ⊆ #{self_type.describe}"
746
+ )
747
+ else
748
+ Type::AcceptsResult.no(
749
+ mode: mode,
750
+ reasons: "#{other_range.describe} not contained in #{self_type.describe}"
751
+ )
752
+ end
753
+ end
754
+
619
755
  # `Difference[base, removed]` accepts another type X when the base accepts X *and* X's value set is
620
756
  # provably disjoint from `removed`. The disjointness test is the subtle part — it is NOT the same as
621
757
  # `removed.accepts(X)`, because `Nominal[String]` includes `""` even though `Constant[""]` does not
@@ -936,7 +1072,7 @@ module Rigor
936
1072
  def resolve_class(name)
937
1073
  return nil unless name.is_a?(::String)
938
1074
 
939
- parts = name.delete_prefix("::").split("::")
1075
+ parts = class_name_parts(name)
940
1076
  return nil if parts.empty?
941
1077
 
942
1078
  mod = ::Object
@@ -950,7 +1086,16 @@ module Rigor
950
1086
  rescue ::StandardError, ::ScriptError, ::SystemExit
951
1087
  nil
952
1088
  end
1089
+
1090
+ # #775 — the split of a class name is a pure function of the string, and the subtype check asks it
1091
+ # twice per Nominal-vs-Nominal comparison (~370k times on the lib self-check) for the same few
1092
+ # hundred names. Only the split is memoised; the constant walk above still runs every time, so a
1093
+ # class loaded (or a spec constant removed) after the first ask is answered by the live tree.
1094
+ def class_name_parts(name)
1095
+ @class_name_parts[name] ||= name.delete_prefix("::").split("::").freeze
1096
+ end
953
1097
  end
1098
+ @class_name_parts = {}
954
1099
  end
955
1100
  end
956
1101
  end
@@ -34,15 +34,14 @@ module Rigor
34
34
  #
35
35
  # See docs/internal-spec/inference-engine.md for the binding contract.
36
36
  class BlockParameterBinder
37
- # @param expected_param_types [Array<Rigor::Type>] positional block parameter types in order. Indices
37
+ # @param expected_param_types positional block parameter types in order. Indices
38
38
  # the binder cannot fill from this array (because the array is shorter than the parameter list, or
39
39
  # because the slot is a kind we do not pull from the array) default to `Dynamic[Top]`.
40
40
  def initialize(expected_param_types: [])
41
41
  @expected_param_types = expected_param_types
42
42
  end
43
43
 
44
- # @param block_node [Prism::BlockNode]
45
- # @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type. Anonymous
44
+ # @return ordered map from parameter name to bound type. Anonymous
46
45
  # parameters are skipped; MultiTargetNode destructuring slots delegate to {MultiTargetBinder} and
47
46
  # contribute every named local in declaration order. Numbered-parameter forms (`_1`, `_2`, ...) bind
48
47
  # `:_1`, `:_2`, ... up to the maximum the block body refers to.
@@ -26,12 +26,12 @@ module Rigor
26
26
 
27
27
  module_function
28
28
 
29
- # @param names [Array<Symbol>] the outer locals the body can rebind.
30
- # @param seed_bindings [Hash{Symbol=>Type}] pre-state binding per name.
31
- # @param widen [#call] value-pinned widener (Constant -> Nominal).
32
- # @param evaluate_body [#call] `bindings -> exit_bindings` — evaluates the body once from `bindings`
29
+ # @param names the outer locals the body can rebind.
30
+ # @param seed_bindings pre-state binding per name.
31
+ # @param widen value-pinned widener (Constant -> Nominal).
32
+ # @param evaluate_body `bindings -> exit_bindings` — evaluates the body once from `bindings`
33
33
  # (the per-name current assumption) and returns the per-name exit binding it produced.
34
- # @return [Hash{Symbol=>Type}] the continuation binding per name.
34
+ # @return the continuation binding per name.
35
35
  def converge(names:, seed_bindings:, widen:, evaluate_body:)
36
36
  return {} if names.empty?
37
37
 
@@ -65,8 +65,6 @@ module Rigor
65
65
  # plain `(receiver, method)` signature was already on the recursion guard stack.
66
66
  # - {MEMO_REFUSE_UNROLL} — pre-ADR-84 blanket refusal (constant-arg unroll in flight). Kept in the table
67
67
  # as the ADR-84 WD3 gate pin: structurally unreachable, must read ~0.
68
- # - {MEMO_REFUSE_CONSULT_TAINTED} — a candidate frame computed a result but an ADR-55 fixpoint summary
69
- # was *consulted* during the compute, so the result is a transient Kleene iterate and was NOT stored.
70
68
  # - {MEMO_REFUSE_TRANSIENT} — ADR-84 WD3: a candidate frame's compute bracket saw a
71
69
  # transient-machinery event (recursion-guard hit / unroll-fuel exhaustion / ADR-55 WD1 clamp /
72
70
  # fixpoint-cap collapse — `ExpressionTyper#note_transient_fallback`) that referenced a stack frame
@@ -79,15 +77,13 @@ module Rigor
79
77
  MEMO_BODY_EVALS = :memo_body_evals
80
78
  MEMO_REFUSE_ON_STACK = :memo_refuse_on_stack
81
79
  MEMO_REFUSE_UNROLL = :memo_refuse_unroll
82
- MEMO_REFUSE_CONSULT_TAINTED = :memo_refuse_consult_tainted
83
80
  MEMO_REFUSE_TRANSIENT = :memo_refuse_transient
84
81
 
85
82
  CATEGORIES = [
86
83
  RECURSION_GUARD, ANCESTOR_WALK_LIMIT, HKT_FUEL_EXHAUSTED, RECURSION_UNROLL_FUEL,
87
84
  RECURSION_FIXPOINT_CAP, BLOCK_WRITEBACK_CAP,
88
85
  MEMO_ENTRIES, MEMO_HITS, MEMO_MISSES, MEMO_BODY_EVALS,
89
- MEMO_REFUSE_ON_STACK, MEMO_REFUSE_UNROLL, MEMO_REFUSE_CONSULT_TAINTED,
90
- MEMO_REFUSE_TRANSIENT
86
+ MEMO_REFUSE_ON_STACK, MEMO_REFUSE_UNROLL, MEMO_REFUSE_TRANSIENT
91
87
  ].freeze
92
88
 
93
89
  # Distribution (histogram) categories — read-only observations of a value's size at a site, used to
@@ -30,9 +30,8 @@ module Rigor
30
30
 
31
31
  module_function
32
32
 
33
- # @param block_node [Prism::BlockNode]
34
- # @param base_scope [Rigor::Scope] the call-site scope the block closes over.
35
- # @return [Array<Symbol>] the captured names the body writes, each once, in first-write order.
33
+ # @param base_scope — the call-site scope the block closes over.
34
+ # @return the captured names the body writes, each once, in first-write order.
36
35
  def writes(block_node, base_scope)
37
36
  body = block_node.body
38
37
  return [] if body.nil?
@@ -51,11 +51,9 @@ module Rigor
51
51
  module ClosureEscapeAnalyzer
52
52
  module_function
53
53
 
54
- # @param receiver_type [Rigor::Type, nil]
55
- # @param method_name [Symbol]
56
- # @param environment [Rigor::Environment, nil] reserved for the future sub-phase that consults
54
+ # @param environment reserved for the future sub-phase that consults
57
55
  # `RBS::Extended` call-timing effects; sub-phase 3a ignores it.
58
- # @return [Symbol] one of `:non_escaping`, `:escaping`, `:unknown`.
56
+ # @return one of `:non_escaping`, `:escaping`, `:unknown`.
59
57
  def classify(receiver_type:, method_name:, environment: nil) # rubocop:disable Lint/UnusedMethodArgument
60
58
  return :unknown if receiver_type.nil?
61
59
 
@@ -22,17 +22,17 @@ module Rigor
22
22
  # a tracer per visited node and discards the inferred type values.
23
23
  class CoverageScanner
24
24
  class Result < Data.define(:visits, :unrecognized, :events)
25
- # @return [Integer] sum of all visits across node classes.
25
+ # @return sum of all visits across node classes.
26
26
  def visited_count
27
27
  visits.values.sum
28
28
  end
29
29
 
30
- # @return [Integer] sum of directly-unrecognized counts across classes.
30
+ # @return sum of directly-unrecognized counts across classes.
31
31
  def unrecognized_count
32
32
  unrecognized.values.sum
33
33
  end
34
34
 
35
- # @return [Float] unrecognized_count / visited_count, or 0.0 when empty.
35
+ # @return unrecognized_count / visited_count, or 0.0 when empty.
36
36
  def unrecognized_ratio
37
37
  total = visited_count
38
38
  return 0.0 if total.zero?
@@ -41,13 +41,11 @@ module Rigor
41
41
  end
42
42
  end
43
43
 
44
- # @param scope [Rigor::Scope] base scope used for every type_of call. Defaults to `Scope.empty`.
44
+ # @param scope base scope used for every type_of call. Defaults to `Scope.empty`.
45
45
  def initialize(scope: nil)
46
46
  @scope = scope || Scope.empty
47
47
  end
48
48
 
49
- # @param root [Prism::Node]
50
- # @return [Result]
51
49
  def scan(root)
52
50
  visits = Hash.new(0)
53
51
  unrecognized = Hash.new(0)