rigortype 0.4.6 → 0.4.8

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 (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
@@ -4,6 +4,7 @@ require "tempfile"
4
4
 
5
5
  require_relative "uri"
6
6
  require_relative "publish_batcher"
7
+ require_relative "config_warning_announcer"
7
8
  require_relative "../analysis/runner"
8
9
  require_relative "../analysis/buffer_binding"
9
10
 
@@ -19,7 +20,7 @@ module Rigor
19
20
  # buffers' debounce timers elapse around the same moment, the `PublishBatcher` coalesces them into one
20
21
  # `#publish_many` round dispatched across the fork-based worker pool (issue #142) instead of N
21
22
  # independent, GVL-serialized `Runner` calls.
22
- class DiagnosticPublisher
23
+ class DiagnosticPublisher # rubocop:disable Metrics/ClassLength
23
24
  # Maps Rigor severity symbols to LSP DiagnosticSeverity
24
25
  # integers per spec § "Diagnostic":
25
26
  # 1 = Error, 2 = Warning, 3 = Information, 4 = Hint.
@@ -51,6 +52,7 @@ module Rigor
51
52
  @project_context = project_context
52
53
  @debouncer = debouncer
53
54
  @debounce_seconds = debounce_seconds
55
+ @config_warning_announcer = ConfigWarningAnnouncer.new(writer: writer, project_context: project_context)
54
56
  @round_lock = Mutex.new
55
57
  @round_running = false
56
58
  @round_pending = false
@@ -130,6 +132,9 @@ module Rigor
130
132
  notify(uri, [])
131
133
  end
132
134
 
135
+ # Issue #2231 — shows the configuration warnings the editor has not seen yet ({ConfigWarningAnnouncer}).
136
+ def announce_config_warnings = @config_warning_announcer.announce
137
+
133
138
  # Cancels every in-flight debounced task. Called from `Server#handle_shutdown` so pending publishes
134
139
  # don't fire against a closed STDOUT.
135
140
  def cancel_pending
@@ -167,8 +172,7 @@ module Rigor
167
172
 
168
173
  # True when a save arrived mid-round and the loop should run once more. Releases ownership otherwise.
169
174
  # `next`, not `break`: inside `Mutex#synchronize`'s block, `break` returns from the SYNCHRONIZE call, so
170
- # a `break` written here would leave the caller's `loop` spinning forever — which is exactly what it did
171
- # before this was split out.
175
+ # a `break` here would leave the caller's `loop` spinning forever.
172
176
  def consume_pending
173
177
  @round_lock.synchronize do
174
178
  next(@round_running = false) unless @round_pending
@@ -185,6 +189,7 @@ module Rigor
185
189
  def execute_project_round(saved_uri)
186
190
  generation = @project_context.generation
187
191
  diagnostics = @project_context.project_diagnostics
192
+ announce_config_warnings # the round re-read the configuration; the reload stands even if the result does not
188
193
  return if generation != @project_context.generation
189
194
 
190
195
  by_path = diagnostics.group_by(&:path)
@@ -30,7 +30,7 @@ module Rigor
30
30
  @project_context = project_context
31
31
  end
32
32
 
33
- # @return LSP `DocumentSymbol[]` for the
33
+ # @return LSP document symbols for the
34
34
  # buffer at `uri`. Returns nil when the URI isn't open or
35
35
  # doesn't parse cleanly enough to surface symbols — LSP
36
36
  # clients fall back to no-outline in that case.
@@ -23,8 +23,7 @@ module Rigor
23
23
  @project_context = project_context
24
24
  end
25
25
 
26
- # @return LSP `FoldingRange[]` for the
27
- # buffer, or nil when the URI isn't open / parseable.
26
+ # @return LSP folding ranges for the buffer, or nil when the URI isn't open / parseable.
28
27
  def provide(uri)
29
28
  path, entry = buffer_for(uri)
30
29
  return nil if entry.nil?
@@ -44,7 +44,7 @@ module Rigor
44
44
  node = locate_node(source: entry.bytes, root: parse_result.value, line: line + 1, character: character + 1)
45
45
  return nil if node.nil?
46
46
 
47
- scope = base_scope(path)
47
+ scope = @project_context.seeded_scope_for(path: path, bytes: entry.bytes)
48
48
  index = Inference::ScopeIndexer.index(parse_result.value, default_scope: scope)
49
49
  node_scope = index[node]
50
50
  type = node_scope.type_of(node)
@@ -59,12 +59,6 @@ module Rigor
59
59
  rescue Source::NodeLocator::OutOfRangeError
60
60
  nil
61
61
  end
62
-
63
- def base_scope(_path)
64
- # Pulls the Environment from the cached ProjectContext so each hover avoids repeating the RBS-load
65
- # build.
66
- Scope.empty(environment: @project_context.environment)
67
- end
68
62
  end
69
63
  end
70
64
  end
@@ -60,9 +60,8 @@ module Rigor
60
60
  end
61
61
 
62
62
  # Renders a `CallNode` hover when the receiver's type can be mapped to a known RBS class and the method
63
- # is declared there. Returns nil for shapes the slice-A1 floor doesn't handle (implicit `self`, Union /
64
- # Refined / Shape receivers — those land in slices A2-A4 and slice 7); the caller falls back to the
65
- # default body.
63
+ # is declared there. Returns nil for shapes this renderer does not handle (implicit `self`, Union /
64
+ # Refined / Shape receivers); the caller falls back to the default body.
66
65
  def render_call(call_node, return_type, node_scope_lookup)
67
66
  receiver_node = call_node.receiver
68
67
  return nil if receiver_node.nil?
@@ -133,17 +132,16 @@ module Rigor
133
132
  body << "#{return_type.describe}\n"
134
133
  body << "```"
135
134
  if (doc = rbs_documentation(definition))
136
- # Close the code fence first so the comment text renders as prose, then a fresh fenced block isn't
137
- # necessary — plain markdown below the code reads better in clients.
135
+ # The fence is already closed, so the RBS comment renders as plain markdown below the code.
138
136
  body << "\n\n---\n\n#{doc}"
139
137
  end
140
138
  body
141
139
  end
142
140
 
143
141
  # Returns the concatenated text of every RBS comment attached to the method definition, or nil when no
144
- # comments exist. `RBS::Definition::Method#comments` is an Array<AST::Comment> with each entry's
145
- # `.string` carrying the raw text (newline-terminated `# foo` lines). Multiple comments are joined with a
146
- # blank line so each upstream `# Foo bar` paragraph is preserved.
142
+ # comments exist. `RBS::Definition::Method#comments` is a list of `AST::Comment`s, each with its raw text
143
+ # in `.string` (newline-terminated `# foo` lines). Multiple comments are joined with a blank line so each
144
+ # upstream `# Foo bar` paragraph is preserved.
147
145
  def rbs_documentation(definition)
148
146
  comments = definition.respond_to?(:comments) ? definition.comments : nil
149
147
  return nil if comments.nil? || comments.empty?
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tempfile"
4
+
3
5
  require_relative "../environment"
4
6
  require_relative "../project_environment"
5
7
  require_relative "../cache/store"
@@ -9,6 +11,7 @@ require_relative "../cache/incremental_snapshot"
9
11
  require_relative "../effects/identity"
10
12
  require_relative "../analysis/path_expansion"
11
13
  require_relative "../protection/discovery_seed"
14
+ require_relative "../config_audit"
12
15
 
13
16
  module Rigor
14
17
  module LanguageServer
@@ -68,6 +71,24 @@ module Rigor
68
71
  @project_scan = nil
69
72
  @incremental_session = nil
70
73
  @session_primed = false
74
+ @announced_config_warnings = Set.new
75
+ @announce_lock = Mutex.new
76
+ end
77
+
78
+ # Issue #2231 — the {ConfigAudit} warnings for the configuration as last loaded that the editor has not been
79
+ # shown yet, or an empty array. `rigor check` prints these on every run; the server is one long run, so it
80
+ # announces each warning once: at `initialized` for the configuration it started with, and after a later reload
81
+ # (every save round re-reads `.rigor.yml`) only the warnings that were not in the previous reload's set. The
82
+ # comparison is by message, not by order, so reordering entries announces nothing, and fixing one of two
83
+ # warnings does not show the other again. A warning that was fixed and then comes back is new and is shown.
84
+ # Called from the dispatch thread and the save round's thread alike, hence the lock.
85
+ def unannounced_config_warnings
86
+ @announce_lock.synchronize do
87
+ warnings = ConfigAudit.warnings(@configuration)
88
+ fresh = warnings.reject { |warning| @announced_config_warnings.include?(warning.message) }
89
+ @announced_config_warnings = warnings.to_set(&:message)
90
+ fresh
91
+ end
71
92
  end
72
93
 
73
94
  # Returns the cached `Rigor::Environment` for this session, building it on first access. The build
@@ -132,6 +153,22 @@ module Rigor
132
153
  buffer: buffer, constant_sources: true)
133
154
  end
134
155
 
156
+ # Issue #2282 — the base scope an editor query (hover, completion, signature help) probes with: the same
157
+ # cross-file discovery seed the diagnostic publisher hands its `Runner` ({#discovery_seed_for}), so a constant
158
+ # or method another file defines answers what `rigor check` would. The seed fold reads the buffer through a
159
+ # {Analysis::BufferBinding}, so `bytes` is staged in a tempfile for the call. An empty seed (no configured
160
+ # files, or nothing discovered) leaves the bare single-file scope every query used before.
161
+ def seeded_scope_for(path:, bytes:)
162
+ seed = Tempfile.create(["rigor-lsp-query-", ".rb"]) do |tmp|
163
+ # Staged as the publisher stages them; the seed's job is to withhold the stale on-disk declarations of
164
+ # this file, so an empty-bytes stage is not observable by design.
165
+ tmp.write(bytes)
166
+ tmp.flush
167
+ discovery_seed_for(Analysis::BufferBinding.new(logical_path: path, physical_path: tmp.path))
168
+ end
169
+ Protection::DiscoverySeed.seeded_scope(environment: environment, seed: seed)
170
+ end
171
+
135
172
  # Builds what {#discovery_seed_for} reads once per generation, so a fork batch's children inherit it instead
136
173
  # of each walking the project.
137
174
  def prepare_discovery_seed
@@ -251,7 +288,8 @@ module Rigor
251
288
  def reload_configuration
252
289
  return unless @configuration_loader
253
290
 
254
- @configuration = @configuration_loader.call
291
+ loaded = @configuration_loader.call
292
+ @announce_lock.synchronize { @configuration = loaded }
255
293
  rescue StandardError
256
294
  nil
257
295
  end
@@ -15,10 +15,9 @@ module Rigor
15
15
  # dropped. The same claim/consume shape `DiagnosticPublisher#run_project_round` already uses for the
16
16
  # whole-project save round (#246), generalised to an arbitrary key type and an arbitrary batch action.
17
17
  class PublishBatcher
18
- # @param on_batch — `(keys) -> void`, called with the deduplicated Array of keys pending at the
19
- # start of one round. May be called more than once in a row when keys keep arriving while a round
20
- # runs.
21
- # @param on_error — `(exception) -> void`, called when `on_batch` raises. A round must
18
+ # @param on_batch — called with the deduplicated keys pending at the start of one round. May be called
19
+ # more than once in a row when keys keep arriving while a round runs.
20
+ # @param on_error — called with the exception when `on_batch` raises. A round must
22
21
  # never wedge the coalescing lock for every future `#enqueue` call — ownership is always released
23
22
  # before this fires. Whatever was mid-flight when the round raised is lost; anything enqueued by a
24
23
  # concurrent `#enqueue` call after this round's drain but before the rescue stays pending and rides
@@ -20,10 +20,8 @@ module Rigor
20
20
  @project_context = project_context
21
21
  end
22
22
 
23
- # @param positions — LSP `Position[]` — each
24
- # `{ line:, character: }` 0-based.
25
- # @return one `SelectionRange` per
26
- # position, or nil when the URI / buffer isn't resolvable.
23
+ # @param positions — LSP positions, each `{ line:, character: }` 0-based.
24
+ # @return one `SelectionRange` per position, or nil when the URI / buffer isn't resolvable.
27
25
  def provide(uri, positions)
28
26
  path, entry = buffer_for(uri)
29
27
  return nil if entry.nil?
@@ -194,7 +194,12 @@ module Rigor
194
194
 
195
195
  # `initialized` is a notification — no response body. Nothing is registered here yet: the server handles
196
196
  # `workspace/didChangeWatchedFiles` when a client sends it, but does not register for it dynamically.
197
+ #
198
+ # Issue #2231 — the configuration the server started with is audited here, the first point at which the
199
+ # client is listening, so a `.rigor.yml` key that does nothing is named in the editor as `rigor check`
200
+ # names it on STDERR.
197
201
  def handle_initialized
202
+ @publisher&.announce_config_warnings
198
203
  nil
199
204
  end
200
205
 
@@ -267,8 +272,7 @@ module Rigor
267
272
 
268
273
  # textDocument/hover REQUEST. Slice 5 returns either a `Hover` payload (markdown contents wrapping type
269
274
  # + erased-RBS info) or nil when no expression is at the queried position. Nil maps to `result: null`
270
- # per LSP spec; clients suppress the popup. Returns `MethodNotFound` when no hover_provider is wired
271
- # (slice 1-4 behaviour).
275
+ # per LSP spec; clients suppress the popup. Returns `MethodNotFound` when no hover_provider is wired.
272
276
  def handle_hover(params)
273
277
  return method_not_found("textDocument/hover") unless @hover_provider
274
278
 
@@ -351,7 +355,7 @@ module Rigor
351
355
  )
352
356
  end
353
357
 
354
- # textDocument/documentSymbol REQUEST. Returns the `DocumentSymbol[]` outline for the buffer at the
358
+ # textDocument/documentSymbol REQUEST. Returns the document-symbol outline for the buffer at the
355
359
  # requested URI. Returns `MethodNotFound` when no provider is wired.
356
360
  def handle_document_symbol(params)
357
361
  return method_not_found("textDocument/documentSymbol") unless @document_symbol_provider
@@ -25,7 +25,7 @@ module Rigor
25
25
  # parses the buffer, locates the enclosing `CallNode`, infers the receiver's type, and returns the
26
26
  # method's first overload as a `SignatureInformation`.
27
27
  #
28
- # Slice C1 (this commit) ships:
28
+ # Slice C1 ships:
29
29
  # - Sentinel patching for `obj.foo(|` so Prism's parse succeeds (mirrors `CompletionProvider`'s slice B4
30
30
  # pattern).
31
31
  # - First-overload signature only.
@@ -111,7 +111,7 @@ module Rigor
111
111
  end
112
112
 
113
113
  def build_signature(call_node, root, path, bytes, line, character)
114
- scope_index = build_scope_index(root, path)
114
+ scope_index = build_scope_index(root, path, bytes)
115
115
  receiver_node = call_node.receiver
116
116
  return nil if receiver_node.nil?
117
117
 
@@ -197,8 +197,8 @@ module Rigor
197
197
  end
198
198
  end
199
199
 
200
- def build_scope_index(root, _path)
201
- scope = Scope.empty(environment: @project_context.environment)
200
+ def build_scope_index(root, path, bytes)
201
+ scope = @project_context.seeded_scope_for(path: path, bytes: bytes)
202
202
  Inference::ScopeIndexer.index(root, default_scope: scope)
203
203
  end
204
204
 
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../environment/member_consistency"
4
+
5
+ module Rigor
6
+ module Lens
7
+ class Claims
8
+ # The deliberately small proof fragment of lens-claims.md. Unknown is not a failed subtype check.
9
+ class Comparison
10
+ def initialize(loader, relative_conflict:)
11
+ @loader = loader
12
+ @proof = Environment::MemberConsistency::RbsProof.new(loader)
13
+ @relative_conflict = relative_conflict
14
+ end
15
+
16
+ def call(sub, sup)
17
+ included = sub.map do |arm|
18
+ match = sup.filter_map do |other|
19
+ (rule = inclusion(arm, other)) && evidence(rule, arm, other.display)
20
+ end.first
21
+ match
22
+ end
23
+ return ["compatible", "proven_inclusion", included] if included.none?(nil)
24
+
25
+ sub.each do |arm|
26
+ rules = sup.map { |other| disjoint(arm, other) }
27
+ next if rules.any?(nil)
28
+
29
+ return ["conflict", "proven_disjoint_arm", [evidence(rules.first, arm, sup.map(&:display).join(" | "))]]
30
+ end
31
+ ["unknown", "unproven", []]
32
+ end
33
+
34
+ private
35
+
36
+ def evidence(rule, arm, against)
37
+ { "rule" => rule, "arm" => arm.display, "against" => against }
38
+ end
39
+
40
+ def inclusion(sub, sup)
41
+ return "equal" if sub.identity == sup.identity
42
+ return nil unless sup.kind == :class && sup.arguments.empty?
43
+ return nil unless @loader.class_type_param_names(sup.value).empty?
44
+
45
+ name = sub.kind == :literal ? sub.value.class.name : sub.value
46
+ unreliable = Environment::MemberConsistency::RbsProof::UNRELIABLE_ANCESTRY
47
+ return nil if unreliable.include?(name) || unreliable.include?(sup.value)
48
+ return nil unless @loader.ancestor_names_for(name).include?(sup.value)
49
+
50
+ sub.kind == :literal ? "literal_class_ancestor" : "class_ancestor"
51
+ end
52
+
53
+ def disjoint(left, right)
54
+ return nil if !@relative_conflict && (left.relative || right.relative)
55
+ return "distinct_literals" if left.kind == :literal && right.kind == :literal && !left.value.eql?(right.value)
56
+
57
+ disjoint_classes(left, right)
58
+ end
59
+
60
+ def disjoint_classes(left, right)
61
+ left_name = left.kind == :literal ? left.value.class.name : left.value
62
+ right_name = right.kind == :literal ? right.value.class.name : right.value
63
+ left_ancestors = @proof.class_ancestors(left_name)
64
+ right_ancestors = @proof.class_ancestors(right_name)
65
+ return nil if left_ancestors.nil? || right_ancestors.nil?
66
+
67
+ if left.kind == :literal
68
+ "literal_outside_class" unless left_ancestors.include?(right_name)
69
+ elsif right.kind == :literal
70
+ "literal_outside_class" unless right_ancestors.include?(left_name)
71
+ elsif !left_ancestors.include?(right_name) && !right_ancestors.include?(left_name)
72
+ "unrelated_classes"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Rigor
6
+ module Lens
7
+ class Claims
8
+ class IntegrityError < StandardError; end
9
+
10
+ # Validates transport and source identity before the incremental session is started.
11
+ class Request
12
+ attr_reader :claims, :digest, :paths
13
+
14
+ def self.read(path, root:, arguments:)
15
+ bytes = File.binread(path).force_encoding(Encoding::UTF_8)
16
+ raise IntegrityError, "claims request is not UTF-8" unless bytes.valid_encoding?
17
+
18
+ new(JSON.parse(bytes), digest: Lens.digest(bytes), root: root, arguments: arguments)
19
+ rescue SystemCallError, JSON::ParserError => e
20
+ raise IntegrityError, "cannot read claims request: #{e.message}"
21
+ end
22
+
23
+ def initialize(data, digest:, root:, arguments:)
24
+ @digest = digest
25
+ @root = root
26
+ @arguments = arguments.map { |argument| File.expand_path(argument, root) }
27
+ validate_header(data)
28
+ @claims = data.fetch("claims")
29
+ @files = data.fetch("files")
30
+ @paths = {}
31
+ @readable_paths = Set.new
32
+ @preflight = true
33
+ @files.each { |file| validate_file(file) }
34
+ ids = []
35
+ @claims.each do |claim|
36
+ validate_claim(claim)
37
+ ids << claim.fetch("id")
38
+ end
39
+ require_value(ids.uniq.size == ids.size, "duplicate claim id")
40
+ verify_sources!
41
+ @preflight = false
42
+ end
43
+
44
+ def verify_sources!(targets = [])
45
+ @files.each do |file|
46
+ absolute = @paths.fetch(file.fetch("path"))
47
+ bytes = readable(absolute)
48
+ target = targets.find { |entry| entry.absolute == absolute }
49
+ require_value(bytes || (!@readable_paths.include?(absolute) && !target&.reader),
50
+ "source became unreadable during analysis: #{file['path']}")
51
+ @readable_paths << absolute if @preflight && bytes
52
+ next unless bytes
53
+
54
+ require_value(Lens.digest(bytes) == file.fetch("digest"), "stale claims digest: #{file['path']}")
55
+ if target&.reader
56
+ require_value(target.reader.digest == file.fetch("digest"),
57
+ "source changed during analysis: #{file['path']}")
58
+ end
59
+ verify_spans(file, bytes.bytesize)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def verify_spans(file, size)
66
+ @claims.select { |claim| claim.fetch("file") == file.fetch("path") }.each do |claim|
67
+ %w[declaration tag].each do |field|
68
+ require_value(claim.fetch(field).fetch("span").fetch("end_offset") <= size,
69
+ "#{field} span outside #{file['path']}")
70
+ end
71
+ end
72
+ end
73
+
74
+ def validate_header(data)
75
+ require_value(data.is_a?(Hash), "claims request must be an object")
76
+ require_value(data["schema"] == "lens-claims-request/v0", "unsupported claims schema")
77
+ require_value(data["revision"].is_a?(Integer) && data["revision"] == 1, "unsupported claims revision")
78
+ require_value(data["grammar"] == "rbs", "unsupported claims grammar")
79
+ %w[files claims].each { |field| require_value(data[field].is_a?(Array), "#{field} must be an array") }
80
+ end
81
+
82
+ def validate_file(file)
83
+ strings(file, %w[path digest])
84
+ path = file.fetch("path")
85
+ absolute = File.expand_path(path, @root)
86
+ relative = Pathname(absolute).relative_path_from(Pathname(@root)).to_s
87
+ require_value(path == relative, "file path must be root-relative: #{path}")
88
+ require_value(@arguments.include?(absolute), "claims file is not a target: #{path}")
89
+ require_value(!@paths.key?(path), "duplicate claims file: #{path}")
90
+ require_value(file.fetch("digest").match?(/\Axxh3-64:[0-9a-f]{16}\z/), "invalid claims file digest")
91
+ @paths[path] = absolute
92
+ end
93
+
94
+ def validate_claim(claim)
95
+ strings(claim, %w[id file claim])
96
+ require_value(@paths.key?(claim.fetch("file")), "claim file is missing from files")
97
+ declaration = claim["declaration"]
98
+ strings(declaration, %w[kind owner scope name])
99
+ tag = claim["tag"]
100
+ strings(tag, %w[name])
101
+ require_value(tag.key?("subject") && (tag["subject"].nil? || tag["subject"].is_a?(String)),
102
+ "tag subject must be a string or null")
103
+ [declaration, tag].each { |entry| validate_span(entry["span"]) }
104
+ end
105
+
106
+ def validate_span(span)
107
+ require_value(span.is_a?(Hash), "span must be an object")
108
+ start = span["start_offset"]
109
+ finish = span["end_offset"]
110
+ require_value(start.is_a?(Integer) && finish.is_a?(Integer) && start >= 0 && finish >= start,
111
+ "invalid byte span")
112
+ end
113
+
114
+ def strings(object, fields)
115
+ require_value(object.is_a?(Hash), "claims entry must be an object")
116
+ fields.each { |field| require_value(object[field].is_a?(String), "#{field} must be a string") }
117
+ end
118
+
119
+ def require_value(condition, message)
120
+ raise IntegrityError, message unless condition
121
+ end
122
+
123
+ def readable(path)
124
+ File.binread(path) if File.file?(path)
125
+ rescue SystemCallError
126
+ nil
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end