rigortype 0.3.9 → 0.4.0
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 +2 -2
- 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/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/manual/02-cli-reference.md +63 -2
- data/docs/manual/03-configuration.md +7 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/10-mcp-server.md +3 -2
- 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 +323 -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 +520 -49
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_session.rb +36 -4
- 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 +1 -0
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +20 -11
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
- data/lib/rigor/analysis/runner.rb +278 -20
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +53 -10
- 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 +6 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/incremental_snapshot.rb +33 -1
- 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 +0 -1
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- 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.rb +21 -3
- data/lib/rigor/configuration/severity_profile.rb +19 -3
- data/lib/rigor/configuration.rb +66 -3
- 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 +43 -31
- 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/lockfile_resolver.rb +17 -0
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_loader.rb +399 -133
- data/lib/rigor/environment.rb +103 -38
- 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 +73 -27
- 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 +387 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- 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/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1532 -496
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -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 +447 -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 +167 -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 +881 -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/multi_target_binder.rb +340 -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 +540 -120
- 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 +3 -2
- 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.rb +4615 -551
- data/lib/rigor/inference/statement_evaluator.rb +3176 -490
- 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 +168 -5
- 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/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.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +3 -1
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +1 -2
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +19 -6
- data/lib/rigor/reflection.rb +72 -105
- data/lib/rigor/scope/discovery_index.rb +135 -2
- data/lib/rigor/scope.rb +859 -49
- 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 +558 -31
- 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 +88 -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-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 +12 -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 +1 -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 +135 -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 +9 -7
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +33 -8
- 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 +108 -1
|
@@ -4,7 +4,9 @@ require "rbs"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../type"
|
|
6
6
|
require_relative "../inference/rbs_type_translator"
|
|
7
|
+
require_relative "../builtins/imported_refinements"
|
|
7
8
|
require_relative "rbs_hierarchy"
|
|
9
|
+
require_relative "member_consistency"
|
|
8
10
|
|
|
9
11
|
module Rigor
|
|
10
12
|
class Environment
|
|
@@ -42,6 +44,19 @@ module Rigor
|
|
|
42
44
|
# cache HIT. Never a real path, so it must never be reported as one (issue #696).
|
|
43
45
|
CACHED_LOCATION_BUFFER_NAME = "<cached>"
|
|
44
46
|
|
|
47
|
+
# Issue #527 — the two directory trees whose `.rbs` files ARE Ruby's own surface: the `rbs` gem's
|
|
48
|
+
# `core/` and its bundled `stdlib/` repository. {#core_stdlib_classes} tests a declaration's file
|
|
49
|
+
# against them. Resolved once at load time, on the main Ractor: reading these constants walks a
|
|
50
|
+
# chain of non-`Ractor.shareable?` module constants and raises `Ractor::IsolationError` inside a
|
|
51
|
+
# worker (see {#prewarm}). Empty when the rbs version in use does not expose them, which switches
|
|
52
|
+
# the feature off rather than widening it.
|
|
53
|
+
CORE_STDLIB_ROOTS = begin
|
|
54
|
+
[::RBS::EnvironmentLoader::DEFAULT_CORE_ROOT, ::RBS::Repository::DEFAULT_STDLIB_ROOT]
|
|
55
|
+
.compact.map { |root| "#{root}/".freeze }.freeze
|
|
56
|
+
rescue StandardError
|
|
57
|
+
[].freeze
|
|
58
|
+
end
|
|
59
|
+
|
|
45
60
|
# Cap on how many quarantined `signature_paths:` files {#warn_about_quarantined_signatures} lists by name
|
|
46
61
|
# before collapsing the tail to "… and N more" — a broken generator can emit many, and a wall of parse
|
|
47
62
|
# errors buries the signal.
|
|
@@ -83,9 +98,13 @@ module Rigor
|
|
|
83
98
|
# mastodon coverage note). Per-file loading quarantines the broken file instead. Vendored / core-overlay
|
|
84
99
|
# sigs are Rigor-shipped and trusted, so they stay on the loader's fast batch path.
|
|
85
100
|
add_bundled_signatures(rbs_loader, loaded_libraries.to_set(&:to_s))
|
|
86
|
-
env = RBS::Environment.from_loader(rbs_loader)
|
|
101
|
+
env = unload_upstream_core_shims(RBS::Environment.from_loader(rbs_loader), rbs_loader)
|
|
87
102
|
project_files = project_sig_files(signature_paths)
|
|
88
|
-
|
|
103
|
+
# Issue #1075 — decided from the inputs before either side enters `env`: the consistency rule may
|
|
104
|
+
# stand the `.rbs` member down, and that has to happen before its file is added.
|
|
105
|
+
consistency = member_consistency_for(project_files, virtual_rbs)
|
|
106
|
+
add_project_signatures(env, signature_paths, deferred_signature_paths,
|
|
107
|
+
standdowns: consistency.signature_standdowns)
|
|
89
108
|
# Issue #928 — the capability-role catalog goes in AFTER the project's own signatures, per
|
|
90
109
|
# declaration, so a project that already declares one of these interface names keeps its own
|
|
91
110
|
# (`add_capability_role_signatures`). The rest of the bundled RBS cannot be ordered that way (it
|
|
@@ -93,10 +112,9 @@ module Rigor
|
|
|
93
112
|
# instead — and losing a whole `sig/` file to a name as ordinary as `_Closable` is exactly what
|
|
94
113
|
# shipping a top-level interface catalog would otherwise cost.
|
|
95
114
|
add_capability_role_signatures(env)
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
add_virtual_rbs(env, virtual_rbs, sig_member_owners: signature_member_owners(project_files, virtual_rbs))
|
|
115
|
+
# Issues #824 / #1075 — every member a `.rbs` file also declares was ranked above; the inline side of
|
|
116
|
+
# each pair the `.rbs` won stands down here ({.add_virtual_rbs}), so the two never collide.
|
|
117
|
+
add_virtual_rbs(env, virtual_rbs, inline_standdowns: consistency.inline_standdowns)
|
|
100
118
|
synthesize_missing_namespaces(env)
|
|
101
119
|
# Issue #777 — resolve-time backstop: also unload colliding PROJECT buffers (class-vs-module /
|
|
102
120
|
# constant redeclarations against bundled RBS). Virtual culprits are preferred when both appear.
|
|
@@ -155,6 +173,55 @@ module Rigor
|
|
|
155
173
|
false
|
|
156
174
|
end
|
|
157
175
|
|
|
176
|
+
# Issue #1109 — the `rbs` gem (a `DEFAULT_LIBRARIES` entry, so in every run) ships the shims its
|
|
177
|
+
# own Steep check needs under `sig/shims/`, and one of them reopens a core module: `enumerable.rbs`
|
|
178
|
+
# prepends a `(2) -> Enumerator[[Elem, Elem], void]` overload to `Enumerable#each_slice`. That is
|
|
179
|
+
# a claim about rbs's own call sites, not about Ruby — `[1, 2, 3].each_slice(2)` yields `[3]` last
|
|
180
|
+
# — and it leaked into every analysed project: the blockless form typed as a fixed pair (so a
|
|
181
|
+
# `nil` guard on the second slot folded away) and the block form returned `void` instead of the
|
|
182
|
+
# receiver. Unloading the file leaves core's `Enumerator[Array[Elem], self]`, which agrees with the
|
|
183
|
+
# block form's `Array[Elem]`. The other shims (`bundler.rbs`, `rubygems.rbs`) only add missing
|
|
184
|
+
# declarations and stay. Fails soft to the env as built: a relocated or absent shim, or an rbs API
|
|
185
|
+
# shape this code does not recognise, keeps the pre-#1109 behaviour rather than failing the build.
|
|
186
|
+
# Because errors are rescued, the guard spec in `rbs_loader_spec.rb` is what catches a broken unload,
|
|
187
|
+
# so it must stay green on both lines of the RBS compatibility matrix.
|
|
188
|
+
RBS_LIBRARY = "rbs"
|
|
189
|
+
private_constant :RBS_LIBRARY
|
|
190
|
+
UNSOUND_CORE_SHIMS = %w[shims/enumerable.rbs].freeze
|
|
191
|
+
private_constant :UNSOUND_CORE_SHIMS
|
|
192
|
+
|
|
193
|
+
def unload_upstream_core_shims(env, rbs_loader)
|
|
194
|
+
return env unless env.respond_to?(:unload)
|
|
195
|
+
|
|
196
|
+
shim_names = upstream_core_shim_names(rbs_loader)
|
|
197
|
+
return env if shim_names.empty?
|
|
198
|
+
|
|
199
|
+
buffers = loaded_signature_buffers(env)&.select { |buffer| shim_names.include?(buffer.name.to_s) }
|
|
200
|
+
buffers.nil? || buffers.empty? ? env : env.unload(buffers)
|
|
201
|
+
rescue StandardError
|
|
202
|
+
env
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# rbs 4.x lists its sources through `each_rbs_source`; rbs 3.x keys `signatures` by `Buffer`, and its
|
|
206
|
+
# `unload` matches those very objects with `include?`, so the env's own keys are what it must receive.
|
|
207
|
+
def loaded_signature_buffers(env)
|
|
208
|
+
if env.respond_to?(:each_rbs_source)
|
|
209
|
+
env.each_rbs_source.map(&:buffer)
|
|
210
|
+
elsif env.respond_to?(:signatures)
|
|
211
|
+
env.signatures.keys
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def upstream_core_shim_names(rbs_loader)
|
|
216
|
+
names = Set.new
|
|
217
|
+
rbs_loader.each_dir do |source, dir|
|
|
218
|
+
next unless source.respond_to?(:name) && source.name == RBS_LIBRARY
|
|
219
|
+
|
|
220
|
+
UNSOUND_CORE_SHIMS.each { |relative| names << Pathname(dir).join(relative).to_s }
|
|
221
|
+
end
|
|
222
|
+
names
|
|
223
|
+
end
|
|
224
|
+
|
|
158
225
|
# True when `content` parses as an RBS signature. {#virtual_rbs_collision_quarantined} uses this to
|
|
159
226
|
# tell a collision-dropped virtual entry (parses, but absent from the env) from a parse-failed one
|
|
160
227
|
# (the synthesizer's own WD6 skip, reported separately).
|
|
@@ -333,16 +400,21 @@ module Rigor
|
|
|
333
400
|
# Buffer names are the file's absolute path (matching {.project_sig_files}) so {.project_entry?} — which
|
|
334
401
|
# attributes a `class_decls` entry to the project by buffer name — still recognises these declarations.
|
|
335
402
|
# Sorted for a deterministic add order (the env feeds the cache, ADR-54).
|
|
336
|
-
|
|
403
|
+
#
|
|
404
|
+
# @param standdowns — issue #1075 — `{signature_path => Set[[class, method, kind]]}`, the members an
|
|
405
|
+
# inline declaration displaced because it was the more precise side of a consistent pair
|
|
406
|
+
# ({MemberConsistency::Resolution#signature_standdowns}). They are removed before the file is added.
|
|
407
|
+
def add_project_signatures(env, signature_paths, deferred_paths = [], standdowns: {})
|
|
337
408
|
deferred = project_sig_files(deferred_paths)
|
|
338
409
|
(project_sig_files(signature_paths) - deferred).sort.each do |file|
|
|
339
410
|
parsed = parse_signature_file(file)
|
|
340
411
|
next if parsed.nil? # quarantined (unparseable) or unreadable — skip so the env survives
|
|
341
412
|
|
|
342
413
|
buffer, directives, decls = parsed
|
|
414
|
+
strip_members!(decls, standdowns[file]) if standdowns.key?(file)
|
|
343
415
|
add_project_parsed_decls(env, buffer, directives, decls)
|
|
344
416
|
end
|
|
345
|
-
add_deferred_signatures(env, deferred)
|
|
417
|
+
add_deferred_signatures(env, deferred, standdowns)
|
|
346
418
|
end
|
|
347
419
|
|
|
348
420
|
# Issue #777 — add one project signature transactionally. `RBS::Environment#add_source` appends to
|
|
@@ -380,7 +452,7 @@ module Rigor
|
|
|
380
452
|
# (rigor-activerecord's is `relation.rbs`, declaring exactly `Relation`), so dropping the file drops
|
|
381
453
|
# exactly the conflicting declaration. Splitting a plugin file that mixes a colliding class with
|
|
382
454
|
# innocent ones is the change to make if that ever stops being true.
|
|
383
|
-
def add_deferred_signatures(env, deferred_files)
|
|
455
|
+
def add_deferred_signatures(env, deferred_files, standdowns = {})
|
|
384
456
|
deferred_files.sort.each do |file|
|
|
385
457
|
parsed = parse_signature_file(file)
|
|
386
458
|
next if parsed.nil?
|
|
@@ -388,6 +460,7 @@ module Rigor
|
|
|
388
460
|
buffer, directives, decls = parsed
|
|
389
461
|
next if generic_arity_conflict(env, decls)
|
|
390
462
|
|
|
463
|
+
strip_members!(decls, standdowns[file]) if standdowns.key?(file)
|
|
391
464
|
add_project_parsed_decls(env, buffer, directives, decls)
|
|
392
465
|
end
|
|
393
466
|
end
|
|
@@ -827,12 +900,13 @@ module Rigor
|
|
|
827
900
|
# provenance (RBS parse errors cite it) — it is not a real file path. Per WD6 the synthesizer-emit
|
|
828
901
|
# path is responsible for catching its own parse errors and returning `nil` rather than garbage; this
|
|
829
902
|
# method assumes its input is parseable and only rescues `RBS::ParsingError` as a fail-soft.
|
|
830
|
-
# @param
|
|
831
|
-
#
|
|
832
|
-
#
|
|
833
|
-
# costing the whole class its method surface
|
|
834
|
-
# silent: {RbsLoader#
|
|
835
|
-
|
|
903
|
+
# @param inline_standdowns — issue #1075 — the `[class, method, kind]` keys whose inline member
|
|
904
|
+
# stands down because a project `.rbs` declares the same member and ADR-112 WD5's consistency rule
|
|
905
|
+
# let the `.rbs` bind ({MemberConsistency::Resolution#inline_standdowns}). Removing them before the
|
|
906
|
+
# declaration is added keeps the two from colliding and costing the whole class its method surface.
|
|
907
|
+
# The outcome is reported, never silent: {RbsLoader#member_consistency} re-derives the same records
|
|
908
|
+
# for the run's rows.
|
|
909
|
+
def add_virtual_rbs(env, virtual_rbs, inline_standdowns: Set.new)
|
|
836
910
|
return if virtual_rbs.nil? || virtual_rbs.empty?
|
|
837
911
|
|
|
838
912
|
virtual_rbs.each do |filename, content|
|
|
@@ -843,7 +917,7 @@ module Rigor
|
|
|
843
917
|
|
|
844
918
|
buffer = ::RBS::Buffer.new(name: filename.to_s, content: content.to_s)
|
|
845
919
|
_, directives, decls = ::RBS::Parser.parse_signature(buffer)
|
|
846
|
-
|
|
920
|
+
strip_members!(decls, inline_standdowns) unless inline_standdowns.empty?
|
|
847
921
|
add_parsed_decls(env, buffer, directives, decls)
|
|
848
922
|
rescue ::RBS::BaseError
|
|
849
923
|
# WD6 fail-soft: a single broken virtual RBS contribution does not pull the whole env down — for
|
|
@@ -863,7 +937,8 @@ module Rigor
|
|
|
863
937
|
end
|
|
864
938
|
end
|
|
865
939
|
|
|
866
|
-
# Issue #
|
|
940
|
+
# Issue #1075 / ADR-112 WD5 — the member-level half of inline-vs-`sig/` consistency, which replaced
|
|
941
|
+
# #824's "`sig/` wins per member" (ADR-32 WD13).
|
|
867
942
|
#
|
|
868
943
|
# A method declared BOTH by a project `.rbs` and by an inline annotation in its own `.rb` lands twice
|
|
869
944
|
# in one `RBS::Environment`: rbs merges the two sources into a single `ClassEntry` and ranks neither,
|
|
@@ -872,135 +947,149 @@ module Rigor
|
|
|
872
947
|
# it, real methods and typos alike, reads `Dynamic[top]`. Measured on Rigor's own tree while
|
|
873
948
|
# [#779](https://github.com/rigortype/rigor/pull/779) was evaluated: 17 of 234 annotated files
|
|
874
949
|
# overlapped `sig/`, and the collision took 44 classes down. Steep 2.1 / rbs 4.1 have no precedence
|
|
875
|
-
# rule to follow here (
|
|
876
|
-
#
|
|
877
|
-
#
|
|
950
|
+
# rule to follow here (ADR-32 WD13), so exactly one of the two members has to go before the
|
|
951
|
+
# environment is built. {MemberConsistency} decides which: the more precise of two consistent
|
|
952
|
+
# declarations binds, and on a contradiction or an undecidable pair the `.rbs` does.
|
|
878
953
|
#
|
|
879
|
-
# @return
|
|
880
|
-
#
|
|
881
|
-
#
|
|
882
|
-
#
|
|
954
|
+
# @return a {MemberConsistency::Resolution}. {MemberConsistency::EMPTY} — and free — for a project
|
|
955
|
+
# that contributes no inline RBS, which is every project without a `source_rbs_synthesizer:` plugin.
|
|
956
|
+
#
|
|
957
|
+
# Derived from the loader's INPUTS — the synthesized sources and the project's signature files —
|
|
958
|
+
# never from a built environment, for {RbsLoader#quarantined_signatures}' reason turned inside out: a
|
|
959
|
+
# cache HIT never runs the build, and a stripped member leaves no trace in the environment to read
|
|
960
|
+
# back. The build and {RbsLoader#member_consistency} call this same function on the same two inputs,
|
|
961
|
+
# so the members they strip and the rows they report agree warm and cold.
|
|
962
|
+
def member_consistency_for(project_files, virtual_rbs, proof: nil)
|
|
963
|
+
return MemberConsistency::EMPTY if virtual_rbs.nil? || virtual_rbs.empty?
|
|
964
|
+
|
|
965
|
+
signature_members, shadowable = signature_member_index(project_files, virtual_rbs)
|
|
966
|
+
inline_members = inline_member_entries(virtual_rbs, all: !signature_members.empty?)
|
|
967
|
+
MemberConsistency.resolve(signature_members, inline_members, shadowable: shadowable, proof: proof)
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# `[owners, shadowable]`. `owners` is `{[class_name, method_name, kind] => [signature_path, member,
|
|
971
|
+
# visibility]}` for every method-shaped member the project's own `signature_paths:` files declare and
|
|
972
|
+
# the inline sources also name; the first file in sorted order owns a key two `.rbs` files declare
|
|
973
|
+
# (that pair fails the definition build anyway). `shadowable` is every `::` segment of every class /
|
|
974
|
+
# module name the inline sources and the signature files declare — the names a relative reference
|
|
975
|
+
# might resolve to inside an enclosing namespace ({MemberConsistency::Comparator#faithful?}).
|
|
883
976
|
#
|
|
884
977
|
# The parse is scoped by a substring pre-filter over each file's text: a declaration has to spell its
|
|
885
978
|
# own name, so a signature file mentioning none of the names the inline sources declare cannot
|
|
886
979
|
# collide with them. The filter over-approximates (it matches a name in a comment or a type position
|
|
887
980
|
# too), so it can only cost an unnecessary parse, never miss a collision.
|
|
888
|
-
def
|
|
889
|
-
return {} if project_files.nil? || virtual_rbs.nil? || virtual_rbs.empty?
|
|
890
|
-
|
|
981
|
+
def signature_member_index(project_files, virtual_rbs)
|
|
891
982
|
names = inline_declared_name_segments(virtual_rbs)
|
|
892
|
-
return {} if names.empty?
|
|
983
|
+
return [{}, names] if project_files.nil? || project_files.empty? || names.empty?
|
|
893
984
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
985
|
+
shadowable = names.dup
|
|
986
|
+
owners = project_files.to_a.sort.each_with_object({}) do |file, index|
|
|
987
|
+
content = read_signature_text(file)
|
|
988
|
+
next if content.nil?
|
|
897
989
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
end
|
|
990
|
+
merge_declared_segments(content, shadowable)
|
|
991
|
+
next if names.none? { |name| content.include?(name) }
|
|
992
|
+
|
|
993
|
+
index_signature_members(index, file, parse_signature_source(file, content))
|
|
903
994
|
end
|
|
995
|
+
[owners, shadowable]
|
|
904
996
|
end
|
|
905
997
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
# set independent of cache state.
|
|
914
|
-
def inline_member_standdowns_for(signature_paths:, virtual_rbs:)
|
|
915
|
-
owners = signature_member_owners(project_sig_files(signature_paths), virtual_rbs)
|
|
916
|
-
return [].freeze if owners.empty?
|
|
917
|
-
|
|
918
|
-
records = virtual_rbs.flat_map do |name, content|
|
|
919
|
-
next [] if content.nil? || content.empty? || invalid_encoding?(content.to_s)
|
|
920
|
-
|
|
921
|
-
decls = parse_signature_source(name, content.to_s)
|
|
922
|
-
decls.nil? ? [] : member_standdowns_in(name, decls, owners)
|
|
998
|
+
def index_signature_members(index, file, decls)
|
|
999
|
+
return if decls.nil?
|
|
1000
|
+
|
|
1001
|
+
each_declared_member(decls) do |class_name, member, visibility|
|
|
1002
|
+
member_method_keys(member).each do |method_name, kind|
|
|
1003
|
+
index[[class_name, method_name, kind]] ||= [file, member, visibility].freeze
|
|
1004
|
+
end
|
|
923
1005
|
end
|
|
924
|
-
records.uniq.sort_by { |record| [record[0], record[1].to_s, record[2].to_s] }.freeze
|
|
925
1006
|
end
|
|
926
1007
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1008
|
+
# The directive heads a refinement {MemberConsistency.refinement_conflicts} checks, used to skip
|
|
1009
|
+
# parsing an inline source that carries none when no `.rbs` overlaps it. Narrower than `rigor:v1:` on
|
|
1010
|
+
# purpose: rbs-inline's output marks every skeleton `%a{rigor:v1:inferred-return}`, so that prefix is
|
|
1011
|
+
# in nearly every synthesized source.
|
|
1012
|
+
REFINEMENT_DIRECTIVE_MARKS = ["rigor:v1:return:", "rigor:v1:param:"].freeze
|
|
1013
|
+
private_constant :REFINEMENT_DIRECTIVE_MARKS
|
|
933
1014
|
|
|
934
|
-
|
|
1015
|
+
# `[[virtual_name, class_name, member], ...]` for the inline members {MemberConsistency.resolve}
|
|
1016
|
+
# reads: every member when a `.rbs` file may overlap them (`all:`), otherwise only the members of a
|
|
1017
|
+
# source that carries a `rigor:v1:return:` / `rigor:v1:param:` refinement, which is checked against
|
|
1018
|
+
# its own declared type. Entries {.add_virtual_rbs} would skip are skipped here too.
|
|
1019
|
+
def inline_member_entries(virtual_rbs, all:)
|
|
1020
|
+
virtual_rbs.each_with_object([]) do |(name, content), entries|
|
|
1021
|
+
content = content.to_s
|
|
1022
|
+
next if content.empty? || invalid_encoding?(content)
|
|
1023
|
+
next unless all || REFINEMENT_DIRECTIVE_MARKS.any? { |mark| content.include?(mark) }
|
|
1024
|
+
|
|
1025
|
+
decls = parse_signature_source(name, content)
|
|
1026
|
+
next if decls.nil?
|
|
1027
|
+
|
|
1028
|
+
each_declared_member(decls) do |class_name, member, visibility|
|
|
1029
|
+
entries << [name.to_s, class_name, member, visibility]
|
|
935
1030
|
end
|
|
936
1031
|
end
|
|
937
|
-
records
|
|
938
1032
|
end
|
|
939
1033
|
|
|
940
|
-
# Removes, in place, every member `
|
|
941
|
-
# declarations are descended into after the rejection, and a declaration member contributes no
|
|
942
|
-
# key, so nesting is never itself removed.
|
|
943
|
-
def
|
|
1034
|
+
# Removes, in place, every member contributing a key in `keys` (a Set of `[class, method, kind]`).
|
|
1035
|
+
# Nested declarations are descended into after the rejection, and a declaration member contributes no
|
|
1036
|
+
# method key, so nesting is never itself removed.
|
|
1037
|
+
def strip_members!(decls, keys, prefix = [])
|
|
944
1038
|
Array(decls).each do |decl|
|
|
945
1039
|
next unless declaration_with_members?(decl)
|
|
946
1040
|
|
|
947
1041
|
inner = prefix + [decl.name.to_s.delete_prefix("::")]
|
|
948
1042
|
class_name = inner.join("::")
|
|
949
1043
|
decl.members.reject! do |member|
|
|
950
|
-
member_method_keys(member).any? { |method_name, kind|
|
|
1044
|
+
member_method_keys(member).any? { |method_name, kind| keys.include?([class_name, method_name, kind]) }
|
|
951
1045
|
end
|
|
952
|
-
|
|
1046
|
+
strip_members!(decl.members, keys, inner)
|
|
953
1047
|
end
|
|
954
1048
|
end
|
|
955
1049
|
|
|
956
|
-
# Yields `[class_name, member]` for every member of every class / module declaration in
|
|
957
|
-
# descending through nesting. The enclosing path is accumulated and the name rendered
|
|
958
|
-
# so a `module Foo; class Bar` and a `class Foo::Bar` elsewhere key the same — which is
|
|
959
|
-
# `sig/` declaration and an inline one comparable at all.
|
|
1050
|
+
# Yields `[class_name, member, visibility]` for every member of every class / module declaration in
|
|
1051
|
+
# `decls`, descending through nesting. The enclosing path is accumulated and the name rendered
|
|
1052
|
+
# `::`-stripped, so a `module Foo; class Bar` and a `class Foo::Bar` elsewhere key the same — which is
|
|
1053
|
+
# what makes a `sig/` declaration and an inline one comparable at all. `visibility` is the member's
|
|
1054
|
+
# own (`private def x`) or else the one the enclosing `private` / `public` section sets; a singleton
|
|
1055
|
+
# method takes no section, as in Ruby.
|
|
960
1056
|
def each_declared_member(decls, prefix = [], &block)
|
|
961
1057
|
Array(decls).each do |decl|
|
|
962
1058
|
next unless declaration_with_members?(decl)
|
|
963
1059
|
|
|
964
1060
|
inner = prefix + [decl.name.to_s.delete_prefix("::")]
|
|
965
1061
|
class_name = inner.join("::")
|
|
966
|
-
|
|
1062
|
+
section = :public
|
|
1063
|
+
decl.members.each do |member|
|
|
1064
|
+
case member
|
|
1065
|
+
when ::RBS::AST::Members::Private then section = :private
|
|
1066
|
+
when ::RBS::AST::Members::Public then section = :public
|
|
1067
|
+
else block.call(class_name, member, member_visibility(member, section))
|
|
1068
|
+
end
|
|
1069
|
+
end
|
|
967
1070
|
each_declared_member(decl.members, inner, &block)
|
|
968
1071
|
end
|
|
969
1072
|
end
|
|
970
1073
|
|
|
1074
|
+
def member_visibility(member, section)
|
|
1075
|
+
own = member.respond_to?(:visibility) ? member.visibility : nil
|
|
1076
|
+
return own if own
|
|
1077
|
+
|
|
1078
|
+
member.respond_to?(:kind) && member.kind == :singleton ? :public : section
|
|
1079
|
+
end
|
|
1080
|
+
|
|
971
1081
|
def declaration_with_members?(decl)
|
|
972
1082
|
(decl.is_a?(::RBS::AST::Declarations::Module) || decl.is_a?(::RBS::AST::Declarations::Class)) &&
|
|
973
1083
|
decl.respond_to?(:members)
|
|
974
1084
|
end
|
|
975
1085
|
|
|
976
|
-
#
|
|
977
|
-
#
|
|
978
|
-
# whether two members collide. An attribute contributes its reader and / or writer name (`foo`,
|
|
979
|
-
# `foo=`), an alias its new name, and `def self?.x` both sides. An `overloading?` member (`def x: ...
|
|
980
|
-
# | ...`) contributes NOTHING: rbs files those under `overloads` rather than `originals`, so they are
|
|
981
|
-
# designed to compose with an existing declaration and must not stand down against it.
|
|
1086
|
+
# {MemberConsistency.member_method_keys} — the `[method_name, kind]` pairs a member contributes to a
|
|
1087
|
+
# definition build, which is what decides whether two members collide.
|
|
982
1088
|
def member_method_keys(member)
|
|
983
|
-
|
|
984
|
-
when ::RBS::AST::Members::MethodDefinition then method_definition_keys(member)
|
|
985
|
-
when ::RBS::AST::Members::AttrReader then [[member.name, member.kind]]
|
|
986
|
-
when ::RBS::AST::Members::AttrWriter then [[:"#{member.name}=", member.kind]]
|
|
987
|
-
when ::RBS::AST::Members::AttrAccessor then [[member.name, member.kind], [:"#{member.name}=", member.kind]]
|
|
988
|
-
when ::RBS::AST::Members::Alias then [[member.new_name, member.kind]]
|
|
989
|
-
else []
|
|
990
|
-
end
|
|
1089
|
+
MemberConsistency.member_method_keys(member)
|
|
991
1090
|
end
|
|
992
1091
|
|
|
993
|
-
|
|
994
|
-
return [] if member.respond_to?(:overloading?) && member.overloading?
|
|
995
|
-
|
|
996
|
-
case member.kind
|
|
997
|
-
when :instance then [[member.name, :instance]]
|
|
998
|
-
when :singleton then [[member.name, :singleton]]
|
|
999
|
-
else [[member.name, :instance], [member.name, :singleton]] # `def self?.x` defines both sides
|
|
1000
|
-
end
|
|
1001
|
-
end
|
|
1002
|
-
|
|
1003
|
-
# A class / module declaration header, as the pre-filter in {.signature_member_owners} reads it out of
|
|
1092
|
+
# A class / module declaration header, as the pre-filter in {.signature_member_index} reads it out of
|
|
1004
1093
|
# synthesized RBS text.
|
|
1005
1094
|
INLINE_DECLARATION_NAME = /^[ \t]*(?:class|module)[ \t]+([A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)/
|
|
1006
1095
|
private_constant :INLINE_DECLARATION_NAME
|
|
@@ -1016,18 +1105,20 @@ module Rigor
|
|
|
1016
1105
|
content = content.to_s
|
|
1017
1106
|
next if content.empty? || invalid_encoding?(content)
|
|
1018
1107
|
|
|
1019
|
-
content
|
|
1108
|
+
merge_declared_segments(content, acc)
|
|
1020
1109
|
end
|
|
1021
1110
|
end
|
|
1022
1111
|
|
|
1023
|
-
#
|
|
1024
|
-
#
|
|
1025
|
-
def
|
|
1026
|
-
content
|
|
1027
|
-
|
|
1028
|
-
return nil if names.none? { |name| content.include?(name) }
|
|
1112
|
+
# Adds every `::` segment of every class / module header in `content` to `segments`. The pattern has
|
|
1113
|
+
# one capture group, which every match fills.
|
|
1114
|
+
def merge_declared_segments(content, segments)
|
|
1115
|
+
content.scan(INLINE_DECLARATION_NAME) { segments.merge(Regexp.last_match(1).to_s.split("::")) }
|
|
1116
|
+
end
|
|
1029
1117
|
|
|
1030
|
-
|
|
1118
|
+
# One project signature file's text, or nil when it is unreadable or not valid UTF-8.
|
|
1119
|
+
def read_signature_text(file)
|
|
1120
|
+
content = File.read(file, encoding: "UTF-8")
|
|
1121
|
+
invalid_encoding?(content) ? nil : content
|
|
1031
1122
|
rescue Errno::ENOENT, Errno::EISDIR, Errno::EACCES
|
|
1032
1123
|
nil
|
|
1033
1124
|
end
|
|
@@ -1056,9 +1147,10 @@ module Rigor
|
|
|
1056
1147
|
private_constant :VENDORED_GEM_SIGS_ROOT
|
|
1057
1148
|
|
|
1058
1149
|
# Rigor-owned core-overlay RBS (`data/core_overlay/`). Reopens Ruby core classes to add methods
|
|
1059
|
-
# upstream `ruby/rbs` omits but which every concrete value answers at runtime
|
|
1060
|
-
#
|
|
1061
|
-
#
|
|
1150
|
+
# upstream `ruby/rbs` omits but which every concrete value answers at runtime. A direct `def` that
|
|
1151
|
+
# another source also declares is a `DuplicatedMethodDefinitionError`, not a precedence question, so a
|
|
1152
|
+
# method a project `sig/` or a later rbs is likely to declare goes on a module the class extends
|
|
1153
|
+
# (`process.rbs`). Public so the cache descriptor can digest these files into the env-blob key.
|
|
1062
1154
|
CORE_OVERLAY_SIGS_ROOT = File.expand_path(
|
|
1063
1155
|
"../../../data/core_overlay",
|
|
1064
1156
|
__dir__
|
|
@@ -1128,6 +1220,10 @@ module Rigor
|
|
|
1128
1220
|
# `core_overlay/pathname.rbs` also carries an `| ...` continuation but is deliberately NOT listed:
|
|
1129
1221
|
# its base (`Pathname#expand_path`) lives in rbs's `core/pathname.rbs` on every supported rbs
|
|
1130
1222
|
# release (3.10 / 4.0 / 4.1), so the continuation is safe — and applicable — with no library loaded.
|
|
1223
|
+
# `core_overlay/string_io.rbs` re-opens a class the `stringio` library declares yet is NOT listed
|
|
1224
|
+
# either: `RBS::EnvironmentLoader#load` adds that library itself whenever it loads core (rbs 3.7+),
|
|
1225
|
+
# and earlier releases declare `StringIO` in core, so gating would only drop its `Enumerable` mixin
|
|
1226
|
+
# where `StringIO` is present anyway.
|
|
1131
1227
|
#
|
|
1132
1228
|
# Keyed by `data/vendored_gem_sigs/` directory basename.
|
|
1133
1229
|
LIBRARY_SUPPLEMENT_VENDORED_DIRS = {
|
|
@@ -1144,12 +1240,23 @@ module Rigor
|
|
|
1144
1240
|
}.freeze
|
|
1145
1241
|
private_constant :LIBRARY_SUPPLEMENT_CORE_OVERLAYS
|
|
1146
1242
|
|
|
1243
|
+
# `data/core_overlay/` files that fill a hole only one `rbs` release line has, keyed by file basename
|
|
1244
|
+
# to the `rbs` version requirement they load under. Each carries an `| ...` continuation of overloads a
|
|
1245
|
+
# later line declares upstream, so loading it there would add a second copy of them. The env-cache key
|
|
1246
|
+
# already carries `RBS::VERSION` ({Cache::RbsDescriptor.rbs_gem_entry}), so switching lines re-keys.
|
|
1247
|
+
# The requirement is a String, not a `Gem::Requirement`, so the constant stays `Ractor.shareable?`.
|
|
1248
|
+
RBS_LINE_CORE_OVERLAYS = {
|
|
1249
|
+
"hash_rbs3.rbs" => "< 4.0"
|
|
1250
|
+
}.freeze
|
|
1251
|
+
private_constant :RBS_LINE_CORE_OVERLAYS
|
|
1252
|
+
|
|
1147
1253
|
# Adds the Rigor-shipped signature sources to `rbs_loader`: every `data/vendored_gem_sigs/<gem>/`
|
|
1148
|
-
# directory, then the `data/core_overlay/` files
|
|
1149
|
-
#
|
|
1150
|
-
#
|
|
1151
|
-
#
|
|
1152
|
-
# per-directory, because the `LIBRARY_SUPPLEMENT_CORE_OVERLAYS`
|
|
1254
|
+
# directory, then the `data/core_overlay/` files, which fill genuine holes only (e.g. `Numeric#to_f`/
|
|
1255
|
+
# `to_i`/`to_r`, which upstream RBS declares on the concrete subclasses but not on the abstract `Numeric`
|
|
1256
|
+
# that Rigor's arithmetic-chain widening produces). Load order does not settle a conflict: a method
|
|
1257
|
+
# both sides declare directly raises `DuplicatedMethodDefinitionError` for the whole class. The
|
|
1258
|
+
# overlay is added per-file, not per-directory, because the `LIBRARY_SUPPLEMENT_CORE_OVERLAYS` and
|
|
1259
|
+
# `RBS_LINE_CORE_OVERLAYS` files must be gated individually.
|
|
1153
1260
|
#
|
|
1154
1261
|
# @param loaded_library_names — libraries that actually resolved on this loader.
|
|
1155
1262
|
def add_bundled_signatures(rbs_loader, loaded_library_names)
|
|
@@ -1165,6 +1272,7 @@ module Rigor
|
|
|
1165
1272
|
dir.children.sort.each do |file|
|
|
1166
1273
|
next unless file.file? && file.extname == ".rbs"
|
|
1167
1274
|
next unless supplement_dependency_loaded?(LIBRARY_SUPPLEMENT_CORE_OVERLAYS, file, loaded_library_names)
|
|
1275
|
+
next unless rbs_line_matches?(file)
|
|
1168
1276
|
|
|
1169
1277
|
rbs_loader.add(path: file)
|
|
1170
1278
|
end
|
|
@@ -1180,6 +1288,13 @@ module Rigor
|
|
|
1180
1288
|
library.nil? || loaded_library_names.include?(library)
|
|
1181
1289
|
end
|
|
1182
1290
|
|
|
1291
|
+
# @param path — a `data/core_overlay/` file.
|
|
1292
|
+
# @return true when `path` is not tied to an `rbs` release line, or the running `rbs` is on its line.
|
|
1293
|
+
def rbs_line_matches?(path)
|
|
1294
|
+
requirement = RBS_LINE_CORE_OVERLAYS[path.basename.to_s]
|
|
1295
|
+
requirement.nil? || Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(::RBS::VERSION))
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1183
1298
|
# Rigor-owned per-gem RBS overlays (`data/gem_overlay/<gem>/`), ADR-72. Unlike the unconditional
|
|
1184
1299
|
# `core_overlay`, each gem's overlay is loaded ONLY when that gem is locked in the project's
|
|
1185
1300
|
# Gemfile.lock but ships no RBS of its own — {Environment.for_project} decides eligibility and passes
|
|
@@ -1362,7 +1477,7 @@ module Rigor
|
|
|
1362
1477
|
# names currently consulted: `:env`, `:env_loaded`, `:env_build_warned`, `:definition_build_warned`,
|
|
1363
1478
|
# `:definition_build_details`, `:definition_build_reported`, `:definition_build_failures`,
|
|
1364
1479
|
# `:definition_build_deferred_count`, `:definition_build_deferred_first`,
|
|
1365
|
-
# `:definition_build_summary_warned`, `:
|
|
1480
|
+
# `:definition_build_summary_warned`, `:member_consistency`, `:member_consistency_proven`, `:internal_demand`,
|
|
1366
1481
|
# `:internal_demand_status`, `:builder`, `:reflection`,
|
|
1367
1482
|
# `:instance_definitions_table`, `:singleton_definitions_table`.
|
|
1368
1483
|
# Constructed via `Hash.new` (NOT a `{ ... }` literal) so Rigor's `HashShape` narrowing doesn't
|
|
@@ -1457,11 +1572,94 @@ module Rigor
|
|
|
1457
1572
|
end
|
|
1458
1573
|
private :build_project_declared_classes
|
|
1459
1574
|
|
|
1575
|
+
# Issue #527 — the class / module names whose PRIMARY declaration lives in the `rbs` gem's own
|
|
1576
|
+
# `core/` or `stdlib/` tree, top-level prefix stripped.
|
|
1577
|
+
#
|
|
1578
|
+
# The distinction it draws is narrower than {#project_declared_classes}'s: not "who wrote this
|
|
1579
|
+
# signature" but "is this Ruby's own documented surface". A core / stdlib signature is the method
|
|
1580
|
+
# set every negative check rule already trusts for a direct receiver of the class, which is what
|
|
1581
|
+
# lets a Ruby-source SUBCLASS of one resolve its inherited calls there (`RbsDispatch`'s
|
|
1582
|
+
# `core_stdlib_ancestor_method`). A GEM's RBS is a different claim — routinely partial, which is
|
|
1583
|
+
# ADR-43's whole reason for declining blanket inherited resolution — so it is deliberately outside
|
|
1584
|
+
# this set even when the gem ships signatures and even when it is one of {DEFAULT_LIBRARIES}
|
|
1585
|
+
# (`prism`, `rbs` are gems whose signatures ship with the gem, not entries of rbs's `stdlib/`).
|
|
1586
|
+
#
|
|
1587
|
+
# Attribution is by buffer NAME, the same mechanism and the same limits as
|
|
1588
|
+
# {#project_declared_classes}: it survives the ADR-54 environment cache, and an old blob's
|
|
1589
|
+
# `<cached>` sentinel lands every class OUTSIDE the set — the feature switches off rather than
|
|
1590
|
+
# widening, which is the direction ADR-5 wants. Memoised per loader; one pass over `class_decls`.
|
|
1591
|
+
def core_stdlib_classes
|
|
1592
|
+
@state[:core_stdlib_classes] ||= build_core_stdlib_classes
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
def build_core_stdlib_classes
|
|
1596
|
+
environment = env
|
|
1597
|
+
return Set.new.freeze if environment.nil? || CORE_STDLIB_ROOTS.empty?
|
|
1598
|
+
|
|
1599
|
+
names = environment.class_decls.each_with_object(Set.new) do |(rbs_name, entry), acc|
|
|
1600
|
+
decl = self.class.primary_decl_for(entry)
|
|
1601
|
+
next if decl.nil?
|
|
1602
|
+
|
|
1603
|
+
path = self.class.declaration_buffer_name(decl)
|
|
1604
|
+
next if path.nil?
|
|
1605
|
+
|
|
1606
|
+
acc << rbs_name.to_s.delete_prefix("::") if CORE_STDLIB_ROOTS.any? { |root| path.start_with?(root) }
|
|
1607
|
+
end
|
|
1608
|
+
names.freeze
|
|
1609
|
+
rescue ::RBS::BaseError
|
|
1610
|
+
Set.new.freeze
|
|
1611
|
+
end
|
|
1612
|
+
private :build_core_stdlib_classes
|
|
1613
|
+
|
|
1614
|
+
# True when `class_name` is declared by Ruby core or a stdlib library (see {#core_stdlib_classes}).
|
|
1615
|
+
def core_or_stdlib_class?(class_name)
|
|
1616
|
+
core_stdlib_classes.include?(class_name.to_s.delete_prefix("::"))
|
|
1617
|
+
end
|
|
1618
|
+
|
|
1460
1619
|
# True when `class_name`'s RBS declaration is one the project wrote (see {#project_declared_classes}).
|
|
1461
1620
|
def project_declared_class?(class_name)
|
|
1462
1621
|
project_declared_classes.include?(class_name.to_s.delete_prefix("::"))
|
|
1463
1622
|
end
|
|
1464
1623
|
|
|
1624
|
+
# Issue #1362 — the translated type of the RBS declaration of the global variable `name` (`:$stdout`, declared
|
|
1625
|
+
# `IO` by the core signatures), or nil when no loaded signature declares it. Translated without an alias
|
|
1626
|
+
# expander, as {#constant_type} translates a constant, so `$DEBUG: boolish` reads `Dynamic[top]` rather than
|
|
1627
|
+
# `top`.
|
|
1628
|
+
def global_type(name)
|
|
1629
|
+
global_declarations[name.to_sym]&.first
|
|
1630
|
+
end
|
|
1631
|
+
|
|
1632
|
+
# True when `name`'s RBS declaration lives in the `rbs` gem's own `core/` or `stdlib/` tree: a global the
|
|
1633
|
+
# interpreter or a default library sets. Attributed by buffer name, as {#core_or_stdlib_class?} is, with the
|
|
1634
|
+
# same limit: an environment blob without buffer names answers false for every global.
|
|
1635
|
+
def core_or_stdlib_global?(name)
|
|
1636
|
+
global_declarations[name.to_sym]&.last || false
|
|
1637
|
+
end
|
|
1638
|
+
|
|
1639
|
+
# `name => [type, core_or_stdlib]` for every global the environment declares; a declaration whose type
|
|
1640
|
+
# translates to `bot` is left out. Memoised per loader: the core signatures declare about fifty.
|
|
1641
|
+
def global_declarations
|
|
1642
|
+
@state[:global_declarations] ||= build_global_declarations
|
|
1643
|
+
end
|
|
1644
|
+
private :global_declarations
|
|
1645
|
+
|
|
1646
|
+
def build_global_declarations
|
|
1647
|
+
environment = env
|
|
1648
|
+
return {}.freeze if environment.nil?
|
|
1649
|
+
|
|
1650
|
+
table = environment.global_decls.each_with_object({}) do |(name, entry), acc|
|
|
1651
|
+
type = Inference::RbsTypeTranslator.translate(entry.decl.type)
|
|
1652
|
+
next if type.is_a?(Type::Bot)
|
|
1653
|
+
|
|
1654
|
+
path = self.class.declaration_buffer_name(entry.decl)
|
|
1655
|
+
acc[name.to_sym] = [type, !path.nil? && CORE_STDLIB_ROOTS.any? { |root| path.start_with?(root) }].freeze
|
|
1656
|
+
end
|
|
1657
|
+
table.freeze
|
|
1658
|
+
rescue ::RBS::BaseError
|
|
1659
|
+
{}.freeze
|
|
1660
|
+
end
|
|
1661
|
+
private :build_global_declarations
|
|
1662
|
+
|
|
1465
1663
|
# The total RBS-environment build failure captured this run, or nil when the env built. Unlike
|
|
1466
1664
|
# {#quarantined_signatures} — which the env survives, one file lighter, and which is re-derived so a
|
|
1467
1665
|
# cache HIT reports it too — a total failure collapses the WHOLE env to nil. Project-vs-bundled
|
|
@@ -1495,9 +1693,10 @@ module Rigor
|
|
|
1495
1693
|
# definition-build failure leaves no trace in the env at all — the env is fine; it is the BUILD over it
|
|
1496
1694
|
# that raised — so the rescue is the only place that ever knows.
|
|
1497
1695
|
#
|
|
1498
|
-
# @return `[class_name, error_class_name,
|
|
1499
|
-
#
|
|
1500
|
-
#
|
|
1696
|
+
# @return `[class_name, error_class_name, member, conflicting_buffer_names,
|
|
1697
|
+
# unresolved_type_name_detail]`, one per class, in first-failure order (issue #997 added the fifth
|
|
1698
|
+
# slot; see {#unresolved_type_name_detail} — `nil` there for every failure kind but an unresolvable
|
|
1699
|
+
# type name). Empty for a healthy sig set, which is the common case.
|
|
1501
1700
|
def definition_build_failures
|
|
1502
1701
|
(@state[:definition_build_failures] || []).dup.freeze
|
|
1503
1702
|
end
|
|
@@ -1524,10 +1723,11 @@ module Rigor
|
|
|
1524
1723
|
end
|
|
1525
1724
|
end
|
|
1526
1725
|
|
|
1527
|
-
# Issue #
|
|
1528
|
-
# `signature_paths:`
|
|
1529
|
-
#
|
|
1530
|
-
#
|
|
1726
|
+
# Issue #1075 / ADR-112 WD5 — one {MemberConsistency::Record} per `(class, method, kind)` the project's
|
|
1727
|
+
# own `signature_paths:` and an inline (rbs-inline) source both declare, saying which side bound and
|
|
1728
|
+
# why, plus one per `rigor:v1:return:` / `rigor:v1:param:` refinement that exceeds its member's own
|
|
1729
|
+
# declared type. The rest of the file's annotations still bind either way, and the class keeps its
|
|
1730
|
+
# method surface instead of failing its definition build.
|
|
1531
1731
|
#
|
|
1532
1732
|
# The sibling of {#virtual_rbs_collision_quarantined} one granularity finer: that one names a whole
|
|
1533
1733
|
# inline FILE dropped for a class-level duplicate, this one names a single member. Both are derived
|
|
@@ -1535,12 +1735,15 @@ module Rigor
|
|
|
1535
1735
|
# derivation reads the loader's inputs rather than the built env, because a stripped member leaves no
|
|
1536
1736
|
# trace in the env at all.
|
|
1537
1737
|
#
|
|
1538
|
-
# @return
|
|
1539
|
-
#
|
|
1540
|
-
#
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1738
|
+
# @return the records, sorted. Empty (and free) whenever the project contributes no inline RBS.
|
|
1739
|
+
#
|
|
1740
|
+
# @param proof — a {MemberConsistency::RbsProof} over this loader, without which no pair is reported
|
|
1741
|
+
# as a contradiction (only the severity of a row depends on it, never which declaration binds).
|
|
1742
|
+
def member_consistency(proof: nil)
|
|
1743
|
+
key = proof ? :member_consistency_proven : :member_consistency
|
|
1744
|
+
@state[key] ||= self.class.member_consistency_for(
|
|
1745
|
+
self.class.project_sig_files(@signature_paths), @virtual_rbs, proof: proof
|
|
1746
|
+
).records
|
|
1544
1747
|
end
|
|
1545
1748
|
|
|
1546
1749
|
# The referenced-but-undeclared types {.stub_missing_referenced_types} stubbed so the project classes
|
|
@@ -2025,11 +2228,21 @@ module Rigor
|
|
|
2025
2228
|
# Building it digests every `.rbs` file under `signature_paths` + the vendored gem sigs, and the
|
|
2026
2229
|
# result is identical across producers, so one build is memoised per loader (on `@state`, alongside
|
|
2027
2230
|
# `:env` — the env itself is loader-lifetime-memoised, so this adds no new staleness class).
|
|
2231
|
+
#
|
|
2232
|
+
# Issue #1014 — nil when the engine's source cannot be digested ({Cache::EngineSource::Unavailable});
|
|
2233
|
+
# {Cache::RbsCacheProducer.fetch} then computes uncached. Memoised through `key?` rather than `||=` so
|
|
2234
|
+
# that answer is reached once: rebuilding it would re-digest the whole signature tree per producer to
|
|
2235
|
+
# arrive at the same nil.
|
|
2028
2236
|
def rbs_cache_descriptor
|
|
2029
|
-
@state[:rbs_cache_descriptor]
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2237
|
+
return @state[:rbs_cache_descriptor] if @state.key?(:rbs_cache_descriptor)
|
|
2238
|
+
|
|
2239
|
+
require_relative "../cache/rbs_descriptor"
|
|
2240
|
+
@state[:rbs_cache_descriptor] =
|
|
2241
|
+
begin
|
|
2242
|
+
Cache::RbsDescriptor.build(self)
|
|
2243
|
+
rescue Cache::EngineSource::Unavailable
|
|
2244
|
+
nil
|
|
2245
|
+
end
|
|
2033
2246
|
end
|
|
2034
2247
|
|
|
2035
2248
|
# ADR-15 Phase 2b — return the loader's read-only query surface as a frozen, `Ractor.shareable?`
|
|
@@ -2397,7 +2610,60 @@ module Rigor
|
|
|
2397
2610
|
return if details.key?(key)
|
|
2398
2611
|
|
|
2399
2612
|
details[key] = [key, error.class.name.to_s, definition_build_member(error),
|
|
2400
|
-
definition_build_conflict_buffers(error)].freeze
|
|
2613
|
+
definition_build_conflict_buffers(error), unresolved_type_name_detail(error)].freeze
|
|
2614
|
+
end
|
|
2615
|
+
|
|
2616
|
+
# Issue #997 — the `RBS::NoTypeFoundError` twin of {#definition_build_member}: `nil` for every other
|
|
2617
|
+
# error class, so the cached tuple gains a populated fifth slot only for the failure kind it
|
|
2618
|
+
# distinguishes — "one declaration names a type that does not exist" from "two declarations collide"
|
|
2619
|
+
# ({DiagnosticAggregator#definition_build_advice}, which folds this in).
|
|
2620
|
+
#
|
|
2621
|
+
# Computed once, at RAISE time, and frozen into the cached detail rather than re-derived when a warm
|
|
2622
|
+
# run replays the tuple from {IncrementalSnapshot} — there is no live `RBS::NoTypeFoundError` to
|
|
2623
|
+
# re-inspect on that path, only the plain strings this method already reduced it to.
|
|
2624
|
+
#
|
|
2625
|
+
# `[token, location, refinement_hint]`:
|
|
2626
|
+
# - `token` is the unresolved name exactly as `RBS::NoTypeFoundError#type_name` carries it — the ONLY
|
|
2627
|
+
# name that ever reaches RBS. A hyphenated payload (`finite-float`) is truncated to its first
|
|
2628
|
+
# segment by rbs-inline's own tolerant type parse (`RBS::Parser.parse_type(..., require_eof: false)`
|
|
2629
|
+
# stops at the first complete type and silently discards the rest), so `-float` never becomes a
|
|
2630
|
+
# token RBS itself can name or complain about.
|
|
2631
|
+
# - `location` is `error.location.to_s` verbatim (`"virtual:rbs-inline:<path>:line:col...line:col"`)
|
|
2632
|
+
# — the position the error already carries, per issue #997, named rather than re-derived. It is a
|
|
2633
|
+
# position in the SYNTHESIZED buffer for an inline annotation, not necessarily the `.rb` line the
|
|
2634
|
+
# author wrote: {Effects::InlineAnchor} maps a known `%a{...}` spelling back by ordinal, which does
|
|
2635
|
+
# not apply to an arbitrary broken type token, so no such mapping is attempted here.
|
|
2636
|
+
# - `refinement_hint` is the full compound name (`"finite-float"`) when the SAME synthesized buffer —
|
|
2637
|
+
# rbs-inline re-emits the author's original `# @rbs name: TYPE` comment verbatim above the member it
|
|
2638
|
+
# generates — carries `token` immediately followed by a hyphenated continuation that is ITSELF a
|
|
2639
|
+
# registered {Builtins::ImportedRefinements} name; `nil` otherwise. Exact-match only: a near-miss
|
|
2640
|
+
# compound name says nothing, so a coincidental `token-suffix` that is not a real refinement can
|
|
2641
|
+
# never manufacture a misleading "did you mean" (false positives outrank worst-case reading).
|
|
2642
|
+
def unresolved_type_name_detail(error)
|
|
2643
|
+
return nil unless error.is_a?(::RBS::NoTypeFoundError)
|
|
2644
|
+
|
|
2645
|
+
token = error.type_name.to_s.delete_prefix("::")
|
|
2646
|
+
[token, error.location&.to_s, refinement_hint_for(token, error)].freeze
|
|
2647
|
+
rescue ::RBS::BaseError, StandardError
|
|
2648
|
+
nil
|
|
2649
|
+
end
|
|
2650
|
+
|
|
2651
|
+
# The buffer rbs-inline synthesized still carries the author's own comment line verbatim (the
|
|
2652
|
+
# "annotation echo" {Effects::InlineAnchor} documents), so a hyphenated refinement name truncated out
|
|
2653
|
+
# of the parsed type ({#unresolved_type_name_detail}) is still readable a few characters further along
|
|
2654
|
+
# the SAME text the error's own location already points into.
|
|
2655
|
+
def refinement_hint_for(token, error)
|
|
2656
|
+
return nil if token.empty?
|
|
2657
|
+
|
|
2658
|
+
content = error.location&.buffer&.content
|
|
2659
|
+
return nil if content.nil?
|
|
2660
|
+
|
|
2661
|
+
candidate = content[/\b#{Regexp.escape(token)}-[A-Za-z0-9][\w-]*/]
|
|
2662
|
+
return nil if candidate.nil?
|
|
2663
|
+
|
|
2664
|
+
Builtins::ImportedRefinements.known?(candidate) ? candidate : nil
|
|
2665
|
+
rescue ::RBS::BaseError, StandardError
|
|
2666
|
+
nil
|
|
2401
2667
|
end
|
|
2402
2668
|
|
|
2403
2669
|
# Issue #696 — the REPORTING half: promote a remembered detail into {#definition_build_failures}
|