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
@@ -36,21 +36,27 @@ path, no vendoring, no `signature_paths:` wiring.
36
36
 
37
37
  Roughly the top ~40 selectors plus their close neighbours, across:
38
38
 
39
- - **Object (universal)** — `#blank?`, `#present?`, `#presence`, `#try`,
40
- `#try!`, `#acts_like?` (+ `NilClass` / `TrueClass` / `FalseClass`).
39
+ - **Object (universal)** — `#blank?`, `#present?`, `#presence`,
40
+ `#presence_in`, `#try`, `#try!`, `#acts_like?`, `#deep_dup`, `#with`,
41
+ `#with_options`, `#html_safe?`, `Kernel#class_eval` (+ `NilClass` /
42
+ `TrueClass` / `FalseClass`).
41
43
  - **Integer / Float** — Duration multipliers (`#days`, `#hours`,
42
44
  `#minutes`, …) and Bytes multipliers (`#megabytes`, `#gigabytes`, …).
43
45
  - **String** — inflections (`#underscore`, `#camelize`, `#classify`,
44
46
  `#constantize`, `#pluralize`, …), filters (`#squish`, `#truncate`),
45
- `#html_safe`, `#starts_with?` / `#ends_with?`, conversions.
47
+ `#html_safe`, `#starts_with?` / `#ends_with?` (also on **Symbol**),
48
+ conversions and `#in_time_zone`.
46
49
  - **Time / Date / DateTime** — `.current`, `.zone`, `#yesterday`,
47
50
  `#tomorrow`, `#beginning_of_*` / `#end_of_*`, `#ago`, `#since`. `Time`
48
51
  additionally carries its **whole** Rails instance surface (see below);
49
52
  `Date` and `DateTime` carry the same subset they always did.
50
53
  - **Array** — `.wrap`, `#to_sentence`, `#in_groups_of`, `#second` …
51
54
  `#fifth`, `#compact_blank`, `#exclude?`.
52
- - **Hash** — `#symbolize_keys` / `#stringify_keys` (+ deep / bang),
53
- `#deep_merge`, `#with_indifferent_access`, `#except!`.
55
+ - **Hash** — `#symbolize_keys` / `#stringify_keys` (+ deep / bang) and
56
+ their `#to_options` alias, `#deep_merge`, `#reverse_merge` /
57
+ `#with_defaults` (+ bang), `#with_indifferent_access`, `#except!`,
58
+ `#extract!`.
59
+ - **Range** — `#overlaps?`, `#to_fs` / `#to_formatted_s`.
54
60
  - **Enumerable** — `#index_by`, `#index_with`, `#pluck`, `#exclude?`.
55
61
 
56
62
  ```ruby
@@ -0,0 +1,106 @@
1
+ # rigor-grape
2
+
3
+ Types the [Grape](https://github.com/ruby-grape/grape)
4
+ endpoint-declaration DSL so calls inside `class API < Grape::API`
5
+ bodies — and inside `Grape::Entity` subclasses from
6
+ [grape-entity](https://github.com/ruby-grape/grape-entity) — resolve to
7
+ real carriers instead of `Dynamic[top]`. It reads source only, with no
8
+ `grape` runtime dependency.
9
+
10
+ It ships bundled in `rigortype`. Activate it under `plugins:` (or let
11
+ bundler auto-detection pick it up when `grape` or `grape-entity` is in
12
+ `Gemfile.lock`):
13
+
14
+ ```yaml
15
+ plugins:
16
+ - rigor-grape
17
+ ```
18
+
19
+ ## What it types
20
+
21
+ ```ruby
22
+ class ThingsAPI < Grape::API
23
+ version "v1", using: :path
24
+ format :json
25
+
26
+ desc "List things" # Object?
27
+ route_setting :swagger, tags: %w[things] # Object?
28
+
29
+ params do # → Grape::Validations::ParamsScope
30
+ requires :id, type: Integer # Object?
31
+ optional :q, type: String # Object?
32
+ requires :filter, type: Hash do # nested scope re-enters ParamsScope
33
+ requires :state, type: String
34
+ end
35
+ mutually_exclusive :q, :filter
36
+ end
37
+
38
+ namespace :things do # self: the API::Instance class object
39
+ get "/:id" do # self: Grape::Endpoint
40
+ params # Hash[untyped, untyped]
41
+ error!("nope", 404) # bot
42
+ present Thing.first, with: Entities::Thing
43
+ end
44
+ end
45
+ end
46
+
47
+ class Entities::Thing < Grape::Entity
48
+ expose :id # Array[untyped]
49
+ expose :name do # `self` stays the class object
50
+ expose :first
51
+ end
52
+ format_with(:iso) { |d| d.to_s } # Object
53
+ end
54
+ ```
55
+
56
+ Recognised surfaces:
57
+
58
+ - **Class-body declarations** on `Grape::API` subclasses (including
59
+ through intermediate source base classes): `params`, `namespace` /
60
+ `group` / `resource` / `resources` / `segment` / `route_param` /
61
+ `version` / `given` / `mounted`, `get`/`put`/`post`/`delete`/`head`/
62
+ `patch`/`options`/`route`, `desc`, `route_setting`, `helpers`, `use`,
63
+ `mount`, `rescue_from`, the format/error-format setters, callbacks
64
+ (`before`/`after`/`before_validation`/`after_validation`), `prefix`,
65
+ `scope`, `contract`, and friends.
66
+ - **`params` block bodies** bind `self` to
67
+ `Grape::Validations::ParamsScope`, so `requires`, `optional`, `given`,
68
+ `with`, `use`, and the grouping macros (`mutually_exclusive`,
69
+ `exactly_one_of`, `at_least_one_of`, `all_or_none_of`) resolve —
70
+ including nested `requires :x, type: Hash do ... end` bodies.
71
+ - **Namespace-family bodies** (`namespace`, `route_param`, `version`,
72
+ `given`, `mounted`) bind `self` to the `Grape::API::Instance` class
73
+ object, matching Grape's `instance_eval`-on-class semantics, so nested
74
+ declarations resolve the same surface.
75
+ - **`desc 'x' do ... end` bodies** bind `self` to
76
+ `Grape::DSL::Desc::ConfigContext` — the closed `ROUTE_ATTRIBUTES`
77
+ setter surface — so `detail`, `success`, `failure`, `tags`,
78
+ `entity`, `hidden`, `is_array`, `consumes`, `headers`, `summary`,
79
+ `deprecated`, `named`, `nickname`, `produces`, `security`,
80
+ `http_codes`, `body_name`, `default`, `description`, and `params`
81
+ (the documentation setter, not `ParamsScope`) resolve.
82
+ - **Verb bodies** bind `self` to `Grape::Endpoint`, so `params`,
83
+ `headers`, `cookies`, `env`, `declared`, `present`, `error!`,
84
+ `status`, `redirect`, `body`, `content_type`, `route`,
85
+ `route_setting`, `stream`, `sendfile` resolve.
86
+ - **`Grape::Entity` class bodies**: `expose`, `unexpose`,
87
+ `with_options`, `documentation`, `format_with`, `root`,
88
+ `root_element`, `represent`, `present_collection`, `root_exposures`.
89
+ Nested `expose` bodies keep `self` on the class object, so they
90
+ resolve the same way.
91
+
92
+ ## Deferred
93
+
94
+ - `helpers do ... end` bodies (an anonymous module is `self` —
95
+ unnameable).
96
+ - Named `params :name` scopes inside `helpers` and `contract` schema
97
+ blocks.
98
+ - Value-level typing: `present`/`declared` results against the declared
99
+ entity or params, entity `represent` result typing.
100
+ - `Grape::Middleware` and custom-middleware DSL.
101
+
102
+ Calls on classes outside `Grape::API` / `Grape::Entity` ancestry keep
103
+ the `Dynamic` fallback, and DSL names the bundled signature does not
104
+ declare stay opaque rather than diagnosed — the declared classes are
105
+ registered as open receivers because Grape's runtime surface is
106
+ generated (`override_all_methods!`) and user base classes extend it.
@@ -58,11 +58,25 @@ name. `null:` extracts to `nullable:` (defaulting to `true`, mirroring
58
58
  graphql-ruby); `required:` defaults to `false`. The single-element
59
59
  `[String]` list form is recognised.
60
60
 
61
+ ## Typed DSL surface
62
+
63
+ Beyond the fact tables, the plugin bundles the graphql-ruby class-level
64
+ DSL signature, so calls inside a recognised subclass type as their real
65
+ carriers instead of staying opaque: `field` returns
66
+ `GraphQL::Schema::Field`, `argument` returns `GraphQL::Schema::Argument`,
67
+ `Enum.value` returns `GraphQL::Schema::EnumValue`, `description` /
68
+ `graphql_name` return their configured strings, and the `Schema`
69
+ registration macros (`query`, `mutation`, `use`, `orphan_types`, …)
70
+ return what they registered. A block parameter on `field`/`argument`
71
+ (`field(:comments) { |f| … }`) types as `GraphQL::Schema::Field` /
72
+ `GraphQL::Schema::Argument`.
73
+
61
74
  ## No diagnostics, no config
62
75
 
63
76
  The plugin emits no diagnostics and has no configuration knobs — it
64
- contributes the type tables above for other plugins to consume. It
65
- walks every `paths:` entry's `.rb` files for the schema-class shapes.
77
+ contributes the type tables and signature above for the engine and
78
+ other plugins to consume. It walks every `paths:` entry's `.rb` files
79
+ for the schema-class shapes.
66
80
 
67
81
  ## Limitations
68
82
 
@@ -72,6 +86,13 @@ walks every `paths:` entry's `.rb` files for the schema-class shapes.
72
86
  - **No `Schema.execute(...)` result typing.** Typing
73
87
  `Schema.execute(query).to_h` against the queried fields is a future
74
88
  plugin.
89
+ - **Interface DSL stays opaque.** `include GraphQL::Schema::Interface`
90
+ wires the DSL onto the includer through an include-time `extend`
91
+ hook RBS cannot express, so `field` inside an interface module is
92
+ not yet typed.
93
+ - **Zero-arity definition blocks keep an opaque `self`.**
94
+ `field :x do ... end` `instance_eval`s on the new `Field` at
95
+ runtime; only the explicit-parameter form (`{ |f| … }`) is typed.
75
96
  - **Constant-form types only.** The string form (`field :foo, "User"`)
76
97
  and the `<Type>.array` / `<Type>!` sugar chains are not recognised
77
98
  (the `[String]` bracket form is). Multi-element and empty list
@@ -33,7 +33,7 @@ authorize(Comment, :edit) # error: no policy class CommentPolicy (did you mean
33
33
  | `plugin.pundit.policy-call` | info | an `authorize` / `policy` / `policy_scope` call resolved to a discovered policy |
34
34
  | `plugin.pundit.unknown-policy-class` | error | the record maps to a `<Type>Policy` with no entry in the index (with a did-you-mean) |
35
35
  | `plugin.pundit.unknown-policy-method` | error | the policy exists but the `:action` has no `<action>?` predicate (lists known predicates + a did-you-mean) |
36
- | `plugin.pundit.load-error` | warning | policy discovery failed (parse/read error) — once per file |
36
+ | `plugin.pundit.load-error` | warning | policy discovery failed (parse/read error) — once per run, on `.rigor.yml` |
37
37
 
38
38
  The record maps to a policy by constant name or inferred
39
39
  `Nominal[T]` (`Post` → `PostPolicy`); `:update` normalises to
@@ -88,6 +88,13 @@ candidate rather than being guessed at.
88
88
  not validated when `local` has no inferred `Nominal[T]`.
89
89
  - **`Scope` policies** are validated for class existence, not for
90
90
  `Scope#resolve`.
91
+ - **The "failed to discover policies" warning is run-scoped.** It is
92
+ a fact about your configuration, not about any one source file, so
93
+ it is reported once per run on `.rigor.yml` rather than repeated on
94
+ every analysed file. It is `:warning`, so if you baselined it at its
95
+ old position that entry no longer matches and the row fails a
96
+ `--fail-on=warning` run — regenerate with `rigor baseline
97
+ regenerate`.
91
98
 
92
99
  ## Plugin internals
93
100
 
@@ -88,11 +88,16 @@ view templates containing lazy `t('.key')` calls.
88
88
  may come from controller instance variables not visible in the
89
89
  template source. Configure `view_search_paths:` to override the
90
90
  default `["app/views"]`.
91
- - **View diagnostics duplicate under `--workers`** — the view
92
- scan is a project-wide pass surfaced through the per-file
93
- diagnostic hook, so each fork-pool worker re-emits the full set
94
- (the same once-per-run limitation the `load-error` diagnostics
95
- carry). Default `rigor check` (sequential) is unaffected.
91
+ - **`load-error` rows are reported on `.rigor.yml`** — a locale
92
+ file that does not parse, or a locale or view scan that fails,
93
+ is reported once per run at `.rigor.yml:1:1`. They are
94
+ `:warning`, so if you baselined one at its old position on a
95
+ Ruby file, that entry no longer matches and the row fails a
96
+ `--fail-on=warning` run — regenerate with `rigor baseline
97
+ regenerate`. View diagnostics keep their view file position and
98
+ are reported once per run whatever `--workers` is set to
99
+ ([#1060](https://github.com/rigortype/rigor/issues/1060)); a
100
+ baseline entry recorded against a view still matches.
96
101
  - **Pluralization is recognised but not validated** — `count:` is
97
102
  treated as a reserved option; whether the locale defines
98
103
  `:zero` / `:one` / `:other` is not checked.
@@ -129,6 +129,13 @@ routed. Helper-name recognition for all three is unaffected.
129
129
  doesn't model) may not register, which can surface a false
130
130
  `unknown-helper`. Record those in a baseline, or
131
131
  `# rigor:disable` the line.
132
+ - **The "routes file did not load" warning is run-scoped.** It is a
133
+ fact about your configuration, not about any one source file, so
134
+ it is reported once per run on `.rigor.yml` rather than on the
135
+ first analysed file. It is `:warning`, so if you baselined it at
136
+ its old position that entry no longer matches and the row fails a
137
+ `--fail-on=warning` run — regenerate with `rigor baseline
138
+ regenerate`.
132
139
  - **Project-custom inflections** declared in
133
140
  `config/initializers/inflections.rb` are not yet fully ingested
134
141
  (ADR-39 slice 3); the standard ActiveSupport inflections are
@@ -45,40 +45,97 @@ plugin id/version + config), so an unchanged second run skips the parse.
45
45
  | Rule | Severity | Fires when |
46
46
  | --- | --- | --- |
47
47
  | `plugin.rbs-inline.source-rbs-synthesis-failed` | info | rbs-inline could not parse a file; analysis falls back to no inline-RBS contribution and the diagnostic carries the upstream error |
48
- | `plugin.rbs-inline.source-rbs-annotation-not-honoured` | info | an annotation parsed successfully but contributed nothing — the file's other annotations still apply. Two causes: a member your `sig/` also declares (see [Precedence](#precedence)), and the `# @rbs module-self: Foo` spelling (see below) |
48
+ | `plugin.rbs-inline.source-rbs-annotation-not-honoured` | info | an annotation parsed successfully but contributed nothing — the file's other annotations still apply. Six causes: a member your `sig/` also declares where Rigor cannot tell which of the two is more precise (see [Precedence](#precedence)), the `# @rbs module-self: Foo` spelling (see below), a `#:` line whose type does not parse (see [Unparseable `#:` types](#unparseable--types)), a same-line `# @rbs %a{…}` whose method type does not parse (see [Same-line annotations](#same-line-annotations)), a `# @rbs name: T` parameter type that does not parse (see [Unparseable `# @rbs name:` types](#unparseable--rbs-name-types)), and an `@rbs`-prefixed tag the gem does not recognise (see [An unrecognised tag after `@rbs`](#an-unrecognised-tag-after-rbs)) |
49
+ | `rbs.contradicting-signature` | error | an inline declaration contradicts the `sig/` declaration of the same method, or an inline `%a{rigor:v1:…}` refinement is outside its own declared type (see [Precedence](#precedence)). A core rule rather than the plugin's own, so it carries no `plugin.rbs-inline.` prefix |
49
50
 
50
51
  ## Precedence
51
52
 
52
53
  When a method is declared **both** in `sig/` and by an inline
53
- annotation, **the `.rbs` wins, per member.** The inline signature for
54
- that one method is dropped; every other annotation in the file still
55
- binds, and the class keeps its method surface.
54
+ annotation, Rigor **compares the two**, position by position — each
55
+ parameter, the return type, a block's parameters and return — and
56
+ exactly one of them binds. Every other annotation in the file still
57
+ binds either way, and the class keeps its method surface.
56
58
 
57
59
  ```ruby
58
- # lib/demo.rb # sig/demo.rbs
59
- class Demo # class Demo
60
- # @rbs (Integer) -> String # def shared: (String) -> Integer ← this one wins
61
- def shared(v) = v.to_s # def only_sig: () -> String
62
- # end
63
- # @rbs (Integer) -> Integer
64
- def only_inline(v) = v + 1 # ← inline-only: still binds
60
+ # lib/demo.rb # sig/demo.rbs
61
+ class Demo # class Demo
62
+ # @rbs dir: :asc | :desc # def order: (Symbol dir) -> void
63
+ def order(dir) = nil # def shared: (String) -> Integer
64
+ # end
65
+ # @rbs (Integer) -> String
66
+ def shared(v) = v.to_s
65
67
  end
66
68
  ```
67
69
 
68
- Each dropped member is reported once as
69
- `plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the
70
- member and the `.rbs` that won. Delete one of the two declarations to
71
- make the inline annotation take effect.
72
-
73
- `sig/` wins because it is the reviewed artefact — the one you diff in
74
- review and the one `rigor sig-gen --diff` reasons about. There is no
75
- upstream rule to defer to: rbs merges an inline `.rb` declaration and a
76
- `.rbs` one into a single class entry and ranks neither, so Steep reports
77
- the same overlap as a signature error and the class still fails to
78
- build. Rigor keeps the reporting and drops the degradation
79
- ([ADR-32](../../adr/32-rbs-inline-comment-ingestion.md) WD13) — left to
80
- collide, one duplicated method costs the class every other method, and
81
- each call on it, real methods and typos alike, reads `Dynamic[top]`.
70
+ - **One refines the other: the more precise one binds, silently.**
71
+ `:asc | :desc` is a subtype of `Symbol`, so `Demo#order` takes the
72
+ inline contract and `order(:up)` is an argument-type error, where
73
+ the `.rbs` alone let it pass. The merge takes the narrower type in
74
+ every position, parameters included, on purpose: both declarations
75
+ are yours, and the narrower one is what you stated. `String` in
76
+ `sig/` beside `non-empty-string` inline reads as `non-empty-string`.
77
+ `untyped`, `void` and `top` are consistent with anything and say the
78
+ least, so `sig/`'s `-> untyped` beside an inline `-> void` is quiet.
79
+ Identical declarations — what `rigor sig-gen` writes for an annotated
80
+ method — are quiet too. The inline side binds only if the `.rbs`
81
+ member loses nothing by it: the same visibility, and every
82
+ annotation it carries (a predicate, an assertion, an effect
83
+ envelope) also on the inline one.
84
+ - **They contradict: an error, and the `.rbs` binds.** If `Demo#shared`
85
+ took a `::String` in `sig/` and an `::Integer` inline, no value would
86
+ be both, so the run would report
87
+ [`rbs.contradicting-signature`](../04-diagnostics.md#rule-rbs-contradicting-signature)
88
+ at the `sig/` line, naming the annotated file. Spelled `String` and
89
+ `Integer`, as in the example, the pair is undecided instead (below). Positional counts that
90
+ cannot meet, or a keyword one side requires and the other cannot take
91
+ in any form, contradict the same way. The error needs a proof, and
92
+ only Ruby core or stdlib classes written absolutely (`::String`,
93
+ `::Integer`) give one, read from their RBS hierarchy. A module such
94
+ as `Comparable` never counts, since any class may include it; nor
95
+ does a class of your own (your `sig/` may omit the superclass Ruby
96
+ gives it), a gem's class, a relative name such as plain `String`
97
+ (it may be your own `App::String`), an optional or rest parameter
98
+ (a call may leave it out), or a block's parameters (the body may
99
+ never yield). A stale generated signature is the usual cause:
100
+ regenerate it, or fix the annotation.
101
+ - **Rigor cannot tell: the `.rbs` binds, with an `:info`.** When a
102
+ position names a type alias, an interface, `self`, a type variable,
103
+ or a relative class name your project also declares, when Rigor
104
+ cannot prove two types disjoint (a subclass relation such as
105
+ `Integer` against `Numeric` included), when the parameter lists are shaped
106
+ differently but overlap, when the overloads do not pair one to one
107
+ (they are paired by what they declare, not by their order), or when
108
+ each side is more precise somewhere, the inline signature is dropped
109
+ and reported as
110
+ `plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the
111
+ member and the `.rbs` that bound. Make one a refinement of the
112
+ other, or delete one, to settle it.
113
+
114
+ `rigor sig-gen --write` produces this overlap on purpose: by default it
115
+ copies each inline declaration into `sig/`, so the generated signature
116
+ is the complete contract a gem ships. An identical copy is quiet, as
117
+ above. When an inline annotation later disagrees with its copy,
118
+ `rigor sig-gen --write` and `--check` refuse the method and exit `1`
119
+ until you make the two agree or pass `--overwrite`, which replaces the
120
+ `sig/` member with the inline declaration. A project whose Steep reads the same
121
+ annotations sets `sig_gen.inline_declared: skip` instead
122
+ ([handbook chapter 11](../../handbook/11-sig-gen.md#methods-declared-inline)).
123
+
124
+ A `%a{rigor:v1:return: …}` or `%a{rigor:v1:param: …}` refinement on an
125
+ inline annotation must also share values with its own declared type:
126
+ `# @rbs %a{rigor:v1:return: positive-int} () -> ::String` is reported as
127
+ `rbs.contradicting-signature` at the annotated file (the same proof rule
128
+ applies, so `-> String` without `::` is left alone).
129
+
130
+ There is no upstream rule to defer to: rbs merges an inline `.rb`
131
+ declaration and a `.rbs` one into a single class entry and ranks
132
+ neither, so Steep reports the same overlap as a signature error and
133
+ the class fails to build. Rigor keeps the reporting and drops the
134
+ degradation ([ADR-112](../../adr/112-extrbs-comment-channel.md) WD5,
135
+ which replaced [ADR-32](../../adr/32-rbs-inline-comment-ingestion.md)
136
+ WD13's "the `.rbs` always wins") — left to collide, one duplicated
137
+ method costs the class every other method, and each call on it, real
138
+ methods and typos alike, reads `Dynamic[top]`.
82
139
 
83
140
  Two overlaps this does **not** cover: a `.rbs` that collides with
84
141
  *bundled* RBS (Ruby core, stdlib, a gem's signatures) is quarantined
@@ -109,6 +166,136 @@ in silence. Constructs the gem supports and the built-in parser does not —
109
166
  `@rbs generic T`, `@rbs!` embedded RBS blocks, `@rbs inherits`, method
110
167
  visibility — all work here.
111
168
 
169
+ ## Same-line annotations
170
+
171
+ An `%a{…}` annotation can sit on the same line as a method type:
172
+
173
+ ```ruby
174
+ class Reader
175
+ # @rbs %a{rigor:v1:return: non-empty-string} () -> String
176
+ def title = "x"
177
+
178
+ #: %a{pure} () -> String
179
+ def label = "x"
180
+ end
181
+ ```
182
+
183
+ This is the spelling the built-in parser and Steep's inline mode accept. The
184
+ gem itself does not: it keeps the annotation and drops the method type in the
185
+ `@rbs` form, and drops the whole `#:` line. Rigor splits the line back into the
186
+ annotation and the method type before the gem's writer runs, so both apply, as
187
+ they do when the annotation has a line of its own
188
+ ([ADR-32](../../adr/32-rbs-inline-comment-ingestion.md) WD11). The gem's own
189
+ `rbs-inline --output` is unchanged and still drops them.
190
+
191
+ When the method type after the annotations does not parse, nothing is split:
192
+ the method types as if no signature had been written, and Rigor reports it — a
193
+ `#:` line under [Unparseable `#:` types](#unparseable--types), and an `@rbs`
194
+ line as `plugin.rbs-inline.source-rbs-annotation-not-honoured` naming the line
195
+ and the text it could not read.
196
+
197
+ ## Unparseable `#:` types
198
+
199
+ A `#:` line whose type does not parse as RBS is dropped — the signature never
200
+ applies, and the method types as if the line had never been written, not
201
+ merely as if its type were wrong:
202
+
203
+ ```ruby
204
+ class BadRefProbe
205
+ #: (finite-float) -> String
206
+ def show(f)
207
+ f.to_s
208
+ end
209
+ end
210
+ ```
211
+
212
+ `finite-float` is a [Rigor refinement](../16-rbs-extended-annotations.md)
213
+ name, not an RBS type, and does not belong in an ordinary type position.
214
+ Rigor reports the drop as
215
+ `plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
216
+ the text that failed to parse, rather than leaving `show` silently `untyped`
217
+ with no diagnostic anywhere. The `# @rbs name: TYPE` tag form of the same
218
+ mistake is a different failure shape — see the next section.
219
+
220
+ ## An unresolvable type name in `# @rbs`
221
+
222
+ Naming a Rigor refinement (or any other unresolvable name) where an RBS type
223
+ belongs in the `# @rbs name: TYPE` tag form does not drop silently the way
224
+ `#:` does — it takes the whole class down:
225
+
226
+ ```ruby
227
+ class ProbeZZ
228
+ # @rbs g: finite-float
229
+ def probe(g)
230
+ g.to_s
231
+ end
232
+ end
233
+ ```
234
+
235
+ Upstream's own type parser truncates `finite-float` to `finite` (a hyphen
236
+ cannot continue an RBS type name) before this ever reaches Rigor, so `finite`
237
+ is the only token that reaches `RBS::DefinitionBuilder` — and it names no
238
+ loaded type, so the build for the WHOLE class fails
239
+ (`RBS::NoTypeFoundError`). `probe`, and every other real method on `ProbeZZ`,
240
+ reads `Dynamic[top]`. This surfaces as
241
+ `rbs.coverage.definition-build-failed`, naming the token and, when it is the
242
+ truncated head of a registered refinement name, the `%a{rigor:v1:…}` spelling
243
+ that IS valid today (see
244
+ [RBS::Extended annotations](../16-rbs-extended-annotations.md)).
245
+
246
+ ## Unparseable `# @rbs name:` types
247
+
248
+ A bounded or parameterised refinement — `Integer[1..10]`, `non-empty-array
249
+ [Integer]` — does not truncate at a hyphen the way `finite-float` does, so it
250
+ does not take the whole class down. In a `# @rbs name: TYPE` position it
251
+ leaves the parameter untyped instead, silently:
252
+
253
+ ```ruby
254
+ class BoundedProbe
255
+ # @rbs n: Integer[1..10]
256
+ def probe(n)
257
+ n
258
+ end
259
+ end
260
+ ```
261
+
262
+ The gem's grammar makes the colon-and-type optional on this annotation, so a
263
+ `TYPE` it cannot parse leaves the parameter's name recorded and its type
264
+ unset rather than raising — indistinguishable downstream from a parameter
265
+ nobody annotated at all. Rigor reports the drop as
266
+ `plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
267
+ the text that failed to parse, and points at the `%a{rigor:v1:param:}`
268
+ spelling that carries a refinement like `Integer[1..10]` correctly (see
269
+ [RBS::Extended annotations](../16-rbs-extended-annotations.md)) — the same
270
+ advice the `%a{rigor:v1:…}` pointer above gives for the tag form.
271
+
272
+ ## An unrecognised tag after `@rbs`
273
+
274
+ The gem recognises a comment as an `@rbs` annotation attempt as soon as it
275
+ sees the word boundary right after `@rbs` — which matches a hyphen, not only
276
+ whitespace or end-of-line:
277
+
278
+ ```ruby
279
+ class TagProbe
280
+ # @rbs-ext return: non-empty-string
281
+ # @rbs return: String
282
+ def name
283
+ "x"
284
+ end
285
+ end
286
+ ```
287
+
288
+ `# @rbs-ext …` is inside that net, but nothing in the gem's grammar
289
+ recognises `-ext`, so the gem gives up on the whole paragraph and folds it
290
+ back into an ordinary comment — the neighbouring `# @rbs return: String`
291
+ line still binds, but the `@rbs-ext` line contributes nothing and, before
292
+ this, said nothing either. Rigor reports the drop as
293
+ `plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
294
+ the comment text — without implying `@rbs-ext` is a recognised tag of any
295
+ kind. A comment that merely mentions `@rbs` in prose, or one that opens with
296
+ a tag not starting with `@rbs` (`# @extrbs …`), is outside the gem's
297
+ detector and stays silent.
298
+
112
299
  ## Configuration
113
300
 
114
301
  ```yaml
@@ -40,7 +40,7 @@ arguments.
40
40
  | `plugin.sidekiq.worker-call` | info | a `Worker.perform_*` call matched a discovered worker's `#perform` arity |
41
41
  | `plugin.sidekiq.wrong-arity` | error | the forwarded argument count falls outside `#perform`'s arity envelope (message names the schedule carve-out for `perform_in` / `perform_at`) |
42
42
  | `plugin.sidekiq.missing-schedule` | error | `perform_in()` / `perform_at()` called with zero arguments (the schedule is required even when `#perform` takes none) |
43
- | `plugin.sidekiq.load-error` | warning | worker discovery failed (parse/read error) — once per file |
43
+ | `plugin.sidekiq.load-error` | warning | worker discovery failed (parse/read error) — once per run, on `.rigor.yml` |
44
44
 
45
45
  ## What it types
46
46
 
@@ -144,6 +144,13 @@ added it is 100.
144
144
  alternative `klass:` spelling, and a schedule built in Ruby with
145
145
  `Sidekiq::Cron::Job.load_from_hash!`, supply no roots — the worker
146
146
  stays a `rigor unused` candidate rather than being guessed at.
147
+ - **The "failed to discover workers" warning is run-scoped.** It is
148
+ a fact about your configuration, not about any one source file, so
149
+ it is reported once per run on `.rigor.yml` rather than repeated on
150
+ every analysed file. It is `:warning`, so if you baselined it at its
151
+ old position that entry no longer matches and the row fails a
152
+ `--fail-on=warning` run — regenerate with `rigor baseline
153
+ regenerate`.
147
154
 
148
155
  ## Plugin internals
149
156
 
@@ -47,11 +47,27 @@ plugins:
47
47
  The plugin is **input-side only**: it translates Sorbet's syntax
48
48
  into Rigor's type model. It does **not** run Sorbet's checker,
49
49
  ship `sorbet-runtime`, or enforce Sorbet's runtime guarantees.
50
- When an RBS sig and a Sorbet sig disagree, RBS wins (the Sorbet
51
- sig may refine but not contradict it). Forms outside the
52
- translation table (`T.proc`, `T.self_type`, `T::Struct` /
53
- `T::Enum` subclasses, …) degrade to `Dynamic[top]`. Chapter 10
54
- documents the full vocabulary and these edges.
50
+ When an RBS sig and a Sorbet sig disagree, call sites get the
51
+ Sorbet sig's return type, and the method's body is checked
52
+ against the RBS return. Forms outside the
53
+ translation table (`T.proc`, `T.self_type`, …) degrade to
54
+ `Dynamic[top]`. Chapter 10 documents the full vocabulary and
55
+ these edges.
56
+
57
+ The plugin also types the annotation DSL's own expressions
58
+ (`sig`, `params`, `returns`, `void`, `abstract`, `override`,
59
+ `type_member`, `T::Array[...]`, `T::Helpers`/`T::Generic` macros,
60
+ `T::Struct`/`T::Enum` class bodies — `sig` on a plain
61
+ `T::Struct` subclass still needs the subclass's own `extend T::Sig`,
62
+ matching the runtime) through a bundled
63
+ `sorbet-runtime` RBS surface — so the sig DSL no longer reads as
64
+ `Dynamic[top]` on a Sorbet codebase. What it does not model:
65
+ `T::Struct` does not synthesise `prop`-declared readers (a
66
+ `Doc#name` accessor still reads opaque), `include T::Props` does
67
+ not confer the `ClassMethods` surface (only the `< T::Struct`
68
+ subclass path does), and the deeper type-member machinery
69
+ (`T.attached_class` variance, `type_member` bounds) is not
70
+ propagated into call sites.
55
71
 
56
72
  ## Plugin internals
57
73
 
data/exe/rigor CHANGED
@@ -11,13 +11,28 @@
11
11
  # context across the exec. The `none` and `process` (default) strategies
12
12
  # need no flag, so this is a no-op for them — behaviour is unchanged
13
13
  # unless a project opts into `ruby_box`.
14
+ #
15
+ # Before re-exec'ing, a child Ruby probes whether this Ruby can run
16
+ # Rigor under `RUBY_BOX=1` at all (Rigor::Plugin::BoxProbe — released
17
+ # Rubies through 4.0.7 segfault on Ruby Bug #22260). When it cannot,
18
+ # the request is dropped with a warning and the run proceeds under the
19
+ # configured strategy (`process` by default) instead of crashing.
14
20
  rigor_isolation = ENV["RIGOR_PLUGIN_ISOLATION"].to_s
15
21
  rigor_wants_box = rigor_isolation == "ruby_box" || !ENV["RIGOR_BOX"].to_s.empty?
16
22
  if rigor_wants_box && ENV["RUBY_BOX"].to_s.empty?
17
- require "rbconfig"
18
- ENV["RUBY_BOX"] = "1"
19
- ENV["RIGOR_PLUGIN_ISOLATION"] = "ruby_box" if rigor_isolation.empty?
20
- exec(RbConfig.ruby, __FILE__, *ARGV)
23
+ require_relative "../lib/rigor/plugin/box_probe"
24
+ if (rigor_box_unsupported = Rigor::Plugin::BoxProbe.unsupported_reason)
25
+ rigor_box_warning = "rigor: ruby_box plugin isolation is unavailable: #{rigor_box_unsupported}. " \
26
+ "Falling back to the configured plugins_isolation strategy (default: process)."
27
+ # `$stderr.puts`, not `warn`: `-W0` silences `warn`, and this fallback must never be silent.
28
+ $stderr.puts rigor_box_warning # rubocop:disable Style/StderrPuts
29
+ ENV.delete("RIGOR_PLUGIN_ISOLATION") if rigor_isolation == "ruby_box"
30
+ ENV.delete("RIGOR_BOX")
31
+ else
32
+ ENV["RUBY_BOX"] = "1"
33
+ ENV["RIGOR_PLUGIN_ISOLATION"] = "ruby_box" if rigor_isolation.empty?
34
+ exec(RbConfig.ruby, __FILE__, *ARGV)
35
+ end
21
36
  end
22
37
 
23
38
  lib = File.expand_path("../lib", __dir__)
@@ -275,7 +275,7 @@ module Rigor
275
275
  YAML.dump(document)
276
276
  end
277
277
 
278
- # The number of buckets recorded. Useful for the CLI summary on `generate`.
278
+ # The bucket count the `generate` summary reports.
279
279
  def size
280
280
  buckets.size
281
281
  end