rigortype 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
- data/docs/handbook/03-narrowing.md +95 -10
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/07-rbs-and-extended.md +16 -11
- data/docs/handbook/08-understanding-errors.md +3 -2
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/install.md +87 -15
- data/docs/manual/02-cli-reference.md +65 -4
- data/docs/manual/03-configuration.md +12 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/08-skills.md +4 -3
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/12-caching.md +43 -4
- data/docs/manual/14-rails-quickstart.md +19 -11
- data/docs/manual/16-rbs-extended-annotations.md +40 -7
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +243 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +110 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +1 -1
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +427 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
- data/lib/rigor/analysis/check_rules.rb +638 -136
- data/lib/rigor/analysis/dependency_recorder.rb +222 -17
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
- data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
- data/lib/rigor/analysis/incremental_session.rb +309 -53
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -1
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +46 -18
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +168 -12
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +109 -17
- data/lib/rigor/analysis/runner/project_pre_passes.rb +44 -17
- data/lib/rigor/analysis/runner.rb +603 -54
- data/lib/rigor/analysis/shadow_harness.rb +27 -0
- data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +93 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +409 -0
- data/lib/rigor/analysis/worker_session.rb +57 -13
- data/lib/rigor/bleeding_edge.rb +0 -2
- data/lib/rigor/builtins/hkt_builtins.rb +1 -0
- data/lib/rigor/builtins/imported_refinements.rb +4 -0
- data/lib/rigor/builtins/regex_refinement.rb +17 -9
- data/lib/rigor/builtins/static_return_refinements.rb +2 -0
- data/lib/rigor/cache/descriptor.rb +45 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/file_digest.rb +28 -0
- data/lib/rigor/cache/incremental_snapshot.rb +102 -8
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_descriptor.rb +37 -7
- data/lib/rigor/cache/store.rb +63 -4
- data/lib/rigor/cache/value_digest.rb +325 -0
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/check_command.rb +83 -13
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/effects_report.rb +2 -2
- data/lib/rigor/cli/plugin_command.rb +3 -3
- data/lib/rigor/cli/prism_colorizer.rb +0 -1
- data/lib/rigor/cli/sig_gen_command.rb +210 -29
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +0 -1
- data/lib/rigor/cli/type_of_command.rb +19 -5
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/unused_command.rb +3 -3
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/config_audit.rb +3 -2
- data/lib/rigor/config_path.rb +29 -0
- data/lib/rigor/configuration/severity_profile.rb +24 -3
- data/lib/rigor/configuration.rb +71 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +11 -2
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +9 -2
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/effect_table.rb +11 -3
- data/lib/rigor/effects/envelope_check.rb +1 -1
- data/lib/rigor/effects/envelope_index.rb +15 -0
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/identity.rb +16 -0
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +21 -0
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +49 -37
- data/lib/rigor/effects/propagator.rb +295 -16
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +121 -72
- data/lib/rigor/effects/signature_sources.rb +1 -1
- data/lib/rigor/effects/snapshot.rb +2 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +385 -30
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
- data/lib/rigor/environment/lockfile_resolver.rb +27 -1
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
- data/lib/rigor/environment/rbs_loader.rb +400 -134
- data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
- data/lib/rigor/environment.rb +104 -96
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +139 -9
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +88 -26
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +2 -1
- data/lib/rigor/inference/budget_trace.rb +2 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +393 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/declaration_walk/context.rb +195 -0
- data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
- data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
- data/lib/rigor/inference/declaration_walk.rb +7 -0
- data/lib/rigor/inference/def_node_resolver.rb +3 -2
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/definer_resolution.rb +266 -0
- data/lib/rigor/inference/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1665 -538
- data/lib/rigor/inference/external_ancestor_resolution.rb +264 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +488 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/index_write_widening.rb +16 -3
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +179 -17
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +901 -38
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +214 -13
- data/lib/rigor/inference/method_parameter_binder.rb +8 -3
- data/lib/rigor/inference/module_function_state.rb +213 -0
- data/lib/rigor/inference/multi_target_binder.rb +345 -52
- data/lib/rigor/inference/mutation_rejoin.rb +4 -1
- data/lib/rigor/inference/mutation_widening.rb +70 -48
- data/lib/rigor/inference/narrowing.rb +674 -124
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +152 -9
- data/lib/rigor/inference/parameter_inference_collector.rb +13 -11
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +7 -2
- data/lib/rigor/inference/project_patched_scanner.rb +7 -3
- data/lib/rigor/inference/receiver_alias.rb +90 -1
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +15 -11
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
- data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
- data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
- data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
- data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
- data/lib/rigor/inference/scope_indexer.rb +5913 -701
- data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
- data/lib/rigor/inference/statement_evaluator.rb +3249 -497
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +44 -7
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
- data/lib/rigor/language_server/hover_renderer.rb +3 -3
- data/lib/rigor/language_server/project_context.rb +5 -3
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +196 -13
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +1 -1
- data/lib/rigor/plugin/effect_attribution.rb +58 -4
- data/lib/rigor/plugin/io_boundary.rb +82 -4
- data/lib/rigor/plugin/isolation.rb +1 -1
- data/lib/rigor/plugin/loader.rb +2 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/manifest.rb +71 -10
- data/lib/rigor/plugin/registry.rb +35 -1
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +20 -9
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +4 -5
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +146 -0
- data/lib/rigor/reflection/constant_path.rb +41 -13
- data/lib/rigor/reflection.rb +73 -107
- data/lib/rigor/scope/discovery_index.rb +351 -2
- data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
- data/lib/rigor/scope/resolution_chain.rb +884 -0
- data/lib/rigor/scope.rb +981 -198
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +580 -47
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +124 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +166 -35
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +1 -1
- data/lib/rigor/source/node_walker.rb +0 -3
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +112 -12
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/hash_shape.rb +1 -1
- data/lib/rigor/type/refined.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +10 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +14 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +6 -4
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +50 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +7 -6
- data/sig/rigor/scope.rbs +177 -20
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +6 -0
- data/sig/rigor.rbs +38 -20
- data/skills/rigor-ask/SKILL.md +8 -5
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +46 -18
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +40 -10
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +4 -6
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
- data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +132 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require_relative "../cache/value_digest"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
class Environment
|
|
8
|
+
# ADR-32 WD5 — one plugin's `source_rbs_synthesizer` output for one source file, cached per (file content,
|
|
9
|
+
# plugin entry, engine source). Two callers read it, and sharing the function is the point:
|
|
10
|
+
#
|
|
11
|
+
# - {Environment.collect_virtual_rbs} hands each output to the loader as a `virtual:` buffer;
|
|
12
|
+
# - {.digest} reduces every loaded synthesizer's output for a file to one comparable value, which the
|
|
13
|
+
# incremental session (ADR-89 WD1, issue #1536) keeps on the file's seed bundle and compares across an
|
|
14
|
+
# edit.
|
|
15
|
+
#
|
|
16
|
+
# Because the digest reads the very function (and the very cache entries) the loader is fed from, "the
|
|
17
|
+
# digest did not move" means "the loader read the same bytes for this file", not "some model of what
|
|
18
|
+
# the plugin reads did not move". That is why it is taken from the output rather than from the file's
|
|
19
|
+
# comment lines: rbs-inline binds an annotation by adjacency, so a plain comment or a blank line can move
|
|
20
|
+
# one to another declaration, and in an annotated file every `def`, `attr`, constant and mixin gets a
|
|
21
|
+
# skeleton whether or not it carries an annotation of its own.
|
|
22
|
+
module SourceRbsSynthesis
|
|
23
|
+
PRODUCER_ID = "plugin.source_rbs_synthesizer"
|
|
24
|
+
|
|
25
|
+
# The digest of a file no loaded synthesizer contributes anything for — the answer for every file of a
|
|
26
|
+
# project without an annotation, and for every file when no synthesizer is loaded at all. Not a hex
|
|
27
|
+
# digest, so it can never collide with one.
|
|
28
|
+
NO_CONTRIBUTION = "none"
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# The cache stores the empty string `""` as the "no contribution" sentinel because `Cache::Store`
|
|
33
|
+
# treats `nil` as a cache miss. Error tuples are stored as the canonical `[:error, message_string]`
|
|
34
|
+
# Array so the same wrapper short-circuits subsequent runs against unchanged broken input.
|
|
35
|
+
def output_for(plugin, callable, path, cache_store)
|
|
36
|
+
return invoke_safely(callable, path) if cache_store.nil?
|
|
37
|
+
return invoke_safely(callable, path) unless File.file?(path)
|
|
38
|
+
|
|
39
|
+
descriptor = cache_descriptor(plugin, path)
|
|
40
|
+
return invoke_safely(callable, path) if descriptor.nil?
|
|
41
|
+
|
|
42
|
+
cache_store.fetch_or_compute(
|
|
43
|
+
producer_id: PRODUCER_ID,
|
|
44
|
+
params: {},
|
|
45
|
+
descriptor: descriptor,
|
|
46
|
+
generation_cap: generation_cap
|
|
47
|
+
) { invoke_safely(callable, path) || "" }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Issue #1536 — every synthesizer's output for `path`, reduced to one value: {NO_CONTRIBUTION} when none
|
|
51
|
+
# of them contributes anything, a SHA-256 hex digest of each plugin's id and {.contribution} otherwise,
|
|
52
|
+
# and nil when an output could not be read at all.
|
|
53
|
+
#
|
|
54
|
+
# nil means "unknown", and a caller must treat it as moved. Every failure is per file: one plugin that
|
|
55
|
+
# cannot answer for one file leaves every other file's digest intact.
|
|
56
|
+
#
|
|
57
|
+
# @param synthesizers — `Plugin::Registry#source_rbs_synthesizers` pairs.
|
|
58
|
+
def digest(synthesizers, path, cache_store)
|
|
59
|
+
sha = Digest::SHA256.new
|
|
60
|
+
contributed = false
|
|
61
|
+
synthesizers.each do |plugin, callable|
|
|
62
|
+
tag, text = contribution(output_for(plugin, callable, path, cache_store))
|
|
63
|
+
next if tag.nil?
|
|
64
|
+
|
|
65
|
+
contributed = true
|
|
66
|
+
sha << plugin.manifest.id.to_s.b << "\x00" << tag << "\x00" << text.to_s.b << "\x01"
|
|
67
|
+
end
|
|
68
|
+
contributed ? sha.hexdigest : NO_CONTRIBUTION
|
|
69
|
+
rescue StandardError
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# What one output puts into the environment, as a `[tag, text]` pair, or nil for nothing: exactly the
|
|
74
|
+
# RBS {Environment.interpret_synthesizer_outcome} hands the loader. The ADR-32 WD6 / WD12 notices are left
|
|
75
|
+
# out. They reach only the run-level `source-rbs-*` rows, which every run regenerates and the per-file
|
|
76
|
+
# cache never serves, and they quote line numbers, so a line shift in a file carrying a malformed `#:`
|
|
77
|
+
# would otherwise read as a moved contribution. A failed synthesis contributes nothing to the loader but
|
|
78
|
+
# is kept as one stable value of its own, so a file flipping between "no annotation" and "broken
|
|
79
|
+
# annotation" still reads as moved. Any shape the contract does not name is digested whole, by value: the
|
|
80
|
+
# output is computed on one run and served from its cache entry on the next, and a digest of its
|
|
81
|
+
# `Marshal.dump` bytes can differ between the two (issue #1574).
|
|
82
|
+
#
|
|
83
|
+
# The RBS text itself is digested whole, comments included. RBS reads a `# resolve-type-names: false`
|
|
84
|
+
# magic comment at the start of a buffer, rbs-inline copies a `.rb` file's first comment line there, and
|
|
85
|
+
# a string-literal type may span lines that start with `#`, so no line-based stripping is sound (#1549).
|
|
86
|
+
def contribution(output)
|
|
87
|
+
return nil if output.nil? || output == ""
|
|
88
|
+
return ["rbs", output] if output.is_a?(String)
|
|
89
|
+
return ["raw", Cache::ValueDigest.hexdigest(output)] unless output.is_a?(Array)
|
|
90
|
+
|
|
91
|
+
case output[0]
|
|
92
|
+
when :error then ["error", nil]
|
|
93
|
+
when :ok then contribution(output[1])
|
|
94
|
+
else ["raw", Cache::ValueDigest.hexdigest(output)]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# One entry per (plugin, source file), all of them live for as long as the file is in the project — a
|
|
99
|
+
# generation count says nothing about staleness here, so this producer declares itself out of
|
|
100
|
+
# `Cache::Store#evict!`'s compaction pass and is bounded only by the size-based LRU pass. A method
|
|
101
|
+
# rather than a constant: `Cache::Store` is not loaded yet when this file is.
|
|
102
|
+
def generation_cap
|
|
103
|
+
Cache::Store::UNBOUNDED_GENERATIONS
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# The key composes the file's content SHA with the plugin's `PluginEntry` (id + version + config_hash),
|
|
107
|
+
# so a config change or a content change invalidates the entry, and with the engine's source identity
|
|
108
|
+
# (issue #1009): a plugin's manifest version does not move when a checkout edits its synthesizer, and
|
|
109
|
+
# a stale string here also keeps the `rbs.virtual_rbs` env key warm, so the new build's rules would
|
|
110
|
+
# read the old build's RBS.
|
|
111
|
+
def cache_descriptor(plugin, path)
|
|
112
|
+
Cache::Descriptor.new(
|
|
113
|
+
files: [Cache::Descriptor::FileEntry.new(
|
|
114
|
+
path: path.to_s,
|
|
115
|
+
comparator: :digest,
|
|
116
|
+
value: input_digest(path)
|
|
117
|
+
)],
|
|
118
|
+
plugins: [plugin.plugin_entry],
|
|
119
|
+
configs: Cache::EngineSource.key_config_entries
|
|
120
|
+
)
|
|
121
|
+
rescue Cache::EngineSource::Unavailable
|
|
122
|
+
# An engine that cannot be identified must not be keyed by its inputs alone (issue #1009): nil runs the
|
|
123
|
+
# synthesizer uncached rather than serving an entry another build may have written.
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def input_digest(path)
|
|
128
|
+
Digest::SHA256.hexdigest(File.binread(path))
|
|
129
|
+
rescue ::SystemCallError
|
|
130
|
+
# Unreadable file → key on the path alone; the synthesizer's File.file?/File.read will see the same
|
|
131
|
+
# failure and return nil.
|
|
132
|
+
Digest::SHA256.hexdigest(path.to_s)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def invoke_safely(callable, path)
|
|
136
|
+
callable.call(path.to_s)
|
|
137
|
+
rescue StandardError
|
|
138
|
+
# WD6 fail-soft — a synthesizer that raises does NOT crash analysis. Unlike the `[:error, msg]`
|
|
139
|
+
# return path (which the runner surfaces as `source-rbs-synthesis-failed`), an unhandled raise is
|
|
140
|
+
# swallowed silently; the unexamined-raise channel is deliberately silent per WD6.
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
private_class_method :contribution, :generation_cap, :cache_descriptor, :input_digest, :invoke_safely
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/rigor/environment.rb
CHANGED
|
@@ -15,7 +15,9 @@ require_relative "environment/bundle_sig_discovery"
|
|
|
15
15
|
require_relative "environment/lockfile_resolver"
|
|
16
16
|
require_relative "environment/installed_gem_set"
|
|
17
17
|
require_relative "environment/rbs_collection_discovery"
|
|
18
|
+
require_relative "environment/source_rbs_synthesis"
|
|
18
19
|
require_relative "plugin/isolation"
|
|
20
|
+
require_relative "cache/engine_source"
|
|
19
21
|
require_relative "environment/rbs_coverage_report"
|
|
20
22
|
require_relative "inference/synthetic_method_index"
|
|
21
23
|
require_relative "inference/project_patched_methods"
|
|
@@ -273,8 +275,8 @@ module Rigor
|
|
|
273
275
|
bundler_lockfile: nil,
|
|
274
276
|
rbs_collection_lockfile: nil, rbs_collection_auto_detect: false,
|
|
275
277
|
synthetic_method_index: nil, project_patched_methods: nil,
|
|
276
|
-
source_files: [])
|
|
277
|
-
resolved_paths = signature_paths
|
|
278
|
+
source_files: [], locked_gems: nil)
|
|
279
|
+
resolved_paths = project_signature_roots(signature_paths, root: root)
|
|
278
280
|
# O4 MVP — append per-gem `sig/` directories discovered under the target project's bundler install
|
|
279
281
|
# root. Empty array when neither an explicit path nor auto-detection finds a bundle. Order: user
|
|
280
282
|
# `signature_paths:` win first (semantic precedence inside `RbsLoader.build_env_for`); gem-shipped
|
|
@@ -284,7 +286,10 @@ module Rigor
|
|
|
284
286
|
# to the project root), use the locked gem set to filter the discovered `sig/` directories. Stale
|
|
285
287
|
# gems in the bundle install tree (out-of-band installs, version drift after a `bundle update`) are
|
|
286
288
|
# silently dropped so only gems the project actually declares contribute RBS.
|
|
287
|
-
|
|
289
|
+
#
|
|
290
|
+
# #1064 — `locked_gems:` is that same map resolved by the caller. The Ractor pool passes it because
|
|
291
|
+
# Bundler's parser cannot run in a non-main Ractor; every other caller leaves it nil and resolves here.
|
|
292
|
+
locked = locked_gems || LockfileResolver.locked_gems(
|
|
288
293
|
lockfile_path: bundler_lockfile,
|
|
289
294
|
project_root: root,
|
|
290
295
|
auto_detect: bundler_auto_detect
|
|
@@ -358,13 +363,14 @@ module Rigor
|
|
|
358
363
|
# the bundle walk and `rbs collection install` all stay authoritative.
|
|
359
364
|
deferred_signature_paths: plugin_sig_paths
|
|
360
365
|
)
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
#
|
|
364
|
-
#
|
|
365
|
-
#
|
|
366
|
-
#
|
|
367
|
-
|
|
366
|
+
# Issues #824 / #1075 — the rows ADR-112 WD5's consistency rule reports for members the project's
|
|
367
|
+
# own `sig/` and an inline source both declare: an error per contradiction (or per refinement that
|
|
368
|
+
# exceeds its own declared type), an `:info` per inline member dropped undecided. Recorded HERE
|
|
369
|
+
# because this is where the reporter is: the run's `source_rbs_synthesis` stream already carries
|
|
370
|
+
# WD12's "parsed but not honoured" rows, drains out of pool workers, and is regenerated (never cached)
|
|
371
|
+
# on every run, so the member-level outcome needs no plumbing of its own. Costs nothing for a project
|
|
372
|
+
# with no inline RBS: the loader's reader short-circuits on an empty `virtual_rbs`.
|
|
373
|
+
record_member_consistency(loader, source_rbs_synthesis_reporter, root)
|
|
368
374
|
# ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
|
|
369
375
|
# `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
|
|
370
376
|
# don't consult HKT (e.g. `rigor check --cache-stats --no-stats`) don't pay the eager env-build cost
|
|
@@ -385,6 +391,13 @@ module Rigor
|
|
|
385
391
|
)
|
|
386
392
|
end
|
|
387
393
|
|
|
394
|
+
# The project-owned `sig/` roots a run reads: the explicit `signature_paths:` when given, else the
|
|
395
|
+
# auto-detected `<root>/sig` (or none). Public so {Cache::IncrementalSnapshot} digests exactly the
|
|
396
|
+
# directories the environment loads, including the auto-detected one a `nil` `signature_paths:` hides.
|
|
397
|
+
def project_signature_roots(signature_paths, root: Dir.pwd)
|
|
398
|
+
signature_paths || default_signature_paths(root)
|
|
399
|
+
end
|
|
400
|
+
|
|
388
401
|
private
|
|
389
402
|
|
|
390
403
|
def default_signature_paths(root)
|
|
@@ -491,8 +504,9 @@ module Rigor
|
|
|
491
504
|
# empty and this method short-circuits to `[]` without walking the file list.
|
|
492
505
|
#
|
|
493
506
|
# WD5 — when `cache_store` is supplied, each (file, plugin) synthesizer call is memoised through
|
|
494
|
-
# `Cache::Store
|
|
495
|
-
#
|
|
507
|
+
# `Cache::Store` ({SourceRbsSynthesis.output_for}, which owns the key). The incremental session's
|
|
508
|
+
# per-file digest (issue #1536) reads the same function, so the two can never disagree on what a file
|
|
509
|
+
# contributed.
|
|
496
510
|
def collect_virtual_rbs(plugin_registry, source_files, cache_store, reporter)
|
|
497
511
|
return [] if plugin_registry.nil?
|
|
498
512
|
|
|
@@ -503,7 +517,7 @@ module Rigor
|
|
|
503
517
|
result = []
|
|
504
518
|
source_files.each do |path|
|
|
505
519
|
synthesizers.each do |plugin, callable|
|
|
506
|
-
outcome =
|
|
520
|
+
outcome = SourceRbsSynthesis.output_for(plugin, callable, path, cache_store)
|
|
507
521
|
outcome = interpret_synthesizer_outcome(outcome, plugin, path, reporter)
|
|
508
522
|
next if outcome.nil? || outcome.empty?
|
|
509
523
|
|
|
@@ -514,23 +528,6 @@ module Rigor
|
|
|
514
528
|
result
|
|
515
529
|
end
|
|
516
530
|
|
|
517
|
-
# ADR-32 WD5 — cache wrapper around a single (plugin, file) invocation. The cache stores the empty
|
|
518
|
-
# string `""` as the "no contribution" sentinel because `Cache::Store` treats `nil` as a cache miss.
|
|
519
|
-
# Error tuples are stored as the canonical `[:error, message_string]` Array so the same wrapper
|
|
520
|
-
# short-circuits subsequent runs against unchanged broken input.
|
|
521
|
-
def synthesizer_output_for(plugin, callable, path, cache_store)
|
|
522
|
-
return invoke_synthesizer_safely(callable, path) if cache_store.nil?
|
|
523
|
-
return invoke_synthesizer_safely(callable, path) unless File.file?(path)
|
|
524
|
-
|
|
525
|
-
descriptor = build_synthesizer_cache_descriptor(plugin, path)
|
|
526
|
-
cache_store.fetch_or_compute(
|
|
527
|
-
producer_id: SYNTHESIZER_CACHE_PRODUCER_ID,
|
|
528
|
-
params: {},
|
|
529
|
-
descriptor: descriptor,
|
|
530
|
-
generation_cap: synthesizer_generation_cap
|
|
531
|
-
) { invoke_synthesizer_safely(callable, path) || "" }
|
|
532
|
-
end
|
|
533
|
-
|
|
534
531
|
# ADR-32 WD6 / WD12 — route a synthesizer return value through the per-run reporter. The
|
|
535
532
|
# synthesizer's contract (declared in
|
|
536
533
|
# `plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb`) admits four return shapes:
|
|
@@ -565,29 +562,78 @@ module Rigor
|
|
|
565
562
|
reporter&.record(plugin_id: plugin.manifest.id, path: path, message: message.to_s, kind: kind)
|
|
566
563
|
end
|
|
567
564
|
|
|
568
|
-
# ADR-
|
|
569
|
-
#
|
|
570
|
-
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
565
|
+
# ADR-112 WD5 / issue #1075 — turn each reported {MemberConsistency::Record} into a row on the
|
|
566
|
+
# synthesis stream. A contradiction, or a refinement outside its own declared type, is a `:contradiction`
|
|
567
|
+
# entry ({Analysis::CheckRules::RULE_CONTRADICTING_SIGNATURE}, an error), positioned at the `.rbs`
|
|
568
|
+
# member when there is one: that file is real and its line is exact, where the inline member's position
|
|
569
|
+
# lives in the synthesized RBS buffer, a document nobody has. An undecided pair keeps ADR-32 WD12's
|
|
570
|
+
# `:not_honoured` row at the annotated `.rb` (line 1, as every entry on that stream is): the inline
|
|
571
|
+
# signature was parsed and dropped, and WD12 forbids swallowing that. The silent outcomes — equal, or
|
|
572
|
+
# merged to the more precise side — record nothing.
|
|
573
573
|
#
|
|
574
|
-
# The
|
|
575
|
-
#
|
|
576
|
-
|
|
577
|
-
def record_inline_member_standdowns(loader, reporter, root)
|
|
574
|
+
# The records are derived with a {MemberConsistency::RbsProof}, so a contradiction is proven from the
|
|
575
|
+
# RBS hierarchy of core and stdlib classes, never from the analyzer process's loaded constants.
|
|
576
|
+
def record_member_consistency(loader, reporter, root)
|
|
578
577
|
return if reporter.nil?
|
|
579
578
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
reporter
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
)
|
|
579
|
+
proof = MemberConsistency::RbsProof.new(loader)
|
|
580
|
+
loader.member_consistency(proof: proof).each do |record|
|
|
581
|
+
case record.outcome
|
|
582
|
+
when :contradiction then record_member_contradiction(reporter, record, root)
|
|
583
|
+
when :refinement then record_refinement_contradiction(reporter, record, root)
|
|
584
|
+
when :undecided then record_member_undecided(reporter, record, root)
|
|
585
|
+
end
|
|
588
586
|
end
|
|
589
587
|
end
|
|
590
588
|
|
|
589
|
+
def record_member_contradiction(reporter, record, root)
|
|
590
|
+
plugin_id, path = split_virtual_source_name(record.virtual_name)
|
|
591
|
+
return if path.nil?
|
|
592
|
+
|
|
593
|
+
reporter.record(
|
|
594
|
+
plugin_id: plugin_id, path: path_relative_to(record.signature_path, root), line: record.signature_line,
|
|
595
|
+
kind: :contradiction,
|
|
596
|
+
message: "`#{member_label(record)}` is declared here and by an inline annotation in " \
|
|
597
|
+
"`#{path_relative_to(path, root)}`, and the two contradict: #{record.detail}. Rigor reads " \
|
|
598
|
+
"this `.rbs` declaration. Regenerate the signature, or fix the annotation, so that one " \
|
|
599
|
+
"of the two refines the other."
|
|
600
|
+
)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def record_refinement_contradiction(reporter, record, root)
|
|
604
|
+
plugin_id, path = split_virtual_source_name(record.virtual_name)
|
|
605
|
+
path = record.signature_path if path.nil?
|
|
606
|
+
return if path.nil?
|
|
607
|
+
|
|
608
|
+
reporter.record(
|
|
609
|
+
plugin_id: plugin_id.to_s, path: path_relative_to(path, root), line: record.signature_line,
|
|
610
|
+
kind: :contradiction,
|
|
611
|
+
message: "`#{member_label(record)}` refines its own signature to a type outside it: " \
|
|
612
|
+
"#{record.detail}. A `rigor:v1:` refinement may narrow the ordinary RBS contract, never " \
|
|
613
|
+
"contradict it, so one of the two is wrong."
|
|
614
|
+
)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def record_member_undecided(reporter, record, root)
|
|
618
|
+
plugin_id, path = split_virtual_source_name(record.virtual_name)
|
|
619
|
+
return if path.nil?
|
|
620
|
+
|
|
621
|
+
reporter.record(
|
|
622
|
+
plugin_id: plugin_id, path: path, kind: :not_honoured,
|
|
623
|
+
message: "`#{member_label(record)}` is also declared in " \
|
|
624
|
+
"`#{path_relative_to(record.signature_path, root)}`, and Rigor cannot tell which of the two " \
|
|
625
|
+
"is the more precise (#{record.detail}), so the `.rbs` declaration binds and the inline " \
|
|
626
|
+
"signature was dropped. Left to collide the two would fail the class's definition build, " \
|
|
627
|
+
"and every call on it — real methods and typos alike — would read `Dynamic[top]`. Make " \
|
|
628
|
+
"one of the two a refinement of the other, or remove one, to settle which binds."
|
|
629
|
+
)
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def member_label(record)
|
|
633
|
+
separator = record.kind == :singleton ? "." : "#"
|
|
634
|
+
"#{record.class_name}#{separator}#{record.method_name}"
|
|
635
|
+
end
|
|
636
|
+
|
|
591
637
|
# Splits a virtual buffer name back into the pair {.collect_virtual_rbs} composed it from
|
|
592
638
|
# (`virtual:<plugin id>:<source path>`). Limited to three fields so a path carrying a colon survives.
|
|
593
639
|
# Returns `[nil, nil]` for a name in any other shape — a synthesizer channel this method does not own.
|
|
@@ -598,59 +644,10 @@ module Rigor
|
|
|
598
644
|
[plugin_id, path]
|
|
599
645
|
end
|
|
600
646
|
|
|
601
|
-
def inline_member_standdown_message(class_name, method_name, kind, signature_path, root)
|
|
602
|
-
separator = kind == :singleton ? "." : "#"
|
|
603
|
-
"`#{class_name}#{separator}#{method_name}` is also declared in " \
|
|
604
|
-
"`#{path_relative_to(signature_path, root)}`, and an explicit `.rbs` declaration wins over an " \
|
|
605
|
-
"inline annotation for the same member, so the inline signature was dropped. Left to collide the " \
|
|
606
|
-
"two would fail the class's definition build, and every call on it — real methods and typos " \
|
|
607
|
-
"alike — would read `Dynamic[top]`. Remove one of the two declarations to make the inline " \
|
|
608
|
-
"annotation bind."
|
|
609
|
-
end
|
|
610
|
-
|
|
611
647
|
def path_relative_to(path, root)
|
|
612
648
|
prefix = "#{root}#{File::SEPARATOR}"
|
|
613
649
|
path.to_s.start_with?(prefix) ? path.to_s.delete_prefix(prefix) : path.to_s
|
|
614
650
|
end
|
|
615
|
-
|
|
616
|
-
SYNTHESIZER_CACHE_PRODUCER_ID = "plugin.source_rbs_synthesizer"
|
|
617
|
-
private_constant :SYNTHESIZER_CACHE_PRODUCER_ID
|
|
618
|
-
|
|
619
|
-
# One entry per (plugin, source file), all of them live for as long as the file is in the project — a
|
|
620
|
-
# generation count says nothing about staleness here, so this producer declares itself out of
|
|
621
|
-
# `Cache::Store#evict!`'s compaction pass and is bounded only by the size-based LRU pass. A method
|
|
622
|
-
# rather than a constant: `Cache::Store` is not loaded yet when this class body runs.
|
|
623
|
-
def synthesizer_generation_cap
|
|
624
|
-
Cache::Store::UNBOUNDED_GENERATIONS
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
def build_synthesizer_cache_descriptor(plugin, path)
|
|
628
|
-
Cache::Descriptor.new(
|
|
629
|
-
files: [Cache::Descriptor::FileEntry.new(
|
|
630
|
-
path: path.to_s,
|
|
631
|
-
comparator: :digest,
|
|
632
|
-
value: synthesizer_input_digest(path)
|
|
633
|
-
)],
|
|
634
|
-
plugins: [plugin.plugin_entry]
|
|
635
|
-
)
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
def synthesizer_input_digest(path)
|
|
639
|
-
Digest::SHA256.hexdigest(File.binread(path))
|
|
640
|
-
rescue ::SystemCallError
|
|
641
|
-
# Unreadable file → key on the path alone; the synthesizer's File.file?/File.read will see the same
|
|
642
|
-
# failure and return nil.
|
|
643
|
-
Digest::SHA256.hexdigest(path.to_s)
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
def invoke_synthesizer_safely(callable, path)
|
|
647
|
-
callable.call(path.to_s)
|
|
648
|
-
rescue StandardError
|
|
649
|
-
# WD6 fail-soft — a synthesizer that raises does NOT crash analysis. Unlike the `[:error, msg]`
|
|
650
|
-
# return path (which the runner surfaces as `source-rbs-synthesis-failed`), an unhandled raise is
|
|
651
|
-
# swallowed silently; the unexamined-raise channel is deliberately silent per WD6.
|
|
652
|
-
nil
|
|
653
|
-
end
|
|
654
651
|
end
|
|
655
652
|
|
|
656
653
|
# Resolves a constant name to a Rigor::Type::Nominal (the *instance* type carrier). Consults the static
|
|
@@ -696,6 +693,17 @@ module Rigor
|
|
|
696
693
|
end
|
|
697
694
|
end
|
|
698
695
|
|
|
696
|
+
# Issue #1362 — the translated type of the RBS declaration of the global variable `name` (`$stdout: IO` answers
|
|
697
|
+
# `Nominal[IO]`), or nil when no signature declares it or the environment carries no RBS loader. With
|
|
698
|
+
# `builtin: true` it answers only for a declaration in Ruby's own core or stdlib signatures, a global the
|
|
699
|
+
# interpreter sets; a project's or a gem's declaration then answers nil.
|
|
700
|
+
def global_for_name(name, builtin: false)
|
|
701
|
+
return nil if rbs_loader.nil?
|
|
702
|
+
return nil if builtin && !rbs_loader.core_or_stdlib_global?(name)
|
|
703
|
+
|
|
704
|
+
rbs_loader.global_type(name)
|
|
705
|
+
end
|
|
706
|
+
|
|
699
707
|
# Returns true when the constant name is known to either the static registry or the RBS loader. Useful
|
|
700
708
|
# for callers that only need a presence check without materialising a type carrier.
|
|
701
709
|
def class_known?(name)
|