rigortype 0.3.9 → 0.4.1

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 (407) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -4
  3. data/data/core_overlay/enumerable.rbs +51 -0
  4. data/data/core_overlay/enumerator.rbs +84 -0
  5. data/data/core_overlay/hash_rbs3.rbs +41 -0
  6. data/data/core_overlay/process.rbs +40 -0
  7. data/data/core_overlay/string_io.rbs +33 -0
  8. data/data/effects/core.yml +3 -3
  9. data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
  10. data/docs/handbook/03-narrowing.md +95 -10
  11. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  12. data/docs/handbook/07-rbs-and-extended.md +16 -11
  13. data/docs/handbook/08-understanding-errors.md +3 -2
  14. data/docs/handbook/10-sorbet.md +9 -10
  15. data/docs/handbook/11-sig-gen.md +454 -13
  16. data/docs/install.md +87 -15
  17. data/docs/manual/02-cli-reference.md +65 -4
  18. data/docs/manual/03-configuration.md +12 -0
  19. data/docs/manual/04-diagnostics.md +4 -0
  20. data/docs/manual/07-plugins.md +1 -1
  21. data/docs/manual/08-skills.md +4 -3
  22. data/docs/manual/10-mcp-server.md +3 -2
  23. data/docs/manual/12-caching.md +43 -4
  24. data/docs/manual/14-rails-quickstart.md +19 -11
  25. data/docs/manual/16-rbs-extended-annotations.md +40 -7
  26. data/docs/manual/19-effect-labels.md +10 -0
  27. data/docs/manual/plugins/README.md +7 -0
  28. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  29. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  30. data/docs/manual/plugins/rigor-actionpack.md +243 -0
  31. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  32. data/docs/manual/plugins/rigor-activejob.md +7 -0
  33. data/docs/manual/plugins/rigor-activerecord.md +110 -4
  34. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  35. data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
  36. data/docs/manual/plugins/rigor-grape.md +106 -0
  37. data/docs/manual/plugins/rigor-graphql.md +23 -2
  38. data/docs/manual/plugins/rigor-pundit.md +8 -1
  39. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  40. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  41. data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
  42. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  43. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  44. data/exe/rigor +19 -4
  45. data/lib/rigor/analysis/baseline.rb +1 -1
  46. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  47. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  48. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
  49. data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
  50. data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
  51. data/lib/rigor/analysis/check_rules/source_arity.rb +427 -0
  52. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  53. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
  54. data/lib/rigor/analysis/check_rules.rb +638 -136
  55. data/lib/rigor/analysis/dependency_recorder.rb +222 -17
  56. data/lib/rigor/analysis/fact_store.rb +9 -0
  57. data/lib/rigor/analysis/incremental.rb +26 -0
  58. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
  59. data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
  60. data/lib/rigor/analysis/incremental_session.rb +309 -53
  61. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
  62. data/lib/rigor/analysis/project_scan.rb +11 -1
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -1
  65. data/lib/rigor/analysis/rule_catalog.rb +127 -0
  66. data/lib/rigor/analysis/run_cache_key.rb +46 -18
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +168 -12
  69. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
  70. data/lib/rigor/analysis/runner/pool_coordinator.rb +109 -17
  71. data/lib/rigor/analysis/runner/project_pre_passes.rb +44 -17
  72. data/lib/rigor/analysis/runner.rb +603 -54
  73. data/lib/rigor/analysis/shadow_harness.rb +27 -0
  74. data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
  75. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  76. data/lib/rigor/analysis/template_unit_paths.rb +93 -0
  77. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  78. data/lib/rigor/analysis/template_units.rb +409 -0
  79. data/lib/rigor/analysis/worker_session.rb +57 -13
  80. data/lib/rigor/bleeding_edge.rb +0 -2
  81. data/lib/rigor/builtins/hkt_builtins.rb +1 -0
  82. data/lib/rigor/builtins/imported_refinements.rb +4 -0
  83. data/lib/rigor/builtins/regex_refinement.rb +17 -9
  84. data/lib/rigor/builtins/static_return_refinements.rb +2 -0
  85. data/lib/rigor/cache/descriptor.rb +45 -1
  86. data/lib/rigor/cache/engine_source.rb +29 -1
  87. data/lib/rigor/cache/file_digest.rb +28 -0
  88. data/lib/rigor/cache/incremental_snapshot.rb +102 -8
  89. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  90. data/lib/rigor/cache/rbs_descriptor.rb +37 -7
  91. data/lib/rigor/cache/store.rb +63 -4
  92. data/lib/rigor/cache/value_digest.rb +325 -0
  93. data/lib/rigor/ci_detector.rb +1 -0
  94. data/lib/rigor/cli/check_command.rb +83 -13
  95. data/lib/rigor/cli/doc_links.rb +1 -1
  96. data/lib/rigor/cli/docs_command.rb +4 -4
  97. data/lib/rigor/cli/doctor_command.rb +1 -1
  98. data/lib/rigor/cli/effects_report.rb +2 -2
  99. data/lib/rigor/cli/plugin_command.rb +3 -3
  100. data/lib/rigor/cli/prism_colorizer.rb +0 -1
  101. data/lib/rigor/cli/sig_gen_command.rb +210 -29
  102. data/lib/rigor/cli/skill_command.rb +1 -1
  103. data/lib/rigor/cli/skill_describe.rb +0 -1
  104. data/lib/rigor/cli/type_of_command.rb +19 -5
  105. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  106. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  107. data/lib/rigor/cli/unused_command.rb +3 -3
  108. data/lib/rigor/cli.rb +21 -3
  109. data/lib/rigor/config_audit.rb +3 -2
  110. data/lib/rigor/config_path.rb +29 -0
  111. data/lib/rigor/configuration/severity_profile.rb +24 -3
  112. data/lib/rigor/configuration.rb +71 -7
  113. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  114. data/lib/rigor/effects/attribution.rb +11 -2
  115. data/lib/rigor/effects/callee_rule.rb +368 -0
  116. data/lib/rigor/effects/catalog.rb +7 -4
  117. data/lib/rigor/effects/collector.rb +11 -5
  118. data/lib/rigor/effects/config_envelopes.rb +9 -2
  119. data/lib/rigor/effects/definition_context.rb +179 -0
  120. data/lib/rigor/effects/effect_table.rb +11 -3
  121. data/lib/rigor/effects/envelope_check.rb +1 -1
  122. data/lib/rigor/effects/envelope_index.rb +15 -0
  123. data/lib/rigor/effects/file_collection.rb +59 -5
  124. data/lib/rigor/effects/framework_units.rb +1 -1
  125. data/lib/rigor/effects/identity.rb +16 -0
  126. data/lib/rigor/effects/local_ownership.rb +38 -12
  127. data/lib/rigor/effects/method_key.rb +21 -0
  128. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  129. data/lib/rigor/effects/plugin_facts.rb +49 -37
  130. data/lib/rigor/effects/propagator.rb +295 -16
  131. data/lib/rigor/effects/registry.rb +1 -1
  132. data/lib/rigor/effects/scanner.rb +121 -72
  133. data/lib/rigor/effects/signature_sources.rb +1 -1
  134. data/lib/rigor/effects/snapshot.rb +2 -1
  135. data/lib/rigor/effects/summary.rb +27 -4
  136. data/lib/rigor/effects/unit_scan.rb +385 -30
  137. data/lib/rigor/effects/visibility.rb +101 -0
  138. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
  139. data/lib/rigor/environment/lockfile_resolver.rb +27 -1
  140. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  141. data/lib/rigor/environment/member_consistency.rb +298 -0
  142. data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
  143. data/lib/rigor/environment/rbs_loader.rb +400 -134
  144. data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
  145. data/lib/rigor/environment.rb +104 -96
  146. data/lib/rigor/hashing/xxh3.rb +264 -0
  147. data/lib/rigor/inference/acceptance.rb +139 -9
  148. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  149. data/lib/rigor/inference/block_call_timing.rb +338 -0
  150. data/lib/rigor/inference/block_parameter_binder.rb +88 -26
  151. data/lib/rigor/inference/block_repetition.rb +71 -0
  152. data/lib/rigor/inference/body_fixpoint.rb +2 -1
  153. data/lib/rigor/inference/budget_trace.rb +2 -1
  154. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  155. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  156. data/lib/rigor/inference/captured_locals.rb +393 -15
  157. data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
  158. data/lib/rigor/inference/content_join.rb +200 -27
  159. data/lib/rigor/inference/declaration_walk/context.rb +195 -0
  160. data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
  161. data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
  162. data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
  163. data/lib/rigor/inference/declaration_walk.rb +7 -0
  164. data/lib/rigor/inference/def_node_resolver.rb +3 -2
  165. data/lib/rigor/inference/def_return_typer.rb +11 -7
  166. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  167. data/lib/rigor/inference/definer_resolution.rb +266 -0
  168. data/lib/rigor/inference/element_read_widening.rb +22 -10
  169. data/lib/rigor/inference/error_info.rb +196 -0
  170. data/lib/rigor/inference/expression_typer.rb +1665 -538
  171. data/lib/rigor/inference/external_ancestor_resolution.rb +264 -0
  172. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  173. data/lib/rigor/inference/global_write_census.rb +239 -0
  174. data/lib/rigor/inference/guard_rebinding.rb +488 -0
  175. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  176. data/lib/rigor/inference/index_write_widening.rb +16 -3
  177. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  178. data/lib/rigor/inference/jump_targets.rb +82 -0
  179. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  180. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  181. data/lib/rigor/inference/last_line.rb +340 -0
  182. data/lib/rigor/inference/last_status.rb +144 -0
  183. data/lib/rigor/inference/macro_block_self_type.rb +179 -17
  184. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  185. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  186. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  187. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  188. data/lib/rigor/inference/match_rebinding.rb +392 -0
  189. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  190. data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
  191. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  192. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  193. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
  194. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  195. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
  196. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  197. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  198. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +901 -38
  199. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  200. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
  201. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
  202. data/lib/rigor/inference/method_dispatcher.rb +214 -13
  203. data/lib/rigor/inference/method_parameter_binder.rb +8 -3
  204. data/lib/rigor/inference/module_function_state.rb +213 -0
  205. data/lib/rigor/inference/multi_target_binder.rb +345 -52
  206. data/lib/rigor/inference/mutation_rejoin.rb +4 -1
  207. data/lib/rigor/inference/mutation_widening.rb +70 -48
  208. data/lib/rigor/inference/narrowing.rb +674 -124
  209. data/lib/rigor/inference/operand_effects.rb +167 -0
  210. data/lib/rigor/inference/operand_walk.rb +88 -0
  211. data/lib/rigor/inference/optimistic_origin.rb +152 -9
  212. data/lib/rigor/inference/parameter_inference_collector.rb +13 -11
  213. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  214. data/lib/rigor/inference/project_patched_methods.rb +7 -2
  215. data/lib/rigor/inference/project_patched_scanner.rb +7 -3
  216. data/lib/rigor/inference/receiver_alias.rb +90 -1
  217. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  218. data/lib/rigor/inference/refinement_mutation.rb +15 -11
  219. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  220. data/lib/rigor/inference/return_barrier.rb +54 -0
  221. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  222. data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
  223. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
  224. data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
  225. data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
  226. data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
  227. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
  228. data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
  229. data/lib/rigor/inference/scope_indexer.rb +5913 -701
  230. data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
  231. data/lib/rigor/inference/statement_evaluator.rb +3249 -497
  232. data/lib/rigor/inference/stored_block_call.rb +54 -0
  233. data/lib/rigor/inference/string_mutation.rb +44 -7
  234. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  235. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  236. data/lib/rigor/language_server/debouncer.rb +0 -1
  237. data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
  238. data/lib/rigor/language_server/hover_renderer.rb +3 -3
  239. data/lib/rigor/language_server/project_context.rb +5 -3
  240. data/lib/rigor/mcp/server.rb +2 -1
  241. data/lib/rigor/plugin/base.rb +196 -13
  242. data/lib/rigor/plugin/box_probe.rb +91 -0
  243. data/lib/rigor/plugin/bundled_catalog.rb +1 -1
  244. data/lib/rigor/plugin/effect_attribution.rb +58 -4
  245. data/lib/rigor/plugin/io_boundary.rb +82 -4
  246. data/lib/rigor/plugin/isolation.rb +1 -1
  247. data/lib/rigor/plugin/loader.rb +2 -1
  248. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  249. data/lib/rigor/plugin/manifest.rb +71 -10
  250. data/lib/rigor/plugin/registry.rb +35 -1
  251. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
  252. data/lib/rigor/plugin/template_unit.rb +196 -0
  253. data/lib/rigor/plugin/trust_policy.rb +3 -3
  254. data/lib/rigor/plugin.rb +1 -0
  255. data/lib/rigor/protection/discovery_seed.rb +20 -9
  256. data/lib/rigor/protection/kill_signature.rb +0 -1
  257. data/lib/rigor/protection/mutation_cache.rb +4 -5
  258. data/lib/rigor/rbs_extended.rb +27 -0
  259. data/lib/rigor/reflection/constant_ancestors.rb +146 -0
  260. data/lib/rigor/reflection/constant_path.rb +41 -13
  261. data/lib/rigor/reflection.rb +73 -107
  262. data/lib/rigor/scope/discovery_index.rb +351 -2
  263. data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
  264. data/lib/rigor/scope/resolution_chain.rb +884 -0
  265. data/lib/rigor/scope.rb +981 -198
  266. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  267. data/lib/rigor/sig_gen/classification.rb +22 -5
  268. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  269. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  270. data/lib/rigor/sig_gen/generator.rb +580 -47
  271. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  272. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  273. data/lib/rigor/sig_gen/observation_collector.rb +1 -1
  274. data/lib/rigor/sig_gen/renderer.rb +124 -9
  275. data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
  276. data/lib/rigor/sig_gen/write_result.rb +19 -3
  277. data/lib/rigor/sig_gen/writer.rb +166 -35
  278. data/lib/rigor/sig_gen.rb +3 -0
  279. data/lib/rigor/signature_path_audit.rb +1 -1
  280. data/lib/rigor/source/node_walker.rb +0 -3
  281. data/lib/rigor/source/parameter_envelope.rb +72 -0
  282. data/lib/rigor/source.rb +1 -0
  283. data/lib/rigor/type/combinator.rb +112 -12
  284. data/lib/rigor/type/difference.rb +1 -0
  285. data/lib/rigor/type/hash_shape.rb +1 -1
  286. data/lib/rigor/type/refined.rb +1 -0
  287. data/lib/rigor/version.rb +1 -1
  288. data/lib/rigor.rb +1 -0
  289. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  290. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
  291. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
  292. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
  293. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  294. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  295. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  296. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  297. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  298. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  299. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
  300. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  301. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  302. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  303. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  304. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  305. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  306. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
  307. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
  308. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  309. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  310. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
  311. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  312. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
  313. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  314. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
  315. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  316. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
  317. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
  318. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  319. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  320. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +10 -0
  321. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  322. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  323. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  324. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
  325. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  326. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
  327. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
  328. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
  329. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
  330. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
  331. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
  332. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
  333. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  334. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
  335. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
  336. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
  337. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
  338. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
  339. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  340. data/sig/rigor/analysis/baseline.rbs +69 -7
  341. data/sig/rigor/analysis/fact_store.rbs +1 -1
  342. data/sig/rigor/analysis/project_scan.rbs +74 -0
  343. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  344. data/sig/rigor/effects/effect_table.rbs +60 -0
  345. data/sig/rigor/effects/envelope.rbs +97 -0
  346. data/sig/rigor/effects/envelope_index.rbs +33 -0
  347. data/sig/rigor/effects/file_collection.rbs +81 -0
  348. data/sig/rigor/effects/label.rbs +26 -0
  349. data/sig/rigor/effects/label_set.rbs +46 -0
  350. data/sig/rigor/effects/method_key.rbs +24 -0
  351. data/sig/rigor/effects/origin.rbs +50 -0
  352. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  353. data/sig/rigor/effects/registry.rbs +68 -0
  354. data/sig/rigor/effects/summary.rbs +50 -0
  355. data/sig/rigor/effects/taint_cause.rbs +12 -0
  356. data/sig/rigor/environment.rbs +14 -10
  357. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  358. data/sig/rigor/inference.rbs +6 -4
  359. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  360. data/sig/rigor/plugin/base.rbs +30 -7
  361. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  362. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  363. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  364. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  365. data/sig/rigor/plugin/io_boundary.rbs +2 -1
  366. data/sig/rigor/plugin/loader.rbs +3 -3
  367. data/sig/rigor/plugin/manifest.rbs +50 -11
  368. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  369. data/sig/rigor/plugin/registry.rbs +62 -1
  370. data/sig/rigor/plugin.rbs +1 -1
  371. data/sig/rigor/rbs_extended.rbs +1 -1
  372. data/sig/rigor/reflection.rbs +7 -6
  373. data/sig/rigor/scope.rbs +177 -20
  374. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
  375. data/sig/rigor/source.rbs +4 -4
  376. data/sig/rigor/testing.rbs +10 -4
  377. data/sig/rigor/type.rbs +6 -0
  378. data/sig/rigor.rbs +38 -20
  379. data/skills/rigor-ask/SKILL.md +8 -5
  380. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  381. data/skills/rigor-ci-setup/SKILL.md +17 -21
  382. data/skills/rigor-doctor/SKILL.md +24 -22
  383. data/skills/rigor-doctor/references/01-checks.md +97 -33
  384. data/skills/rigor-editor-setup/SKILL.md +6 -4
  385. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  386. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  387. data/skills/rigor-next-steps/SKILL.md +4 -2
  388. data/skills/rigor-plugin-author/SKILL.md +19 -23
  389. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  390. data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
  391. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  392. data/skills/rigor-plugin-review/SKILL.md +6 -4
  393. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  394. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  395. data/skills/rigor-project-init/SKILL.md +46 -18
  396. data/skills/rigor-project-init/references/01-detect.md +13 -9
  397. data/skills/rigor-project-init/references/02-configure.md +40 -10
  398. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  399. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  400. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  401. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  402. data/skills/rigor-type-oracle/SKILL.md +4 -6
  403. data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
  404. data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
  405. data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
  406. data/skills/rigor-upgrade/SKILL.md +13 -8
  407. metadata +132 -1
@@ -45,9 +45,10 @@ module Rigor
45
45
  # out of scope for the v0.0.7 first pass; v0.1.0's plugin API added it as a separate
46
46
  # concern.
47
47
  module Reflection
48
- # #354 — thread-local slot for the per-run ancestor-scope memo. See {.ancestor_constant_scopes}.
49
- ANCESTOR_SCOPES_KEY = :__rigor_ancestor_constant_scopes__
50
- private_constant :ANCESTOR_SCOPES_KEY
48
+ # Issue #1290 — what {.constant_type_at} answers for a candidate the project writes but no source types, on
49
+ # a ladder that checks the census. Never a type: {.resolve_constant_type} replaces it before it returns.
50
+ WRITTEN_CANDIDATE = Object.new.freeze
51
+ private_constant :WRITTEN_CANDIDATE
51
52
 
52
53
  module_function
53
54
 
@@ -140,10 +141,16 @@ module Rigor
140
141
  # answer to `::Foo`, and falling back to it was the bug (`::Rails` inside `module MyApp` typed
141
142
  # as `MyApp::Rails`).
142
143
  #
144
+ # `caller_derived: false` drops what {.toplevel_first_constant_type} takes from the caller — its
145
+ # namespaces and ancestors, for a bare name and for a path's head — for a reader whose miss is a
146
+ # meaningful answer rather than a read Ruby raises on. A constant compound write is one: an unset
147
+ # top-level `REGISTRY ||= {}` is the memoization idiom, and the caller's `Plugin::REGISTRY` is a binding
148
+ # Ruby never consults there. A path's later segments still walk their owner's ancestors.
149
+ #
143
150
  # This is the shared owner of the lexical-constant resolution: `Inference::ExpressionTyper`
144
151
  # reads it to type a constant read, and `Inference::Narrowing` reads it to recognise a
145
152
  # value-pinned `Constant[Regexp]` match-predicate operand.
146
- def resolve_constant_type(name, scope: Scope.empty, rooted: false)
153
+ def resolve_constant_type(name, scope: Scope.empty, rooted: false, caller_derived: true)
147
154
  # Issue #644 — the ADR-46 name edge, recorded ONCE PER REFERENCE and BEFORE the ladder, so it does not
148
155
  # depend on which resolver answers. A reference that resolves through RBS (`Math::PI`) or through the
149
156
  # class registry is exactly as dependent on the project's constant write set as one that resolves
@@ -153,10 +160,25 @@ module Rigor
153
160
  # go stale. Over-recording is ADR-46's safe direction.
154
161
  record_constant_reference(name) if Analysis::DependencyRecorder.active?
155
162
  return constant_type_at(name, scope) if rooted
163
+
164
+ # Issue #1290 — a candidate the project writes but no source types is where Ruby's lookup stops, so it
165
+ # REPLACES what a lower rung answers, and nothing else. The first pass hands the census's shadowing
166
+ # names to the rungs ({.constant_type_at}); a stop is `Dynamic[top]` only when the plain ladder, run
167
+ # again, answers below it. When nothing below answers, the reference stays unresolved exactly as before,
168
+ # so the nil its caller turns into the fallback, the missing-constant edge and the gem-origin label stay.
169
+ hit = lexical_constant_type(name, scope, caller_derived, scope.shadowing_constant_names(name.to_s))
170
+ return hit unless hit.equal?(WRITTEN_CANDIDATE)
171
+
172
+ Type::Combinator.untyped if lexical_constant_type(name, scope, caller_derived, nil)
173
+ end
174
+
175
+ # {.resolve_constant_type}'s unrooted ladder. `shadows` is the census's shadowing names for the
176
+ # reference, which the rungs above the last check their candidates against, or nil for the plain ladder.
177
+ def lexical_constant_type(name, scope, caller_derived, shadows)
156
178
  # Issue #716 — a body whose `Module.nesting` was RECORDED EMPTY is definitively top level, and Ruby
157
179
  # resolves its constants there no matter which namespace calls it. Steps 1 and 2 both derive from the
158
180
  # caller and are both wrong for it, so it takes its own ladder.
159
- return toplevel_first_constant_type(name, scope) if recorded_toplevel_nesting?(scope)
181
+ return toplevel_first_constant_type(name, scope, caller_derived, shadows) if recorded_toplevel_nesting?(scope)
160
182
 
161
183
  # Step 1 — `Module.nesting`, innermost first. Each entry contributes only its OWN constants.
162
184
  #
@@ -174,19 +196,20 @@ module Rigor
174
196
  # beating the top-level whole-string answer is the point: `A::B` inside `P::Guard` with
175
197
  # `P::A < P::Base` is `P::Base::B`, and step 3's `A::B` is a real but different class.
176
198
  #
177
- # Step 3 — the bare name (top level).
178
- first_constant_hit(lexical_nesting_chain(scope), name, scope) ||
179
- ancestor_constant_type(name, scope, enclosing_class_path(scope)) ||
180
- constant_path_type(name, scope) ||
199
+ # Step 3 — the bare name (top level). It takes no `shadows`: nothing sits below it for a stop to replace.
200
+ first_constant_hit(lexical_nesting_chain(scope), name, scope, shadows) ||
201
+ ancestor_constant_type(name, scope, enclosing_class_path(scope), shadows) ||
202
+ constant_path_type(name, scope, shadows: shadows) ||
181
203
  constant_type_at(name, scope)
182
204
  end
205
+ private_class_method :lexical_constant_type
183
206
 
184
207
  # The first candidate `<entry>::<name>` any source knows, walking `entries` in order, or nil. The one
185
208
  # place a qualifying prefix is joined to a name, so every rung of the ladder consults its candidates
186
209
  # identically.
187
- def first_constant_hit(entries, name, scope)
210
+ def first_constant_hit(entries, name, scope, shadows)
188
211
  entries.each do |entry|
189
- hit = constant_type_at("#{entry}::#{name}", scope)
212
+ hit = constant_type_at("#{entry}::#{name}", scope, shadows)
190
213
  return hit if hit
191
214
  end
192
215
  nil
@@ -194,10 +217,10 @@ module Rigor
194
217
  private_class_method :first_constant_hit
195
218
 
196
219
  # Step 2's rung on its own: nil when there is no enclosing class path to take ancestors of.
197
- def ancestor_constant_type(name, scope, prefix)
220
+ def ancestor_constant_type(name, scope, prefix, shadows)
198
221
  return nil if prefix.nil? || prefix.empty?
199
222
 
200
- first_constant_hit(ancestor_constant_scopes(prefix, scope), name, scope)
223
+ agreed_ancestor_hit(prefix, scope) { |entry| constant_type_at("#{entry}::#{name}", scope, shadows) }
201
224
  end
202
225
  private_class_method :ancestor_constant_type
203
226
 
@@ -222,21 +245,27 @@ module Rigor
222
245
  #
223
246
  # Issue #656's rung goes LAST here rather than second: for a top-level body Ruby's own first answer
224
247
  # 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.
226
- def toplevel_first_constant_type(name, scope)
227
- constant_type_at(name, scope) || caller_derived_constant_type(name, scope) || constant_path_type(name, scope)
248
+ # segment-wise walk only has something to add once that misses. Its head consults the caller's
249
+ # ancestors, so under `caller_derived: false` it resolves the head at the top level alone.
250
+ #
251
+ # Issue #1290 — the top level is this ladder's FIRST rung, so a top-level name the project writes stops it
252
+ # ({.constant_type_at}) before a caller's `Plug::TOP` can answer a read Ruby gives to the top-level `TOP`.
253
+ def toplevel_first_constant_type(name, scope, caller_derived, shadows)
254
+ constant_type_at(name, scope, shadows) ||
255
+ (caller_derived && caller_derived_constant_type(name, scope, shadows)) ||
256
+ constant_path_type(name, scope, caller_derived: caller_derived, shadows: shadows)
228
257
  end
229
258
  private_class_method :toplevel_first_constant_type
230
259
 
231
260
  # The peel and its ancestors — {.resolve_constant_type}'s steps 1 and 2 as they answer for a scope with
232
261
  # no recorded chain, factored out so the top-level ladder above can consult them BELOW the top level
233
262
  # instead of above it.
234
- def caller_derived_constant_type(name, scope)
263
+ def caller_derived_constant_type(name, scope, shadows)
235
264
  prefix = enclosing_class_path(scope)
236
265
  return nil if prefix.nil? || prefix.empty?
237
266
 
238
- first_constant_hit(peeled_nesting(prefix), name, scope) ||
239
- ancestor_constant_type(name, scope, prefix)
267
+ first_constant_hit(peeled_nesting(prefix), name, scope, shadows) ||
268
+ ancestor_constant_type(name, scope, prefix, shadows)
240
269
  end
241
270
  private_class_method :caller_derived_constant_type
242
271
 
@@ -244,7 +273,25 @@ module Rigor
244
273
  # `Singleton[C]`), source-discovered classes, in-source value constants, then RBS-side
245
274
  # constants. In-source values win over RBS constant decls because the user's source is
246
275
  # authoritative for its own constants. Returns nil when no source knows `candidate`.
247
- def constant_type_at(candidate, scope)
276
+ #
277
+ # Issue #1290 — `shadows`, passed on every rung that has a rung below it, is the last source: a candidate
278
+ # no other source types answers {WRITTEN_CANDIDATE} when `shadows` holds it, and {.resolve_constant_type}
279
+ # settles that stop. `shadows` is `Scope#shadowing_constant_names` for the reference, whose last segment
280
+ # every candidate shares. Ruby's lookup stops at a constant that exists, whatever its value, and the
281
+ # cross-file table publishes only a frozen scalar one file writes: another file's `App::LIMIT = { n: 1 }`
282
+ # was missed by every source, and the ladder moved on to a published top-level `LIMIT = 5`, typing `LIMIT`
283
+ # inside `module App` as `5` and reporting `call.undefined-method` on correct code. The value is one the
284
+ # analyzer does not carry, so the stop is gradual.
285
+ #
286
+ # Only an EXACT census name stops a candidate. The census keeps a path write as written
287
+ # ([#690](https://github.com/rigortype/rigor/issues/690)), so a suffix match would reach the as-written
288
+ # `Foo::BAR` from `M::Foo::BAR`, but would also stop `Admin::UsersController::X` on a top-level
289
+ # `UsersController::X`, losing the answer an ordinary Rails layout gets from the namespaced controller's
290
+ # superclass. A name written only through `||=` does not shadow, however many files memoize it: `X ||= v`
291
+ # resolves `X` through this same lookup, so wherever a lower rung answers, the memo finds that constant and
292
+ # never creates the name. The answer moves with the project's write set for the last segment, which the
293
+ # `constant:` edge {.resolve_constant_type} records once per reference already covers.
294
+ def constant_type_at(candidate, scope, shadows = nil)
248
295
  env = scope.environment
249
296
 
250
297
  singleton = env.singleton_for_name(candidate)
@@ -264,7 +311,7 @@ module Rigor
264
311
  return in_source_value
265
312
  end
266
313
 
267
- env.constant_for_name(candidate)
314
+ env.constant_for_name(candidate) || (WRITTEN_CANDIDATE if shadows&.include?(candidate))
268
315
  end
269
316
  private_class_method :constant_type_at
270
317
 
@@ -275,92 +322,10 @@ module Rigor
275
322
  # ({Analysis::Incremental.changed_constant_publications}). Gated by the caller on the recorder being
276
323
  # active, so an ordinary run pays one integer read.
277
324
  def record_constant_reference(name)
278
- segment = name.delete_prefix("::").split("::").last
279
- Analysis::DependencyRecorder.read_name(:constant, segment) if segment
325
+ Analysis::DependencyRecorder.read_last_segment(:constant, name)
280
326
  end
281
327
  private_class_method :record_constant_reference
282
328
 
283
- # #354 — the project classes and modules whose own constants `class_name` inherits, in Ruby's
284
- # ancestor order: included / prepended modules before the superclass (Ruby places mixins nearer),
285
- # transitively, breadth-first. `class_name` itself is excluded — step 1 already covered it.
286
- #
287
- # Only PROJECT ancestors appear. `Scope#superclass_of` / `#includes_of` carry as-written names
288
- # from the discovery pre-pass, and an as-written name that resolves to no discovered class or
289
- # module is dropped — so a `class Foo < ActiveRecord::Base` contributes nothing and a constant
290
- # owned by an RBS-known ancestor still resolves only if the bare name reaches it at step 3. That
291
- # gap is deliberate for this slice: widening to the RBS ancestor graph is a separate question
292
- # with its own FP surface.
293
- #
294
- # Memoised per run because step 2 runs on every constant reference whose lexical candidates all
295
- # miss — which is the common case for a core-class reference (`String` inside `class Foo`). The
296
- # bucket keys on the identity of the runner-seeded run-generation token (ADR-84 WD2), falling
297
- # back to the per-file discovery table for runner-less scopes, so a re-run in one process (LSP,
298
- # ADR-62 warm loop) cannot hit stale entries.
299
- def ancestor_constant_scopes(class_name, scope)
300
- # ADR-46: `superclass_of` / `includes_of` record a cross-file class dependency per consumer
301
- # file, and the memo is run-scoped rather than file-scoped — a hit would skip the recording and
302
- # under-record the edge for every later file. Recording runs are rare (incremental only), so
303
- # they simply bypass the memo rather than complicate its key.
304
- return compute_ancestor_constant_scopes(class_name, scope) if Analysis::DependencyRecorder.active?
305
-
306
- generation = scope.run_generation || scope.discovered_superclasses
307
- slot = Thread.current[ANCESTOR_SCOPES_KEY]
308
- unless slot && slot[0].equal?(generation)
309
- slot = [generation, {}]
310
- Thread.current[ANCESTOR_SCOPES_KEY] = slot
311
- end
312
- bucket = slot[1]
313
- bucket.fetch(class_name) { bucket[class_name] = compute_ancestor_constant_scopes(class_name, scope) }
314
- end
315
- private_class_method :ancestor_constant_scopes
316
-
317
- def compute_ancestor_constant_scopes(class_name, scope)
318
- queue = [class_name]
319
- seen = { class_name => true }
320
- out = []
321
- until queue.empty?
322
- current = queue.shift
323
- # Mixins first, then the superclass — Ruby's ancestor order.
324
- scope.includes_of(current).each do |raw|
325
- resolved = resolve_ancestor_name(current, raw, scope)
326
- next if resolved.nil? || seen[resolved]
327
-
328
- seen[resolved] = true
329
- out << resolved
330
- queue << resolved
331
- end
332
- raw_super = scope.superclass_of(current)
333
- next if raw_super.nil?
334
-
335
- resolved_super = resolve_ancestor_name(current, raw_super, scope)
336
- next if resolved_super.nil? || seen[resolved_super]
337
-
338
- seen[resolved_super] = true
339
- out << resolved_super
340
- queue << resolved_super
341
- end
342
- out.freeze
343
- end
344
- private_class_method :compute_ancestor_constant_scopes
345
-
346
- # Resolves an ancestor name AS WRITTEN (`"Base"`, or a qualified `"A::B"`) against the nesting in
347
- # force where the subclass's header is written — `Scope#ancestor_name_candidates`, the single
348
- # owner of that order, which `Scope#enqueue_ancestors` reads for method lookup and the
349
- # override-visibility rule reads for its own walk. Returns nil when no candidate names a
350
- # discovered project class or module.
351
- def resolve_ancestor_name(subclass_qualified, raw, scope)
352
- scope.ancestor_name_candidates(subclass_qualified, raw)
353
- .find { |candidate| known_project_namespace?(candidate, scope) }
354
- end
355
- private_class_method :resolve_ancestor_name
356
-
357
- def known_project_namespace?(name, scope)
358
- scope.discovered_superclasses.key?(name) ||
359
- scope.discovered_includes.key?(name) ||
360
- scope.discovered_classes.key?(name)
361
- end
362
- private_class_method :known_project_namespace?
363
-
364
329
  # Pulls the enclosing qualified class name out of `scope.self_type` when one is set.
365
330
  # `Nominal[T]` and `Singleton[T]` both expose `class_name`. Returns nil at the top level.
366
331
  def enclosing_class_path(scope)
@@ -447,7 +412,7 @@ module Rigor
447
412
  nil
448
413
  end
449
414
 
450
- # Returns the full RBS singleton-side class definition.
415
+ # The singleton-side twin of {#instance_definition}, nil under the same conditions.
451
416
  def singleton_definition(class_name, scope: nil, environment: nil)
452
417
  loader = rbs_loader_for(scope, environment)
453
418
  return nil if loader.nil?
@@ -510,6 +475,7 @@ module Rigor
510
475
  end
511
476
  end
512
477
 
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.
478
+ # Loaded last: the ancestor walk and the segment-wise path walk reopen the module above and read its
479
+ # private candidate lookups, so the facade has to exist first.
480
+ require_relative "reflection/constant_ancestors"
515
481
  require_relative "reflection/constant_path"