rigortype 0.4.5 → 0.4.7
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/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +60 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +27 -21
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +28 -9
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +98 -4
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +96 -2
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +7 -6
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +5 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
|
@@ -10,6 +10,10 @@ require_relative "member_consistency"
|
|
|
10
10
|
require_relative "required_features"
|
|
11
11
|
require_relative "gated_signature_guard"
|
|
12
12
|
require_relative "gated_signature_plan"
|
|
13
|
+
require_relative "deferred_signature_plan"
|
|
14
|
+
require_relative "vendored_signature_plan"
|
|
15
|
+
require_relative "ruby_library_roots"
|
|
16
|
+
require_relative "core_root"
|
|
13
17
|
require_relative "../rooted_glob"
|
|
14
18
|
|
|
15
19
|
module Rigor
|
|
@@ -95,8 +99,33 @@ module Rigor
|
|
|
95
99
|
# Vendored gem stubs (`data/vendored_gem_sigs/<gem>/`) are loaded on top of `signature_paths` so the
|
|
96
100
|
# per-gem RBS bundled with Rigor itself is in scope for every analysis run. The gem stubs are
|
|
97
101
|
# intentionally read-only and appended LAST so user-supplied `signature_paths` win on name conflicts.
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
#
|
|
103
|
+
# @param project_signature_paths — issue #2422 — the project's own `signature_paths:` (not the collection,
|
|
104
|
+
# gem or plugin directories also on `signature_paths`): a plugin member another source declares composes
|
|
105
|
+
# with it as an overload unless that source is the project's own ({DeferredSignaturePlan}).
|
|
106
|
+
# @param core_root — issue #2517 — a validated {CoreRoot} directory loaded in place of the `rbs` gem's
|
|
107
|
+
# `core/`, or nil for the default core.
|
|
108
|
+
def build_env_for(libraries:, signature_paths:, virtual_rbs: [], deferred_signature_paths: [],
|
|
109
|
+
project_signature_paths: [], core_root: nil)
|
|
110
|
+
inputs = [signature_paths, virtual_rbs, deferred_signature_paths, project_signature_paths]
|
|
111
|
+
with_core_root(core_root) { build_env_from(libraries, inputs) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Issue #2517 — the core root {.library_loader} builds every loader of one environment build against. Held
|
|
115
|
+
# for the build rather than threaded through each attempt, because every trial and fallback of
|
|
116
|
+
# {.build_env_from} must read the same core, and a parameter missed on one of them would build that attempt
|
|
117
|
+
# against the default core without a sign. Thread-local, so a concurrent build on another thread keeps its
|
|
118
|
+
# own.
|
|
119
|
+
def with_core_root(core_root)
|
|
120
|
+
previous = Thread.current[:rigor_rbs_core_root]
|
|
121
|
+
Thread.current[:rigor_rbs_core_root] = core_root
|
|
122
|
+
yield
|
|
123
|
+
ensure
|
|
124
|
+
Thread.current[:rigor_rbs_core_root] = previous
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def build_env_from(libraries, inputs)
|
|
128
|
+
signature_paths, virtual_rbs = inputs
|
|
100
129
|
env, gated_dirs = whole_gated_attempt(libraries, inputs)
|
|
101
130
|
return env if gated_dirs.empty?
|
|
102
131
|
|
|
@@ -170,7 +199,27 @@ module Rigor
|
|
|
170
199
|
#
|
|
171
200
|
# @param gated_plan — issue #1713 — a {GatedSignaturePlan} loaded in place of the gated directories: its
|
|
172
201
|
# kept declarations where the directories would have loaded, its shells after the project's signatures.
|
|
173
|
-
|
|
202
|
+
#
|
|
203
|
+
# Issue #2470 — re-made without each {VendoredSignaturePlan} taker the attempt quarantined
|
|
204
|
+
# ({.quarantined_takers}), so a vendored member stands down only to a file the env keeps. Each round adds
|
|
205
|
+
# at least one file to the excluded set, and a round that adds none returns, so the loop ends.
|
|
206
|
+
def build_env_attempt(libraries, *inputs, gated_plan: nil)
|
|
207
|
+
excluded = Set.new
|
|
208
|
+
loop do
|
|
209
|
+
env, gated_dirs, plan = planned_env_attempt(libraries, *inputs, gated_plan: gated_plan,
|
|
210
|
+
vendored_excluded: excluded)
|
|
211
|
+
dropped = quarantined_takers(env, plan)
|
|
212
|
+
return [env, gated_dirs] if dropped.empty? || dropped.subset?(excluded)
|
|
213
|
+
|
|
214
|
+
excluded |= dropped
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# One {.build_env_attempt} round, which also returns its {VendoredSignaturePlan}.
|
|
219
|
+
#
|
|
220
|
+
# @param vendored_excluded — signature files the vendored plan is made without.
|
|
221
|
+
def planned_env_attempt(libraries, signature_paths, virtual_rbs, deferred_signature_paths,
|
|
222
|
+
project_signature_paths = [], gated_plan: nil, vendored_excluded: Set.new)
|
|
174
223
|
libraries = libraries_without_shadowed_bigdecimal_math(libraries)
|
|
175
224
|
rbs_loader, loaded_libraries = library_loader(libraries)
|
|
176
225
|
gated_dirs = loadable_gated_dirs(libraries, loaded_libraries)
|
|
@@ -180,13 +229,16 @@ module Rigor
|
|
|
180
229
|
# type-of query then degrades to `Dynamic[top]` — the "sig looks harmful" failure of the 2026-07-06
|
|
181
230
|
# mastodon coverage note). Per-file loading quarantines the broken file instead. Vendored / core-overlay
|
|
182
231
|
# sigs are Rigor-shipped and trusted, so they stay on the loader's fast batch path.
|
|
183
|
-
add_bundled_signatures(rbs_loader, loaded_libraries.to_set(&:to_s), gated_plan ? [] : gated_dirs)
|
|
184
|
-
env = unload_upstream_core_shims(RBS::Environment.from_loader(rbs_loader), rbs_loader)
|
|
185
|
-
gated_plan&.leading&.each { |source| add_parsed_decls(env, *source) }
|
|
186
232
|
project_files = project_sig_files(signature_paths)
|
|
233
|
+
undeferred_files = project_files - project_sig_files(deferred_signature_paths)
|
|
234
|
+
env, vendored_plan = bundled_env(rbs_loader, loaded_libraries, gated_plan ? [] : gated_dirs,
|
|
235
|
+
undeferred_files, vendored_excluded)
|
|
236
|
+
gated_plan&.leading&.each { |source| add_parsed_decls(env, *source) }
|
|
187
237
|
# Issue #1075 — decided from the inputs before either side enters `env`: the consistency rule may
|
|
188
238
|
# stand the `.rbs` member down, and that has to happen before its file is added.
|
|
189
|
-
|
|
239
|
+
# Issue #2422 — a bundled plugin's deferred files are not the project's: they never take part in the
|
|
240
|
+
# inline-vs-`.rbs` consistency rule, and stand down to an inline member like to any other source.
|
|
241
|
+
consistency = member_consistency_for(undeferred_files, virtual_rbs)
|
|
190
242
|
add_project_signatures(env, signature_paths, deferred_signature_paths,
|
|
191
243
|
standdowns: consistency.signature_standdowns)
|
|
192
244
|
gated_plan&.shells&.each { |source| add_parsed_decls(env, *source) }
|
|
@@ -200,16 +252,52 @@ module Rigor
|
|
|
200
252
|
# Issues #824 / #1075 — every member a `.rbs` file also declares was ranked above; the inline side of
|
|
201
253
|
# each pair the `.rbs` won stands down here ({.add_virtual_rbs}), so the two never collide.
|
|
202
254
|
add_virtual_rbs(env, virtual_rbs, inline_standdowns: consistency.inline_standdowns)
|
|
255
|
+
# Issues #610 / #2422 — the plugins' files last, so each is planned against every other source.
|
|
256
|
+
add_plugin_signatures(env, deferred_signature_paths, project_signature_paths, virtual_rbs)
|
|
203
257
|
synthesize_missing_namespaces(env)
|
|
204
258
|
# Issue #777 — resolve-time backstop: also unload colliding PROJECT buffers (class-vs-module /
|
|
205
259
|
# constant redeclarations against bundled RBS). Virtual culprits are preferred when both appear.
|
|
206
260
|
env, resolved = resolve_quarantining_virtual_collisions(env, virtual_rbs, project_files: project_files)
|
|
207
|
-
[stub_missing_referenced_types(env, resolved, project_files), gated_dirs]
|
|
261
|
+
[stub_missing_referenced_types(env, resolved, project_files), gated_dirs, vendored_plan]
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Issue #2470 — the {VendoredSignaturePlan} takers `env` does not hold: a file the build quarantined after a
|
|
265
|
+
# vendored member stood down to it (a `DuplicatedDeclarationError`, a class-vs-module clash). The answer reads
|
|
266
|
+
# only the build's inputs, so a cold, a warm and a `--no-cache` run agree.
|
|
267
|
+
def quarantined_takers(env, plan)
|
|
268
|
+
return Set.new if plan.takers.empty? || env.nil?
|
|
269
|
+
|
|
270
|
+
buffers = loaded_signature_buffers(env)
|
|
271
|
+
return Set.new if buffers.nil?
|
|
272
|
+
|
|
273
|
+
present = buffers.to_set { |buffer| buffer.name.to_s }
|
|
274
|
+
plan.takers.reject { |file| present.include?(file) }.to_set
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# The environment of the loader's libraries and the Rigor-shipped signatures. Issue #2470 — a vendored
|
|
278
|
+
# directory with a member a `signature_files` file also declares loads from its {VendoredSignaturePlan},
|
|
279
|
+
# right after the batch, instead of whole on it.
|
|
280
|
+
def bundled_env(rbs_loader, loaded_libraries, gated_dirs, signature_files, excluded)
|
|
281
|
+
loaded_library_names = loaded_libraries.to_set(&:to_s)
|
|
282
|
+
plan = vendored_signature_plan(loaded_library_names, signature_files, excluded)
|
|
283
|
+
add_bundled_signatures(rbs_loader, loaded_library_names, gated_dirs, planned: plan.dirs)
|
|
284
|
+
env = unload_upstream_core_shims(RBS::Environment.from_loader(rbs_loader), rbs_loader)
|
|
285
|
+
plan.sources.each { |source| add_parsed_decls(env, *source) }
|
|
286
|
+
[env, plan]
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Issue #2422 — {.add_deferred_signatures} over the plugins' files, with the buffer names of the project's
|
|
290
|
+
# own signature sources, which a plugin member stands down to rather than composing with: its
|
|
291
|
+
# `signature_paths:` files and its inline RBS.
|
|
292
|
+
def add_plugin_signatures(env, deferred_signature_paths, project_signature_paths, virtual_rbs)
|
|
293
|
+
own = project_sig_files(project_signature_paths) | virtual_rbs.to_set { |name, _| name.to_s }
|
|
294
|
+
add_deferred_signatures(env, project_sig_files(deferred_signature_paths), own)
|
|
208
295
|
end
|
|
209
296
|
|
|
210
297
|
# An `RBS::EnvironmentLoader` with every library of `libraries` that resolves added, and those libraries.
|
|
211
298
|
def library_loader(libraries)
|
|
212
|
-
|
|
299
|
+
core_root = Thread.current[:rigor_rbs_core_root]
|
|
300
|
+
rbs_loader = core_root ? RBS::EnvironmentLoader.new(core_root: Pathname(core_root)) : RBS::EnvironmentLoader.new
|
|
213
301
|
# Issue #1700 — a `RequiredFeatures` token names a gated vendored directory, never an RBS library.
|
|
214
302
|
loaded_libraries = libraries.select do |library|
|
|
215
303
|
next false if RequiredFeatures.feature_of_token(library)
|
|
@@ -578,7 +666,6 @@ module Rigor
|
|
|
578
666
|
strip_members!(decls, standdowns[file]) if standdowns.key?(file)
|
|
579
667
|
add_project_parsed_decls(env, buffer, directives, decls)
|
|
580
668
|
end
|
|
581
|
-
add_deferred_signatures(env, deferred, standdowns)
|
|
582
669
|
end
|
|
583
670
|
|
|
584
671
|
# Issue #777 — add one project signature transactionally. `RBS::Environment#add_source` appends to
|
|
@@ -616,7 +703,15 @@ module Rigor
|
|
|
616
703
|
# (rigor-activerecord's is `relation.rbs`, declaring exactly `Relation`), so dropping the file drops
|
|
617
704
|
# exactly the conflicting declaration. Splitting a plugin file that mixes a colliding class with
|
|
618
705
|
# innocent ones is the change to make if that ever stops being true.
|
|
619
|
-
|
|
706
|
+
#
|
|
707
|
+
# Issue #2422 — at equal arity the plugin reopens the class, and a method both sources declare fails the
|
|
708
|
+
# definition build (`DuplicatedMethodDefinitionError`) for the class and everything under it; a constant
|
|
709
|
+
# both declare raises `DuplicatedDeclarationError` and drops the plugin's whole file. So the file is planned
|
|
710
|
+
# against the env before it is added ({DeferredSignaturePlan}): a colliding method composes with another
|
|
711
|
+
# source's `def` as an overload continuation, or stands down when the other source is the project's own
|
|
712
|
+
# (`own`, buffer names) or cannot be continued; a colliding declaration stands down on its own; the rest
|
|
713
|
+
# of the file loads. {.deferred_member_standdowns} reads what stood down back off the built env.
|
|
714
|
+
def add_deferred_signatures(env, deferred_files, own = Set.new)
|
|
620
715
|
deferred_files.sort.each do |file|
|
|
621
716
|
parsed = parse_signature_file(file)
|
|
622
717
|
next if parsed.nil?
|
|
@@ -624,8 +719,7 @@ module Rigor
|
|
|
624
719
|
buffer, directives, decls = parsed
|
|
625
720
|
next if generic_arity_conflict(env, decls)
|
|
626
721
|
|
|
627
|
-
|
|
628
|
-
add_project_parsed_decls(env, buffer, directives, decls)
|
|
722
|
+
add_project_parsed_decls(env, buffer, directives, DeferredSignaturePlan.plan(decls, env, own))
|
|
629
723
|
end
|
|
630
724
|
end
|
|
631
725
|
|
|
@@ -647,6 +741,24 @@ module Rigor
|
|
|
647
741
|
end
|
|
648
742
|
end
|
|
649
743
|
|
|
744
|
+
# Issue #2422 — the members {.add_deferred_signatures} left out of each deferred file it loaded, as
|
|
745
|
+
# `[absolute_path, declaration, nil, nil, cause_file, :member]` — `declaration` reading `Object#acts_like?`
|
|
746
|
+
# or `Time.at`, `cause_file` the buffer of the source that declares it, or nil when that cannot be named.
|
|
747
|
+
# Derived from the built env for {.deferred_standdowns}' reason; a file that stood down whole is
|
|
748
|
+
# {.deferred_standdowns}' row, and one the env does not hold has nothing to read back.
|
|
749
|
+
def deferred_member_standdowns(env, deferred_paths)
|
|
750
|
+
return [] if env.nil? || deferred_paths.nil? || deferred_paths.empty?
|
|
751
|
+
|
|
752
|
+
loaded = loaded_signature_buffers(env)&.to_set { |buffer| buffer.name.to_s } || Set.new
|
|
753
|
+
project_sig_files(deferred_paths).sort.flat_map do |file|
|
|
754
|
+
parsed = loaded.include?(file) ? parse_signature_file(file) : nil
|
|
755
|
+
next [] if parsed.nil? || generic_arity_conflict(env, parsed[2])
|
|
756
|
+
|
|
757
|
+
rows = DeferredSignaturePlan.standdown_rows(env, file, parsed[2]).sort_by(&:first)
|
|
758
|
+
rows.map { |label, cause| [file, label, nil, nil, cause, :member] }
|
|
759
|
+
end
|
|
760
|
+
end
|
|
761
|
+
|
|
650
762
|
# The first `[class_name, existing_arity, incoming_arity, existing_file]` where `decls` would
|
|
651
763
|
# re-declare a class the env already has with a DIFFERENT number of type parameters, or nil when
|
|
652
764
|
# they agree everywhere. Equal arity is not a conflict: RBS reopens the class, which is the ordinary
|
|
@@ -689,9 +801,9 @@ module Rigor
|
|
|
689
801
|
decls.each do |decl|
|
|
690
802
|
case decl
|
|
691
803
|
when ::RBS::AST::Declarations::Module
|
|
692
|
-
each_declared_class(decl.members,
|
|
804
|
+
each_declared_class(decl.members, declared_class_path(decl, prefix), &block)
|
|
693
805
|
when ::RBS::AST::Declarations::Class
|
|
694
|
-
inner = prefix
|
|
806
|
+
inner = declared_class_path(decl, prefix)
|
|
695
807
|
block.call("::#{inner.join('::')}", decl.type_params.size)
|
|
696
808
|
each_declared_class(decl.members, inner, &block)
|
|
697
809
|
end
|
|
@@ -1132,11 +1244,12 @@ module Rigor
|
|
|
1132
1244
|
end
|
|
1133
1245
|
|
|
1134
1246
|
# `[owners, shadowable]`. `owners` is `{[class_name, method_name, kind] => [signature_path, member,
|
|
1135
|
-
# visibility]}` for every method-shaped member the project's own
|
|
1136
|
-
# the inline sources also name; the first file in sorted order
|
|
1137
|
-
# (that pair fails the definition build anyway). `shadowable` is
|
|
1138
|
-
# module name the inline sources and the signature files declare
|
|
1139
|
-
# might resolve to inside an enclosing namespace
|
|
1247
|
+
# visibility]}` for every method-shaped or instance-variable member the project's own
|
|
1248
|
+
# `signature_paths:` files declare and the inline sources also name; the first file in sorted order
|
|
1249
|
+
# owns a key two `.rbs` files declare (that pair fails the definition build anyway). `shadowable` is
|
|
1250
|
+
# every `::` segment of every class / module name the inline sources and the signature files declare
|
|
1251
|
+
# — the names a relative reference might resolve to inside an enclosing namespace
|
|
1252
|
+
# ({MemberConsistency::Comparator#faithful?}).
|
|
1140
1253
|
#
|
|
1141
1254
|
# The parse is scoped by a substring pre-filter over each file's text: a declaration has to spell its
|
|
1142
1255
|
# own name, so a signature file mentioning none of the names the inline sources declare cannot
|
|
@@ -1162,14 +1275,14 @@ module Rigor
|
|
|
1162
1275
|
def index_signature_members(index, file, decls)
|
|
1163
1276
|
return if decls.nil?
|
|
1164
1277
|
|
|
1165
|
-
each_declared_member(decls) do |class_name, member, visibility|
|
|
1166
|
-
|
|
1278
|
+
each_declared_member(decls) do |class_name, member, visibility, nesting|
|
|
1279
|
+
member_keys(member, nesting).each do |method_name, kind|
|
|
1167
1280
|
index[[class_name, method_name, kind]] ||= [file, member, visibility].freeze
|
|
1168
1281
|
end
|
|
1169
1282
|
end
|
|
1170
1283
|
end
|
|
1171
1284
|
|
|
1172
|
-
# The directive heads a refinement {MemberConsistency
|
|
1285
|
+
# The directive heads a refinement {MemberConsistency::Comparator#refinement_conflicts} checks, used to skip
|
|
1173
1286
|
# parsing an inline source that carries none when no `.rbs` overlaps it. Narrower than `rigor:v1:` on
|
|
1174
1287
|
# purpose: rbs-inline's output marks every skeleton `%a{rigor:v1:inferred-return}`, so that prefix is
|
|
1175
1288
|
# in nearly every synthesized source.
|
|
@@ -1189,49 +1302,65 @@ module Rigor
|
|
|
1189
1302
|
decls = parse_signature_source(name, content)
|
|
1190
1303
|
next if decls.nil?
|
|
1191
1304
|
|
|
1192
|
-
each_declared_member(decls) do |class_name, member, visibility|
|
|
1193
|
-
entries << [name.to_s, class_name, member, visibility]
|
|
1305
|
+
each_declared_member(decls) do |class_name, member, visibility, nesting|
|
|
1306
|
+
entries << [name.to_s, class_name, member, visibility, nesting]
|
|
1194
1307
|
end
|
|
1195
1308
|
end
|
|
1196
1309
|
end
|
|
1197
1310
|
|
|
1198
|
-
# Removes, in place, every member contributing a key in `keys` (a Set of `[class, method, kind]
|
|
1199
|
-
# Nested declarations are descended into after the
|
|
1200
|
-
#
|
|
1201
|
-
def strip_members!(decls, keys, prefix = [])
|
|
1311
|
+
# Removes, in place, every member contributing a key in `keys` (a Set of `[class, method, kind]`,
|
|
1312
|
+
# instance-variable keys included, #2440). Nested declarations are descended into after the
|
|
1313
|
+
# rejection, and a declaration member contributes no key, so nesting is never itself removed.
|
|
1314
|
+
def strip_members!(decls, keys, prefix = [], written = [])
|
|
1202
1315
|
Array(decls).each do |decl|
|
|
1203
1316
|
next unless declaration_with_members?(decl)
|
|
1204
1317
|
|
|
1205
|
-
inner = prefix
|
|
1318
|
+
inner = declared_class_path(decl, prefix)
|
|
1319
|
+
nesting = written + [decl.name.to_s]
|
|
1206
1320
|
class_name = inner.join("::")
|
|
1207
1321
|
decl.members.reject! do |member|
|
|
1208
|
-
|
|
1322
|
+
member_keys(member, nesting.join("/")).any? do |method_name, kind|
|
|
1323
|
+
keys.include?([class_name, method_name, kind])
|
|
1324
|
+
end
|
|
1209
1325
|
end
|
|
1210
|
-
strip_members!(decl.members, keys, inner)
|
|
1326
|
+
strip_members!(decl.members, keys, inner, nesting)
|
|
1211
1327
|
end
|
|
1212
1328
|
end
|
|
1213
1329
|
|
|
1214
|
-
#
|
|
1215
|
-
#
|
|
1216
|
-
#
|
|
1217
|
-
#
|
|
1218
|
-
#
|
|
1219
|
-
|
|
1220
|
-
|
|
1330
|
+
# The class path a declaration contributes to member keys: its name `::`-stripped, under the enclosing
|
|
1331
|
+
# path unless the name is absolute. A leading `::` resets the path, as RBS resolves `module A; class
|
|
1332
|
+
# ::Abs` to the top-level `Abs` (issue #2474), and so do the children of that declaration. The
|
|
1333
|
+
# `nesting` a mixin is keyed by is not reset: it keeps the names as written, which is the lexical
|
|
1334
|
+
# scope a relative mixin name resolves through.
|
|
1335
|
+
def declared_class_path(decl, prefix)
|
|
1336
|
+
base = decl.name.to_s.start_with?("::") ? [] : prefix
|
|
1337
|
+
base + [decl.name.to_s.delete_prefix("::")]
|
|
1338
|
+
end
|
|
1339
|
+
|
|
1340
|
+
# Yields `[class_name, member, visibility, nesting]` for every member of every class / module
|
|
1341
|
+
# declaration in `decls`, descending through nesting. The enclosing path is accumulated and the name
|
|
1342
|
+
# rendered `::`-stripped, so a `module Foo; class Bar` and a `class Foo::Bar` elsewhere key the same —
|
|
1343
|
+
# which is what makes a `sig/` declaration and an inline one comparable at all. `visibility` is the
|
|
1344
|
+
# member's own (`private def x`) or else the one the enclosing `private` / `public` section sets; a
|
|
1345
|
+
# singleton method takes no section, as in Ruby. `nesting` is the declaration names exactly as
|
|
1346
|
+
# written, outermost first, joined by `/` (`Foo/Bar` against `Foo::Bar`): a mixin's relative name
|
|
1347
|
+
# resolves through it, so {MemberConsistency.member_keys} keys a mixin by it (#2440).
|
|
1348
|
+
def each_declared_member(decls, prefix = [], written = [], &block)
|
|
1221
1349
|
Array(decls).each do |decl|
|
|
1222
1350
|
next unless declaration_with_members?(decl)
|
|
1223
1351
|
|
|
1224
|
-
inner = prefix
|
|
1352
|
+
inner = declared_class_path(decl, prefix)
|
|
1353
|
+
nesting = written + [decl.name.to_s]
|
|
1225
1354
|
class_name = inner.join("::")
|
|
1226
1355
|
section = :public
|
|
1227
1356
|
decl.members.each do |member|
|
|
1228
1357
|
case member
|
|
1229
1358
|
when ::RBS::AST::Members::Private then section = :private
|
|
1230
1359
|
when ::RBS::AST::Members::Public then section = :public
|
|
1231
|
-
else block.call(class_name, member, member_visibility(member, section))
|
|
1360
|
+
else block.call(class_name, member, member_visibility(member, section), nesting.join("/"))
|
|
1232
1361
|
end
|
|
1233
1362
|
end
|
|
1234
|
-
each_declared_member(decl.members, inner, &block)
|
|
1363
|
+
each_declared_member(decl.members, inner, nesting, &block)
|
|
1235
1364
|
end
|
|
1236
1365
|
end
|
|
1237
1366
|
|
|
@@ -1253,6 +1382,12 @@ module Rigor
|
|
|
1253
1382
|
MemberConsistency.member_method_keys(member)
|
|
1254
1383
|
end
|
|
1255
1384
|
|
|
1385
|
+
# {MemberConsistency.member_keys} — the method keys plus an instance variable's and a mixin's (#2440),
|
|
1386
|
+
# which is what the inline-vs-`sig/` consistency rule indexes and strips by.
|
|
1387
|
+
def member_keys(member, nesting = nil)
|
|
1388
|
+
MemberConsistency.member_keys(member, nesting)
|
|
1389
|
+
end
|
|
1390
|
+
|
|
1256
1391
|
# A class / module declaration header, as the pre-filter in {.signature_member_index} reads it out of
|
|
1257
1392
|
# synthesized RBS text.
|
|
1258
1393
|
INLINE_DECLARATION_NAME = /^[ \t]*(?:class|module)[ \t]+([A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)/
|
|
@@ -1427,8 +1562,12 @@ module Rigor
|
|
|
1427
1562
|
#
|
|
1428
1563
|
# @param loaded_library_names — libraries that actually resolved on this loader.
|
|
1429
1564
|
# @param gated_dirs — the {RequiredFeatures}-gated directory basenames this library list activates.
|
|
1430
|
-
|
|
1565
|
+
#
|
|
1566
|
+
# @param planned — issue #2470 — the {VendoredSignaturePlan} directories, which load from the plan instead.
|
|
1567
|
+
def add_bundled_signatures(rbs_loader, loaded_library_names, gated_dirs = [], planned: [])
|
|
1431
1568
|
vendored_gem_sig_paths.each do |path|
|
|
1569
|
+
next if planned.include?(path)
|
|
1570
|
+
|
|
1432
1571
|
rbs_loader.add(path: path) if vendored_dir_loads?(path, loaded_library_names, gated_dirs)
|
|
1433
1572
|
end
|
|
1434
1573
|
core_overlay_sig_paths.each do |dir|
|
|
@@ -1444,6 +1583,18 @@ module Rigor
|
|
|
1444
1583
|
end
|
|
1445
1584
|
end
|
|
1446
1585
|
|
|
1586
|
+
# Issue #2470 — the ungated vendored directories this build loads, planned against the signature files
|
|
1587
|
+
# that outrank them ({VendoredSignaturePlan}). The gated ones have their own plan (#1713).
|
|
1588
|
+
#
|
|
1589
|
+
# @param signature_files — the `signature_paths:` files a bundled plugin did not contribute: the project's
|
|
1590
|
+
# own, a collection's and a gem's. A plugin's files are planned against the env instead (#2422).
|
|
1591
|
+
def vendored_signature_plan(loaded_library_names, signature_files, excluded = Set.new)
|
|
1592
|
+
roots = vendored_gem_sig_paths.select do |path|
|
|
1593
|
+
!RequiredFeatures.gated_dir?(path.basename.to_s) && vendored_dir_loads?(path, loaded_library_names, [])
|
|
1594
|
+
end
|
|
1595
|
+
VendoredSignaturePlan.build(roots, signature_files, excluded)
|
|
1596
|
+
end
|
|
1597
|
+
|
|
1447
1598
|
# Issue #1700 — the {RequiredFeatures}-gated directories `libraries` activates, less one whose feature
|
|
1448
1599
|
# resolved as an RBS library here (`libraries: [prime]` on a host whose `prime` gem ships its `sig/`),
|
|
1449
1600
|
# whose own copy is then already loaded. {.build_env_for} then keeps a directory that joined only if the
|
|
@@ -1631,7 +1782,8 @@ module Rigor
|
|
|
1631
1782
|
end
|
|
1632
1783
|
end
|
|
1633
1784
|
|
|
1634
|
-
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs, :deferred_signature_paths
|
|
1785
|
+
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs, :deferred_signature_paths,
|
|
1786
|
+
:project_signature_paths, :core_root
|
|
1635
1787
|
|
|
1636
1788
|
# @param libraries — stdlib library names to load on top of core (e.g.,
|
|
1637
1789
|
# `["pathname", "json"]`). Empty by default. Each entry MUST correspond to a directory under the
|
|
@@ -1644,7 +1796,7 @@ module Rigor
|
|
|
1644
1796
|
# `RbsEnvironment`, `RbsKnownClassNames`, `RbsConstantTable`, `RbsClassAncestorTable`, and
|
|
1645
1797
|
# `RbsClassTypeParamNames` producers. Pass `nil` (the default) to skip caching; the runner threads
|
|
1646
1798
|
# its own Store through here when enabled.
|
|
1647
|
-
# @param virtual_rbs —
|
|
1799
|
+
# @param virtual_rbs — ADR-32 WD4 — `[virtual_filename, rbs_source]` pairs
|
|
1648
1800
|
# synthesised from project source by a plugin's `Manifest#source_rbs_synthesizer`. Merged into the
|
|
1649
1801
|
# env after `signature_paths:` and the vendored stubs. Pass `[]` (the default) when no
|
|
1650
1802
|
# synthesizer-emitting plugin is loaded.
|
|
@@ -1652,10 +1804,15 @@ module Rigor
|
|
|
1652
1804
|
# contributed (issue #610): loaded last and allowed to stand down against a colliding generic
|
|
1653
1805
|
# arity. Read back by the env-cache producer, which rebuilds the env from the loader's readers on
|
|
1654
1806
|
# a miss — a list it cannot read is a stand-down that runs only on a `--no-cache` build.
|
|
1655
|
-
|
|
1656
|
-
|
|
1807
|
+
# @param core_root — issue #2517 — a directory loaded in place of the `rbs` gem's `core/`, or nil (the
|
|
1808
|
+
# default) for that core. Validated here, so a bad selection fails at construction ({CoreRoot.validate!}).
|
|
1809
|
+
def initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: [], # rubocop:disable Metrics/ParameterLists
|
|
1810
|
+
deferred_signature_paths: [], project_signature_paths: [], required_features: [], core_root: nil)
|
|
1811
|
+
@core_root = core_root.nil? ? nil : CoreRoot.validate!(core_root)
|
|
1812
|
+
CoreRoot.reject_overlap!(@core_root, project_signature_paths) if @core_root
|
|
1657
1813
|
@libraries = libraries.map(&:to_s).freeze
|
|
1658
1814
|
@signature_paths = signature_paths.map { |p| Pathname(p) }.freeze
|
|
1815
|
+
assign_reopen_inputs(project_signature_paths, required_features)
|
|
1659
1816
|
# Issue #610 — the subset of `signature_paths:` a bundled PLUGIN contributed. Loaded last and
|
|
1660
1817
|
# allowed to stand down against a colliding generic arity ({.add_deferred_signatures}). Every entry
|
|
1661
1818
|
# is also in `@signature_paths`, so the env cache descriptor already digests these files.
|
|
@@ -1723,8 +1880,14 @@ module Rigor
|
|
|
1723
1880
|
# plugin sig collides, which is every project that has not run `rbs collection install` for a gem a
|
|
1724
1881
|
# bundled plugin also declares. Reported as `rbs.coverage.plugin-signature-stood-down` (`:info`); the
|
|
1725
1882
|
# same file MUST NOT also appear in {#quarantined_signatures}.
|
|
1883
|
+
#
|
|
1884
|
+
# Issue #2422 — followed by the members a deferred file that did load left out because another source
|
|
1885
|
+
# declares them ({.deferred_member_standdowns}), one row each.
|
|
1726
1886
|
def signature_standdowns
|
|
1727
|
-
@state[:signature_standdowns] ||=
|
|
1887
|
+
@state[:signature_standdowns] ||= (
|
|
1888
|
+
self.class.deferred_standdowns(env, @deferred_signature_paths) +
|
|
1889
|
+
self.class.deferred_member_standdowns(env, @deferred_signature_paths)
|
|
1890
|
+
).freeze
|
|
1728
1891
|
end
|
|
1729
1892
|
|
|
1730
1893
|
# Issue #1713 — what stood down from each {RequiredFeatures}-gated vendored directory this loader's library
|
|
@@ -1764,6 +1927,9 @@ module Rigor
|
|
|
1764
1927
|
# a partial sidecar as authoritative made `sig-gen --write` on redmine report 5.9x more
|
|
1765
1928
|
# `call.undefined-method` than the same project with no `sig/` at all.
|
|
1766
1929
|
#
|
|
1930
|
+
# A class declared only by RBS synthesized from the project's own source joins the set too
|
|
1931
|
+
# ({#inline_only_entry?}, issue #1934).
|
|
1932
|
+
#
|
|
1767
1933
|
# Memoised per loader: one pass over `class_decls` (~1-2k entries), and every consumer is a check
|
|
1768
1934
|
# rule on the analysis hot path. Attribution is by buffer NAME, which survives the ADR-54 environment
|
|
1769
1935
|
# cache since #725 — an environment blob written before that carries the `<cached>` sentinel instead,
|
|
@@ -1777,10 +1943,14 @@ module Rigor
|
|
|
1777
1943
|
return Set.new.freeze if environment.nil?
|
|
1778
1944
|
|
|
1779
1945
|
project_files = self.class.project_sig_files(@signature_paths)
|
|
1780
|
-
|
|
1946
|
+
virtual_names = @virtual_rbs.to_set(&:first)
|
|
1947
|
+
return Set.new.freeze if project_files.empty? && virtual_names.empty?
|
|
1781
1948
|
|
|
1782
1949
|
names = environment.class_decls.each_with_object(Set.new) do |(rbs_name, entry), acc|
|
|
1783
|
-
|
|
1950
|
+
next unless self.class.project_entry?(entry, project_files) ||
|
|
1951
|
+
inline_only_entry?(entry, virtual_names, project_files)
|
|
1952
|
+
|
|
1953
|
+
acc << rbs_name.to_s.delete_prefix("::")
|
|
1784
1954
|
end
|
|
1785
1955
|
names.freeze
|
|
1786
1956
|
rescue ::RBS::BaseError
|
|
@@ -1788,6 +1958,148 @@ module Rigor
|
|
|
1788
1958
|
end
|
|
1789
1959
|
private :build_project_declared_classes
|
|
1790
1960
|
|
|
1961
|
+
# Issue #1934 — a class whose every declaration a source RBS synthesizer (`rigor-rbs-inline`) wrote from
|
|
1962
|
+
# the project's own `.rb` files is the project's as much as a `sig/` sidecar is: the declaration is the
|
|
1963
|
+
# inline comments of the source under analysis, so a method another project file adds to the class (a
|
|
1964
|
+
# plain `def`, or an `alias_method` whose target the inline declaration names) is the second file of an
|
|
1965
|
+
# ordinary class, not an ADR-17 monkey-patch. Reading the synthesized RBS as bundled reported
|
|
1966
|
+
# `alias_method :title, :label` from another file while the `alias` keyword, which upstream translates,
|
|
1967
|
+
# read clean.
|
|
1968
|
+
#
|
|
1969
|
+
# EVERY declaration, not only the primary one, and only virtual or `sig/` buffers: a core, stdlib or gem
|
|
1970
|
+
# class an inline-annotated file reopens (`class String` with a `#:` method) keeps its bundled
|
|
1971
|
+
# declaration, and a project `def` on it stays the monkey-patch ADR-17 reports. A declaration whose
|
|
1972
|
+
# buffer cannot be attributed (the pre-#725 `<cached>` sentinel) fails the test, which keeps the
|
|
1973
|
+
# pre-#1934 reading.
|
|
1974
|
+
def inline_only_entry?(entry, virtual_names, project_files)
|
|
1975
|
+
return false if virtual_names.empty?
|
|
1976
|
+
|
|
1977
|
+
project_authored_entry?(entry, virtual_names, project_files)
|
|
1978
|
+
end
|
|
1979
|
+
private :inline_only_entry?
|
|
1980
|
+
|
|
1981
|
+
# Every declaration of `entry` sits in a project `sig/` file or in RBS synthesized from the project's
|
|
1982
|
+
# source; a declaration whose buffer cannot be attributed fails the test.
|
|
1983
|
+
def project_authored_entry?(entry, virtual_names, project_files)
|
|
1984
|
+
buffers = self.class.entry_declarations(entry).map { |decl| self.class.declaration_buffer_name(decl) }
|
|
1985
|
+
return false if buffers.empty? || buffers.any?(&:nil?)
|
|
1986
|
+
|
|
1987
|
+
buffers.all? { |name| virtual_names.include?(name) || project_files.include?(File.absolute_path(name)) }
|
|
1988
|
+
end
|
|
1989
|
+
private :project_authored_entry?
|
|
1990
|
+
|
|
1991
|
+
# Issue #2174 — true when every RBS declaration of `class_name` is the project's own (rbs-inline `#:`
|
|
1992
|
+
# annotations, or a file under the configured `signature_paths:`) and its root constant belongs to a library
|
|
1993
|
+
# Ruby ships ({RubyLibraryRoots}) that the project's source requires ({RequiredFeatures.features_of}). Such
|
|
1994
|
+
# a declaration reopens a library class whose real signatures did not load (`class Matrix` with one
|
|
1995
|
+
# annotated method beside `require "matrix"`, and no `matrix` RBS), so it lists only the methods the
|
|
1996
|
+
# project added: the class's surface is not enumerable from it, and a call it omits is not evidence of an
|
|
1997
|
+
# undefined method. A library whose RBS did load contributes a declaration of its own (core, stdlib, a gem's
|
|
1998
|
+
# `sig/`, an rbs collection, a plugin's `sig/`), which takes the class out of this predicate. A project
|
|
1999
|
+
# class that shares a required library's root name declines too; that is the accepted cost.
|
|
2000
|
+
def ruby_library_reopen?(class_name)
|
|
2001
|
+
name = class_name.to_s.delete_prefix("::")
|
|
2002
|
+
return false if @required_features.empty? || !project_authored_classes.include?(name)
|
|
2003
|
+
|
|
2004
|
+
segments = ruby_library_roots[name.split("::").first]
|
|
2005
|
+
!segments.nil? && @required_features.any? { |feature| segments.include?(feature.split("/").first) }
|
|
2006
|
+
end
|
|
2007
|
+
|
|
2008
|
+
def ruby_library_roots
|
|
2009
|
+
@state[:ruby_library_roots] ||= RubyLibraryRoots.shared
|
|
2010
|
+
end
|
|
2011
|
+
|
|
2012
|
+
# Issue #2174 — the subset of `signature_paths:` the user configured (every entry is also in
|
|
2013
|
+
# `@signature_paths`), and the features the project's source requires: read only by
|
|
2014
|
+
# {#ruby_library_reopen?}.
|
|
2015
|
+
def assign_reopen_inputs(project_signature_paths, required_features)
|
|
2016
|
+
@project_signature_paths = project_signature_paths.map { |p| Pathname(p) }.freeze
|
|
2017
|
+
@required_features = required_features.map { |feature| feature.to_s.dup.freeze }.freeze
|
|
2018
|
+
end
|
|
2019
|
+
private :assign_reopen_inputs
|
|
2020
|
+
private :ruby_library_roots
|
|
2021
|
+
|
|
2022
|
+
# Issue #2438 — true when every RBS declaration of `class_name` is the project's own
|
|
2023
|
+
# ({#project_authored_classes}) and none of them declares a member of the class itself: no method, attribute,
|
|
2024
|
+
# alias or mixin, only nested class / module / constant / type declarations (and variable or visibility
|
|
2025
|
+
# lines, which add no method). `rigor sig-gen --write` writes exactly this for a namespace whose source
|
|
2026
|
+
# lists no methods of its own (`module NS; class Foo; …; end; end`), while the namespace's methods live
|
|
2027
|
+
# where the generator cannot see them: a file outside `paths:`, a mixin, a hook. Such a declaration
|
|
2028
|
+
# names the class without describing its surface. Bundled (core, stdlib, gem) declarations never count.
|
|
2029
|
+
def memberless_project_class?(class_name)
|
|
2030
|
+
memberless_project_classes.key?(class_name.to_s.delete_prefix("::"))
|
|
2031
|
+
end
|
|
2032
|
+
|
|
2033
|
+
# The superclass a member-less project declaration of `class_name` writes (`class Poll < ::ApplicationRecord`),
|
|
2034
|
+
# as RBS spells it (rooted), or nil when none of its declarations writes one or the class is not member-less.
|
|
2035
|
+
def memberless_project_superclass(class_name)
|
|
2036
|
+
memberless_project_classes[class_name.to_s.delete_prefix("::")]
|
|
2037
|
+
end
|
|
2038
|
+
|
|
2039
|
+
def memberless_project_classes
|
|
2040
|
+
@state[:memberless_project_classes] ||= build_memberless_project_classes
|
|
2041
|
+
end
|
|
2042
|
+
private :memberless_project_classes
|
|
2043
|
+
|
|
2044
|
+
SURFACE_MEMBERS = [
|
|
2045
|
+
::RBS::AST::Members::MethodDefinition, ::RBS::AST::Members::Attribute,
|
|
2046
|
+
::RBS::AST::Members::Alias, ::RBS::AST::Members::Mixin
|
|
2047
|
+
].freeze
|
|
2048
|
+
private_constant :SURFACE_MEMBERS
|
|
2049
|
+
|
|
2050
|
+
def build_memberless_project_classes
|
|
2051
|
+
authored = project_authored_classes
|
|
2052
|
+
environment = env
|
|
2053
|
+
return {}.freeze if authored.empty? || environment.nil?
|
|
2054
|
+
|
|
2055
|
+
environment.class_decls.each_with_object({}) do |(rbs_name, entry), acc|
|
|
2056
|
+
name = rbs_name.to_s.delete_prefix("::")
|
|
2057
|
+
next unless authored.include?(name)
|
|
2058
|
+
|
|
2059
|
+
decls = self.class.entry_declarations(entry)
|
|
2060
|
+
acc[name] = declared_superclass_name(decls) if decls.none? { |decl| surface_member?(decl) }
|
|
2061
|
+
end.freeze
|
|
2062
|
+
rescue ::RBS::BaseError
|
|
2063
|
+
{}.freeze
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
def surface_member?(decl)
|
|
2067
|
+
members = decl.respond_to?(:members) ? decl.members : []
|
|
2068
|
+
members.any? { |member| SURFACE_MEMBERS.any? { |kind| member.is_a?(kind) } }
|
|
2069
|
+
end
|
|
2070
|
+
private :surface_member?
|
|
2071
|
+
|
|
2072
|
+
def declared_superclass_name(decls)
|
|
2073
|
+
decls.filter_map { |decl| decl.respond_to?(:super_class) && decl.super_class&.name&.to_s }.first&.freeze
|
|
2074
|
+
end
|
|
2075
|
+
private :declared_superclass_name
|
|
2076
|
+
private :build_memberless_project_classes
|
|
2077
|
+
|
|
2078
|
+
# The class / module names every one of whose declarations is the project's own
|
|
2079
|
+
# ({#project_authored_entry?}, which {#inline_only_entry?} asks behind its inline-only gate).
|
|
2080
|
+
def project_authored_classes
|
|
2081
|
+
@state[:project_authored_classes] ||= build_project_authored_classes
|
|
2082
|
+
end
|
|
2083
|
+
private :project_authored_classes
|
|
2084
|
+
|
|
2085
|
+
def build_project_authored_classes
|
|
2086
|
+
environment = env
|
|
2087
|
+
return Set.new.freeze if environment.nil?
|
|
2088
|
+
|
|
2089
|
+
# Only the user's configured `signature_paths:`: `@signature_paths` also holds plugin, gem-shipped,
|
|
2090
|
+
# rbs-collection and overlay directories, whose declarations are a library's own.
|
|
2091
|
+
project_files = self.class.project_sig_files(@project_signature_paths)
|
|
2092
|
+
virtual_names = @virtual_rbs.to_set(&:first)
|
|
2093
|
+
return Set.new.freeze if project_files.empty? && virtual_names.empty?
|
|
2094
|
+
|
|
2095
|
+
environment.class_decls.each_with_object(Set.new) do |(rbs_name, entry), acc|
|
|
2096
|
+
acc << rbs_name.to_s.delete_prefix("::") if project_authored_entry?(entry, virtual_names, project_files)
|
|
2097
|
+
end.freeze
|
|
2098
|
+
rescue ::RBS::BaseError
|
|
2099
|
+
Set.new.freeze
|
|
2100
|
+
end
|
|
2101
|
+
private :build_project_authored_classes
|
|
2102
|
+
|
|
1791
2103
|
# Issue #527 — the class / module names whose PRIMARY declaration lives in the `rbs` gem's own
|
|
1792
2104
|
# `core/` or `stdlib/` tree, top-level prefix stripped.
|
|
1793
2105
|
#
|
|
@@ -1808,9 +2120,39 @@ module Rigor
|
|
|
1808
2120
|
@state[:core_stdlib_classes] ||= build_core_stdlib_classes
|
|
1809
2121
|
end
|
|
1810
2122
|
|
|
1811
|
-
|
|
2123
|
+
# Issue #1154 — the subset of {#core_stdlib_classes} whose primary declaration is in `core/` alone: the classes
|
|
2124
|
+
# the interpreter implements, as opposed to a stdlib library written in Ruby (`Logger` rewrites `@level` in
|
|
2125
|
+
# its `initialize`). Same attribution, same cache behaviour.
|
|
2126
|
+
def core_class?(class_name)
|
|
2127
|
+
(@state[:core_classes] ||= build_core_stdlib_classes(core_roots)).include?(class_name.to_s.delete_prefix("::"))
|
|
2128
|
+
end
|
|
2129
|
+
|
|
2130
|
+
# Issue #2517 — the `core/` prefixes a declaration's buffer name is tested against: the selected
|
|
2131
|
+
# {#core_root} when there is one, which is what the environment loaded core from, else the `rbs` gem's.
|
|
2132
|
+
def core_roots
|
|
2133
|
+
@core_root ? ["#{@core_root}/".freeze].freeze : CORE_ROOTS
|
|
2134
|
+
end
|
|
2135
|
+
private :core_roots
|
|
2136
|
+
|
|
2137
|
+
# {CORE_STDLIB_ROOTS} with {#core_roots} in place of the `rbs` gem's `core/`.
|
|
2138
|
+
def core_stdlib_roots
|
|
2139
|
+
return CORE_STDLIB_ROOTS unless @core_root
|
|
2140
|
+
return [].freeze if CORE_STDLIB_ROOTS.empty?
|
|
2141
|
+
|
|
2142
|
+
(core_roots + (CORE_STDLIB_ROOTS - CORE_ROOTS)).freeze
|
|
2143
|
+
end
|
|
2144
|
+
private :core_stdlib_roots
|
|
2145
|
+
|
|
2146
|
+
CORE_ROOTS = begin
|
|
2147
|
+
[::RBS::EnvironmentLoader::DEFAULT_CORE_ROOT].compact.map { |root| "#{root}/".freeze }.freeze
|
|
2148
|
+
rescue StandardError
|
|
2149
|
+
[].freeze
|
|
2150
|
+
end
|
|
2151
|
+
private_constant :CORE_ROOTS
|
|
2152
|
+
|
|
2153
|
+
def build_core_stdlib_classes(roots = core_stdlib_roots)
|
|
1812
2154
|
environment = env
|
|
1813
|
-
return Set.new.freeze if environment.nil? ||
|
|
2155
|
+
return Set.new.freeze if environment.nil? || roots.empty?
|
|
1814
2156
|
|
|
1815
2157
|
names = environment.class_decls.each_with_object(Set.new) do |(rbs_name, entry), acc|
|
|
1816
2158
|
decl = self.class.primary_decl_for(entry)
|
|
@@ -1819,7 +2161,7 @@ module Rigor
|
|
|
1819
2161
|
path = self.class.declaration_buffer_name(decl)
|
|
1820
2162
|
next if path.nil?
|
|
1821
2163
|
|
|
1822
|
-
acc << rbs_name.to_s.delete_prefix("::") if
|
|
2164
|
+
acc << rbs_name.to_s.delete_prefix("::") if roots.any? { |root| path.start_with?(root) }
|
|
1823
2165
|
end
|
|
1824
2166
|
names.freeze
|
|
1825
2167
|
rescue ::RBS::BaseError
|
|
@@ -1868,7 +2210,7 @@ module Rigor
|
|
|
1868
2210
|
next if type.is_a?(Type::Bot)
|
|
1869
2211
|
|
|
1870
2212
|
path = self.class.declaration_buffer_name(entry.decl)
|
|
1871
|
-
acc[name.to_sym] = [type, !path.nil? &&
|
|
2213
|
+
acc[name.to_sym] = [type, !path.nil? && core_stdlib_roots.any? { |root| path.start_with?(root) }].freeze
|
|
1872
2214
|
end
|
|
1873
2215
|
table.freeze
|
|
1874
2216
|
rescue ::RBS::BaseError
|
|
@@ -1892,6 +2234,13 @@ module Rigor
|
|
|
1892
2234
|
@state[:env_build_failure]
|
|
1893
2235
|
end
|
|
1894
2236
|
|
|
2237
|
+
# Issue #2517 — builds the environment now when a core is selected, so a core it does not build from raises
|
|
2238
|
+
# its {CoreRootError} at the caller rather than at the first query inside analysis. No-op for the default core.
|
|
2239
|
+
def verify_core_root!
|
|
2240
|
+
env if @core_root
|
|
2241
|
+
nil
|
|
2242
|
+
end
|
|
2243
|
+
|
|
1895
2244
|
# Issue #696 — the PER-CLASS sibling of {#env_build_failure}, one tier quieter in consequence: an
|
|
1896
2245
|
# `RBS::DefinitionBuilder` failure ({#build_instance_definition} / {#build_singleton_definition}'s
|
|
1897
2246
|
# rescue) leaves the class KNOWN but with no method surface, so every call on it — real methods and
|
|
@@ -1977,7 +2326,8 @@ module Rigor
|
|
|
1977
2326
|
def member_consistency(proof: nil)
|
|
1978
2327
|
key = proof ? :member_consistency_proven : :member_consistency
|
|
1979
2328
|
@state[key] ||= self.class.member_consistency_for(
|
|
1980
|
-
self.class.project_sig_files(@signature_paths)
|
|
2329
|
+
self.class.project_sig_files(@signature_paths) - self.class.project_sig_files(@deferred_signature_paths),
|
|
2330
|
+
@virtual_rbs, proof: proof
|
|
1981
2331
|
).records
|
|
1982
2332
|
end
|
|
1983
2333
|
|
|
@@ -2105,6 +2455,32 @@ module Rigor
|
|
|
2105
2455
|
end
|
|
2106
2456
|
private :mixin_parent
|
|
2107
2457
|
|
|
2458
|
+
# Issue #2321 — every class name an RBS class declaration names as its superclass, `::`-stripped: a class
|
|
2459
|
+
# absent from the set has no RBS-declared subclass. `nil` when the environment is unavailable or fails to read,
|
|
2460
|
+
# so a caller cannot mistake an unread environment for one without subclasses. Memoised per loader.
|
|
2461
|
+
def superclass_names
|
|
2462
|
+
return @state[:superclass_names] if @state.key?(:superclass_names)
|
|
2463
|
+
|
|
2464
|
+
@state[:superclass_names] = build_superclass_names&.freeze
|
|
2465
|
+
end
|
|
2466
|
+
|
|
2467
|
+
def build_superclass_names
|
|
2468
|
+
return nil if env.nil?
|
|
2469
|
+
|
|
2470
|
+
names = Set.new
|
|
2471
|
+
|
|
2472
|
+
env.class_decls.each_value do |entry|
|
|
2473
|
+
self.class.entry_declarations(entry).each do |decl|
|
|
2474
|
+
superclass = decl.respond_to?(:super_class) ? decl.super_class : nil
|
|
2475
|
+
names << superclass.name.to_s.delete_prefix("::") if superclass
|
|
2476
|
+
end
|
|
2477
|
+
end
|
|
2478
|
+
names
|
|
2479
|
+
rescue ::RBS::BaseError
|
|
2480
|
+
nil
|
|
2481
|
+
end
|
|
2482
|
+
private :build_superclass_names
|
|
2483
|
+
|
|
2108
2484
|
# Yields every known class / module / alias name (top-level prefixed) currently loaded into the
|
|
2109
2485
|
# environment. The cache producer that materialises the known-name set uses this so it never recurses
|
|
2110
2486
|
# back through {#class_known?}.
|
|
@@ -2121,7 +2497,7 @@ module Rigor
|
|
|
2121
2497
|
end
|
|
2122
2498
|
|
|
2123
2499
|
# Yields every type-alias declaration loaded into the environment (`type foo = ...`).
|
|
2124
|
-
# Yields `
|
|
2500
|
+
# Yields `[type_name, decl_entry]` pairs.
|
|
2125
2501
|
def each_type_alias_decl
|
|
2126
2502
|
return enum_for(:each_type_alias_decl) unless block_given?
|
|
2127
2503
|
return unless env
|
|
@@ -2179,7 +2555,7 @@ module Rigor
|
|
|
2179
2555
|
# fail-soft: see #each_class_decl_annotation.
|
|
2180
2556
|
end
|
|
2181
2557
|
|
|
2182
|
-
# Returns a frozen
|
|
2558
|
+
# Returns a frozen mapping from each loaded class / module name (top-level prefixed)
|
|
2183
2559
|
# to the file path of its FIRST declaration's RBS source. Used by {Rigor::Analysis::RunStats} to
|
|
2184
2560
|
# attribute the type universe between "project sig/" (paths under the configured `signature_paths`)
|
|
2185
2561
|
# and "bundled" (everything else — RBS core, stdlib libraries, gem-bundled RBS). Each value is a frozen
|
|
@@ -2400,7 +2776,7 @@ module Rigor
|
|
|
2400
2776
|
# singleton methods (e.g., `Array.new`) parameterize over the same `Elem` as instance methods.
|
|
2401
2777
|
#
|
|
2402
2778
|
# Returns an empty array for non-generic classes and for unknown names (the loader stays fail-soft).
|
|
2403
|
-
# NOTE: in the `rbs` gem, `RBS::Definition#type_params` returns
|
|
2779
|
+
# NOTE: in the `rbs` gem, `RBS::Definition#type_params` returns the names directly, not the AST
|
|
2404
2780
|
# `TypeParam` object (those live on the AST level).
|
|
2405
2781
|
#
|
|
2406
2782
|
# When `cache_store` is set, the loader fetches the entire type-parameter-name table once (per
|
|
@@ -2430,6 +2806,31 @@ module Rigor
|
|
|
2430
2806
|
NO_TYPE_PARAMS = [].freeze
|
|
2431
2807
|
private_constant :NO_TYPE_PARAMS
|
|
2432
2808
|
|
|
2809
|
+
# Issue #2436 — the `RBS::AST::TypeParam` list a loaded declaration of `class_name` spells (`[unchecked out
|
|
2810
|
+
# E]` for `Array`), read off the declarations rather than through {#class_type_param_names}: a definition
|
|
2811
|
+
# build validates the entry and raises on one whose declarations already disagree, which is the env sig-gen
|
|
2812
|
+
# is asked to repair. The first declaration answers, as it is the one rbs compares every other against
|
|
2813
|
+
# ({.generic_arity_conflict}). Empty for a non-generic or unknown name, and when the environment did not
|
|
2814
|
+
# build.
|
|
2815
|
+
def class_declaration_type_params(class_name)
|
|
2816
|
+
memo = (@state[:declaration_type_params] ||= {})
|
|
2817
|
+
key = class_name.to_s.delete_prefix("::")
|
|
2818
|
+
memo.fetch(key) { memo[key] = read_declaration_type_params(key) }
|
|
2819
|
+
end
|
|
2820
|
+
|
|
2821
|
+
def read_declaration_type_params(key)
|
|
2822
|
+
rbs_name = parse_type_name(key)
|
|
2823
|
+
entry = rbs_name && env&.class_decls&.[](rbs_name)
|
|
2824
|
+
return NO_TYPE_PARAMS if entry.nil?
|
|
2825
|
+
|
|
2826
|
+
first = self.class.entry_declarations(entry).first
|
|
2827
|
+
params = first.respond_to?(:type_params) ? first.type_params : nil
|
|
2828
|
+
params.nil? || params.empty? ? NO_TYPE_PARAMS : params.dup.freeze
|
|
2829
|
+
rescue ::RBS::BaseError
|
|
2830
|
+
NO_TYPE_PARAMS
|
|
2831
|
+
end
|
|
2832
|
+
private :read_declaration_type_params
|
|
2833
|
+
|
|
2433
2834
|
def class_ordering(lhs, rhs)
|
|
2434
2835
|
@hierarchy.class_ordering(lhs, rhs)
|
|
2435
2836
|
end
|
|
@@ -2702,7 +3103,7 @@ module Rigor
|
|
|
2702
3103
|
producer.send(:compute, self)
|
|
2703
3104
|
end
|
|
2704
3105
|
|
|
2705
|
-
# ADR-15 Phase 2b —
|
|
3106
|
+
# ADR-15 Phase 2b — the normalised ancestor chain of each class. Consumes
|
|
2706
3107
|
# the existing `RbsClassAncestorTable` producer when `cache_store` is set; falls back to the
|
|
2707
3108
|
# producer's `compute` otherwise. Used by {#reflection}.
|
|
2708
3109
|
def ancestor_names_table
|
|
@@ -2737,6 +3138,7 @@ module Rigor
|
|
|
2737
3138
|
# per AST node touched during analysis, multiplying per-file analysis cost by ~100x. Failures
|
|
2738
3139
|
# short-circuit to `nil` here and are surfaced to the user via `warn_about_env_build_failure_once`.
|
|
2739
3140
|
def env
|
|
3141
|
+
raise @state[:core_root_error] if @state[:core_root_error]
|
|
2740
3142
|
return @state[:env] if @state[:env_loaded]
|
|
2741
3143
|
|
|
2742
3144
|
@state[:env_loaded] = true
|
|
@@ -2745,11 +3147,24 @@ module Rigor
|
|
|
2745
3147
|
warn_about_virtual_rbs_collisions
|
|
2746
3148
|
@state[:env]
|
|
2747
3149
|
rescue ::RBS::BaseError => e
|
|
3150
|
+
# Issue #2517 — an environment that does not build from an explicitly selected core is not analysed
|
|
3151
|
+
# against at all: the degraded `Dynamic[top]` env would report the very APIs the selection removes as
|
|
3152
|
+
# fine. Memoised like the nil env, so every later reader raises the same error instead of rebuilding.
|
|
3153
|
+
raise(@state[:core_root_error] = core_root_build_error(e)) if @core_root
|
|
3154
|
+
|
|
2748
3155
|
record_env_build_failure(e)
|
|
2749
3156
|
warn_about_env_build_failure_once(e)
|
|
2750
3157
|
@state[:env] = nil
|
|
2751
3158
|
end
|
|
2752
3159
|
|
|
3160
|
+
def core_root_build_error(error)
|
|
3161
|
+
CoreRootError.new(
|
|
3162
|
+
"core_root: #{@core_root.inspect} did not build an RBS environment " \
|
|
3163
|
+
"(#{error.class.name}: #{error.message.lines.first.to_s.strip})"
|
|
3164
|
+
)
|
|
3165
|
+
end
|
|
3166
|
+
private :core_root_build_error
|
|
3167
|
+
|
|
2753
3168
|
# Tell the user, once per run, which of their `signature_paths:` `.rbs` files were skipped because they
|
|
2754
3169
|
# do not parse ({RbsLoader.add_project_signatures} quarantines them so the rest of the env survives). A
|
|
2755
3170
|
# skipped file's types are absent, so calls into them read `Dynamic[top]` — silently, without this. This
|
|
@@ -2857,7 +3272,7 @@ module Rigor
|
|
|
2857
3272
|
Cache::RbsEnvironment.fetch(loader: self, store: cache_store)
|
|
2858
3273
|
end
|
|
2859
3274
|
|
|
2860
|
-
# Full `
|
|
3275
|
+
# Full tables of `RBS::Definition` per class for the {#prewarm} / {#reflection} consumers (ADR-15
|
|
2861
3276
|
# Phase 2b/4b.x), which need every definition materialised up front. Built from the (cached) env via
|
|
2862
3277
|
# `RBS::DefinitionBuilder` — ADR-54 WD1 retired the disk blobs these used to `Marshal.load` (building
|
|
2863
3278
|
# all definitions from a cached env is faster), so the eager-table cost is now a compute, not a load.
|
|
@@ -2898,7 +3313,9 @@ module Rigor
|
|
|
2898
3313
|
libraries: @libraries,
|
|
2899
3314
|
signature_paths: @signature_paths,
|
|
2900
3315
|
virtual_rbs: @virtual_rbs,
|
|
2901
|
-
deferred_signature_paths: @deferred_signature_paths
|
|
3316
|
+
deferred_signature_paths: @deferred_signature_paths,
|
|
3317
|
+
project_signature_paths: @project_signature_paths,
|
|
3318
|
+
core_root: @core_root
|
|
2902
3319
|
)
|
|
2903
3320
|
end
|
|
2904
3321
|
|
|
@@ -2925,6 +3342,15 @@ module Rigor
|
|
|
2925
3342
|
rbs_name = canonical_module_name(rbs_name)
|
|
2926
3343
|
return nil unless env.class_decls.key?(rbs_name)
|
|
2927
3344
|
|
|
3345
|
+
# rbs's `AncestorBuilder#singleton_ancestors` has no cycle check: a class whose superclass chain loops
|
|
3346
|
+
# (`class S < ::S`, which a collection copy of the same class used to hide, #2441) recurses until the stack
|
|
3347
|
+
# runs out and aborts the run, where `#instance_ancestors` raises `RBS::RecursiveAncestorError`. Walking the
|
|
3348
|
+
# instance side first turns the loop into that error, so it is this class's build failure. Memoised by the
|
|
3349
|
+
# builder, so a class whose instance side was already built pays nothing. Any other stack exhaustion stays
|
|
3350
|
+
# fatal: it depends on how deep the analysis already was, and must not be memoised as a build failure.
|
|
3351
|
+
if env.class_decls[rbs_name].is_a?(::RBS::Environment::ClassEntry)
|
|
3352
|
+
builder.ancestor_builder.instance_ancestors(rbs_name)
|
|
3353
|
+
end
|
|
2928
3354
|
builder.build_singleton(rbs_name)
|
|
2929
3355
|
rescue ::RBS::BaseError => e
|
|
2930
3356
|
store_definition_build_detail(class_name, e)
|
|
@@ -2954,7 +3380,8 @@ module Rigor
|
|
|
2954
3380
|
key = class_name.to_s.delete_prefix("::")
|
|
2955
3381
|
details = ((@state[:definition_build_details] ||= {})[key] ||= [])
|
|
2956
3382
|
details << [key, error.class.name.to_s, definition_build_member(error),
|
|
2957
|
-
definition_build_conflict_buffers(error), unresolved_type_name_detail(error)
|
|
3383
|
+
definition_build_conflict_buffers(error, key), unresolved_type_name_detail(error),
|
|
3384
|
+
variable_mixin_count(error, key)].freeze
|
|
2958
3385
|
end
|
|
2959
3386
|
|
|
2960
3387
|
# Issue #997 — the `RBS::NoTypeFoundError` twin of {#definition_build_member}: `nil` for every other
|
|
@@ -3105,7 +3532,7 @@ module Rigor
|
|
|
3105
3532
|
end
|
|
3106
3533
|
|
|
3107
3534
|
# The third twin of {#warn_about_quarantined_signatures} / {#warn_about_virtual_rbs_collisions}: name,
|
|
3108
|
-
# once per class per
|
|
3535
|
+
# once per class per loader, a `RBS::DefinitionBuilder` failure caught in
|
|
3109
3536
|
# {#build_instance_definition} / {#build_singleton_definition}'s rescue. Without it, `class_known?`
|
|
3110
3537
|
# stays true (it only consults {#known_class_names_set}, never a definition build), so every call on
|
|
3111
3538
|
# the class — real methods and typos alike — silently degrades to `Dynamic[top]`.
|
|
@@ -3142,14 +3569,14 @@ module Rigor
|
|
|
3142
3569
|
|
|
3143
3570
|
def definition_build_failure_warning(class_name, error)
|
|
3144
3571
|
first_line = error.message.to_s.lines.first.to_s.strip
|
|
3145
|
-
buffers = definition_build_conflict_buffers(error)
|
|
3572
|
+
buffers = definition_build_conflict_buffers(error, class_name)
|
|
3146
3573
|
collisions =
|
|
3147
3574
|
if buffers.empty?
|
|
3148
3575
|
""
|
|
3149
3576
|
else
|
|
3150
3577
|
listed = buffers.first(QUARANTINE_WARN_LIMIT)
|
|
3151
3578
|
more = buffers.size - listed.size
|
|
3152
|
-
lines = listed.map { |name| " - #{name}" }
|
|
3579
|
+
lines = listed.map { |name| " - #{signature_source_label(name)}" }
|
|
3153
3580
|
lines << " … and #{more} more" if more.positive?
|
|
3154
3581
|
"\n Colliding declaration(s):\n#{lines.join("\n")}"
|
|
3155
3582
|
end
|
|
@@ -3158,6 +3585,14 @@ module Rigor
|
|
|
3158
3585
|
"`Dynamic[top]` — real methods and typos alike — instead of resolving normally.#{collisions}"
|
|
3159
3586
|
end
|
|
3160
3587
|
|
|
3588
|
+
# Issue #2440 — a `virtual:<plugin>:<path>` buffer named by the `.rb` file it was synthesized from.
|
|
3589
|
+
def signature_source_label(name)
|
|
3590
|
+
prefix, plugin_id, path = name.to_s.split(":", 3)
|
|
3591
|
+
return name.to_s unless prefix == "virtual" && plugin_id && path && !path.empty?
|
|
3592
|
+
|
|
3593
|
+
"#{path} (inline RBS via #{plugin_id})"
|
|
3594
|
+
end
|
|
3595
|
+
|
|
3161
3596
|
def warn_about_deferred_definition_build_failures
|
|
3162
3597
|
return if @state[:definition_build_summary_warned]
|
|
3163
3598
|
|
|
@@ -3196,10 +3631,11 @@ module Rigor
|
|
|
3196
3631
|
# `InvalidOverloadMethodError` carry `#members` (each with a `#location`);
|
|
3197
3632
|
# `DuplicatedInterfaceMethodDefinitionError` carries a single `#member`; most others
|
|
3198
3633
|
# (`NoSuperclassFoundError`, `UnknownMethodAliasError`, `RecursiveAncestorError`, …) carry only a bare
|
|
3199
|
-
# `#location`.
|
|
3200
|
-
#
|
|
3201
|
-
|
|
3202
|
-
|
|
3634
|
+
# `#location`. A duplicated instance variable carries one location and a `SuperclassMismatchError` none,
|
|
3635
|
+
# so both read the class entry's declarations for the other side (#2440). Falls back to an empty list —
|
|
3636
|
+
# the warning still names the class and the exception's own message — for the remainder.
|
|
3637
|
+
def definition_build_conflict_buffers(error, class_name = nil)
|
|
3638
|
+
definition_build_error_locations(error, class_name)
|
|
3203
3639
|
.compact.filter_map { |loc| loc.buffer&.name }.map(&:to_s)
|
|
3204
3640
|
.reject { |name| name == CACHED_LOCATION_BUFFER_NAME }.uniq.freeze
|
|
3205
3641
|
rescue ::RBS::BaseError, StandardError
|
|
@@ -3208,14 +3644,100 @@ module Rigor
|
|
|
3208
3644
|
|
|
3209
3645
|
# The `RBS::Location`s an error carries, by whichever accessor its class happens to expose (see the
|
|
3210
3646
|
# shape survey above). Split out so the buffer-name extraction reads as one pipeline.
|
|
3211
|
-
def definition_build_error_locations(error)
|
|
3647
|
+
def definition_build_error_locations(error, class_name = nil)
|
|
3212
3648
|
return Array(error.members).map(&:location) if error.respond_to?(:members) && error.members
|
|
3213
3649
|
return [error.member.location] if error.respond_to?(:member) && error.member
|
|
3650
|
+
# `VariableDuplicationError` is younger than the rbs floor the gemspec allows.
|
|
3651
|
+
if defined?(::RBS::VariableDuplicationError) && error.is_a?(::RBS::VariableDuplicationError)
|
|
3652
|
+
return variable_duplication_locations(error, class_name)
|
|
3653
|
+
end
|
|
3654
|
+
return superclass_mismatch_locations(error) if error.is_a?(::RBS::SuperclassMismatchError)
|
|
3214
3655
|
return [error.location] if error.respond_to?(:location)
|
|
3215
3656
|
|
|
3216
3657
|
[]
|
|
3217
3658
|
end
|
|
3218
3659
|
|
|
3660
|
+
# Issue #2440 — a duplicated `@x` / `self.@x` carries the location of ONE of its two declarations, so
|
|
3661
|
+
# the file list named only that side (typically the `virtual:rbs-inline:` buffer, leaving out the
|
|
3662
|
+
# committed `.rbs` the user can actually edit). The class entry's declarations name the rest: every
|
|
3663
|
+
# declaration of the class that declares the same variable, of the same kind. When the variable's
|
|
3664
|
+
# owner is a module the failing class mixes in (rbs counts it once per declaration that does), the
|
|
3665
|
+
# declarations of the failing class that mix that module in are named too: they are the duplicate.
|
|
3666
|
+
def variable_duplication_locations(error, class_name = nil)
|
|
3667
|
+
[error.location, *variable_declarations(error).map(&:location), *mixin_locations(class_name, error.type_name)]
|
|
3668
|
+
end
|
|
3669
|
+
|
|
3670
|
+
# Every declaration of the variable the error names, of the error's kind, in its owner's entry.
|
|
3671
|
+
def variable_declarations(error)
|
|
3672
|
+
member_class =
|
|
3673
|
+
if error.is_a?(::RBS::ClassInstanceVariableDuplicationError)
|
|
3674
|
+
::RBS::AST::Members::ClassInstanceVariable
|
|
3675
|
+
else
|
|
3676
|
+
::RBS::AST::Members::InstanceVariable
|
|
3677
|
+
end
|
|
3678
|
+
class_entry_decls(error.type_name).flat_map do |decl|
|
|
3679
|
+
decl_members(decl).select { |m| m.instance_of?(member_class) && m.name == error.variable_name }
|
|
3680
|
+
end
|
|
3681
|
+
end
|
|
3682
|
+
|
|
3683
|
+
# Issue #2440 — how many times `class_name`'s own declarations mix in the module that owns the
|
|
3684
|
+
# duplicated variable, when that is what duplicated it: the owner is another module, it declares the
|
|
3685
|
+
# variable exactly once, and the class mixes it in at least twice (in one declaration or across
|
|
3686
|
+
# several). nil otherwise — a variable duplicated in the owner itself and inherited (`Sub < Base`)
|
|
3687
|
+
# is not a double mixin. Carried in the failure tuple's sixth slot so the advice can tell the two
|
|
3688
|
+
# apart on a replayed tuple too.
|
|
3689
|
+
def variable_mixin_count(error, class_name)
|
|
3690
|
+
return nil unless defined?(::RBS::VariableDuplicationError) && error.is_a?(::RBS::VariableDuplicationError)
|
|
3691
|
+
|
|
3692
|
+
owner = parse_type_name(class_name)
|
|
3693
|
+
return nil if owner.nil? || owner == error.type_name
|
|
3694
|
+
return nil unless variable_declarations(error).size == 1
|
|
3695
|
+
|
|
3696
|
+
count = class_entry_decls(owner).sum do |decl|
|
|
3697
|
+
decl_members(decl).count { |m| mixin_of?(m, error.type_name) }
|
|
3698
|
+
end
|
|
3699
|
+
count >= 2 ? count : nil
|
|
3700
|
+
rescue ::RBS::BaseError, StandardError
|
|
3701
|
+
nil
|
|
3702
|
+
end
|
|
3703
|
+
|
|
3704
|
+
MIXIN_MEMBERS = [::RBS::AST::Members::Include, ::RBS::AST::Members::Extend,
|
|
3705
|
+
::RBS::AST::Members::Prepend].freeze
|
|
3706
|
+
private_constant :MIXIN_MEMBERS
|
|
3707
|
+
|
|
3708
|
+
# `resolve_type_names` has made every mixin name absolute.
|
|
3709
|
+
def mixin_of?(member, module_name)
|
|
3710
|
+
MIXIN_MEMBERS.any? { |klass| member.is_a?(klass) } && member.name == module_name
|
|
3711
|
+
end
|
|
3712
|
+
|
|
3713
|
+
def mixin_locations(class_name, module_name)
|
|
3714
|
+
return [] if class_name.nil?
|
|
3715
|
+
|
|
3716
|
+
owner = parse_type_name(class_name)
|
|
3717
|
+
return [] if owner.nil? || owner == module_name
|
|
3718
|
+
|
|
3719
|
+
class_entry_decls(owner).filter_map do |decl|
|
|
3720
|
+
decl.location if decl_members(decl).any? { |m| mixin_of?(m, module_name) }
|
|
3721
|
+
end
|
|
3722
|
+
end
|
|
3723
|
+
|
|
3724
|
+
def decl_members(decl)
|
|
3725
|
+
decl.respond_to?(:members) ? Array(decl.members) : []
|
|
3726
|
+
end
|
|
3727
|
+
|
|
3728
|
+
# Issue #2440 — `SuperclassMismatchError` carries the class entry and no location of its own: the
|
|
3729
|
+
# colliding declarations are the ones that name a superclass. {.entry_declarations} reads the entry on
|
|
3730
|
+
# both rbs majors (`each_decl` on 4.x, `MultiEntry::D` wrappers on 3.x).
|
|
3731
|
+
def superclass_mismatch_locations(error)
|
|
3732
|
+
decls = self.class.entry_declarations(error.entry)
|
|
3733
|
+
decls.select { |decl| decl.respond_to?(:super_class) && decl.super_class }.map(&:location)
|
|
3734
|
+
end
|
|
3735
|
+
|
|
3736
|
+
def class_entry_decls(type_name)
|
|
3737
|
+
entry = env&.class_decls&.[](type_name)
|
|
3738
|
+
entry.nil? ? [] : self.class.entry_declarations(entry)
|
|
3739
|
+
end
|
|
3740
|
+
|
|
3219
3741
|
# The member the failure is ABOUT, taken off the error object rather than parsed out of its message.
|
|
3220
3742
|
#
|
|
3221
3743
|
# Issue #696 — the message cannot serve here. It is built from `RBS::Location`s, and the ADR-54 env
|