rigortype 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +96 -15
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +39 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +12 -1
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +69 -13
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +100 -29
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
  78. data/lib/rigor/analysis/runner.rb +200 -43
  79. data/lib/rigor/analysis/worker_session.rb +36 -12
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +166 -30
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +70 -0
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
  97. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  98. data/lib/rigor/cache/store.rb +85 -33
  99. data/lib/rigor/cli/annotate_command.rb +5 -6
  100. data/lib/rigor/cli/check_command.rb +57 -10
  101. data/lib/rigor/cli/check_invocation.rb +6 -11
  102. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  103. data/lib/rigor/cli/coverage_command.rb +1 -1
  104. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  105. data/lib/rigor/cli/coverage_scan.rb +1 -5
  106. data/lib/rigor/cli/diff_command.rb +1 -1
  107. data/lib/rigor/cli/doc_links.rb +3 -3
  108. data/lib/rigor/cli/docs_command.rb +2 -2
  109. data/lib/rigor/cli/doctor_command.rb +85 -38
  110. data/lib/rigor/cli/effects_command.rb +2 -2
  111. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  112. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  113. data/lib/rigor/cli/explain_command.rb +34 -1
  114. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  115. data/lib/rigor/cli/lsp_command.rb +1 -1
  116. data/lib/rigor/cli/mcp_command.rb +1 -1
  117. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  118. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  119. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  120. data/lib/rigor/cli/plugin_command.rb +1 -1
  121. data/lib/rigor/cli/plugins_command.rb +2 -1
  122. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  123. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  124. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  125. data/lib/rigor/cli/sig_gen_command.rb +31 -2
  126. data/lib/rigor/cli/skill_command.rb +1 -1
  127. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  128. data/lib/rigor/cli/skill_describe.rb +36 -35
  129. data/lib/rigor/cli/trace_command.rb +4 -4
  130. data/lib/rigor/cli/trace_renderer.rb +5 -6
  131. data/lib/rigor/cli/triage_command.rb +1 -1
  132. data/lib/rigor/cli/type_of_command.rb +10 -6
  133. data/lib/rigor/cli/type_scan_command.rb +4 -4
  134. data/lib/rigor/cli/unused_command.rb +11 -3
  135. data/lib/rigor/cli/upgrade_command.rb +1 -1
  136. data/lib/rigor/cli.rb +68 -6
  137. data/lib/rigor/config_audit.rb +30 -5
  138. data/lib/rigor/configuration/severity_profile.rb +6 -6
  139. data/lib/rigor/configuration.rb +43 -4
  140. data/lib/rigor/effects/attribution.rb +1 -3
  141. data/lib/rigor/effects/config_envelopes.rb +6 -8
  142. data/lib/rigor/effects/definition_lines.rb +25 -6
  143. data/lib/rigor/effects/effect_table.rb +0 -1
  144. data/lib/rigor/effects/entry_points.rb +0 -2
  145. data/lib/rigor/effects/envelope_check.rb +8 -8
  146. data/lib/rigor/effects/envelope_index.rb +5 -8
  147. data/lib/rigor/effects/framework_units.rb +4 -6
  148. data/lib/rigor/effects/identity.rb +4 -6
  149. data/lib/rigor/effects/inline_anchor.rb +7 -7
  150. data/lib/rigor/effects/label_intent.rb +3 -4
  151. data/lib/rigor/effects/liskov_check.rb +8 -8
  152. data/lib/rigor/effects/method_key.rb +1 -1
  153. data/lib/rigor/effects/plugin_facts.rb +4 -6
  154. data/lib/rigor/effects/propagator.rb +2 -3
  155. data/lib/rigor/effects/scanner.rb +1 -1
  156. data/lib/rigor/effects/signature_sources.rb +3 -5
  157. data/lib/rigor/effects/snapshot.rb +10 -10
  158. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  159. data/lib/rigor/effects/unit_scan.rb +10 -10
  160. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  161. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  162. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  163. data/lib/rigor/environment/class_registry.rb +3 -1
  164. data/lib/rigor/environment/failure_slot.rb +28 -0
  165. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  166. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  167. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  168. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  169. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  170. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  171. data/lib/rigor/environment/rbs_loader.rb +613 -113
  172. data/lib/rigor/environment.rb +189 -27
  173. data/lib/rigor/flow_contribution/merger.rb +0 -2
  174. data/lib/rigor/flow_contribution.rb +11 -13
  175. data/lib/rigor/inference/acceptance.rb +153 -8
  176. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  177. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  178. data/lib/rigor/inference/budget_trace.rb +1 -5
  179. data/lib/rigor/inference/captured_locals.rb +2 -3
  180. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  181. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  182. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  183. data/lib/rigor/inference/element_read_widening.rb +168 -0
  184. data/lib/rigor/inference/expression_typer.rb +763 -143
  185. data/lib/rigor/inference/fork_map.rb +5 -7
  186. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  187. data/lib/rigor/inference/hkt_registry.rb +19 -10
  188. data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
  189. data/lib/rigor/inference/index_write_widening.rb +1 -5
  190. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  191. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  192. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  194. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  195. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  197. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  199. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  200. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  201. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  202. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  203. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  204. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  205. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  206. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  209. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  210. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  211. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  212. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  213. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  217. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  219. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  220. data/lib/rigor/inference/mutation_widening.rb +100 -51
  221. data/lib/rigor/inference/narrowing.rb +299 -46
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  223. data/lib/rigor/inference/origin_lookup.rb +2 -3
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  226. data/lib/rigor/inference/precision_scanner.rb +3 -4
  227. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  228. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  229. data/lib/rigor/inference/protection_scanner.rb +1 -2
  230. data/lib/rigor/inference/range_constant.rb +57 -0
  231. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  232. data/lib/rigor/inference/receiver_alias.rb +3 -3
  233. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  234. data/lib/rigor/inference/scope_indexer.rb +688 -151
  235. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  236. data/lib/rigor/inference/string_mutation.rb +60 -0
  237. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  238. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  239. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  240. data/lib/rigor/inference/version_guard.rb +28 -21
  241. data/lib/rigor/inference/void_origin.rb +3 -3
  242. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  243. data/lib/rigor/language_server/buffer_table.rb +4 -4
  244. data/lib/rigor/language_server/completion_provider.rb +1 -1
  245. data/lib/rigor/language_server/debouncer.rb +1 -1
  246. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  247. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  248. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  249. data/lib/rigor/language_server/hover_provider.rb +1 -1
  250. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  251. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  252. data/lib/rigor/language_server/project_context.rb +2 -5
  253. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  254. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  255. data/lib/rigor/language_server/server.rb +9 -9
  256. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  257. data/lib/rigor/language_server/uri.rb +1 -1
  258. data/lib/rigor/plugin/base.rb +90 -21
  259. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  260. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  261. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  262. data/lib/rigor/plugin/fact_store.rb +6 -6
  263. data/lib/rigor/plugin/io_boundary.rb +93 -8
  264. data/lib/rigor/plugin/isolation.rb +29 -6
  265. data/lib/rigor/plugin/loader.rb +31 -12
  266. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  267. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  268. data/lib/rigor/plugin/manifest.rb +23 -1
  269. data/lib/rigor/plugin/registry.rb +67 -14
  270. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  271. data/lib/rigor/plugin/trust_policy.rb +2 -4
  272. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  273. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  274. data/lib/rigor/project_environment.rb +138 -0
  275. data/lib/rigor/protection/analysis_guard.rb +93 -14
  276. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  277. data/lib/rigor/protection/dependency_closure.rb +5 -8
  278. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  279. data/lib/rigor/protection/discovery_seed.rb +11 -11
  280. data/lib/rigor/protection/kill_signature.rb +2 -4
  281. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  282. data/lib/rigor/protection/mutation_cache.rb +6 -9
  283. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  284. data/lib/rigor/protection/mutator.rb +2 -1
  285. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  286. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  287. data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
  288. data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
  289. data/lib/rigor/rbs_extended/reporter.rb +122 -16
  290. data/lib/rigor/rbs_extended.rb +67 -22
  291. data/lib/rigor/reflection/constant_path.rb +126 -0
  292. data/lib/rigor/reflection.rb +26 -10
  293. data/lib/rigor/runtime/jit.rb +6 -8
  294. data/lib/rigor/scope/discovery_index.rb +30 -0
  295. data/lib/rigor/scope.rb +203 -4
  296. data/lib/rigor/sig_gen/generator.rb +181 -102
  297. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  298. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  299. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  300. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  301. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  302. data/lib/rigor/sig_gen/renderer.rb +17 -11
  303. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  304. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  305. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  306. data/lib/rigor/sig_gen/writer.rb +23 -6
  307. data/lib/rigor/signature_path_audit.rb +153 -6
  308. data/lib/rigor/source/literals.rb +0 -23
  309. data/lib/rigor/source/node_children.rb +0 -2
  310. data/lib/rigor/source/node_locator.rb +5 -11
  311. data/lib/rigor/source/node_walker.rb +2 -5
  312. data/lib/rigor/triage/catalogue.rb +1 -3
  313. data/lib/rigor/triage.rb +3 -5
  314. data/lib/rigor/type/accepts_result.rb +24 -6
  315. data/lib/rigor/type/combinator.rb +68 -5
  316. data/lib/rigor/type/data_class.rb +2 -2
  317. data/lib/rigor/type/data_instance.rb +4 -4
  318. data/lib/rigor/type/float_range.rb +128 -0
  319. data/lib/rigor/type/hash_shape.rb +5 -5
  320. data/lib/rigor/type/integer_range.rb +13 -8
  321. data/lib/rigor/type/nominal.rb +8 -2
  322. data/lib/rigor/type/refined.rb +3 -3
  323. data/lib/rigor/type/struct_class.rb +3 -3
  324. data/lib/rigor/type/struct_instance.rb +4 -4
  325. data/lib/rigor/type.rb +1 -0
  326. data/lib/rigor/type_node/generic.rb +1 -1
  327. data/lib/rigor/type_node/range_literal.rb +25 -0
  328. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  329. data/lib/rigor/type_node.rb +1 -0
  330. data/lib/rigor/version.rb +1 -1
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  332. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  333. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  334. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  336. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  337. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  338. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  340. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  341. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  342. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  343. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  347. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  348. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  349. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  351. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  352. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  353. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  354. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  355. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  356. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  357. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  358. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  359. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  361. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  363. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  365. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  366. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  367. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  369. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  372. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  373. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  376. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  377. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  378. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  379. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  386. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  398. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  399. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  400. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  406. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  408. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  409. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  416. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  417. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  426. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  427. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  428. data/sig/rigor/cache.rbs +2 -3
  429. data/sig/rigor/environment.rbs +11 -0
  430. data/sig/rigor/inference.rbs +3 -10
  431. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  432. data/sig/rigor/plugin/manifest.rbs +2 -1
  433. data/sig/rigor/reflection.rbs +3 -1
  434. data/sig/rigor/scope.rbs +24 -3
  435. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  436. data/sig/rigor/type.rbs +27 -1
  437. data/sig/rigor.rbs +1 -0
  438. data/skills/rigor-ask/SKILL.md +8 -6
  439. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  440. data/skills/rigor-project-init/SKILL.md +6 -3
  441. data/skills/rigor-project-init/references/02-configure.md +10 -6
  442. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  443. data/skills/rigor-type-oracle/SKILL.md +228 -0
  444. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  445. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  446. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  447. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  448. metadata +28 -3
  449. data/lib/rigor/cli/probe_environment.rb +0 -85
  450. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -6,47 +6,92 @@ module Rigor
6
6
  # cannot surface at the point of failure (the parsers are fail-soft, returning `nil` so call sites fall back
7
7
  # to the RBS-declared type).
8
8
  #
9
- # Owns two event streams:
9
+ # Owns three event streams:
10
10
  #
11
11
  # - `#unresolved_payloads` — `rigor:v1:*` directive payloads the resolver could not turn into a
12
- # {Rigor::Type}. Surface as `dynamic.rbs-extended.unresolved` `:info` diagnostics.
12
+ # {Rigor::Type}. Surface as `dynamic.rbs-extended.unresolved` `:info` diagnostics, except an
13
+ # unresolved `conforms-to` interface, which the aggregator stamps `:warning` (#928).
13
14
  # - `#lossy_projections` — shape-projection type functions (`pick_of` / `omit_of` / `partial_of` /
14
15
  # `required_of` / `readonly_of`) applied to a carrier that does not preserve shape information (anything
15
16
  # other than `Type::HashShape` / `Type::Tuple`). Surface as `dynamic.shape.lossy-projection` `:info`
16
17
  # diagnostics.
18
+ # - `#hkt_directive_errors` — malformed `rigor:v1:hkt_register` / `rigor:v1:hkt_define` directives the
19
+ # ADR-20 parser declined. Surface as `dynamic.rbs-extended.hkt-directive-invalid` `:info` diagnostics.
20
+ # - `#deprecated_forms` — payload spellings the grammar still accepts for one deprecation window but no
21
+ # longer displays (ADR-109 WD3: the PHPStan-style `int<a, b>`). Surface as
22
+ # `dynamic.rbs-extended.deprecated-form` `:info` diagnostics naming the replacement.
17
23
  #
18
24
  # Mutable through the run; consumed once by {Rigor::Analysis::Runner} at end-of-run. Each event is
19
- # deduplicated by `(payload, source_location)` for unresolved and `(head, source_location)` for
20
- # lossy-projection so a single annotation read from many call sites yields one diagnostic.
25
+ # deduplicated by its whole entry — `(payload, path, line, column)` for unresolved, `(head, path, line,
26
+ # column)` for lossy-projection, `(message, path, line, column)` for an hkt directive, `(payload,
27
+ # replacement, path, line, column)` for a deprecated form — so a single annotation read from many call
28
+ # sites yields one diagnostic.
21
29
  #
22
30
  # The reporter is intentionally thread-safe via a coarse `Mutex` because the inference engine may read the
23
31
  # same method definition from multiple files in parallel; the critical sections are short (Array#include? +
24
32
  # Array#<<) so the lock contention is negligible.
25
33
  class Reporter
26
- UnresolvedEntry = Data.define(:payload, :source_location)
27
- LossyProjectionEntry = Data.define(:head, :source_location)
34
+ # Every entry carries its position as `(path, line, column)` primitives, flattened from the parser's
35
+ # `RBS::Location` by {.position_of} at record time. Issues #785 (hkt) and #805 (the two older streams).
36
+ # Two reasons, and either alone decides it. All three streams are drained out of every fork-pool worker
37
+ # ({Rigor::Analysis::WorkerSession#drain_reporters}), and an `RBS::Location` is a C-extension object with
38
+ # no `_dump`, so a `Marshal.dump` of the drain payload raises `TypeError` — which kills the worker at
39
+ # drain time and degrades the run to in-process re-analysis. And an `RBS::Location` compares equal only
40
+ # against a location over the SAME `RBS::Buffer` object, so two workers that each read the same `.rbs`
41
+ # would hand the coordinator two entries the dedup cannot collapse — `--workers=N` would then print N
42
+ # copies of a row `--workers=0` prints once.
43
+ #
44
+ # The record methods take the triple rather than the location for the same reason: a door that accepts
45
+ # an `RBS::Location` is a door a future caller reintroduces the bug through.
46
+ UnresolvedEntry = Data.define(:payload, :path, :line, :column)
47
+ LossyProjectionEntry = Data.define(:head, :path, :line, :column)
48
+ HktDirectiveEntry = Data.define(:message, :path, :line, :column)
49
+ DeprecatedFormEntry = Data.define(:payload, :replacement, :path, :line, :column)
50
+
51
+ # Flattens an `RBS::Location` (or anything answering the same readers) to the `(path, line, column)`
52
+ # triple every entry carries. `column` is 1-based, since `RBS::Location#start_column` is 0-based and
53
+ # diagnostics are not. Each component is `nil` when the location cannot supply it, and the diagnostic
54
+ # then falls back to `.rigor.yml:1:1`.
55
+ #
56
+ # Fail-soft by construction, like every parser that calls it: a location that raises while being read
57
+ # costs the entry its position, never the run.
58
+ def self.position_of(source_location)
59
+ return [nil, nil, nil] if source_location.nil?
60
+
61
+ buffer = source_location.respond_to?(:buffer) ? source_location.buffer : nil
62
+ name = buffer.respond_to?(:name) ? buffer.name.to_s : ""
63
+ line = source_location.respond_to?(:start_line) ? source_location.start_line : nil
64
+ column = source_location.respond_to?(:start_column) ? source_location.start_column + 1 : nil
65
+ [name.empty? ? nil : name, line, column]
66
+ rescue StandardError
67
+ [nil, nil, nil]
68
+ end
28
69
 
29
70
  def initialize
30
71
  @unresolved_payloads = []
31
72
  @lossy_projections = []
73
+ @hkt_directive_errors = []
74
+ @deprecated_forms = []
32
75
  @mutex = Mutex.new
33
76
  end
34
77
 
35
- # @return [Array<UnresolvedEntry>] frozen snapshot of the accumulated unresolved-payload events.
78
+ # @return frozen snapshot of the accumulated unresolved-payload events.
36
79
  def unresolved_payloads
37
80
  @mutex.synchronize { @unresolved_payloads.dup.freeze }
38
81
  end
39
82
 
40
- # @return [Array<LossyProjectionEntry>] frozen snapshot of the accumulated lossy-projection events.
83
+ # @return frozen snapshot of the accumulated lossy-projection events.
41
84
  def lossy_projections
42
85
  @mutex.synchronize { @lossy_projections.dup.freeze }
43
86
  end
44
87
 
45
- # Records a `dynamic.rbs-extended.unresolved` event. The `source_location` argument is the {RBS::Location}
46
- # attached to the source annotation (or `nil` when the caller doesn't have one — the diagnostic falls back
47
- # to a generic location in that case).
48
- def record_unresolved(payload:, source_location: nil)
49
- entry = UnresolvedEntry.new(payload: payload.to_s, source_location: source_location)
88
+ # Records a `dynamic.rbs-extended.unresolved` event. The position triple is the source annotation's
89
+ # `.rbs` file / line / 1-based column — {.position_of} flattens the caller's `RBS::Location` into it —
90
+ # each `nil` when the caller had no location (the diagnostic then falls back to `.rigor.yml:1:1`).
91
+ def record_unresolved(payload:, path: nil, line: nil, column: nil)
92
+ entry = UnresolvedEntry.new(
93
+ payload: frozen_text(payload), path: frozen_text(path), line: line, column: column
94
+ )
50
95
  @mutex.synchronize do
51
96
  return if @unresolved_payloads.include?(entry)
52
97
 
@@ -56,8 +101,11 @@ module Rigor
56
101
 
57
102
  # Records a `dynamic.shape.lossy-projection` event for one of the five shape-projection heads. `head` MUST
58
103
  # be a String (`"pick_of"`, `"omit_of"`, …); the diagnostic message identifies which projection degraded.
59
- def record_lossy_projection(head:, source_location: nil)
60
- entry = LossyProjectionEntry.new(head: head.to_s, source_location: source_location)
104
+ # The position triple is read exactly as {#record_unresolved}'s is.
105
+ def record_lossy_projection(head:, path: nil, line: nil, column: nil)
106
+ entry = LossyProjectionEntry.new(
107
+ head: frozen_text(head), path: frozen_text(path), line: line, column: column
108
+ )
61
109
  @mutex.synchronize do
62
110
  return if @lossy_projections.include?(entry)
63
111
 
@@ -65,10 +113,68 @@ module Rigor
65
113
  end
66
114
  end
67
115
 
116
+ # @return frozen snapshot of the accumulated hkt-directive failures.
117
+ def hkt_directive_errors
118
+ @mutex.synchronize { @hkt_directive_errors.dup.freeze }
119
+ end
120
+
121
+ # Records a `dynamic.rbs-extended.hkt-directive-invalid` event: one malformed ADR-20 HKT directive the
122
+ # {Rigor::RbsExtended::HktDirectives} parser declined. `message` names what the parser objected to; the
123
+ # position triple is the annotation's `.rbs` file / line / 1-based column, each `nil` when the caller had
124
+ # no location (the diagnostic then falls back to `.rigor.yml:1:1`).
125
+ #
126
+ # Every String is frozen individually, not just the enclosing `Data`: the entry crosses the pool drain
127
+ # channel, whose stated invariant is that its payload is `Ractor.shareable?` as well as Marshal-clean.
128
+ def record_hkt_error(message:, path: nil, line: nil, column: nil)
129
+ entry = HktDirectiveEntry.new(
130
+ message: frozen_text(message), path: frozen_text(path), line: line, column: column
131
+ )
132
+ @mutex.synchronize do
133
+ return if @hkt_directive_errors.include?(entry)
134
+
135
+ @hkt_directive_errors << entry
136
+ end
137
+ end
138
+
139
+ # @return frozen snapshot of the accumulated deprecated-form events.
140
+ def deprecated_forms
141
+ @mutex.synchronize { @deprecated_forms.dup.freeze }
142
+ end
143
+
144
+ # Records a `dynamic.rbs-extended.deprecated-form` event (ADR-109 WD3): `payload` is the spelling the
145
+ # author wrote (`int<5, 10>`), `replacement` the spelling the same type displays as (`Integer[5..10]`),
146
+ # so the row can say exactly what to write instead. The position triple is read exactly as
147
+ # {#record_unresolved}'s is, and every String is frozen for the pool drain.
148
+ def record_deprecated_form(payload:, replacement:, path: nil, line: nil, column: nil)
149
+ entry = DeprecatedFormEntry.new(
150
+ payload: frozen_text(payload), replacement: frozen_text(replacement),
151
+ path: frozen_text(path), line: line, column: column
152
+ )
153
+ @mutex.synchronize do
154
+ return if @deprecated_forms.include?(entry)
155
+
156
+ @deprecated_forms << entry
157
+ end
158
+ end
159
+
68
160
  # True when no events have accumulated. Used by callers that want to skip the diagnostic-emission pass
69
161
  # entirely on the common no-event path.
70
162
  def empty?
71
- @mutex.synchronize { @unresolved_payloads.empty? && @lossy_projections.empty? }
163
+ @mutex.synchronize do
164
+ @unresolved_payloads.empty? && @lossy_projections.empty? && @hkt_directive_errors.empty? &&
165
+ @deprecated_forms.empty?
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ # `nil` stays `nil` — the diagnostic falls back to `.rigor.yml:1:1` on a missing path. Anything else
172
+ # becomes an individually frozen String: the `Data` wrapper is frozen on its own, but the pool drain's
173
+ # stated invariant is that its payload is `Ractor.shareable?`, which is a DEEP freeze. Coercing here also
174
+ # means a caller that hands the door an `RBS::Location` by mistake stores its `#to_s`, not the object —
175
+ # the entry cannot become un-Marshalable from the outside.
176
+ def frozen_text(value)
177
+ value.nil? ? nil : value.to_s.dup.freeze
72
178
  end
73
179
  end
74
180
  end
@@ -92,23 +92,60 @@ module Rigor
92
92
  end
93
93
  end
94
94
 
95
+ # The shared empty answers of the per-method readers. Dispatch consults the three readers on every RBS
96
+ # method it resolves (~150k each on the lib self-check) and nearly every method carries no `rigor:v1:`
97
+ # annotation, so a fresh `[]` / `{}` per decline was pure allocation. Callers never mutate a reader's
98
+ # result (`read_flow_contribution` selects into fresh arrays; the override map is documented read-only).
99
+ NO_DIRECTIVES = [].freeze
100
+ private_constant :NO_DIRECTIVES
101
+ NO_OVERRIDES = {}.freeze
102
+ private_constant :NO_OVERRIDES
103
+
104
+ # `%a{rigor:v1:inferred-return}` — the declaration states the member's presence and its parameters and
105
+ # says nothing about what it returns, so the return comes from the body. Normative in
106
+ # `docs/type-specification/rbs-extended.md`.
107
+ INFERRED_RETURN_DIRECTIVE = "rigor:v1:inferred-return"
108
+
95
109
  module_function
96
110
 
111
+ # Whether `method_def` declares its return type inferred rather than contracted (issue #823).
112
+ #
113
+ # The producer is `rigor-rbs-inline`: inside a file that carries one annotation, upstream rbs-inline
114
+ # emits a `(untyped, …) -> untyped` skeleton for every unannotated `def` too, and an accepted signature
115
+ # outranks body inference — so one annotation used to retype every sibling in its file to `untyped`.
116
+ # The plugin now marks exactly the type slots it DEFAULTED, and {MethodDispatcher::RbsDispatch} declines
117
+ # a marked member so the call takes the same body-inference tier an undeclared method takes. Nothing
118
+ # else about the declaration changes: the class keeps its full method surface, `new` keeps its arity,
119
+ # and cross-file references still resolve.
120
+ #
121
+ # Reads `RBS::Definition::Method#annotations`, so it answers for an inherited member too, and a
122
+ # hand-written `.rbs` may carry the annotation for the same effect.
123
+ #
124
+ # @param method_def — an `RBS::Definition::Method`, or anything else answering `#annotations`.
125
+ def inferred_return?(method_def)
126
+ return false if method_def.nil?
127
+
128
+ annotations = method_def.annotations
129
+ return false if annotations.nil? || annotations.empty?
130
+
131
+ annotations.any? { |annotation| annotation.string.to_s.strip == INFERRED_RETURN_DIRECTIVE }
132
+ end
133
+
97
134
  # Reads RBS::Extended predicate effects off `RBS::Definition::Method#annotations`. Returns the effects in
98
135
  # source order; duplicates and unrecognised `rigor:v1:` directives are dropped. Returns an empty array (NEVER
99
136
  # `nil`) for a method with no recognised annotations so callers can iterate unconditionally.
100
137
  #
101
- # @param environment [Rigor::Environment, nil] ADR-13 slice
138
+ # @param environment — ADR-13 slice
102
139
  # 3b. When provided, threads the plugin-supplied
103
140
  # `name_scope:` and the per-run reporter through the
104
141
  # annotation-parse path. `nil` (default) preserves the
105
142
  # pre-slice-3b behaviour — no plugin resolvers consulted
106
143
  # and no diagnostics accumulated.
107
144
  def read_predicate_effects(method_def, environment: nil)
108
- return [] if method_def.nil?
145
+ return NO_DIRECTIVES if method_def.nil?
109
146
 
110
147
  annotations = method_def.annotations
111
- return [] if annotations.nil? || annotations.empty?
148
+ return NO_DIRECTIVES if annotations.nil? || annotations.empty?
112
149
 
113
150
  name_scope = environment&.name_scope
114
151
  reporter = environment&.rbs_extended_reporter
@@ -127,10 +164,11 @@ module Rigor
127
164
  end
128
165
 
129
166
  # The right-hand side accepts either a Capitalised class name (with optional `~` negation, optional `::`
130
- # prefix, qualified names) OR a kebab-case refinement payload routed through
131
- # `Builtins::ImportedRefinements::Parser` (bare names, `name[T]`, `name<min, max>`). The two arms share the
132
- # same overall directive shape; the parser detects which form matched by looking at the `class_name` vs
133
- # `refinement` capture groups.
167
+ # prefix, qualified names) OR a refinement payload routed through
168
+ # `Builtins::ImportedRefinements::Parser` (bare kebab-case names, `name[T]`, the numeric range form
169
+ # `Integer[1..10]`, and the deprecated `name<min, max>`). The two arms share the same overall directive
170
+ # shape; the parser detects which form matched by looking at the `class_name` vs `refinement` capture
171
+ # groups. A class head followed by a bracketed range literal is a refinement (ADR-109), not a class name.
134
172
  PREDICATE_DIRECTIVE_PATTERN = /
135
173
  \A
136
174
  rigor:v1:(?<directive>predicate-if-(?:true|false))
@@ -141,7 +179,7 @@ module Rigor
141
179
  (?:
142
180
  (?<class_name>(?:::)?[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)
143
181
  |
144
- (?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?)
182
+ (?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?|[A-Z][A-Za-z0-9_]*\[[^\]]*\.\.[^\]]*\])
145
183
  )
146
184
  \s*
147
185
  \z
@@ -183,10 +221,10 @@ module Rigor
183
221
  #
184
222
  # See {.read_predicate_effects} for the `environment:` keyword contract.
185
223
  def read_assert_effects(method_def, environment: nil)
186
- return [] if method_def.nil?
224
+ return NO_DIRECTIVES if method_def.nil?
187
225
 
188
226
  annotations = method_def.annotations
189
- return [] if annotations.nil? || annotations.empty?
227
+ return NO_DIRECTIVES if annotations.nil? || annotations.empty?
190
228
 
191
229
  name_scope = environment&.name_scope
192
230
  reporter = environment&.rbs_extended_reporter
@@ -214,7 +252,7 @@ module Rigor
214
252
  (?:
215
253
  (?<class_name>(?:::)?[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)
216
254
  |
217
- (?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?)
255
+ (?<refinement>[a-z][a-z0-9-]*(?:[\[<][^\]>]*[\]>])?|[A-Z][A-Za-z0-9_]*\[[^\]]*\.\.[^\]]*\])
218
256
  )
219
257
  \s*
220
258
  \z
@@ -352,7 +390,8 @@ module Rigor
352
390
 
353
391
  # The trailing payload supports the full refinement grammar in `Builtins::ImportedRefinements::Parser` — bare
354
392
  # kebab-case names plus parameterised forms like `non-empty-array[Integer]`, `non-empty-hash[Symbol,
355
- # Integer]`, and `int<5, 10>`. The directive head is consumed by the regex; the rest is forwarded to the
393
+ # Integer]`, `Integer[5..10]`, and the deprecated `int<5, 10>`. The directive head is consumed by the regex;
394
+ # the rest is forwarded to the
356
395
  # refinement parser. Anything the parser cannot resolve falls back to nil so the call site keeps the
357
396
  # RBS-declared return type.
358
397
  RETURN_DIRECTIVE_PATTERN = /
@@ -449,7 +488,7 @@ module Rigor
449
488
 
450
489
  # Returned for `rigor:v1:param: <name> <refinement>`. The parameter name is a Ruby identifier (Symbol); the
451
490
  # type is any `Rigor::Type` the refinement parser resolves (bare kebab-case name, parameterised form, or
452
- # `int<...>` range — the same grammar the `return:` directive accepts).
491
+ # `Integer[a..b]` range, or the deprecated `int<a, b>` — the same grammar the `return:` directive accepts).
453
492
  ParamOverride = Data.define(:param_name, :type)
454
493
 
455
494
  # Reads every `rigor:v1:param: <name> <refinement>` directive off `RBS::Definition::Method#annotations` and
@@ -469,10 +508,10 @@ module Rigor
469
508
  # argument-check purposes; passing a too-wide `Nominal[String]` argument is flagged as an argument-type
470
509
  # mismatch at the call site.
471
510
  def read_param_type_overrides(method_def, environment: nil)
472
- return [] if method_def.nil?
511
+ return NO_DIRECTIVES if method_def.nil?
473
512
 
474
513
  annotations = method_def.annotations
475
- return [] if annotations.nil? || annotations.empty?
514
+ return NO_DIRECTIVES if annotations.nil? || annotations.empty?
476
515
 
477
516
  name_scope = environment&.name_scope
478
517
  reporter = environment&.rbs_extended_reporter
@@ -491,9 +530,10 @@ module Rigor
491
530
  # frozen Hash<Symbol, Rigor::Type>; missing keys mean "use the RBS-declared type". Callers MUST treat the
492
531
  # hash as read-only.
493
532
  def param_type_override_map(method_def, environment: nil)
494
- read_param_type_overrides(method_def, environment: environment)
495
- .to_h { |o| [o.param_name, o.type] }
496
- .freeze
533
+ overrides = read_param_type_overrides(method_def, environment: environment)
534
+ return NO_OVERRIDES if overrides.empty?
535
+
536
+ overrides.to_h { |o| [o.param_name, o.type] }.freeze
497
537
  end
498
538
 
499
539
  # The `is` glue word is optional so authors can write either `param: id is non-empty-string` (consistent with
@@ -708,9 +748,9 @@ module Rigor
708
748
  # `RBS::Extended` conflict channel) rather than silently resolved. Returns `nil` when the list
709
749
  # carries neither spelling.
710
750
  #
711
- # @param annotations [Array<#string>] the node's annotations, in source order.
712
- # @param owner_key [String] the method key (`Class#m` / `Class.m`) or class name the bound binds.
713
- # @param source [Symbol] {Effects::Envelope::SOURCES} member to stamp when the labelled spelling
751
+ # @param annotations — the node's annotations, in source order.
752
+ # @param owner_key — the method key (`Class#m` / `Class.m`) or class name the bound binds.
753
+ # @param source — {Effects::Envelope::SOURCES} member to stamp when the labelled spelling
714
754
  # matched; `%a{pure}` always stamps `:pure_annotation`, and a class-level read overrides both.
715
755
  def read_effect_envelope(annotations, owner_key:, source: :effect_annotation, registry: nil, reporter: nil)
716
756
  return nil if annotations.nil? || annotations.empty?
@@ -812,10 +852,15 @@ module Rigor
812
852
  # ADR-13 slice 3b — guards every reporter call so the in-RbsExtended-module call sites can record events
813
853
  # uniformly without nil-checking each time. When the reporter is nil (the v0.1.0 → v0.1.3 default for call
814
854
  # sites that do not yet thread `environment:`), the call is a no-op and the parser stays fail-soft.
855
+ #
856
+ # Issue #805 — the location is flattened to `(path, line, column)` here, at record time, because the
857
+ # entry is drained out of every fork-pool worker and an `RBS::Location` neither survives `Marshal.dump`
858
+ # nor compares equal across two workers' buffers (see {Rigor::RbsExtended::Reporter}).
815
859
  def record_unresolved(reporter, payload, source_location)
816
860
  return if reporter.nil?
817
861
 
818
- reporter.record_unresolved(payload: payload, source_location: source_location)
862
+ path, line, column = Reporter.position_of(source_location)
863
+ reporter.record_unresolved(payload: payload, path: path, line: line, column: column)
819
864
  end
820
865
  end
821
866
  end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ # {Reflection}'s second constant-resolution phase, split out for the reader rather than for the
5
+ # loader: `reflection.rb` asks what a whole spelling names under a series of prefixes, and this file
6
+ # asks what a PATH names segment by segment. Reopening the module rather than nesting under it keeps
7
+ # the walk inside the facade its callers already read, and lets it use the same private candidate
8
+ # lookups (`constant_type_at`, `enclosing_class_path`, `ancestor_constant_scopes`) the ladder uses.
9
+ module Reflection
10
+ module_function
11
+
12
+ # Issue #656 — Ruby's lookup for an as-written, unrooted constant PATH (`A::B`, `A::B::C`), which is
13
+ # a different question from "what does this whole string name under some prefix". Ruby resolves the
14
+ # FIRST segment through the ladder a bare name gets — `Module.nesting`, then the enclosing class's
15
+ # ancestors, then the top level — and then resolves each LATER segment inside the constant the
16
+ # previous segment produced, searching THAT constant's own ancestors in turn. So `A::B` written in
17
+ # `P::Guard` with `P::A < P::Base` names `P::Base::B`, while every whole-path candidate
18
+ # (`P::Guard::A::B`, `P::A::B`, `A::B`) either misses or names a real but different class.
19
+ #
20
+ # `known` decides the FINAL segment only, so the constant typer (any constant, values included) and
21
+ # `Inference::Narrowing`'s class-guard resolver (classes only) share one walk without sharing an
22
+ # acceptance test — the two cannot answer different names for the same spelling. Every earlier
23
+ # segment must name a namespace, because only a namespace can own the next one.
24
+ #
25
+ # Returns nil for a single-segment name — the callers' own ladders already own that case — and the
26
+ # moment any segment declines. Declining hands the caller back its unchanged fallback instead of a
27
+ # guess: a walk that finds the WRONG constant is worse than the wrong constant answered today
28
+ # (AGENTS.md § "Implementation Guidelines").
29
+ #
30
+ # Checked against MRI 4.0.5 rather than recollection: a later segment does NOT reach the top level
31
+ # for either a Class or a Module owner (`P::Mod::B` raises `NameError` where a top-level `B`
32
+ # exists), which is why {.constant_in_namespace} consults the owner's ancestors and stops. Rigor
33
+ # still lets its callers fall through to their own top-level rung there — that read raises at
34
+ # runtime and Rigor reports nothing about it either way, so retracting the resolution would trade a
35
+ # silent wrong answer for a silent absent one at no gain (the same reasoning as
36
+ # {.toplevel_first_constant_type}'s caller-derived rungs).
37
+ def resolve_constant_path_name(name, scope, &known)
38
+ segments = name.to_s.split("::")
39
+ return nil unless segments.size > 1
40
+
41
+ owner = path_head_owner(segments.first, scope)
42
+ last = segments.size - 1
43
+ segments.each_with_index do |segment, index|
44
+ next if index.zero?
45
+ return nil if owner.nil?
46
+
47
+ owner = constant_in_namespace(owner, segment, scope) do |candidate|
48
+ index == last ? known.call(candidate) : namespace_known?(candidate, scope)
49
+ end
50
+ end
51
+ owner
52
+ end
53
+
54
+ # Step 2.5's rung as a type: the segment-wise name, then that name looked up like any other
55
+ # candidate so the answer comes from the same source-precedence order as every other rung.
56
+ def constant_path_type(name, scope)
57
+ qualified = resolve_constant_path_name(name, scope) { |candidate| constant_type_at(candidate, scope) }
58
+ qualified && constant_type_at(qualified, scope)
59
+ end
60
+ private_class_method :constant_path_type
61
+
62
+ # The namespace a path's FIRST segment names — the bare-name ladder, restricted to answers that can
63
+ # OWN the segments that follow. A value constant is not one of them, so it declines here rather
64
+ # than producing a namespace prefix no source knows.
65
+ def path_head_owner(head, scope)
66
+ hit = first_namespace_hit(lexical_nesting_chain(scope), head, scope)
67
+ return hit if hit
68
+
69
+ prefix = enclosing_class_path(scope)
70
+ unless prefix.nil? || prefix.empty?
71
+ hit = first_namespace_hit(bounded_ancestor_scopes(prefix, scope), head, scope)
72
+ return hit if hit
73
+ end
74
+
75
+ head if namespace_known?(head, scope)
76
+ end
77
+ private_class_method :path_head_owner
78
+
79
+ # {.first_constant_hit}'s namespace twin: the first `<entry>::<name>` that can own further segments.
80
+ def first_namespace_hit(entries, name, scope)
81
+ entries.each do |entry|
82
+ candidate = "#{entry}::#{name}"
83
+ return candidate if namespace_known?(candidate, scope)
84
+ end
85
+ nil
86
+ end
87
+ private_class_method :first_namespace_hit
88
+
89
+ # `segment` looked up inside `owner` and then through `owner`'s own ancestors — Ruby's lookup for
90
+ # every segment after the first. `Object` is deliberately absent: MRI raises rather than reaching a
91
+ # top-level constant through an explicit `::` receiver.
92
+ def constant_in_namespace(owner, segment, scope)
93
+ candidate = "#{owner}::#{segment}"
94
+ return candidate if yield(candidate)
95
+
96
+ bounded_ancestor_scopes(owner, scope).each do |ancestor|
97
+ inherited = "#{ancestor}::#{segment}"
98
+ return inherited if yield(inherited)
99
+ end
100
+ nil
101
+ end
102
+ private_class_method :constant_in_namespace
103
+
104
+ # How many of {.ancestor_constant_scopes}' entries one segment may consult, under
105
+ # `Scope::ANCESTOR_WALK_LIMIT` — the budget `Scope`'s method lookup already spends on this same
106
+ # graph, reused rather than re-chosen so one pathological ancestry costs one bound. The list
107
+ # itself is the memoised one step 2 builds, so this bounds the per-segment consultation the walk
108
+ # adds and not the graph. Reported through the shared counter, so a truncated walk is visible in a
109
+ # budget trace instead of looking like a plain miss.
110
+ def bounded_ancestor_scopes(class_name, scope)
111
+ scopes = ancestor_constant_scopes(class_name, scope)
112
+ return scopes if scopes.size <= Scope::ANCESTOR_WALK_LIMIT
113
+
114
+ Inference::BudgetTrace.hit(Inference::BudgetTrace::ANCESTOR_WALK_LIMIT)
115
+ scopes.first(Scope::ANCESTOR_WALK_LIMIT)
116
+ end
117
+ private_class_method :bounded_ancestor_scopes
118
+
119
+ # Whether `name` can OWN a constant. `class_known?` covers the RBS and registry classes and modules;
120
+ # the project tables cover a namespace the project declares with no RBS for it.
121
+ def namespace_known?(name, scope)
122
+ scope.environment.class_known?(name) || known_project_namespace?(name, scope)
123
+ end
124
+ private_class_method :namespace_known?
125
+ end
126
+ end
@@ -51,9 +51,6 @@ module Rigor
51
51
 
52
52
  module_function
53
53
 
54
- # @param class_name [String, Symbol]
55
- # @param scope [Rigor::Scope]
56
- # @return [Boolean]
57
54
  def class_known?(class_name, scope: Scope.empty)
58
55
  return true if scope.discovered_classes.key?(class_name.to_s)
59
56
 
@@ -92,7 +89,7 @@ module Rigor
92
89
  loader.project_declared_class?(class_name)
93
90
  end
94
91
 
95
- # @return [Symbol] one of `:equal`, `:subclass`, `:superclass`,
92
+ # @return one of `:equal`, `:subclass`, `:superclass`,
96
93
  # `:disjoint`, `:unknown`.
97
94
  def class_ordering(lhs, rhs, scope: Scope.empty)
98
95
  scope.environment.class_ordering(lhs, rhs)
@@ -171,9 +168,16 @@ module Rigor
171
168
  # § "Implementation Guidelines"). Only project ancestors are walked; an RBS-known superclass
172
169
  # contributes no name here (see {.ancestor_constant_scopes}).
173
170
  #
171
+ # Step 2.5 (#656) — the path read SEGMENT BY SEGMENT, which is the only rung that can answer a
172
+ # path whose head and tail have different owners. See {.resolve_constant_path_name}. It sits
173
+ # BELOW the whole-path rungs so no read that resolves today moves, and ABOVE step 3 because
174
+ # beating the top-level whole-string answer is the point: `A::B` inside `P::Guard` with
175
+ # `P::A < P::Base` is `P::Base::B`, and step 3's `A::B` is a real but different class.
176
+ #
174
177
  # Step 3 — the bare name (top level).
175
178
  first_constant_hit(lexical_nesting_chain(scope), name, scope) ||
176
179
  ancestor_constant_type(name, scope, enclosing_class_path(scope)) ||
180
+ constant_path_type(name, scope) ||
177
181
  constant_type_at(name, scope)
178
182
  end
179
183
 
@@ -215,8 +219,12 @@ module Rigor
215
219
  # dependabot-core — `docs/notes/20260905-toplevel-def-cref-movable-sites.md`). Rigor reports nothing
216
220
  # about them either way, so retracting them would trade a silent wrong answer for a silent absent one at
217
221
  # no gain. Making them FIRE is a separate question with its own false-positive budget.
222
+ #
223
+ # Issue #656's rung goes LAST here rather than second: for a top-level body Ruby's own first answer
224
+ # for `A::B` is the top level, and a whole-string hit there already means "`A` owns `B`". The
225
+ # segment-wise walk only has something to add once that misses.
218
226
  def toplevel_first_constant_type(name, scope)
219
- constant_type_at(name, scope) || caller_derived_constant_type(name, scope)
227
+ constant_type_at(name, scope) || caller_derived_constant_type(name, scope) || constant_path_type(name, scope)
220
228
  end
221
229
  private_class_method :toplevel_first_constant_type
222
230
 
@@ -242,11 +250,16 @@ module Rigor
242
250
  singleton = env.singleton_for_name(candidate)
243
251
  return singleton if singleton
244
252
 
253
+ # Issue #639 — the class-EXISTENCE edge. Without it a reference-only consumer (`Foo` with no call on
254
+ # it) recorded nothing, so deleting the declaring file left the warm `--incremental` run serving
255
+ # `singleton(Foo)` where `--no-cache` answers the honest unresolved type. Recorded ahead of the lookup
256
+ # rather than on the hit: every rung of the ladder spells the same last segment, which is the whole of
257
+ # the key, so one call covers the candidates instead of a branch around each.
258
+ scope.record_class_existence(candidate) if Analysis::DependencyRecorder.active?
245
259
  in_source_class = scope.discovered_classes[candidate]
246
260
  return in_source_class if in_source_class
247
261
 
248
- in_source_value = scope.in_source_constants[candidate]
249
- if in_source_value
262
+ if (in_source_value = scope.in_source_constants[candidate])
250
263
  scope.record_constant_dependency(candidate) if Analysis::DependencyRecorder.active?
251
264
  return in_source_value
252
265
  end
@@ -465,15 +478,14 @@ module Rigor
465
478
  end
466
479
  private_class_method :rbs_loader_for
467
480
 
468
- # @return [Boolean] true when the analyzed source contains a class / module declaration
481
+ # @return true when the analyzed source contains a class / module declaration
469
482
  # for the given name. Does NOT consult the RBS loader (use {.class_known?} for the
470
483
  # union).
471
484
  def discovered_class?(class_name, scope: Scope.empty)
472
485
  scope.discovered_classes.key?(class_name.to_s)
473
486
  end
474
487
 
475
- # @param kind [:instance, :singleton]
476
- # @return [Boolean] true when the ScopeIndexer recorded a `def` for the given method on
488
+ # @return true when the ScopeIndexer recorded a `def` for the given method on
477
489
  # the given class with the matching kind.
478
490
  #
479
491
  # ADR-46 — a MISS records a negative cross-file dependency, so a consumer whose analysis
@@ -497,3 +509,7 @@ module Rigor
497
509
  end
498
510
  end
499
511
  end
512
+
513
+ # Loaded last: the segment-wise path walk reopens the module above and reads its private candidate
514
+ # lookups, so the facade has to exist first.
515
+ require_relative "reflection/constant_path"
@@ -70,7 +70,7 @@ module Rigor
70
70
  # call is what enabled YJIT (so the `?`-suffix predicate convention does
71
71
  # not apply; hence the Naming/PredicateMethod exemption).
72
72
  #
73
- # @return [Boolean] true if this call enabled YJIT; false if it was a
73
+ # @return true if this call enabled YJIT; false if it was a
74
74
  # no-op (unavailable / opted out / already enabled).
75
75
  def enable_now # rubocop:disable Naming/PredicateMethod
76
76
  return false unless available?
@@ -90,8 +90,8 @@ module Rigor
90
90
  # up front (unavailable / opted out / already enabled), so callers never
91
91
  # pay for a thread that could only no-op.
92
92
  #
93
- # @param seconds [Numeric] the amortization deadline.
94
- # @return [Thread, nil] the deadline thread, or nil when no-op up front.
93
+ # @param seconds — the amortization deadline.
94
+ # @return the deadline thread, or nil when no-op up front.
95
95
  def enable_after(seconds)
96
96
  return nil unless available?
97
97
  return nil if disabled?
@@ -143,7 +143,7 @@ module Rigor
143
143
  # Re-arming goes through {enable_after}, so the child records the same
144
144
  # absolute deadline it inherited and a grandchild fork carries it too.
145
145
  #
146
- # @return [Thread, nil] the child's deadline thread; nil when YJIT was
146
+ # @return the child's deadline thread; nil when YJIT was
147
147
  # enabled immediately, and nil when the call is a no-op up front
148
148
  # (unavailable / opted out / already enabled — a child forked after the
149
149
  # parent enabled inherits the enabled state and has nothing to do).
@@ -166,8 +166,6 @@ module Rigor
166
166
 
167
167
  # The resolved {enable_after} deadline: {DEADLINE_ENV} when it parses to a
168
168
  # non-negative float, else {DEFAULT_DEADLINE_SECONDS}.
169
- #
170
- # @return [Numeric]
171
169
  def deadline_seconds
172
170
  raw = ENV.fetch(DEADLINE_ENV, nil)
173
171
  return DEFAULT_DEADLINE_SECONDS if raw.nil? || raw.empty?
@@ -176,12 +174,12 @@ module Rigor
176
174
  value && value >= 0 ? value : DEFAULT_DEADLINE_SECONDS
177
175
  end
178
176
 
179
- # @return [Boolean] whether this Ruby exposes `RubyVM::YJIT.enable`.
177
+ # @return whether this Ruby exposes `RubyVM::YJIT.enable`.
180
178
  def available?
181
179
  defined?(RubyVM::YJIT.enable) ? true : false
182
180
  end
183
181
 
184
- # @return [Boolean] whether the opt-out env switch is set.
182
+ # @return whether the opt-out env switch is set.
185
183
  def disabled?
186
184
  ENV[DISABLE_ENV] == "1"
187
185
  end