rigortype 0.4.7 → 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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  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 +29 -0
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/docs/handbook/01-getting-started.md +1 -1
  8. data/docs/handbook/11-sig-gen.md +35 -0
  9. data/docs/install.md +20 -0
  10. data/docs/manual/01-installation.md +67 -26
  11. data/docs/manual/02-cli-reference.md +307 -91
  12. data/docs/manual/03-configuration.md +41 -3
  13. data/docs/manual/04-diagnostics.md +5 -4
  14. data/docs/manual/08-skills.md +11 -0
  15. data/docs/manual/09-editor-integration.md +8 -0
  16. data/docs/manual/14-rails-quickstart.md +1 -1
  17. data/docs/manual/19-effect-labels.md +3 -4
  18. data/docs/manual/plugins/rigor-actionpack.md +15 -6
  19. data/docs/manual/plugins/rigor-activesupport-core-ext.md +3 -0
  20. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  21. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  22. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  23. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +8 -0
  24. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +13 -2
  25. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -0
  26. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  27. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +40 -11
  28. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  29. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  30. data/lib/rigor/analysis/check_rules/rule_ids.rb +8 -1
  31. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
  32. data/lib/rigor/analysis/check_rules.rb +598 -99
  33. data/lib/rigor/analysis/crash_signature.rb +43 -1
  34. data/lib/rigor/analysis/dependency_recorder.rb +233 -76
  35. data/lib/rigor/analysis/incremental.rb +11 -1
  36. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -1
  37. data/lib/rigor/analysis/incremental_session.rb +279 -37
  38. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  39. data/lib/rigor/analysis/rule_catalog.rb +37 -1
  40. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  41. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  42. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +4 -0
  43. data/lib/rigor/analysis/runner/declaration_position.rb +7 -7
  44. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +72 -20
  45. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +3 -8
  46. data/lib/rigor/analysis/runner/pool_coordinator.rb +26 -5
  47. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  48. data/lib/rigor/analysis/runner.rb +222 -39
  49. data/lib/rigor/analysis/self_call_resolution_recorder.rb +61 -22
  50. data/lib/rigor/analysis/worker_session.rb +42 -41
  51. data/lib/rigor/bleeding_edge.rb +19 -4
  52. data/lib/rigor/cache/file_digest.rb +39 -31
  53. data/lib/rigor/cache/incremental_snapshot.rb +66 -7
  54. data/lib/rigor/cache/rbs_descriptor.rb +37 -2
  55. data/lib/rigor/cache/rbs_environment.rb +5 -1
  56. data/lib/rigor/cli/baseline_command.rb +15 -2
  57. data/lib/rigor/cli/check_command.rb +4 -2
  58. data/lib/rigor/cli/effects_snapshot_command.rb +8 -1
  59. data/lib/rigor/cli/lens_command.rb +17 -2
  60. data/lib/rigor/cli/sig_gen_command.rb +8 -0
  61. data/lib/rigor/cli/skill_describe.rb +1 -0
  62. data/lib/rigor/cli.rb +5 -2
  63. data/lib/rigor/config_audit.rb +32 -18
  64. data/lib/rigor/configuration/severity_profile.rb +9 -0
  65. data/lib/rigor/configuration.rb +32 -5
  66. data/lib/rigor/effects/collector.rb +53 -21
  67. data/lib/rigor/environment/bundle_sig_discovery.rb +63 -28
  68. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  69. data/lib/rigor/environment/default_libraries.rb +17 -0
  70. data/lib/rigor/environment/deferred_signature_plan.rb +104 -6
  71. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  72. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  73. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  74. data/lib/rigor/environment/rbs_hierarchy.rb +6 -0
  75. data/lib/rigor/environment/rbs_loader.rb +312 -63
  76. data/lib/rigor/environment.rb +131 -35
  77. data/lib/rigor/inference/acceptance.rb +50 -15
  78. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  79. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  80. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  81. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  82. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  83. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  84. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  85. data/lib/rigor/inference/block_run_count.rb +9 -2
  86. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  87. data/lib/rigor/inference/callee_mutation_chain.rb +1 -1
  88. data/lib/rigor/inference/captured_locals.rb +2 -0
  89. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  90. data/lib/rigor/inference/clause_widening.rb +1 -1
  91. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  92. data/lib/rigor/inference/construction_escape.rb +56 -15
  93. data/lib/rigor/inference/declaration_walk.rb +4 -0
  94. data/lib/rigor/inference/def_node_resolver.rb +32 -14
  95. data/lib/rigor/inference/def_return_typer.rb +1 -1
  96. data/lib/rigor/inference/definer_resolution.rb +15 -7
  97. data/lib/rigor/inference/definers_index.rb +72 -16
  98. data/lib/rigor/inference/error_info.rb +16 -0
  99. data/lib/rigor/inference/expression_typer.rb +1459 -287
  100. data/lib/rigor/inference/flow_tracer.rb +31 -14
  101. data/lib/rigor/inference/fork_map.rb +3 -0
  102. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  103. data/lib/rigor/inference/global_write_census.rb +8 -6
  104. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  105. data/lib/rigor/inference/indexed_narrowing.rb +57 -8
  106. data/lib/rigor/inference/ivar_writer_census.rb +106 -11
  107. data/lib/rigor/inference/key_presence_guard.rb +51 -25
  108. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  109. data/lib/rigor/inference/last_line.rb +5 -2
  110. data/lib/rigor/inference/loop_value_record.rb +63 -15
  111. data/lib/rigor/inference/match_rebinding/frame.rb +11 -0
  112. data/lib/rigor/inference/match_rebinding/yielding_match.rb +74 -18
  113. data/lib/rigor/inference/match_rebinding.rb +21 -8
  114. data/lib/rigor/inference/method_dispatcher/block_folding.rb +43 -29
  115. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  116. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  117. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  118. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  119. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  120. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  121. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +12 -0
  122. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +409 -67
  123. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +1 -1
  124. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +384 -64
  125. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  126. data/lib/rigor/inference/method_dispatcher.rb +66 -18
  127. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  128. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  129. data/lib/rigor/inference/module_function_state.rb +13 -2
  130. data/lib/rigor/inference/multi_target_binder.rb +1 -1
  131. data/lib/rigor/inference/mutation_widening.rb +44 -4
  132. data/lib/rigor/inference/narrowing.rb +201 -27
  133. data/lib/rigor/inference/operand_effects.rb +43 -2
  134. data/lib/rigor/inference/optimistic_origin.rb +133 -8
  135. data/lib/rigor/inference/origin_lookup.rb +1 -1
  136. data/lib/rigor/inference/precision_scanner.rb +1 -1
  137. data/lib/rigor/inference/project_method_ownership.rb +28 -10
  138. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  139. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  140. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  141. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  142. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  143. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  144. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +56 -18
  145. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -17
  146. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  147. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  148. data/lib/rigor/inference/remover_holder_scan.rb +76 -22
  149. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  150. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +12 -11
  151. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +188 -28
  152. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  153. data/lib/rigor/inference/scope_indexer.rb +2079 -183
  154. data/lib/rigor/inference/self_call_ivar_writes.rb +26 -7
  155. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  156. data/lib/rigor/inference/singleton_hook_decline.rb +41 -6
  157. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  158. data/lib/rigor/inference/statement_evaluator.rb +970 -250
  159. data/lib/rigor/inference/string_mutation.rb +14 -0
  160. data/lib/rigor/inference/synthetic_method_scanner.rb +35 -11
  161. data/lib/rigor/inference/toplevel_def_census.rb +14 -6
  162. data/lib/rigor/inference/unknown_store_widening.rb +1 -1
  163. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  164. data/lib/rigor/inference/void_tail_summary.rb +29 -10
  165. data/lib/rigor/lens/claims/comparison.rb +78 -0
  166. data/lib/rigor/lens/claims/request.rb +131 -0
  167. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  168. data/lib/rigor/lens/claims.rb +208 -0
  169. data/lib/rigor/lens/contract_reader.rb +5 -0
  170. data/lib/rigor/lens/document.rb +1 -1
  171. data/lib/rigor/lens/file_reader.rb +21 -8
  172. data/lib/rigor/lens/run.rb +8 -0
  173. data/lib/rigor/lens.rb +1 -1
  174. data/lib/rigor/library_probe.rb +68 -0
  175. data/lib/rigor/memo_audit.rb +282 -0
  176. data/lib/rigor/plugin/base.rb +15 -2
  177. data/lib/rigor/plugin/io_boundary.rb +25 -3
  178. data/lib/rigor/plugin/manifest.rb +7 -3
  179. data/lib/rigor/plugin/registry.rb +13 -2
  180. data/lib/rigor/plugin/trust_policy.rb +46 -0
  181. data/lib/rigor/protection/mutation_cache.rb +4 -1
  182. data/lib/rigor/reflection/constant_ancestors.rb +79 -22
  183. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  184. data/lib/rigor/reflection/declarations.rb +58 -0
  185. data/lib/rigor/reflection/project_method.rb +177 -0
  186. data/lib/rigor/reflection.rb +2 -0
  187. data/lib/rigor/rooted_glob.rb +18 -1
  188. data/lib/rigor/run_scoped_slots.rb +20 -20
  189. data/lib/rigor/scope/discovery_index.rb +149 -2
  190. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  191. data/lib/rigor/scope/resolution_chain.rb +900 -55
  192. data/lib/rigor/scope.rb +307 -34
  193. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  194. data/lib/rigor/sig_gen/classification.rb +14 -1
  195. data/lib/rigor/sig_gen/generator.rb +222 -13
  196. data/lib/rigor/sig_gen/method_candidate.rb +14 -0
  197. data/lib/rigor/sig_gen/observation_collector.rb +11 -2
  198. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  199. data/lib/rigor/sig_gen/skip_reason_catalog.rb +47 -0
  200. data/lib/rigor/sig_gen/writer.rb +21 -0
  201. data/lib/rigor/sig_gen.rb +1 -0
  202. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  203. data/lib/rigor/source/node_children.rb +13 -0
  204. data/lib/rigor/type/combinator.rb +164 -8
  205. data/lib/rigor/type/interface.rb +60 -0
  206. data/lib/rigor/type.rb +1 -0
  207. data/lib/rigor/type_of_memo/serving.rb +99 -0
  208. data/lib/rigor/type_of_memo/stats.rb +43 -0
  209. data/lib/rigor/type_of_memo.rb +337 -0
  210. data/lib/rigor/typing_state.rb +1094 -0
  211. data/lib/rigor/version.rb +1 -1
  212. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +2 -2
  213. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +57 -25
  214. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  215. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  216. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  217. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  218. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  219. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +130 -30
  220. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -5
  221. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  222. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +49 -13
  223. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  224. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +4 -2
  225. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +13 -5
  226. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +29 -0
  227. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  228. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  229. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -2
  230. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  231. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +6 -0
  232. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +199 -1
  233. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +3 -2
  234. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +4 -4
  235. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +209 -2
  236. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  237. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  238. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +80 -3
  239. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  240. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +1 -6
  241. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  242. data/sig/rigor/analysis/fact_store.rbs +2 -3
  243. data/sig/rigor/cache/descriptor.rbs +14 -0
  244. data/sig/rigor/environment.rbs +4 -1
  245. data/sig/rigor/plugin/base.rbs +53 -19
  246. data/sig/rigor/plugin/interfaces.rbs +51 -0
  247. data/sig/rigor/plugin/io_boundary.rbs +2 -3
  248. data/sig/rigor/plugin/node_context.rbs +21 -0
  249. data/sig/rigor/reflection.rbs +37 -0
  250. data/sig/rigor/scope.rbs +22 -3
  251. data/sig/rigor/trinary.rbs +1 -0
  252. data/sig/rigor/type.rbs +9 -3
  253. data/sig/rigor.rbs +3 -2
  254. data/skills/rigor-ask/SKILL.md +1 -0
  255. data/skills/rigor-project-init/references/01-detect.md +1 -0
  256. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +5 -1
  257. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  258. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  259. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  260. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  261. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  262. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  263. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  264. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  265. data/skills/rigor-type-oracle/SKILL.md +26 -1
  266. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  267. metadata +48 -2
  268. data/lib/rigor/inference/memo_audit.rb +0 -55
@@ -4,6 +4,7 @@ require "tmpdir"
4
4
 
5
5
  require_relative "../../environment"
6
6
  require_relative "../../runtime/jit"
7
+ require_relative "../../type_of_memo"
7
8
 
8
9
  module Rigor
9
10
  module Analysis
@@ -164,10 +165,13 @@ module Rigor
164
165
  # `exit!` skips `at_exit` / stdio flush; the payload is already durable on disk by then.
165
166
  def run_fork_worker(slice, out_path)
166
167
  Runtime::Jit.rearm_after_fork
168
+ TypeOfMemo.forked
167
169
  results = slice.to_h { |binding, index| [index, run_one(binding)] }
168
170
  File.binwrite(out_path, Marshal.dump(results))
171
+ TypeOfMemo.write_report
169
172
  exit!(0)
170
173
  rescue StandardError
174
+ TypeOfMemo.write_report
171
175
  exit!(1)
172
176
  end
173
177
 
@@ -8,25 +8,25 @@ module Rigor
8
8
  # The declaration, not the method: this is a fact about what the author wrote, and the fix is on
9
9
  # that line. A `.rbs` position is the right answer here even though its sibling deliberately avoids
10
10
  # one (the `rbs_extended.unsatisfied-conformance` precedent) — there is no Ruby `def` that could
11
- # carry the typo. A value written in configuration has no location at all and lands at
12
- # `.rigor.yml:1`, the `rbs.coverage.quarantined-signature` precedent.
11
+ # carry the typo. A value written in configuration has no location at all and lands at the loaded
12
+ # configuration file's `1:1` (`.rigor.yml` when none was loaded — #2742), the
13
+ # `rbs.coverage.quarantined-signature` precedent.
13
14
  # A declaration's `location` is already the position a reader can open, `.rbs` and rbs-inline
14
15
  # alike: the synthesized-buffer line is re-anchored onto the Ruby file where the envelope is
15
16
  # built ({Rigor::Effects::InlineAnchor}, #432), so this module only has to split it. Doing the
16
17
  # re-anchoring here as well used to be the fix, and it could not tell two identically-spelled
17
18
  # annotations in one file apart — every finding in it landed on the first.
18
19
  module DeclarationPosition
19
- CONFIG_PATH = ".rigor.yml"
20
-
21
20
  module_function
22
21
 
22
+ # @param config_path — the path a config-value finding lands at ({Rigor::Configuration#config_display_path}).
23
23
  # @return the path and the one-based line of the finding
24
- def of(finding)
24
+ def of(finding, config_path)
25
25
  location = finding.location
26
- return [CONFIG_PATH, 1] if location.nil?
26
+ return [config_path, 1] if location.nil?
27
27
 
28
28
  path, _, raw_line = location.rpartition(":")
29
- return [CONFIG_PATH, 1] if path.empty?
29
+ return [config_path, 1] if path.empty?
30
30
 
31
31
  line = raw_line.to_i
32
32
  [path, line.positive? ? line : 1]
@@ -3,6 +3,7 @@
3
3
  require_relative "../check_rules/rule_ids"
4
4
  require_relative "../crash_signature"
5
5
  require_relative "../diagnostic"
6
+ require_relative "../run_cache_key"
6
7
  require_relative "../severity_stamp"
7
8
 
8
9
  module Rigor
@@ -99,6 +100,7 @@ module Rigor
99
100
  dependency_source_budget_diagnostics +
100
101
  dependency_source_config_conflict_diagnostics +
101
102
  rbs_coverage_diagnostics +
103
+ rbs_collection_setup_hint_diagnostics +
102
104
  rbs_inline_annotation_hint_diagnostics(expansion) +
103
105
  expansion.fetch(:errors)
104
106
  end
@@ -115,7 +117,7 @@ module Rigor
115
117
  next if File.file?(path)
116
118
 
117
119
  Diagnostic.new(
118
- path: ".rigor.yml", line: 1, column: 1,
120
+ path: @configuration.config_display_path, line: 1, column: 1,
119
121
  message: "pre_eval entry not found: #{path.inspect}. " \
120
122
  "Pre-evaluation requires the file to exist on disk; remove the entry " \
121
123
  "or create the file before re-running analysis.",
@@ -163,7 +165,7 @@ module Rigor
163
165
  def plugin_load_diagnostics
164
166
  plugin_registry.load_errors.map do |error|
165
167
  Diagnostic.new(
166
- path: ".rigor.yml",
168
+ path: @configuration.config_display_path,
167
169
  line: 1,
168
170
  column: 1,
169
171
  message: plugin_load_error_message(error),
@@ -192,7 +194,7 @@ module Rigor
192
194
  def dependency_source_diagnostics
193
195
  dependency_source_index.unresolvable.map do |entry|
194
196
  Diagnostic.new(
195
- path: ".rigor.yml",
197
+ path: @configuration.config_display_path,
196
198
  line: 1,
197
199
  column: 1,
198
200
  message: "dependencies.source_inference[].gem #{entry.gem_name.inspect} could not be " \
@@ -217,7 +219,7 @@ module Rigor
217
219
  def dependency_source_config_conflict_diagnostics
218
220
  @configuration.dependencies.warnings.map do |message|
219
221
  Diagnostic.new(
220
- path: ".rigor.yml",
222
+ path: @configuration.config_display_path,
221
223
  line: 1,
222
224
  column: 1,
223
225
  message: message,
@@ -232,7 +234,7 @@ module Rigor
232
234
  budget = @configuration.dependencies.budget_per_gem
233
235
  dependency_source_index.budget_exceeded.map do |gem_name|
234
236
  Diagnostic.new(
235
- path: ".rigor.yml",
237
+ path: @configuration.config_display_path,
236
238
  line: 1,
237
239
  column: 1,
238
240
  message: "dependencies.source_inference[].gem #{gem_name.inspect} exceeded the per-gem " \
@@ -248,7 +250,8 @@ module Rigor
248
250
 
249
251
  # O4 Layer 3 slice 3 — graceful-degradation coverage report. When the project has a `Gemfile.lock`
250
252
  # (slice 1) and one or more locked gems are not covered by ANY of the four RBS resolution paths
251
- # (`DEFAULT_LIBRARIES`, `data/vendored_gem_sigs/`, slice-1 bundle-shipped `sig/`, slice-2
253
+ # (the resolved library list — `DEFAULT_LIBRARIES` + `libraries:` + the collection lockfile's stdlib
254
+ # entries, `data/vendored_gem_sigs/`, slice-1 bundle-shipped `sig/`, slice-2
252
255
  # `rbs_collection.lock.yaml`), emit a single `:info` diagnostic summarising the uncovered set so the
253
256
  # user can act on it (run `rbs collection install`, opt the gem into `dependencies.source_inference:`,
254
257
  # or accept the `Dynamic[T]` fallback).
@@ -279,7 +282,7 @@ module Rigor
279
282
  )
280
283
  rows = Environment::RbsCoverageReport.classify(
281
284
  locked_gems: locked,
282
- default_libraries: Environment::DEFAULT_LIBRARIES,
285
+ default_libraries: resolved_default_libraries,
283
286
  bundle_sig_paths: bundle_sig_paths,
284
287
  rbs_collection_paths: collection_paths
285
288
  )
@@ -289,6 +292,55 @@ module Rigor
289
292
  [build_rbs_coverage_missing_diagnostic(missing)]
290
293
  end
291
294
 
295
+ # Issue #3030 — one `:info` when the run found no `rbs_collection.lock.yaml` but the directory looks set up for
296
+ # one ({Environment::RbsCollectionDiscovery.missing_lockfile_hint}): the dependency RBS a collection would
297
+ # load is absent, and Rigor does not resolve it from `manifest.yaml` or a gemspec itself.
298
+ def rbs_collection_setup_hint_diagnostics
299
+ # The run-result key and the `--incremental` fingerprint carry the same answer ({RunCacheKey}).
300
+ hint = RunCacheKey.collection_setup_hint(@configuration)
301
+ return [] if hint.nil?
302
+
303
+ [build_rbs_collection_setup_hint_diagnostic(hint)]
304
+ end
305
+
306
+ RBS_COLLECTION_SETUP_STEPS = {
307
+ collection_test_dir: "This looks like a gem_rbs_collection test directory: run the collection's " \
308
+ "`bin/test` setup for this gem first (it writes the lockfile and " \
309
+ "`.gem_rbs_collection/`), then run Rigor here again.",
310
+ collection_config: "An `rbs_collection.yaml` is here: run `rbs collection install` to write the " \
311
+ "lockfile, then run Rigor again."
312
+ }.freeze
313
+ private_constant :RBS_COLLECTION_SETUP_STEPS
314
+
315
+ def build_rbs_collection_setup_hint_diagnostic(hint)
316
+ Diagnostic.new(
317
+ path: @configuration.config_display_path,
318
+ line: 1,
319
+ column: 1,
320
+ message: "No `rbs_collection.lock.yaml` was found, so no `rbs collection` RBS was loaded: the signatures " \
321
+ "of this directory's gem dependencies are absent and calls into them read `Dynamic[top]`. " \
322
+ "#{RBS_COLLECTION_SETUP_STEPS.fetch(hint)}",
323
+ severity: :info,
324
+ rule: "rbs.coverage.collection-not-installed",
325
+ source_family: :builtin
326
+ )
327
+ end
328
+
329
+ # Issue #2962 — the library list is {Environment::BaseLibraries.resolve}'s, the same merge
330
+ # `Environment.for_project` loads, so a gem covered through `libraries:` or a lockfile stdlib
331
+ # entry is not reported missing. Only the names the environment loads count ({LibraryProbe.loadable}): a
332
+ # locked gem named there that RBS has no library for is still missing.
333
+ def resolved_default_libraries
334
+ resolved = Environment::BaseLibraries.resolve(
335
+ @configuration.libraries,
336
+ rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
337
+ rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect,
338
+ root: Dir.pwd
339
+ )
340
+ LibraryProbe.loadable(resolved)
341
+ end
342
+ private :resolved_default_libraries
343
+
292
344
  # Issue #2441 — the same project-own set `Environment.for_project` skips the collection copy of.
293
345
  def project_own_gem_names(locked)
294
346
  Environment::ProjectPathGems.names(
@@ -345,7 +397,7 @@ module Rigor
345
397
 
346
398
  def build_rbs_inline_unsynthesized_diagnostic(sample_path)
347
399
  Diagnostic.new(
348
- path: ".rigor.yml",
400
+ path: @configuration.config_display_path,
349
401
  line: 1,
350
402
  column: 1,
351
403
  message: "Inline rbs-inline annotations (`# @rbs …`, `#: <type>`) are present in your project " \
@@ -575,7 +627,7 @@ module Rigor
575
627
  sample = quarantined.first(sample_size).map { |path, _first_line| relative_signature_path(path) }
576
628
  suffix = quarantined.size > sample_size ? ", and #{quarantined.size - sample_size} more" : ""
577
629
  Diagnostic.new(
578
- path: ".rigor.yml",
630
+ path: @configuration.config_display_path,
579
631
  line: 1,
580
632
  column: 1,
581
633
  message: "#{quarantined.size} RBS file(s) under `signature_paths:` were SKIPPED " \
@@ -598,7 +650,7 @@ module Rigor
598
650
  suffix = files.size > sample_size ? ", and #{files.size - sample_size} more" : ""
599
651
  conflicts = sample.empty? ? "" : " Conflicting signature file(s): #{sample.join(', ')}#{suffix}."
600
652
  Diagnostic.new(
601
- path: ".rigor.yml",
653
+ path: @configuration.config_display_path,
602
654
  line: 1,
603
655
  column: 1,
604
656
  message: "The RBS environment failed to build (#{error_class}): #{first_line}.#{conflicts} " \
@@ -637,7 +689,7 @@ module Rigor
637
689
  files = failures.flat_map { |failure| Array(failure[3]) }
638
690
  .map { |name| signature_source_label(name.to_s) }.uniq.sort
639
691
  Diagnostic.new(
640
- path: ".rigor.yml",
692
+ path: @configuration.config_display_path,
641
693
  line: 1,
642
694
  column: 1,
643
695
  message: "#{failures.size} RBS class definition(s) failed to build: " \
@@ -834,7 +886,7 @@ module Rigor
834
886
  frame_clause = relative_frame ? " at #{relative_frame}" : ""
835
887
  raised = "(#{error_class}): #{first_line}#{frame_clause}."
836
888
  Diagnostic.new(
837
- path: ".rigor.yml",
889
+ path: @configuration.config_display_path,
838
890
  line: 1,
839
891
  column: 1,
840
892
  message: stage == :overlay ? hkt_overlay_failed_message(raised) : hkt_scan_failed_message(raised),
@@ -889,7 +941,7 @@ module Rigor
889
941
  sample = synthesized.first(sample_size)
890
942
  suffix = synthesized.size > sample_size ? ", and #{synthesized.size - sample_size} more" : ""
891
943
  Diagnostic.new(
892
- path: ".rigor.yml",
944
+ path: @configuration.config_display_path,
893
945
  line: 1,
894
946
  column: 1,
895
947
  message: "#{synthesized.size} RBS namespace(s) under `signature_paths:` are " \
@@ -908,7 +960,7 @@ module Rigor
908
960
  path, class_name, existing_arity, incoming_arity, existing_file = entry
909
961
  displaced_by = displacing_source_phrase(existing_file)
910
962
  Diagnostic.new(
911
- path: ".rigor.yml",
963
+ path: @configuration.config_display_path,
912
964
  line: 1,
913
965
  column: 1,
914
966
  message: "`#{relative_signature_path(path.to_s)}` (a signature file a plugin contributes) declares " \
@@ -937,7 +989,7 @@ module Rigor
937
989
  "`#{declaration}`#{cause}"
938
990
  end
939
991
  Diagnostic.new(
940
- path: ".rigor.yml",
992
+ path: @configuration.config_display_path,
941
993
  line: 1,
942
994
  column: 1,
943
995
  message: "`#{relative_signature_path(path.to_s)}` (a signature file a plugin contributes) loaded without " \
@@ -956,7 +1008,7 @@ module Rigor
956
1008
  def build_rbs_vendored_signature_stood_down_diagnostic(entry)
957
1009
  dir, declarations, whole = entry
958
1010
  Diagnostic.new(
959
- path: ".rigor.yml",
1011
+ path: @configuration.config_display_path,
960
1012
  line: 1,
961
1013
  column: 1,
962
1014
  message: vendored_standdown_message(dir, declarations, whole),
@@ -1035,7 +1087,7 @@ module Rigor
1035
1087
  sample = missing.first(sample_size).map(&:gem_name)
1036
1088
  suffix = missing.size > sample_size ? ", and #{missing.size - sample_size} more" : ""
1037
1089
  Diagnostic.new(
1038
- path: ".rigor.yml",
1090
+ path: @configuration.config_display_path,
1039
1091
  line: 1,
1040
1092
  column: 1,
1041
1093
  message: "#{missing.size} gem(s) in Gemfile.lock have no RBS available: " \
@@ -1201,7 +1253,7 @@ module Rigor
1201
1253
 
1202
1254
  @boundary_cross_reporter.entries.map do |entry|
1203
1255
  Diagnostic.new(
1204
- path: ".rigor.yml", line: 1, column: 1,
1256
+ path: @configuration.config_display_path, line: 1, column: 1,
1205
1257
  message: "`#{entry.class_name}##{entry.method_name}` is contributed by both " \
1206
1258
  "RBS (#{entry.rbs_display}) and the `mode: :full` opt-in gem " \
1207
1259
  "`#{entry.gem_name}`. RBS wins on dispatch; verify the gem source " \
@@ -1328,7 +1380,7 @@ module Rigor
1328
1380
 
1329
1381
  def run_disclosure_diagnostic(record)
1330
1382
  Diagnostic.new(
1331
- path: ".rigor.yml", line: 1, column: 1,
1383
+ path: @configuration.config_display_path, line: 1, column: 1,
1332
1384
  message: record.fetch(:message),
1333
1385
  severity: record.fetch(:severity),
1334
1386
  rule: record.fetch(:rule),
@@ -1371,7 +1423,7 @@ module Rigor
1371
1423
  def plugin_trust_refusal_diagnostic(plugin, summary)
1372
1424
  plugin_id = plugin.manifest.id
1373
1425
  Diagnostic.new(
1374
- path: ".rigor.yml", line: 1, column: 1,
1426
+ path: @configuration.config_display_path, line: 1, column: 1,
1375
1427
  message: "plugin #{plugin_id.inspect} had #{summary[:count]} read(s) refused by the trust " \
1376
1428
  "policy; first refused path: #{summary[:first_path].inspect}, outside read root " \
1377
1429
  "#{summary[:nearest_root].inspect}. Spell the path the way that read root spells it " \
@@ -52,11 +52,6 @@ module Rigor
52
52
  LISKOV_RULE = CheckRules::RULE_EFFECT_LISKOV_WIDENED
53
53
  UNKNOWN_LABEL_RULE = CheckRules::RULE_EFFECT_UNKNOWN_LABEL
54
54
 
55
- # Where a diagnostic about a configuration value is positioned when the loader cannot say
56
- # which line the key was written on — the `rbs.coverage.quarantined-signature` precedent.
57
- CONFIG_PATH = ".rigor.yml"
58
- private_constant :CONFIG_PATH
59
-
60
55
  RUBY_EXTENSION = ".rb"
61
56
  private_constant :RUBY_EXTENSION
62
57
 
@@ -222,7 +217,7 @@ module Rigor
222
217
  end
223
218
 
224
219
  def position_of(finding)
225
- DeclarationPosition.of(finding)
220
+ DeclarationPosition.of(finding, @configuration.config_display_path)
226
221
  end
227
222
 
228
223
  # The vocabulary an unknown label is judged against: the shipped registry plus whatever
@@ -299,7 +294,7 @@ module Rigor
299
294
 
300
295
  def build_diagnostic(finding)
301
296
  Diagnostic.new(
302
- path: finding.path || unit_source_path(finding.key) || ".rigor.yml",
297
+ path: finding.path || unit_source_path(finding.key) || @configuration.config_display_path,
303
298
  line: finding.line,
304
299
  column: 1,
305
300
  message: message_for(finding),
@@ -323,7 +318,7 @@ module Rigor
323
318
 
324
319
  def build_liskov_diagnostic(finding)
325
320
  Diagnostic.new(
326
- path: finding.path || CONFIG_PATH,
321
+ path: finding.path || @configuration.config_display_path,
327
322
  line: finding.line,
328
323
  column: 1,
329
324
  message: EnvelopeMessages.liskov(finding),
@@ -10,6 +10,7 @@ require_relative "../run_stats"
10
10
  require_relative "../../effects/signature_sources"
11
11
  require_relative "../../rbs_extended/conformance_checker"
12
12
  require_relative "../../runtime/jit"
13
+ require_relative "../../type_of_memo"
13
14
  require_relative "../../cache/engine_source"
14
15
  require_relative "run_level_facts"
15
16
  require_relative "worker_payload"
@@ -198,6 +199,18 @@ module Rigor
198
199
  @buffer.nil?
199
200
  end
200
201
 
202
+ # ADR-130 WD2/WD5 — `RbsTypeTranslator.with_interface_erasure` and `RbsDispatch.without_interface_dispatch`
203
+ # are per-thread switches, which a Ractor worker would not see: a pooled run under either would analyse with
204
+ # the default engine and pass as the erasure baseline or the dispatch-off mode.
205
+ def refuse_pool_under_interface_switches
206
+ if Rigor::Inference::RbsTypeTranslator.interface_erasure?
207
+ raise ArgumentError, "RbsTypeTranslator.with_interface_erasure supports only a sequential run (workers: 0)"
208
+ end
209
+ return if Rigor::Inference::MethodDispatcher::RbsDispatch.interface_dispatch?
210
+
211
+ raise ArgumentError, "RbsDispatch.without_interface_dispatch supports only a sequential run (workers: 0)"
212
+ end
213
+
201
214
  # ADR-15 Phase 4b — routes per-file analysis to either the sequential coordinator-side Environment
202
215
  # (legacy path, default) or a Ractor worker pool built around {WorkerSession} (opt-in via
203
216
  # `workers:`). The sequential path is bit-for-bit unchanged from v0.1.4 / earlier; the pool path is
@@ -225,6 +238,7 @@ module Rigor
225
238
  source_files = project_files || files
226
239
  result =
227
240
  if pool_mode?
241
+ refuse_pool_under_interface_switches
228
242
  dispatch_pool(files, source_files: source_files)
229
243
  else
230
244
  environment ||= resolve_sequential_environment(source_files: source_files)
@@ -483,8 +497,8 @@ module Rigor
483
497
  # runner's fresh per-run reporter pair attached so dispatcher events route to THIS runner's
484
498
  # diagnostics) when present; otherwise builds a fresh Environment per-call via
485
499
  # {#build_runner_environment} — preserving the pre-override behaviour bit-for-bit.
486
- def resolve_sequential_environment(source_files: [])
487
- return build_runner_environment(source_files: source_files) unless @environment_override
500
+ def resolve_sequential_environment(source_files: [], eager: true)
501
+ return build_runner_environment(source_files: source_files, eager: eager) unless @environment_override
488
502
 
489
503
  @environment_override.attach_reporters!(
490
504
  rbs_extended_reporter: @rbs_extended_reporter,
@@ -546,8 +560,9 @@ module Rigor
546
560
  # loaded plugin's `source_rbs_synthesizer` callable per project source file at env-build time.
547
561
  # Defaults to `[]` for callers that don't have a file list yet (e.g. pre-pass-only build paths); in
548
562
  # that case no synthesised RBS is contributed.
549
- def build_runner_environment(source_files: [])
563
+ def build_runner_environment(source_files: [], eager: true)
550
564
  Environment.for_project(
565
+ eager: eager,
551
566
  libraries: @configuration.libraries,
552
567
  signature_paths: @configuration.signature_paths,
553
568
  cache_store: @cache_store,
@@ -719,6 +734,9 @@ module Rigor
719
734
  # so the worker's own table is the only place a disclosure exists, and it is complete only
720
735
  # once the worker has finished its files.
721
736
  main.send([:done, Rigor::Analysis::Runner::WorkerPayload.harvest(session)])
737
+ # `Ractor#send` answers the Ractor; the worker's own value is unread (`join` only), and G3 reads a
738
+ # messaging call as an address only while its value is discarded.
739
+ nil
722
740
  end
723
741
  end
724
742
 
@@ -933,12 +951,15 @@ module Rigor
933
951
  # mode is opted into for large projects, so a slice reliably outlasts the remainder; a small-project
934
952
  # worker finishes first and stays at no-YJIT parity, exactly as the sequential path would.
935
953
  Runtime::Jit.rearm_after_fork
954
+ TypeOfMemo.forked
936
955
  results = slice.to_h { |path| [path, session.analyze(path)] }
937
956
  # ADR-103 WD13 — the effects slot is filled only on a collecting run.
938
957
  payload = WorkerPayload.harvest(session, results: results, record_effects: @record_effects)
939
958
  File.binwrite(out_path, Marshal.dump(payload))
959
+ TypeOfMemo.write_report
940
960
  exit!(0)
941
961
  rescue StandardError
962
+ TypeOfMemo.write_report
942
963
  exit!(1)
943
964
  end
944
965
 
@@ -986,7 +1007,7 @@ module Rigor
986
1007
  # process — so they say so the same way, naming which backend it was (#414).
987
1008
  def pool_degraded_diagnostic(count, backend)
988
1009
  Diagnostic.new(
989
- path: ".rigor.yml", line: 1, column: 1,
1010
+ path: @configuration.config_display_path, line: 1, column: 1,
990
1011
  message: "#{backend} pool degraded: #{count} file(s) re-analysed in-process " \
991
1012
  "after a worker exited abnormally",
992
1013
  severity: :warning, rule: "pool-degraded", source_family: :builtin
@@ -1069,7 +1090,7 @@ module Rigor
1069
1090
  harvest_stats { environment }
1070
1091
  diagnostics.unshift(
1071
1092
  Diagnostic.new(
1072
- path: ".rigor.yml", line: 1, column: 1,
1093
+ path: @configuration.config_display_path, line: 1, column: 1,
1073
1094
  message: "pool mode degraded to sequential: #{reason}",
1074
1095
  severity: :warning, rule: "pool-degraded", source_family: :builtin
1075
1096
  )
@@ -375,43 +375,10 @@ module Rigor
375
375
  end
376
376
  end
377
377
 
378
- # Builds the {Rigor::Plugin::TrustPolicy} for this run. Trusted gems are the gem-name half of every
379
- # entry in `Configuration#plugins`. Allowed read roots default to the project root (CWD), the
380
- # project's signature_paths, and each trusted gem's `Gem::Specification#full_gem_path`, plus any
381
- # extras the user listed under `plugins_io.allowed_paths`. `network_policy` is `plugins_io.network`
382
- # (`:disabled` or `:allowlist`).
378
+ # The run's {Rigor::Plugin::TrustPolicy}: {Rigor::Plugin::TrustPolicy.for_configuration} is the single
379
+ # derivation `rigor check`, pooled workers and `rigor unused`'s plugin roots share (#2586).
383
380
  def build_trust_policy
384
- trusted_gems = @configuration.plugins.map { |entry| trusted_gem_name(entry) }.uniq
385
- roots = [Dir.pwd]
386
- Array(@configuration.signature_paths).each { |sp| roots << File.absolute_path(sp) }
387
- trusted_gems.each do |gem_name|
388
- path = trusted_gem_root(gem_name)
389
- roots << path if path
390
- end
391
- @configuration.plugins_io_allowed_paths.each { |p| roots << File.absolute_path(p) }
392
-
393
- Plugin::TrustPolicy.new(
394
- trusted_gems: trusted_gems,
395
- allowed_read_roots: roots,
396
- network_policy: @configuration.plugins_io_network,
397
- allowed_url_hosts: @configuration.plugins_io_allowed_url_hosts
398
- )
399
- end
400
-
401
- def trusted_gem_name(entry)
402
- case entry
403
- when String then entry
404
- when Hash then entry["gem"] || entry["id"]
405
- end
406
- end
407
-
408
- def trusted_gem_root(gem_name)
409
- return nil if gem_name.nil? || gem_name.empty?
410
-
411
- spec = Gem.loaded_specs[gem_name]
412
- spec&.full_gem_path
413
- rescue StandardError
414
- nil
381
+ Plugin::TrustPolicy.for_configuration(@configuration)
415
382
  end
416
383
 
417
384
  # ADR-9 slice 3 — invokes every loaded plugin's `#prepare` hook once per run, after the loader's
@@ -438,7 +405,7 @@ module Rigor
438
405
  def plugin_prepare_error_diagnostic(plugin, error)
439
406
  plugin_id = safe_plugin_id(plugin)
440
407
  Diagnostic.new(
441
- path: ".rigor.yml",
408
+ path: @configuration.config_display_path,
442
409
  line: 1,
443
410
  column: 1,
444
411
  message: "plugin #{plugin_id.inspect} raised during prepare: " \