rigortype 0.3.8 → 0.4.0

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 (634) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/core_overlay/enumerable.rbs +51 -0
  5. data/data/core_overlay/enumerator.rbs +84 -0
  6. data/data/core_overlay/hash_rbs3.rbs +41 -0
  7. data/data/core_overlay/process.rbs +40 -0
  8. data/data/core_overlay/string_io.rbs +33 -0
  9. data/data/effects/core.yml +3 -3
  10. data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
  11. data/docs/handbook/02-everyday-types.md +17 -14
  12. data/docs/handbook/03-narrowing.md +132 -14
  13. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  14. data/docs/handbook/05-methods-and-blocks.md +1 -1
  15. data/docs/handbook/06-classes.md +2 -2
  16. data/docs/handbook/07-rbs-and-extended.md +19 -1
  17. data/docs/handbook/10-sorbet.md +9 -10
  18. data/docs/handbook/11-sig-gen.md +525 -13
  19. data/docs/handbook/README.md +1 -1
  20. data/docs/handbook/appendix-elixir.md +2 -2
  21. data/docs/handbook/appendix-go.md +2 -2
  22. data/docs/handbook/appendix-java-csharp.md +2 -2
  23. data/docs/handbook/appendix-mypy.md +2 -2
  24. data/docs/handbook/appendix-phpstan.md +1 -1
  25. data/docs/handbook/appendix-rust.md +1 -1
  26. data/docs/handbook/appendix-type-theory.md +4 -4
  27. data/docs/handbook/appendix-typescript.md +1 -1
  28. data/docs/llms.txt +2 -0
  29. data/docs/manual/02-cli-reference.md +95 -6
  30. data/docs/manual/03-configuration.md +18 -0
  31. data/docs/manual/04-diagnostics.md +15 -0
  32. data/docs/manual/07-plugins.md +15 -5
  33. data/docs/manual/08-skills.md +23 -2
  34. data/docs/manual/10-mcp-server.md +3 -2
  35. data/docs/manual/11-ci.md +9 -0
  36. data/docs/manual/15-type-protection-coverage.md +8 -0
  37. data/docs/manual/16-rbs-extended-annotations.md +46 -8
  38. data/docs/manual/18-removing-dead-code.md +10 -8
  39. data/docs/manual/19-effect-labels.md +10 -0
  40. data/docs/manual/plugins/README.md +7 -0
  41. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  42. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  43. data/docs/manual/plugins/rigor-actionpack.md +261 -0
  44. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  45. data/docs/manual/plugins/rigor-activejob.md +7 -0
  46. data/docs/manual/plugins/rigor-activerecord.md +138 -4
  47. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  48. data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
  49. data/docs/manual/plugins/rigor-grape.md +106 -0
  50. data/docs/manual/plugins/rigor-graphql.md +23 -2
  51. data/docs/manual/plugins/rigor-pundit.md +8 -1
  52. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  53. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  54. data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
  55. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  56. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  57. data/exe/rigor +19 -4
  58. data/lib/rigor/analysis/baseline.rb +3 -3
  59. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  60. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  61. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  62. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  63. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  64. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
  65. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  66. data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
  67. data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
  68. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  69. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  70. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  71. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  72. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
  73. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  74. data/lib/rigor/analysis/check_rules.rb +718 -79
  75. data/lib/rigor/analysis/crash_signature.rb +8 -24
  76. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  77. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  78. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  79. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  80. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  81. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  82. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  83. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  84. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  85. data/lib/rigor/analysis/fact_store.rb +15 -1
  86. data/lib/rigor/analysis/incremental.rb +42 -0
  87. data/lib/rigor/analysis/incremental_session.rb +117 -26
  88. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  89. data/lib/rigor/analysis/project_scan.rb +11 -1
  90. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  91. data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
  92. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  93. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  94. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
  95. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  96. data/lib/rigor/analysis/result.rb +1 -3
  97. data/lib/rigor/analysis/rule_catalog.rb +131 -1
  98. data/lib/rigor/analysis/run_cache_key.rb +21 -12
  99. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  100. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  101. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  102. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
  103. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  104. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
  105. data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
  106. data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
  107. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  108. data/lib/rigor/analysis/runner.rb +407 -51
  109. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  110. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  111. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  112. data/lib/rigor/analysis/template_units.rb +399 -0
  113. data/lib/rigor/analysis/worker_session.rb +62 -20
  114. data/lib/rigor/bleeding_edge.rb +10 -24
  115. data/lib/rigor/builtins/hkt_builtins.rb +3 -2
  116. data/lib/rigor/builtins/imported_refinements.rb +159 -21
  117. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  118. data/lib/rigor/builtins/regex_refinement.rb +21 -13
  119. data/lib/rigor/builtins/static_return_refinements.rb +7 -6
  120. data/lib/rigor/cache/annotation_location.rb +2 -4
  121. data/lib/rigor/cache/descriptor.rb +63 -20
  122. data/lib/rigor/cache/engine_source.rb +33 -5
  123. data/lib/rigor/cache/file_digest.rb +9 -1
  124. data/lib/rigor/cache/incremental_snapshot.rb +72 -6
  125. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  126. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  127. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  128. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  129. data/lib/rigor/cache/rbs_descriptor.rb +112 -14
  130. data/lib/rigor/cache/rbs_environment.rb +8 -4
  131. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  132. data/lib/rigor/cache/store.rb +76 -32
  133. data/lib/rigor/ci_detector.rb +1 -0
  134. data/lib/rigor/cli/annotate_command.rb +5 -6
  135. data/lib/rigor/cli/check_command.rb +57 -10
  136. data/lib/rigor/cli/check_invocation.rb +6 -11
  137. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  138. data/lib/rigor/cli/coverage_command.rb +1 -1
  139. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  140. data/lib/rigor/cli/coverage_scan.rb +1 -5
  141. data/lib/rigor/cli/diff_command.rb +1 -1
  142. data/lib/rigor/cli/doc_links.rb +4 -4
  143. data/lib/rigor/cli/docs_command.rb +6 -6
  144. data/lib/rigor/cli/doctor_command.rb +85 -38
  145. data/lib/rigor/cli/effects_command.rb +2 -2
  146. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  147. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  148. data/lib/rigor/cli/explain_command.rb +34 -1
  149. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  150. data/lib/rigor/cli/lsp_command.rb +1 -1
  151. data/lib/rigor/cli/mcp_command.rb +1 -1
  152. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  153. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  154. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  155. data/lib/rigor/cli/plugin_command.rb +4 -4
  156. data/lib/rigor/cli/plugins_command.rb +2 -1
  157. data/lib/rigor/cli/prism_colorizer.rb +1 -2
  158. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  159. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  160. data/lib/rigor/cli/sig_gen_command.rb +212 -31
  161. data/lib/rigor/cli/skill_command.rb +2 -2
  162. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  163. data/lib/rigor/cli/skill_describe.rb +35 -35
  164. data/lib/rigor/cli/trace_command.rb +4 -4
  165. data/lib/rigor/cli/trace_renderer.rb +5 -6
  166. data/lib/rigor/cli/triage_command.rb +1 -1
  167. data/lib/rigor/cli/type_of_command.rb +29 -11
  168. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  169. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  170. data/lib/rigor/cli/type_scan_command.rb +4 -4
  171. data/lib/rigor/cli/unused_command.rb +11 -3
  172. data/lib/rigor/cli/upgrade_command.rb +1 -1
  173. data/lib/rigor/cli.rb +89 -9
  174. data/lib/rigor/config_audit.rb +30 -5
  175. data/lib/rigor/configuration/severity_profile.rb +25 -9
  176. data/lib/rigor/configuration.rb +109 -7
  177. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  178. data/lib/rigor/effects/attribution.rb +12 -5
  179. data/lib/rigor/effects/callee_rule.rb +368 -0
  180. data/lib/rigor/effects/catalog.rb +7 -4
  181. data/lib/rigor/effects/collector.rb +11 -5
  182. data/lib/rigor/effects/config_envelopes.rb +15 -10
  183. data/lib/rigor/effects/definition_context.rb +179 -0
  184. data/lib/rigor/effects/definition_lines.rb +25 -6
  185. data/lib/rigor/effects/effect_table.rb +11 -4
  186. data/lib/rigor/effects/entry_points.rb +0 -2
  187. data/lib/rigor/effects/envelope_check.rb +9 -9
  188. data/lib/rigor/effects/envelope_index.rb +20 -8
  189. data/lib/rigor/effects/file_collection.rb +59 -5
  190. data/lib/rigor/effects/framework_units.rb +5 -7
  191. data/lib/rigor/effects/identity.rb +20 -6
  192. data/lib/rigor/effects/inline_anchor.rb +7 -7
  193. data/lib/rigor/effects/label_intent.rb +3 -4
  194. data/lib/rigor/effects/liskov_check.rb +8 -8
  195. data/lib/rigor/effects/local_ownership.rb +38 -12
  196. data/lib/rigor/effects/method_key.rb +22 -1
  197. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  198. data/lib/rigor/effects/plugin_facts.rb +47 -37
  199. data/lib/rigor/effects/propagator.rb +297 -19
  200. data/lib/rigor/effects/registry.rb +1 -1
  201. data/lib/rigor/effects/scanner.rb +122 -73
  202. data/lib/rigor/effects/signature_sources.rb +4 -6
  203. data/lib/rigor/effects/snapshot.rb +12 -11
  204. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  205. data/lib/rigor/effects/summary.rb +27 -4
  206. data/lib/rigor/effects/unit_scan.rb +393 -38
  207. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  208. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  209. data/lib/rigor/effects/visibility.rb +101 -0
  210. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  211. data/lib/rigor/environment/class_registry.rb +3 -1
  212. data/lib/rigor/environment/failure_slot.rb +2 -2
  213. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  214. data/lib/rigor/environment/lockfile_resolver.rb +68 -4
  215. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  216. data/lib/rigor/environment/member_consistency.rb +298 -0
  217. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  218. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  219. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  220. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  221. data/lib/rigor/environment/rbs_loader.rb +674 -79
  222. data/lib/rigor/environment.rb +174 -24
  223. data/lib/rigor/flow_contribution/merger.rb +0 -2
  224. data/lib/rigor/flow_contribution.rb +11 -13
  225. data/lib/rigor/hashing/xxh3.rb +264 -0
  226. data/lib/rigor/inference/acceptance.rb +292 -17
  227. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  228. data/lib/rigor/inference/block_call_timing.rb +338 -0
  229. data/lib/rigor/inference/block_parameter_binder.rb +75 -30
  230. data/lib/rigor/inference/block_repetition.rb +71 -0
  231. data/lib/rigor/inference/body_fixpoint.rb +7 -6
  232. data/lib/rigor/inference/budget_trace.rb +3 -6
  233. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  234. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  235. data/lib/rigor/inference/captured_locals.rb +389 -18
  236. data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
  237. data/lib/rigor/inference/content_join.rb +200 -27
  238. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  239. data/lib/rigor/inference/def_return_typer.rb +11 -7
  240. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  241. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  242. data/lib/rigor/inference/element_read_widening.rb +180 -0
  243. data/lib/rigor/inference/error_info.rb +196 -0
  244. data/lib/rigor/inference/expression_typer.rb +2124 -468
  245. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  246. data/lib/rigor/inference/fork_map.rb +5 -7
  247. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  248. data/lib/rigor/inference/global_write_census.rb +239 -0
  249. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  250. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  251. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  252. data/lib/rigor/inference/hkt_registry.rb +4 -7
  253. data/lib/rigor/inference/index_write_widening.rb +16 -7
  254. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  255. data/lib/rigor/inference/jump_targets.rb +82 -0
  256. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  257. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  258. data/lib/rigor/inference/last_line.rb +340 -0
  259. data/lib/rigor/inference/last_status.rb +144 -0
  260. data/lib/rigor/inference/macro_block_self_type.rb +168 -21
  261. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  262. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  263. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  264. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  265. data/lib/rigor/inference/match_rebinding.rb +392 -0
  266. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  267. data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
  268. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  269. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  270. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  271. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  272. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  273. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  274. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
  275. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  276. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  277. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  278. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  279. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  280. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
  281. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  282. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  283. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  284. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
  285. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  286. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  287. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  288. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  289. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  290. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
  291. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  292. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  293. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
  294. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  295. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  296. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  297. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  298. data/lib/rigor/inference/method_dispatcher.rb +225 -25
  299. data/lib/rigor/inference/method_parameter_binder.rb +12 -9
  300. data/lib/rigor/inference/multi_target_binder.rb +341 -55
  301. data/lib/rigor/inference/mutation_rejoin.rb +171 -0
  302. data/lib/rigor/inference/mutation_widening.rb +156 -85
  303. data/lib/rigor/inference/narrowing.rb +835 -162
  304. data/lib/rigor/inference/operand_effects.rb +167 -0
  305. data/lib/rigor/inference/operand_walk.rb +88 -0
  306. data/lib/rigor/inference/optimistic_origin.rb +153 -16
  307. data/lib/rigor/inference/origin_lookup.rb +2 -3
  308. data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
  309. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  310. data/lib/rigor/inference/precision_scanner.rb +3 -4
  311. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  312. data/lib/rigor/inference/project_patched_methods.rb +9 -4
  313. data/lib/rigor/inference/project_patched_scanner.rb +10 -6
  314. data/lib/rigor/inference/protection_scanner.rb +1 -2
  315. data/lib/rigor/inference/range_constant.rb +57 -0
  316. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  317. data/lib/rigor/inference/receiver_alias.rb +93 -4
  318. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  319. data/lib/rigor/inference/refinement_mutation.rb +76 -0
  320. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  321. data/lib/rigor/inference/return_barrier.rb +54 -0
  322. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  323. data/lib/rigor/inference/scope_indexer.rb +5228 -627
  324. data/lib/rigor/inference/statement_evaluator.rb +3452 -595
  325. data/lib/rigor/inference/stored_block_call.rb +54 -0
  326. data/lib/rigor/inference/string_mutation.rb +97 -0
  327. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  328. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  329. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  330. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  331. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  332. data/lib/rigor/inference/version_guard.rb +28 -21
  333. data/lib/rigor/inference/void_origin.rb +3 -3
  334. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  335. data/lib/rigor/language_server/buffer_table.rb +4 -4
  336. data/lib/rigor/language_server/completion_provider.rb +1 -1
  337. data/lib/rigor/language_server/debouncer.rb +0 -1
  338. data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
  339. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  340. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  341. data/lib/rigor/language_server/hover_provider.rb +1 -1
  342. data/lib/rigor/language_server/hover_renderer.rb +4 -4
  343. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  344. data/lib/rigor/language_server/project_context.rb +7 -8
  345. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  346. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  347. data/lib/rigor/language_server/server.rb +9 -9
  348. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  349. data/lib/rigor/language_server/uri.rb +1 -1
  350. data/lib/rigor/mcp/server.rb +2 -1
  351. data/lib/rigor/plugin/base.rb +258 -26
  352. data/lib/rigor/plugin/box_probe.rb +91 -0
  353. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  354. data/lib/rigor/plugin/effect_attribution.rb +65 -11
  355. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  356. data/lib/rigor/plugin/fact_store.rb +6 -6
  357. data/lib/rigor/plugin/io_boundary.rb +93 -8
  358. data/lib/rigor/plugin/isolation.rb +29 -6
  359. data/lib/rigor/plugin/loader.rb +33 -13
  360. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  361. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  362. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  363. data/lib/rigor/plugin/manifest.rb +94 -11
  364. data/lib/rigor/plugin/registry.rb +76 -12
  365. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
  366. data/lib/rigor/plugin/template_unit.rb +196 -0
  367. data/lib/rigor/plugin/trust_policy.rb +2 -4
  368. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  369. data/lib/rigor/plugin.rb +1 -0
  370. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  371. data/lib/rigor/project_environment.rb +138 -0
  372. data/lib/rigor/protection/analysis_guard.rb +93 -14
  373. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  374. data/lib/rigor/protection/dependency_closure.rb +5 -8
  375. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  376. data/lib/rigor/protection/discovery_seed.rb +14 -12
  377. data/lib/rigor/protection/kill_signature.rb +1 -4
  378. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  379. data/lib/rigor/protection/mutation_cache.rb +7 -11
  380. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  381. data/lib/rigor/protection/mutator.rb +2 -1
  382. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  383. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  384. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  385. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  386. data/lib/rigor/rbs_extended.rb +88 -21
  387. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  388. data/lib/rigor/reflection/constant_path.rb +139 -0
  389. data/lib/rigor/reflection.rb +93 -110
  390. data/lib/rigor/runtime/jit.rb +6 -8
  391. data/lib/rigor/scope/discovery_index.rb +165 -2
  392. data/lib/rigor/scope.rb +1041 -32
  393. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  394. data/lib/rigor/sig_gen/classification.rb +22 -5
  395. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  396. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  397. data/lib/rigor/sig_gen/generator.rb +673 -53
  398. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  399. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  400. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  401. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  402. data/lib/rigor/sig_gen/observation_collector.rb +18 -16
  403. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  404. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  405. data/lib/rigor/sig_gen/renderer.rb +128 -14
  406. data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
  407. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  408. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  409. data/lib/rigor/sig_gen/write_result.rb +19 -3
  410. data/lib/rigor/sig_gen/writer.rb +189 -41
  411. data/lib/rigor/sig_gen.rb +3 -0
  412. data/lib/rigor/signature_path_audit.rb +153 -6
  413. data/lib/rigor/source/literals.rb +0 -23
  414. data/lib/rigor/source/node_children.rb +0 -2
  415. data/lib/rigor/source/node_locator.rb +5 -11
  416. data/lib/rigor/source/node_walker.rb +0 -6
  417. data/lib/rigor/source/parameter_envelope.rb +72 -0
  418. data/lib/rigor/source.rb +1 -0
  419. data/lib/rigor/triage/catalogue.rb +1 -3
  420. data/lib/rigor/triage.rb +3 -5
  421. data/lib/rigor/type/accepts_result.rb +24 -6
  422. data/lib/rigor/type/combinator.rb +144 -5
  423. data/lib/rigor/type/data_class.rb +2 -2
  424. data/lib/rigor/type/data_instance.rb +4 -4
  425. data/lib/rigor/type/difference.rb +1 -0
  426. data/lib/rigor/type/float_range.rb +128 -0
  427. data/lib/rigor/type/hash_shape.rb +6 -6
  428. data/lib/rigor/type/integer_range.rb +13 -8
  429. data/lib/rigor/type/nominal.rb +8 -2
  430. data/lib/rigor/type/refined.rb +4 -3
  431. data/lib/rigor/type/struct_class.rb +3 -3
  432. data/lib/rigor/type/struct_instance.rb +4 -4
  433. data/lib/rigor/type.rb +1 -0
  434. data/lib/rigor/type_node/generic.rb +1 -1
  435. data/lib/rigor/type_node/range_literal.rb +25 -0
  436. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  437. data/lib/rigor/type_node.rb +1 -0
  438. data/lib/rigor/version.rb +1 -1
  439. data/lib/rigor.rb +1 -0
  440. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  441. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  442. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  443. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  444. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
  445. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  446. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  447. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  448. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
  449. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
  450. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  451. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  452. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  453. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  454. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  455. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  456. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  457. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  458. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
  459. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  460. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  461. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  462. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  463. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  464. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  465. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  466. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  467. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
  468. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  469. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  470. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  471. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
  472. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  473. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  474. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
  475. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  476. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  477. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  478. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
  479. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  480. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  481. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
  482. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  483. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  484. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
  485. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  486. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
  487. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  488. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  489. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  490. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  491. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  492. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  493. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  494. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
  495. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  496. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  497. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  498. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  499. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  500. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  501. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  502. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  503. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  504. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  505. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  506. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
  507. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  508. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  509. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  510. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
  511. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  512. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  513. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  514. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  515. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  516. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
  517. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  518. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  519. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  520. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
  521. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  522. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
  523. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
  524. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
  525. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  526. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  527. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  528. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  529. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
  530. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  531. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  532. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  533. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
  534. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  535. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  536. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  537. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  538. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  539. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  540. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  541. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  542. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  543. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  544. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  545. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  546. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
  547. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  548. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  549. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
  550. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  551. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  552. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  553. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  554. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  555. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  556. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
  557. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  558. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
  559. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  560. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  561. data/sig/rigor/analysis/baseline.rbs +69 -7
  562. data/sig/rigor/analysis/fact_store.rbs +1 -1
  563. data/sig/rigor/analysis/project_scan.rbs +74 -0
  564. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  565. data/sig/rigor/cache.rbs +2 -3
  566. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  567. data/sig/rigor/effects/effect_table.rbs +60 -0
  568. data/sig/rigor/effects/envelope.rbs +97 -0
  569. data/sig/rigor/effects/envelope_index.rbs +33 -0
  570. data/sig/rigor/effects/file_collection.rbs +81 -0
  571. data/sig/rigor/effects/label.rbs +26 -0
  572. data/sig/rigor/effects/label_set.rbs +46 -0
  573. data/sig/rigor/effects/method_key.rbs +24 -0
  574. data/sig/rigor/effects/origin.rbs +50 -0
  575. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  576. data/sig/rigor/effects/registry.rbs +68 -0
  577. data/sig/rigor/effects/summary.rbs +50 -0
  578. data/sig/rigor/effects/taint_cause.rbs +12 -0
  579. data/sig/rigor/environment.rbs +17 -10
  580. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  581. data/sig/rigor/inference.rbs +9 -14
  582. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  583. data/sig/rigor/plugin/base.rbs +30 -7
  584. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  585. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  586. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  587. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  588. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  589. data/sig/rigor/plugin/loader.rbs +3 -3
  590. data/sig/rigor/plugin/manifest.rbs +51 -11
  591. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  592. data/sig/rigor/plugin/registry.rbs +62 -1
  593. data/sig/rigor/plugin.rbs +1 -1
  594. data/sig/rigor/rbs_extended.rbs +1 -1
  595. data/sig/rigor/reflection.rbs +9 -6
  596. data/sig/rigor/scope.rbs +148 -12
  597. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
  598. data/sig/rigor/source.rbs +4 -4
  599. data/sig/rigor/testing.rbs +10 -4
  600. data/sig/rigor/type.rbs +33 -1
  601. data/sig/rigor.rbs +39 -20
  602. data/skills/rigor-ask/SKILL.md +16 -11
  603. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  604. data/skills/rigor-ci-setup/SKILL.md +17 -21
  605. data/skills/rigor-doctor/SKILL.md +24 -22
  606. data/skills/rigor-doctor/references/01-checks.md +97 -33
  607. data/skills/rigor-editor-setup/SKILL.md +6 -4
  608. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  609. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  610. data/skills/rigor-next-steps/SKILL.md +4 -2
  611. data/skills/rigor-plugin-author/SKILL.md +19 -23
  612. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  613. data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
  614. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  615. data/skills/rigor-plugin-review/SKILL.md +6 -4
  616. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  617. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  618. data/skills/rigor-project-init/SKILL.md +15 -10
  619. data/skills/rigor-project-init/references/01-detect.md +13 -9
  620. data/skills/rigor-project-init/references/02-configure.md +43 -14
  621. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  622. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  623. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  624. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  625. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  626. data/skills/rigor-type-oracle/SKILL.md +226 -0
  627. data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
  628. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  629. data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
  630. data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
  631. data/skills/rigor-upgrade/SKILL.md +13 -8
  632. metadata +133 -3
  633. data/lib/rigor/cli/probe_environment.rb +0 -85
  634. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -51,14 +51,14 @@ module Rigor
51
51
  # migration note and a user's `bleeding_edge:` list both key on.
52
52
  #
53
53
  # @!attribute id
54
- # @return [String] the stable feature id (contract vocabulary).
54
+ # @return the stable feature id (contract vocabulary).
55
55
  # @!attribute summary
56
- # @return [String] a one-line description of what it changes. For a `:behaviour`
56
+ # @return a one-line description of what it changes. For a `:behaviour`
57
57
  # feature this is the *whole* explanation — there is no severity diff to read.
58
58
  # @!attribute kind
59
- # @return [Symbol] one of {KINDS}.
59
+ # @return one of {KINDS}.
60
60
  # @!attribute severity_overrides
61
- # @return [Hash{String => Symbol}] canonical rule id → the severity this feature
61
+ # @return canonical rule id → the severity this feature
62
62
  # imposes. Composed *below* the user's own `severity_overrides:` and *above* the
63
63
  # active `severity_profile` (see {Configuration::SeverityProfile.resolve}). Empty for
64
64
  # a `:behaviour` feature.
@@ -76,12 +76,10 @@ module Rigor
76
76
  super
77
77
  end
78
78
 
79
- # @return [Boolean]
80
79
  def severity?
81
80
  kind == :severity
82
81
  end
83
82
 
84
- # @return [Boolean]
85
83
  def behaviour?
86
84
  kind == :behaviour
87
85
  end
@@ -193,36 +191,28 @@ module Rigor
193
191
  # cleanup can lag graduation by as many releases as it takes. The id also stays in the
194
192
  # contract vocabulary the CHANGELOG migration note keys on. Entries are removed only once
195
193
  # no call site names them.
196
- #
197
- # @return [Array<String>]
198
194
  GRADUATED = [].freeze
199
195
 
200
196
  module_function
201
197
 
202
- # @return [Array<Feature>] the whole overlay.
203
198
  def features
204
199
  FEATURES
205
200
  end
206
201
 
207
- # @return [Array<String>] every feature id in the overlay.
208
202
  def feature_ids
209
203
  FEATURES.map(&:id)
210
204
  end
211
205
 
212
- # @param id [String]
213
- # @return [Feature, nil]
214
206
  def feature(id)
215
207
  FEATURES.find { |f| f.id == id }
216
208
  end
217
209
 
218
- # @param id [String]
219
- # @return [Boolean] whether the id has graduated to default-on ({GRADUATED}).
210
+ # @return whether the id has graduated to default-on ({GRADUATED}).
220
211
  def graduated?(id)
221
212
  GRADUATED.include?(id)
222
213
  end
223
214
 
224
- # @param id [String]
225
- # @return [Boolean] whether the id names a feature this gem knows at all — queued or
215
+ # @return whether the id names a feature this gem knows at all — queued or
226
216
  # graduated. Distinct from "adopted"; see {Configuration#bleeding_edge_active?}.
227
217
  def known_id?(id)
228
218
  graduated?(id) || FEATURES.any? { |f| f.id == id }
@@ -234,10 +224,9 @@ module Rigor
234
224
  # `severity_overrides:` keeps an unknown rule id inert until it lands (robust across gem
235
225
  # versions).
236
226
  #
237
- # @param selector [Hash] `{ "mode" => "none" }`,
227
+ # @param selector — `{ "mode" => "none" }`,
238
228
  # `{ "mode" => "all" }`, `{ "mode" => "all", "except" => [ids] }`,
239
229
  # or `{ "mode" => "list", "ids" => [ids] }`.
240
- # @return [Array<Feature>]
241
230
  def active_features(selector)
242
231
  case selector["mode"]
243
232
  when "all"
@@ -254,8 +243,7 @@ module Rigor
254
243
  # The merged severity-override map the active features impose for a selector. Frozen so
255
244
  # the result is `Ractor.shareable?`.
256
245
  #
257
- # @param selector [Hash] see {#active_features}.
258
- # @return [Hash{String => Symbol}]
246
+ # @param selector — see {#active_features}.
259
247
  def severity_overrides_for(selector)
260
248
  active_features(selector).each_with_object({}) do |feature, acc|
261
249
  acc.merge!(feature.severity_overrides)
@@ -267,8 +255,7 @@ module Rigor
267
255
  # Precomputed once per Configuration; frozen (with frozen members) so the carrier stays
268
256
  # `Ractor.shareable?` across the worker boundary.
269
257
  #
270
- # @param selector [Hash] see {#active_features}.
271
- # @return [Set<String>]
258
+ # @param selector — see {#active_features}.
272
259
  def active_ids_for(selector)
273
260
  Set.new(active_features(selector).map(&:id)).freeze
274
261
  end
@@ -276,8 +263,7 @@ module Rigor
276
263
  # Feature ids named by a selector that are NOT in the overlay (typo / graduated / from a
277
264
  # newer gem). Surfaced by `rigor show-bleedingedge` as a hint; never an error.
278
265
  #
279
- # @param selector [Hash] see {#active_features}.
280
- # @return [Array<String>]
266
+ # @param selector — see {#active_features}.
281
267
  def unknown_selected_ids(selector)
282
268
  named =
283
269
  case selector["mode"]
@@ -118,7 +118,7 @@ module Rigor
118
118
  )
119
119
  end
120
120
 
121
- # @return [Rigor::Inference::HktRegistry] frozen registry
121
+ # @return frozen registry
122
122
  # pre-seeded with all bundled HKT registrations +
123
123
  # bodies. Allocated fresh each call rather than
124
124
  # memoised — memoisation through a module-level
@@ -241,8 +241,9 @@ module Rigor
241
241
  ["CSV", :readlines, :singleton] => CSV_PARSED_SPEC,
242
242
  ["CSV", :parse_line, :singleton] => CSV_ROW_SPEC
243
243
  }.freeze
244
+ Ractor.make_shareable(METHOD_RETURN_OVERRIDES)
244
245
 
245
- # @return [Rigor::Type, nil] the reduced HKT type for
246
+ # @return the reduced HKT type for
246
247
  # the given (class_name, method_name, kind) triple,
247
248
  # or `nil` when no built-in override is registered.
248
249
  # When `arg_types` is supplied AND the entry carries a
@@ -31,11 +31,12 @@ module Rigor
31
31
  # - **No-argument refinement names** (`non-empty-string`, `non-zero-int`,
32
32
  # `lowercase-string`, …) live in `REGISTRY` and resolve through `lookup(name)`.
33
33
  # - **Parameterised refinement payloads** (`non-empty-array[Integer]`,
34
- # `non-empty-hash[Symbol, Integer]`, `int<5, 10>`) are accepted by `parse(payload)`. The
35
- # full grammar is documented on `Parser`. The two surfaces share `REGISTRY` for the
36
- # no-arg head names; the parameterised head names live in `PARAMETERISED_TYPE_BUILDERS`
37
- # (square-bracket form, type args) and `PARAMETERISED_INT_BUILDERS` (angle-bracket form,
38
- # integer bounds).
34
+ # `non-empty-hash[Symbol, Integer]`, `Integer[5..10]`, and the deprecated `int<5, 10>`)
35
+ # are accepted by `parse(payload)`. The full grammar is documented on `Parser`. The two
36
+ # surfaces share `REGISTRY` for the no-arg head names; the parameterised head names live
37
+ # in `PARAMETERISED_TYPE_BUILDERS` (square-bracket form, type args), `RANGE_HEAD_BUILDERS`
38
+ # (a numeric class over a range literal) and `PARAMETERISED_INT_BUILDERS` (the deprecated
39
+ # angle-bracket form, integer bounds).
39
40
  module ImportedRefinements
40
41
  REGISTRY = {
41
42
  "non-empty-string" => -> { Type::Combinator.non_empty_string },
@@ -58,8 +59,12 @@ module Rigor
58
59
  "non-empty-lowercase-string" => -> { Type::Combinator.non_empty_lowercase_string },
59
60
  "non-empty-uppercase-string" => -> { Type::Combinator.non_empty_uppercase_string },
60
61
  "literal-string" => -> { Type::Combinator.literal_string },
61
- "non-empty-literal-string" => -> { Type::Combinator.non_empty_literal_string }
62
+ "non-empty-literal-string" => -> { Type::Combinator.non_empty_literal_string },
63
+ # ADR-109 WD4 — names for the two Float ranges people mean most; each is an alias of a range.
64
+ "non-nan-float" => -> { Type::Combinator.non_nan_float },
65
+ "finite-float" => -> { Type::Combinator.finite_float }
62
66
  }.freeze
67
+ Ractor.make_shareable(REGISTRY)
63
68
  private_constant :REGISTRY
64
69
 
65
70
  # `name[T]` / `name[K, V]` — type-arg parameterised refinements. Each builder takes an
@@ -137,49 +142,91 @@ module Rigor
137
142
  Type::Combinator.readonly_of(args.first)
138
143
  }
139
144
  }.freeze
145
+ Ractor.make_shareable(PARAMETERISED_TYPE_BUILDERS)
140
146
  private_constant :PARAMETERISED_TYPE_BUILDERS
141
147
 
142
- # `name<min, max>` — integer-bound parameterised refinements. Each builder takes an
148
+ # `Class[range]` — a numeric class head bounded by a Ruby range literal (ADR-109). The
149
+ # builder receives the `Range` value and returns the carrier, or `nil` when the range is
150
+ # empty or its endpoints are not literals of the head class; `nil` surfaces as the ordinary
151
+ # unresolved-payload diagnostic. `Float` joins this table with the `FloatRange` carrier.
152
+ RANGE_HEAD_BUILDERS = {
153
+ "Integer" => lambda { |range|
154
+ lo = range.begin
155
+ hi = range.end
156
+ return nil unless (lo.nil? || lo.is_a?(Integer)) && (hi.nil? || hi.is_a?(Integer))
157
+
158
+ # `1...10` is the set `1..9`: Integer ranges canonicalise to the closed form.
159
+ hi -= 1 if range.exclude_end? && hi.is_a?(Integer)
160
+ return nil if lo.is_a?(Integer) && hi.is_a?(Integer) && lo > hi
161
+
162
+ Type::Combinator.integer_range(lo || Type::IntegerRange::NEG_INFINITY, hi || Type::IntegerRange::POS_INFINITY)
163
+ },
164
+ # ADR-109 WD4 — `Float[R]` is the set of Floats `R.cover?` accepts. A missing or `nil` endpoint
165
+ # is the infinity on that side, except that `nil..nil` covers NaN in Ruby and so is `Float`
166
+ # itself; an Integer endpoint coerces (`(0..1).cover?(0.5)` is true); an exclusive end is kept
167
+ # (the carrier canonicalises it for equality), and an empty range declines.
168
+ "Float" => lambda { |range|
169
+ return Type::Combinator.nominal_of("Float") if range.begin.nil? && range.end.nil?
170
+
171
+ lo = ImportedRefinements.float_range_endpoint(range.begin, -Float::INFINITY)
172
+ hi = ImportedRefinements.float_range_endpoint(range.end, Float::INFINITY)
173
+ return nil if lo.nil? || hi.nil?
174
+
175
+ exclusive = range.exclude_end? && !range.end.nil?
176
+ return nil if lo > (exclusive ? hi.prev_float : hi)
177
+
178
+ Type::Combinator.float_range(lo, hi, exclude_end: exclusive)
179
+ }
180
+ }.freeze
181
+ Ractor.make_shareable(RANGE_HEAD_BUILDERS)
182
+ private_constant :RANGE_HEAD_BUILDERS
183
+
184
+ # `name<min, max>` — the PHPStan-style integer-bound form ADR-109 deprecates: still parsed so
185
+ # signatures written against v0.1–v0.3 keep resolving, never displayed. Each builder takes an
143
186
  # `Array<Integer>` and returns a `Rigor::Type` (or `nil`). Bounds are signed integer
144
- # literals; `min` MUST be ≤ `max` for the carrier to construct successfully
145
- # (`Type::IntegerRange` enforces the invariant).
187
+ # literals. A reversed pair (`min` > `max`) is declined as `nil` like every other shape
188
+ # mismatch, so the caller surfaces `dynamic.rbs-extended.unresolved` at the annotation;
189
+ # `Type::IntegerRange` raises on it instead, and that `ArgumentError` used to escape as an
190
+ # `internal analyzer error` that abandoned the whole Ruby file.
146
191
  PARAMETERISED_INT_BUILDERS = {
147
192
  "int" => lambda { |bounds|
148
193
  return nil unless bounds.size == 2
194
+ return nil if bounds[0] > bounds[1]
149
195
 
150
196
  Type::Combinator.integer_range(bounds[0], bounds[1])
151
197
  }
152
198
  }.freeze
199
+ Ractor.make_shareable(PARAMETERISED_INT_BUILDERS)
153
200
  private_constant :PARAMETERISED_INT_BUILDERS
154
201
 
155
202
  module_function
156
203
 
157
- # @param name [String] kebab-case refinement name.
158
- # @return [Rigor::Type, nil] the matching refinement carrier, or `nil` if the name is
204
+ # @param name — kebab-case refinement name.
205
+ # @return the matching refinement carrier, or `nil` if the name is
159
206
  # not registered.
160
207
  def lookup(name)
161
208
  builder = REGISTRY[name.to_s]
162
209
  builder&.call
163
210
  end
164
211
 
165
- # @param payload [String] the trailing payload of a `rigor:v1:return:` (or sibling)
212
+ # @param payload — the trailing payload of a `rigor:v1:return:` (or sibling)
166
213
  # directive. Accepts the bare-name forms `lookup` already handles plus the
167
214
  # parameterised forms documented on {Parser}.
168
- # @param name_scope [Rigor::TypeNode::NameScope, nil]
215
+ # @param name_scope —
169
216
  # ADR-13 slice 3 — when provided, the parser consults the scope's `#resolver` chain
170
217
  # after the built-in registry and built-in parametric forms but before the RBS
171
218
  # Nominal fallback. `nil` (default) preserves the slice-1 / slice-2 behaviour of
172
219
  # consulting only built-ins + RBS.
173
- # @param reporter [Rigor::RbsExtended::Reporter, nil]
220
+ # @param reporter —
174
221
  # ADR-13 slice 3b — collector that the Resolver feeds `dynamic.shape.lossy-projection`
175
222
  # events into when a shape-projection head (`pick_of`, `omit_of`, `partial_of`,
176
223
  # `required_of`, `readonly_of`) is applied to a carrier that does not preserve shape
177
224
  # information. `nil` (default) suppresses event accumulation; legacy call sites that
178
225
  # have no reporter to thread keep the pre-slice-3b silent fall-through.
179
- # @param source_location [RBS::Location, nil] location attribution for the events the
226
+ # @param source_location — location attribution for the events the
180
227
  # Resolver records. Carries the annotation's filename / line / column so the runner
181
228
  # can stamp diagnostics with the user-visible source site.
182
- # @return [Rigor::Type, nil] the resolved refinement carrier, or `nil` when the payload
229
+ # @return the resolved refinement carrier, or `nil` when the payload
183
230
  # is unparseable or names a refinement / class no registered source resolved.
184
231
  def parse(payload, name_scope: nil, reporter: nil, source_location: nil)
185
232
  Parser.new(
@@ -201,6 +248,20 @@ module Rigor
201
248
  PARAMETERISED_INT_BUILDERS[name]
202
249
  end
203
250
 
251
+ def range_head_builder(name)
252
+ RANGE_HEAD_BUILDERS[name]
253
+ end
254
+
255
+ # A Float-head range endpoint as a double: `nil` (absent) is `default`, an Integer literal
256
+ # coerces, a non-NaN Float stands, anything else declines with `nil`.
257
+ def float_range_endpoint(value, default)
258
+ case value
259
+ when nil then default
260
+ when Integer then value.to_f
261
+ when Float then value.nan? ? nil : value
262
+ end
263
+ end
264
+
204
265
  def known?(name)
205
266
  REGISTRY.key?(name.to_s) ||
206
267
  PARAMETERISED_TYPE_BUILDERS.key?(name.to_s) ||
@@ -219,8 +280,10 @@ module Rigor
219
280
  # parametric := simple_name '[' type_arg_list ']'
220
281
  # | simple_name '<' int_bound_list '>'
221
282
  # type_arg_list := type_arg (',' type_arg)*
222
- # type_arg := type | class_name
283
+ # type_arg := range_literal | type | class_name
223
284
  # class_name := /[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*/
285
+ # range_literal := endpoint? ('..' | '...') endpoint? (at least one side written)
286
+ # endpoint := signed_int | 'nil'
224
287
  # int_bound_list := signed_int (',' signed_int)*
225
288
  # signed_int := /-?\d+/
226
289
  #
@@ -274,7 +337,16 @@ module Rigor
274
337
  # delimiter.
275
338
  SYMBOL_LITERAL = /:(?<value>[a-zA-Z_][a-zA-Z0-9_]*[?!=]?)/
276
339
  STRING_LITERAL = /"(?<value>[^"\\]*)"/
277
- private_constant :SIMPLE_NAME, :CLASS_NAME, :SIGNED_INT, :SYMBOL_LITERAL, :STRING_LITERAL
340
+ # ADR-109 — a Ruby range literal at type-arg position, `1..10` / `1...10` / `1..` / `..10` /
341
+ # `nil..nil`, and for the Float head `0.0...1.0` / `1e-3..` / `-Float::INFINITY..Float::MAX`.
342
+ # Tried before the class-name and integer scans so `Float::INFINITY..` and `1..10` are not
343
+ # consumed as a class name or a bare integer; the operator is mandatory, so a bare integer or
344
+ # constant never matches. `..` with nothing on either side is not Ruby and is rejected after
345
+ # the match.
346
+ RANGE_ENDPOINT = /-?(?:\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|Float::INFINITY|Float::MAX)|nil/
347
+ RANGE_LITERAL = /(?<lo>#{RANGE_ENDPOINT})?\s*(?<op>\.\.\.?)\s*(?<hi>#{RANGE_ENDPOINT})?/
348
+ private_constant :SIMPLE_NAME, :CLASS_NAME, :SIGNED_INT, :SYMBOL_LITERAL, :STRING_LITERAL,
349
+ :RANGE_ENDPOINT, :RANGE_LITERAL
278
350
 
279
351
  def parse_type_ast
280
352
  if (class_name = @scanner.scan(CLASS_NAME))
@@ -382,7 +454,9 @@ module Rigor
382
454
  end
383
455
 
384
456
  def parse_single_type_arg_ast
385
- if (class_name = @scanner.scan(CLASS_NAME))
457
+ if (range = scan_range_literal)
458
+ range
459
+ elsif (class_name = @scanner.scan(CLASS_NAME))
386
460
  parse_class_arg_tail_ast(class_name)
387
461
  elsif (literal = @scanner.scan(SIGNED_INT))
388
462
  TypeNode::IntegerLiteral.new(value: Integer(literal))
@@ -421,6 +495,36 @@ module Rigor
421
495
  Integer(literal)
422
496
  end
423
497
 
498
+ # Returns a {TypeNode::RangeLiteral} when the scanner sits on a Ruby range literal, `nil`
499
+ # (without consuming input) otherwise. `nil` and an absent endpoint both become Ruby's
500
+ # `nil` endpoint; the two spellings denote the same range.
501
+ def scan_range_literal
502
+ return nil unless @scanner.scan(RANGE_LITERAL)
503
+
504
+ lo = @scanner[:lo]
505
+ hi = @scanner[:hi]
506
+ return nil if lo.nil? && hi.nil?
507
+
508
+ TypeNode::RangeLiteral.new(
509
+ value: Range.new(range_endpoint(lo), range_endpoint(hi), @scanner[:op] == "...")
510
+ )
511
+ end
512
+
513
+ # The endpoint as the Ruby value the token denotes: an Integer literal stays an Integer (the
514
+ # `Integer` head needs to tell `1` from `1.0`), a Float literal or one of the four spelled
515
+ # constants becomes the double.
516
+ def range_endpoint(token)
517
+ case token
518
+ when nil, "nil" then nil
519
+ when "Float::INFINITY" then Float::INFINITY
520
+ when "-Float::INFINITY" then -Float::INFINITY
521
+ when "Float::MAX" then Float::MAX
522
+ when "-Float::MAX" then -Float::MAX
523
+ when /[.eE]/ then Float(token)
524
+ else Integer(token)
525
+ end
526
+ end
527
+
424
528
  def skip_ws
425
529
  @scanner.skip(/\s+/)
426
530
  end
@@ -463,7 +567,7 @@ module Rigor
463
567
  # `StringLiteral`) carries a Ruby value that lifts directly to a `Constant<value>`
464
568
  # carrier through the same helper.
465
569
  LITERAL_AST_NODES = [
466
- TypeNode::IntegerLiteral, TypeNode::SymbolLiteral, TypeNode::StringLiteral
570
+ TypeNode::IntegerLiteral, TypeNode::SymbolLiteral, TypeNode::StringLiteral, TypeNode::RangeLiteral
467
571
  ].freeze
468
572
  private_constant :LITERAL_AST_NODES
469
573
 
@@ -519,6 +623,11 @@ module Rigor
519
623
  end
520
624
 
521
625
  def resolve_generic(node)
626
+ # ADR-109 — `Integer[1..10]`. A range literal under a numeric head is that head's range
627
+ # refinement or nothing: an empty or malformed range declines here rather than falling
628
+ # through to a `Nominal[Integer, [Constant<Range>]]` the RBS fallback would otherwise build.
629
+ return resolve_range_head(node) if range_headed?(node)
630
+
522
631
  builtin = try_builtin_parametric(node)
523
632
  return builtin unless builtin.nil?
524
633
 
@@ -547,6 +656,19 @@ module Rigor
547
656
  try_parametric_type_builder(node) || try_parametric_int_builder(node)
548
657
  end
549
658
 
659
+ def range_headed?(node)
660
+ !ImportedRefinements.range_head_builder(node.head).nil? &&
661
+ node.args.any?(TypeNode::RangeLiteral)
662
+ end
663
+
664
+ # Exactly one range literal under the head; `Integer[1..10, 3]` is not a range refinement.
665
+ # Anything else under the same head (`Integer[Foo]`) keeps its pre-ADR-109 path.
666
+ def resolve_range_head(node)
667
+ return nil unless node.args.size == 1
668
+
669
+ ImportedRefinements.range_head_builder(node.head).call(node.args.first.value)
670
+ end
671
+
550
672
  def try_parametric_type_builder(node)
551
673
  builder = ImportedRefinements.parametric_type_builder(node.head)
552
674
  return nil if builder.nil?
@@ -585,7 +707,23 @@ module Rigor
585
707
  bounds = node.args.map { |a| a.is_a?(TypeNode::IntegerLiteral) ? a.value : nil }
586
708
  return nil if bounds.any?(&:nil?)
587
709
 
588
- builder.call(bounds)
710
+ result = builder.call(bounds)
711
+ record_deprecated_form(node, bounds, result) unless result.nil?
712
+ result
713
+ end
714
+
715
+ # ADR-109 WD3 — the angle-bracket form still resolves, and the run says so once per annotation with
716
+ # the spelling the same type now displays as. Recorded only when the payload resolved: an
717
+ # unresolvable one already surfaces as `dynamic.rbs-extended.unresolved`, and a second row for it
718
+ # would name a replacement for something that never parsed.
719
+ def record_deprecated_form(node, bounds, result)
720
+ return if @reporter.nil?
721
+
722
+ path, line, column = RbsExtended::Reporter.position_of(@source_location)
723
+ @reporter.record_deprecated_form(
724
+ payload: "#{node.head}<#{bounds.join(', ')}>", replacement: result.describe,
725
+ path: path, line: line, column: column
726
+ )
589
727
  end
590
728
 
591
729
  def resolve_args(args)
@@ -117,8 +117,7 @@ module Rigor
117
117
 
118
118
  # --- private ------------------------------------------------------
119
119
 
120
- # @param value [String] a non-empty string
121
- # @return [Rigor::Type]
120
+ # @param value — a non-empty string
122
121
  def self.classify_string(value)
123
122
  if Type::Refined.ruby_numeric_literal?(value)
124
123
  NUMERIC_STRING
@@ -133,8 +132,7 @@ module Rigor
133
132
  # Called ONLY with the names in {RUNTIME_STRING_CONSTANTS} — Rigor's own source — and only
134
133
  # while this file is being loaded. It is never handed a name from the analysed program.
135
134
  #
136
- # @param name [String] a qualified constant path without a leading "::"
137
- # @return [String, nil]
135
+ # @param name — a qualified constant path without a leading "::"
138
136
  def self.runtime_string_value(name)
139
137
  mod = ::Object
140
138
  name.split("::").each do |part|
@@ -161,7 +159,7 @@ module Rigor
161
159
  end
162
160
  private_class_method :runtime_string_value
163
161
 
164
- # @return [Hash{String => Rigor::Type}] frozen, built once at load time.
162
+ # @return frozen, built once at load time.
165
163
  def self.build_runtime_string_refinements
166
164
  RUNTIME_STRING_CONSTANTS.each_with_object({}) do |name, table|
167
165
  value = runtime_string_value(name)
@@ -175,9 +173,9 @@ module Rigor
175
173
 
176
174
  # --- public API ---------------------------------------------------
177
175
 
178
- # @param name [String] unqualified constant name (e.g. `"Math::PI"`,
176
+ # @param name — unqualified constant name (e.g. `"Math::PI"`,
179
177
  # `"RUBY_VERSION"`, `"Ruby::ENGINE"`)
180
- # @return [Rigor::Type, nil] refined type, or nil to fall through
178
+ # @return refined type, or nil to fall through
181
179
  def self.lookup(name)
182
180
  FOLDED_CONSTANTS[name] || RUNTIME_STRING_REFINEMENTS[name]
183
181
  end
@@ -20,14 +20,22 @@ module Rigor
20
20
  # `{n,m}` forms with `n >= 1`):
21
21
  #
22
22
  # - `\d`, `[0-9]` -> decimal-int-string
23
- # - `\h` -> hex-int-string
24
- # - `[0-9a-fA-F]` -> hex-int-string
25
- # - `[0-9a-f]`, `[0-9A-F]` -> hex-int-string
26
- # - `[0-7]` -> octal-int-string
23
+ # - `\h` -> non-empty-string
24
+ # - `[0-9a-fA-F]` -> non-empty-string
25
+ # - `[0-9a-f]`, `[0-9A-F]` -> non-empty-string
26
+ # - `[0-7]` -> non-empty-string
27
27
  # - `[a-z]` -> lowercase-string
28
28
  # - `[A-Z]` -> uppercase-string
29
29
  # - `[[:digit:]]` -> numeric-string
30
30
  #
31
+ # #1004 — the hex/octal digit classes do NOT map to `hex-int-string` / `octal-int-string`.
32
+ # Those refinements' predicates (`Type::Refined` PREDICATES, in `type/refined.rb`) REQUIRE the
33
+ # conventional `0x` / `0o` prefix, but a bare digit class like `[0-9a-fA-F]+` matches "ff" —
34
+ # a string with no prefix at all. Mapping a matched digit run to a refinement its own
35
+ # predicate rejects is unsound (worse than the plain `String` it replaces), so these rows
36
+ # drop to `non-empty-string`: the `+` / `{n,}` (n >= 1) quantifier already guarantees the
37
+ # captured run is never empty, and that much is true regardless of prefix.
38
+ #
31
39
  # Anything outside the table returns `nil` so the calling narrowing site falls back to
32
40
  # its previous behaviour (plain `String`). Arbitrary regex semantic equivalence is
33
41
  # undecidable, so the table is intentionally a small audited set of canonical shapes
@@ -46,11 +54,11 @@ module Rigor
46
54
  RULES = Ractor.make_shareable([
47
55
  [/\A\\d#{QUANTIFIER_SOURCE}\z/, :decimal_int_string],
48
56
  [/\A\[0-9\]#{QUANTIFIER_SOURCE}\z/, :decimal_int_string],
49
- [/\A\\h#{QUANTIFIER_SOURCE}\z/, :hex_int_string],
50
- [/\A\[0-9a-fA-F\]#{QUANTIFIER_SOURCE}\z/, :hex_int_string],
51
- [/\A\[0-9a-f\]#{QUANTIFIER_SOURCE}\z/, :hex_int_string],
52
- [/\A\[0-9A-F\]#{QUANTIFIER_SOURCE}\z/, :hex_int_string],
53
- [/\A\[0-7\]#{QUANTIFIER_SOURCE}\z/, :octal_int_string],
57
+ [/\A\\h#{QUANTIFIER_SOURCE}\z/, :non_empty_string],
58
+ [/\A\[0-9a-fA-F\]#{QUANTIFIER_SOURCE}\z/, :non_empty_string],
59
+ [/\A\[0-9a-f\]#{QUANTIFIER_SOURCE}\z/, :non_empty_string],
60
+ [/\A\[0-9A-F\]#{QUANTIFIER_SOURCE}\z/, :non_empty_string],
61
+ [/\A\[0-7\]#{QUANTIFIER_SOURCE}\z/, :non_empty_string],
54
62
  [/\A\[a-z\]#{QUANTIFIER_SOURCE}\z/, :lowercase_string],
55
63
  [/\A\[A-Z\]#{QUANTIFIER_SOURCE}\z/, :uppercase_string],
56
64
  [/\A\[\[:digit:\]\]#{QUANTIFIER_SOURCE}\z/, :numeric_string]
@@ -62,11 +70,11 @@ module Rigor
62
70
 
63
71
  module_function
64
72
 
65
- # @param body [String, nil] a regex sub-pattern, typically the inner body of a
73
+ # @param body — a regex sub-pattern, typically the inner body of a
66
74
  # `(?<name>body)` named capture. Anchors (`\A`, `\z`, `^`, `$`) are not stripped — the
67
75
  # recogniser table targets bodies that the regex engine treats as anchored to the
68
76
  # capture group bounds.
69
- # @return [Rigor::Type, nil] the matching imported refinement carrier, or `nil` if
77
+ # @return the matching imported refinement carrier, or `nil` if
70
78
  # `body` is not a recognised shape.
71
79
  def for_capture_body(body)
72
80
  return nil if body.nil? || body.empty?
@@ -100,8 +108,8 @@ module Rigor
100
108
  # free-whitespace/`#`-comment flag from the `source` alone, so consumers bail on it before
101
109
  # calling here.
102
110
  #
103
- # @param source [String, nil] the full regex source string.
104
- # @return [Rigor::Type, nil] the matching imported refinement carrier, or `nil`.
111
+ # @param source — the full regex source string.
112
+ # @return the matching imported refinement carrier, or `nil`.
105
113
  def for_whole_pattern(source)
106
114
  return nil if source.nil?
107
115
  return nil unless source.start_with?('\A') && source.end_with?('\z')
@@ -93,20 +93,20 @@ module Rigor
93
93
  ["IO", :read, :singleton] => IO_READ_NO_LENGTH,
94
94
  ["File", :read, :singleton] => IO_READ_NO_LENGTH
95
95
  }.freeze
96
+ Ractor.make_shareable(OVERRIDES)
96
97
  private_constant :OVERRIDES
97
98
 
98
99
  # Looks up a refined return type for the given call.
99
100
  #
100
- # @param owner_class_name [String, nil] the class on which the method is defined
101
+ # @param owner_class_name — the class on which the method is defined
101
102
  # (e.g., `"Kernel"`). Pass `nil` when the caller hasn't resolved a defining owner
102
103
  # yet — the lookup will then fall back to matching by `(method_name, kind)` against
103
104
  # entries whose owner is currently in the table.
104
- # @param method_name [Symbol]
105
- # @param kind [Symbol] one of `:singleton`, `:instance`. The caller passes the shape of
105
+ # @param kind — one of `:singleton`, `:instance`. The caller passes the shape of
106
106
  # the actual call site; the table stores `:both` for entries that match either.
107
- # @param arg_types [Array<Rigor::Type>] positional argument types. Forwarded to the
107
+ # @param arg_types — positional argument types. Forwarded to the
108
108
  # handler so future entries can discriminate on argument shape.
109
- # @return [Rigor::Type, nil] the refined return type, or `nil` when no override
109
+ # @return the refined return type, or `nil` when no override
110
110
  # matches.
111
111
  def self.lookup(owner_class_name:, method_name:, kind:, arg_types: [])
112
112
  return nil if owner_class_name.nil?
@@ -125,9 +125,10 @@ module Rigor
125
125
  acc[mname] ||= []
126
126
  acc[mname] << owner unless acc[mname].include?(owner)
127
127
  end.freeze
128
+ Ractor.make_shareable(OWNERS_BY_METHOD)
128
129
  private_constant :OWNERS_BY_METHOD
129
130
 
130
- # @return [Array<String>] the candidate owner class names for a bare method-name
131
+ # @return the candidate owner class names for a bare method-name
131
132
  # lookup. Empty when no override names this method.
132
133
  NO_OWNERS = [].freeze
133
134
  private_constant :NO_OWNERS
@@ -41,8 +41,7 @@ module Rigor
41
41
 
42
42
  module_function
43
43
 
44
- # @param location [RBS::Location, nil]
45
- # @return [Array, nil] `[name, start_line, start_column, end_line, end_column]`, or nil when there is
44
+ # @return `[name, start_line, start_column, end_line, end_column]`, or nil when there is
46
45
  # no location to carry. Fail-soft: a location whose buffer cannot answer is dumped as nil rather
47
46
  # than failing the whole environment's dump.
48
47
  def dump(location)
@@ -55,8 +54,7 @@ module Rigor
55
54
  nil
56
55
  end
57
56
 
58
- # @param payload [Array, nil] what {dump} produced.
59
- # @return [RBS::Location, nil]
57
+ # @param payload — what {dump} produced.
60
58
  def load(payload)
61
59
  return nil if payload.nil?
62
60