rigortype 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- 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 +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- 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 +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- 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 +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- 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/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- 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 +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- 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/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- 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 +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -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 +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- 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 +25 -20
- 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 +26 -18
- 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 +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- 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 +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- 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 +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- 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 +2 -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 +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- 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 +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- 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 +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -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/hkt_registry_holder.rb +6 -0
- 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_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -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 +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -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 +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- 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/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- 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 +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- 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 +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- 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/singleton_type_params.rb +104 -0
- 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 +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.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/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -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 +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- 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 +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -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 +8 -3
- 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 +39 -1
- 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 +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- 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 +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- 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 +48 -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 +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- 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 +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- 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 +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- 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/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- 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 +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- 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 +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- 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/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- 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 +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -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 +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- 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 +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- 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 +11 -14
- 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 +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -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 +1 -1
- 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 +8 -7
- 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-graphql/lib/rigor/plugin/graphql.rb +12 -2
- 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 +8 -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 +200 -4
- 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 +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- 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 +3 -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 +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- 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/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -10,8 +10,12 @@ 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"
|
|
13
15
|
require_relative "ruby_library_roots"
|
|
16
|
+
require_relative "core_root"
|
|
14
17
|
require_relative "../rooted_glob"
|
|
18
|
+
require_relative "../typing_state"
|
|
15
19
|
|
|
16
20
|
module Rigor
|
|
17
21
|
class Environment
|
|
@@ -32,6 +36,12 @@ module Rigor
|
|
|
32
36
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
33
37
|
# rubocop:disable-next Metrics/ClassLength
|
|
34
38
|
class RbsLoader
|
|
39
|
+
# ADR-129 G4 — keyed on the class name, answered from the loader's RBS environment (or, for `class_known?`, the
|
|
40
|
+
# known-name set cached for it), fixed for the loader's lifetime. `@state` is not declared: beside its lazy
|
|
41
|
+
# derivations it holds warn-once flags and failure counters.
|
|
42
|
+
TypingState.memo_field(self, :@instance_definition_cache, :@singleton_definition_cache, :@class_known_cache,
|
|
43
|
+
reason: "keyed on the class name; a function of the loader's RBS environment")
|
|
44
|
+
|
|
35
45
|
# Buffer name stamped on the `module` declarations synthesized by {.synthesize_missing_namespaces}.
|
|
36
46
|
# Re-read off the built env by {#synthesized_namespaces} so the analysis layer can surface an `:info`
|
|
37
47
|
# diagnostic naming the project's malformed-RBS namespaces — robust across the marshalled env cache,
|
|
@@ -60,13 +70,27 @@ module Rigor
|
|
|
60
70
|
.compact.map { |root| "#{root}/".freeze }.freeze
|
|
61
71
|
rescue StandardError
|
|
62
72
|
[].freeze
|
|
63
|
-
end
|
|
73
|
+
end.freeze
|
|
64
74
|
|
|
65
75
|
# Cap on how many quarantined `signature_paths:` files {#warn_about_quarantined_signatures} lists by name
|
|
66
76
|
# before collapsing the tail to "… and N more" — a broken generator can emit many, and a wall of parse
|
|
67
77
|
# errors buries the signal.
|
|
68
78
|
QUARANTINE_WARN_LIMIT = 10
|
|
69
79
|
|
|
80
|
+
# ADR-129 WD1 — a Frame: {.with_core_root} installs the core root for one environment build and restores the
|
|
81
|
+
# previous one on the way out. It changes answers (a loader built under it reads a different `core/`), and the
|
|
82
|
+
# installed value is a {CoreRoot.validate!}d frozen String or nil. Per thread (per fiber), so a concurrent build
|
|
83
|
+
# on another thread keeps its own.
|
|
84
|
+
CORE_ROOT = TypingState.frame(
|
|
85
|
+
:rbs_core_root,
|
|
86
|
+
reason: "installed per environment build by with_core_root; selects the core/ directory every loader of the " \
|
|
87
|
+
"build reads (a validated absolute path, or nil for the rbs gem's own)",
|
|
88
|
+
# G1′: the build installs the core root and reads it, and every analysing entry point builds the environment
|
|
89
|
+
# before anything types ({Environment.for_project}, `eager:`; #2883), so no `type_of` reads it.
|
|
90
|
+
typing_never_reads: true
|
|
91
|
+
)
|
|
92
|
+
private_constant :CORE_ROOT
|
|
93
|
+
|
|
70
94
|
class << self
|
|
71
95
|
# Issue #1902 — the fixed total order that picks ONE detail tuple for a class out of several: its two
|
|
72
96
|
# sides in {#report_definition_build_failure}, or a fresh and a replayed record in
|
|
@@ -96,14 +120,40 @@ module Rigor
|
|
|
96
120
|
# Vendored gem stubs (`data/vendored_gem_sigs/<gem>/`) are loaded on top of `signature_paths` so the
|
|
97
121
|
# per-gem RBS bundled with Rigor itself is in scope for every analysis run. The gem stubs are
|
|
98
122
|
# intentionally read-only and appended LAST so user-supplied `signature_paths` win on name conflicts.
|
|
99
|
-
|
|
100
|
-
|
|
123
|
+
#
|
|
124
|
+
# @param project_signature_paths — issue #2422 — the project's own `signature_paths:` (not the collection,
|
|
125
|
+
# gem or plugin directories also on `signature_paths`): a plugin member another source declares composes
|
|
126
|
+
# with it as an overload unless that source is the project's own ({DeferredSignaturePlan}).
|
|
127
|
+
# @param core_root — issue #2517 — a validated {CoreRoot} directory loaded in place of the `rbs` gem's
|
|
128
|
+
# `core/`, or nil for the default core.
|
|
129
|
+
# @param subordinate_signature_paths — issue #2978 — the subset of `signature_paths` that loads right after
|
|
130
|
+
# the other `signature_paths` files and stands down member by member to them ({.add_project_signatures}).
|
|
131
|
+
# @param collection_signature_paths — issue #3030 — the subset of `signature_paths` that are `rbs collection`
|
|
132
|
+
# gem directories, read without their `_`-prefixed directories ({.project_sig_files}).
|
|
133
|
+
def build_env_for(libraries:, signature_paths:, virtual_rbs: [], deferred_signature_paths: [], # rubocop:disable Metrics/ParameterLists
|
|
134
|
+
project_signature_paths: [], core_root: nil, subordinate_signature_paths: [],
|
|
135
|
+
collection_signature_paths: [])
|
|
136
|
+
inputs = [signature_paths, virtual_rbs, deferred_signature_paths, project_signature_paths,
|
|
137
|
+
subordinate_signature_paths, collection_signature_paths]
|
|
138
|
+
with_core_root(core_root) { build_env_from(libraries, inputs) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Issue #2517 — the core root {.library_loader} builds every loader of one environment build against. Held
|
|
142
|
+
# for the build rather than threaded through each attempt, because every trial and fallback of
|
|
143
|
+
# {.build_env_from} must read the same core, and a parameter missed on one of them would build that attempt
|
|
144
|
+
# against the default core without a sign. Thread-local, so a concurrent build on another thread keeps its
|
|
145
|
+
# own.
|
|
146
|
+
def with_core_root(core_root, &)
|
|
147
|
+
TypingState.current.with(CORE_ROOT, core_root, &)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def build_env_from(libraries, inputs)
|
|
101
151
|
env, gated_dirs = whole_gated_attempt(libraries, inputs)
|
|
102
152
|
return env if gated_dirs.empty?
|
|
103
153
|
|
|
104
154
|
# Issue #1700 — a {RequiredFeatures}-gated directory joined; keep it whole only if it does not cost the
|
|
105
155
|
# environment anything ({.gated_failures}).
|
|
106
|
-
failed, quarantined = env ? gated_failures(env, gated_dirs,
|
|
156
|
+
failed, quarantined = env ? gated_failures(env, gated_dirs, inputs) : [[], 1]
|
|
107
157
|
return env if failed.empty? && quarantined.zero?
|
|
108
158
|
|
|
109
159
|
tokens = gated_dirs.map { |dir| RequiredFeatures.token(RequiredFeatures.feature_for_dir(dir)) }
|
|
@@ -114,7 +164,7 @@ module Rigor
|
|
|
114
164
|
trouble = failed.size + quarantined
|
|
115
165
|
return fallback if env.nil?
|
|
116
166
|
|
|
117
|
-
gated_trouble(fallback, gated_dirs,
|
|
167
|
+
gated_trouble(fallback, gated_dirs, inputs) < trouble ? fallback : env
|
|
118
168
|
end
|
|
119
169
|
|
|
120
170
|
# The first attempt, with every activated gated directory whole. rbs 3.x validates a class's generic
|
|
@@ -142,7 +192,7 @@ module Rigor
|
|
|
142
192
|
plan = GatedSignaturePlan.build(roots, fallback)
|
|
143
193
|
if plan.changed
|
|
144
194
|
env = gated_plan_attempt(libraries, inputs, plan)
|
|
145
|
-
failed, quarantined = env ? gated_failures(env, gated_dirs, inputs
|
|
195
|
+
failed, quarantined = env ? gated_failures(env, gated_dirs, inputs) : [failed, 0]
|
|
146
196
|
return env if env && failed.empty? && quarantined.zero?
|
|
147
197
|
end
|
|
148
198
|
return nil if failed.empty?
|
|
@@ -151,7 +201,7 @@ module Rigor
|
|
|
151
201
|
env = gated_plan_attempt(libraries, inputs, plan)
|
|
152
202
|
return nil if env.nil?
|
|
153
203
|
|
|
154
|
-
failed, quarantined = gated_failures(env, gated_dirs, inputs
|
|
204
|
+
failed, quarantined = gated_failures(env, gated_dirs, inputs)
|
|
155
205
|
env if failed.empty? && quarantined.zero?
|
|
156
206
|
end
|
|
157
207
|
|
|
@@ -171,7 +221,28 @@ module Rigor
|
|
|
171
221
|
#
|
|
172
222
|
# @param gated_plan — issue #1713 — a {GatedSignaturePlan} loaded in place of the gated directories: its
|
|
173
223
|
# kept declarations where the directories would have loaded, its shells after the project's signatures.
|
|
174
|
-
|
|
224
|
+
#
|
|
225
|
+
# Issue #2470 — re-made without each {VendoredSignaturePlan} taker the attempt quarantined
|
|
226
|
+
# ({.quarantined_takers}), so a vendored member stands down only to a file the env keeps. Each round adds
|
|
227
|
+
# at least one file to the excluded set, and a round that adds none returns, so the loop ends.
|
|
228
|
+
def build_env_attempt(libraries, *inputs, gated_plan: nil)
|
|
229
|
+
excluded = Set.new
|
|
230
|
+
loop do
|
|
231
|
+
env, gated_dirs, plan = planned_env_attempt(libraries, *inputs, gated_plan: gated_plan,
|
|
232
|
+
vendored_excluded: excluded)
|
|
233
|
+
dropped = quarantined_takers(env, plan)
|
|
234
|
+
return [env, gated_dirs] if dropped.empty? || dropped.subset?(excluded)
|
|
235
|
+
|
|
236
|
+
excluded |= dropped
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# One {.build_env_attempt} round, which also returns its {VendoredSignaturePlan}.
|
|
241
|
+
#
|
|
242
|
+
# @param vendored_excluded — signature files the vendored plan is made without.
|
|
243
|
+
def planned_env_attempt(libraries, signature_paths, virtual_rbs, deferred_signature_paths, # rubocop:disable Metrics/ParameterLists
|
|
244
|
+
project_signature_paths = [], subordinate_signature_paths = [],
|
|
245
|
+
collection_signature_paths = [], gated_plan: nil, vendored_excluded: Set.new)
|
|
175
246
|
libraries = libraries_without_shadowed_bigdecimal_math(libraries)
|
|
176
247
|
rbs_loader, loaded_libraries = library_loader(libraries)
|
|
177
248
|
gated_dirs = loadable_gated_dirs(libraries, loaded_libraries)
|
|
@@ -181,15 +252,20 @@ module Rigor
|
|
|
181
252
|
# type-of query then degrades to `Dynamic[top]` — the "sig looks harmful" failure of the 2026-07-06
|
|
182
253
|
# mastodon coverage note). Per-file loading quarantines the broken file instead. Vendored / core-overlay
|
|
183
254
|
# sigs are Rigor-shipped and trusted, so they stay on the loader's fast batch path.
|
|
184
|
-
|
|
185
|
-
|
|
255
|
+
project_files = project_sig_files(signature_paths, collection_signature_paths)
|
|
256
|
+
undeferred_files = project_files - project_sig_files(deferred_signature_paths)
|
|
257
|
+
env, vendored_plan = bundled_env(rbs_loader, loaded_libraries, gated_plan ? [] : gated_dirs,
|
|
258
|
+
undeferred_files, vendored_excluded)
|
|
186
259
|
gated_plan&.leading&.each { |source| add_parsed_decls(env, *source) }
|
|
187
|
-
project_files = project_sig_files(signature_paths)
|
|
188
260
|
# Issue #1075 — decided from the inputs before either side enters `env`: the consistency rule may
|
|
189
261
|
# stand the `.rbs` member down, and that has to happen before its file is added.
|
|
190
|
-
|
|
262
|
+
# Issue #2422 — a bundled plugin's deferred files are not the project's: they never take part in the
|
|
263
|
+
# inline-vs-`.rbs` consistency rule, and stand down to an inline member like to any other source.
|
|
264
|
+
consistency = member_consistency_for(undeferred_files, virtual_rbs)
|
|
191
265
|
add_project_signatures(env, signature_paths, deferred_signature_paths,
|
|
192
|
-
standdowns: consistency.signature_standdowns
|
|
266
|
+
standdowns: consistency.signature_standdowns,
|
|
267
|
+
subordinate_paths: subordinate_signature_paths,
|
|
268
|
+
collection_paths: collection_signature_paths)
|
|
193
269
|
gated_plan&.shells&.each { |source| add_parsed_decls(env, *source) }
|
|
194
270
|
# Issue #928 — the capability-role catalog goes in AFTER the project's own signatures, per
|
|
195
271
|
# declaration, so a project that already declares one of these interface names keeps its own
|
|
@@ -201,16 +277,52 @@ module Rigor
|
|
|
201
277
|
# Issues #824 / #1075 — every member a `.rbs` file also declares was ranked above; the inline side of
|
|
202
278
|
# each pair the `.rbs` won stands down here ({.add_virtual_rbs}), so the two never collide.
|
|
203
279
|
add_virtual_rbs(env, virtual_rbs, inline_standdowns: consistency.inline_standdowns)
|
|
280
|
+
# Issues #610 / #2422 — the plugins' files last, so each is planned against every other source.
|
|
281
|
+
add_plugin_signatures(env, deferred_signature_paths, project_signature_paths, virtual_rbs)
|
|
204
282
|
synthesize_missing_namespaces(env)
|
|
205
283
|
# Issue #777 — resolve-time backstop: also unload colliding PROJECT buffers (class-vs-module /
|
|
206
284
|
# constant redeclarations against bundled RBS). Virtual culprits are preferred when both appear.
|
|
207
285
|
env, resolved = resolve_quarantining_virtual_collisions(env, virtual_rbs, project_files: project_files)
|
|
208
|
-
[stub_missing_referenced_types(env, resolved, project_files), gated_dirs]
|
|
286
|
+
[stub_missing_referenced_types(env, resolved, project_files), gated_dirs, vendored_plan]
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Issue #2470 — the {VendoredSignaturePlan} takers `env` does not hold: a file the build quarantined after a
|
|
290
|
+
# vendored member stood down to it (a `DuplicatedDeclarationError`, a class-vs-module clash). The answer reads
|
|
291
|
+
# only the build's inputs, so a cold, a warm and a `--no-cache` run agree.
|
|
292
|
+
def quarantined_takers(env, plan)
|
|
293
|
+
return Set.new if plan.takers.empty? || env.nil?
|
|
294
|
+
|
|
295
|
+
buffers = loaded_signature_buffers(env)
|
|
296
|
+
return Set.new if buffers.nil?
|
|
297
|
+
|
|
298
|
+
present = buffers.to_set { |buffer| buffer.name.to_s }
|
|
299
|
+
plan.takers.reject { |file| present.include?(file) }.to_set
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# The environment of the loader's libraries and the Rigor-shipped signatures. Issue #2470 — a vendored
|
|
303
|
+
# directory with a member a `signature_files` file also declares loads from its {VendoredSignaturePlan},
|
|
304
|
+
# right after the batch, instead of whole on it.
|
|
305
|
+
def bundled_env(rbs_loader, loaded_libraries, gated_dirs, signature_files, excluded)
|
|
306
|
+
loaded_library_names = loaded_libraries.to_set(&:to_s)
|
|
307
|
+
plan = vendored_signature_plan(loaded_library_names, signature_files, excluded)
|
|
308
|
+
add_bundled_signatures(rbs_loader, loaded_library_names, gated_dirs, planned: plan.dirs)
|
|
309
|
+
env = unload_upstream_core_shims(RBS::Environment.from_loader(rbs_loader), rbs_loader)
|
|
310
|
+
plan.sources.each { |source| add_parsed_decls(env, *source) }
|
|
311
|
+
[env, plan]
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Issue #2422 — {.add_deferred_signatures} over the plugins' files, with the buffer names of the project's
|
|
315
|
+
# own signature sources, which a plugin member stands down to rather than composing with: its
|
|
316
|
+
# `signature_paths:` files and its inline RBS.
|
|
317
|
+
def add_plugin_signatures(env, deferred_signature_paths, project_signature_paths, virtual_rbs)
|
|
318
|
+
own = project_sig_files(project_signature_paths) | virtual_rbs.to_set { |name, _| name.to_s }
|
|
319
|
+
add_deferred_signatures(env, project_sig_files(deferred_signature_paths), own)
|
|
209
320
|
end
|
|
210
321
|
|
|
211
322
|
# An `RBS::EnvironmentLoader` with every library of `libraries` that resolves added, and those libraries.
|
|
212
323
|
def library_loader(libraries)
|
|
213
|
-
|
|
324
|
+
core_root = TypingState.current[CORE_ROOT]
|
|
325
|
+
rbs_loader = core_root ? RBS::EnvironmentLoader.new(core_root: Pathname(core_root)) : RBS::EnvironmentLoader.new
|
|
214
326
|
# Issue #1700 — a `RequiredFeatures` token names a gated vendored directory, never an RBS library.
|
|
215
327
|
loaded_libraries = libraries.select do |library|
|
|
216
328
|
next false if RequiredFeatures.feature_of_token(library)
|
|
@@ -233,14 +345,16 @@ module Rigor
|
|
|
233
345
|
# rather than a comparison of declarations because it answers exactly what RBS itself decides — an
|
|
234
346
|
# overload continuation (`| ...`), a superclass written relative or absolute, a type parameter — with no
|
|
235
347
|
# second reading of the rules to drift. It runs only when a gated directory joined, on an env-cache miss.
|
|
236
|
-
def gated_trouble(env, dirs,
|
|
237
|
-
failed, quarantined = gated_failures(env, dirs,
|
|
348
|
+
def gated_trouble(env, dirs, inputs)
|
|
349
|
+
failed, quarantined = gated_failures(env, dirs, inputs)
|
|
238
350
|
failed.size + quarantined
|
|
239
351
|
end
|
|
240
352
|
|
|
241
353
|
# {.gated_trouble} split in two: the gated type names whose definitions fail, and the number of quarantined
|
|
242
|
-
# signature sources that declare one. Issue #1713 re-plans against the first.
|
|
243
|
-
|
|
354
|
+
# signature sources that declare one. Issue #1713 re-plans against the first. `inputs` is
|
|
355
|
+
# {.build_env_for}'s list: the signature paths, the virtual RBS, and (last) the collection directories.
|
|
356
|
+
def gated_failures(env, dirs, inputs)
|
|
357
|
+
signature_paths, virtual_rbs = inputs
|
|
244
358
|
type_names = gated_type_names(dirs)
|
|
245
359
|
builder = RBS::DefinitionBuilder.new(env: env)
|
|
246
360
|
failed = type_names.select do |type_name|
|
|
@@ -257,7 +371,8 @@ module Rigor
|
|
|
257
371
|
true
|
|
258
372
|
end
|
|
259
373
|
declares = gated_declaration_pattern(type_names)
|
|
260
|
-
|
|
374
|
+
collided = collision_quarantined_project_signatures(env, signature_paths, inputs[5] || [])
|
|
375
|
+
quarantined = collided.count do |file, _note|
|
|
261
376
|
File.read(file).match?(declares)
|
|
262
377
|
rescue SystemCallError, IOError, ArgumentError
|
|
263
378
|
false
|
|
@@ -546,12 +661,22 @@ module Rigor
|
|
|
546
661
|
# The absolute paths of every `.rbs` file under the project's `signature_paths:` (NOT vendored /
|
|
547
662
|
# stdlib RBS — those are well-formed, so attempting to build them would only waste time). Used to
|
|
548
663
|
# scope the referenced-type build sweep.
|
|
549
|
-
|
|
664
|
+
#
|
|
665
|
+
# @param collection_paths — issue #3030 — the entries of `signature_paths` that are `rbs collection` gem
|
|
666
|
+
# directories, read without the files below a `_`-prefixed directory, as rbs reads a collection gem
|
|
667
|
+
# ({RbsCollectionDiscovery.hidden_file?}). Compared by spelling, as the loader receives both lists from
|
|
668
|
+
# one `Environment.for_project` call.
|
|
669
|
+
def project_sig_files(signature_paths, collection_paths = [])
|
|
670
|
+
collection = collection_paths.to_set(&:to_s)
|
|
550
671
|
signature_paths.flat_map do |path|
|
|
551
672
|
path = Pathname(path) unless path.is_a?(Pathname)
|
|
552
673
|
next [] unless path.directory?
|
|
553
674
|
|
|
554
|
-
RootedGlob.glob(path, "**/*.rbs")
|
|
675
|
+
files = RootedGlob.glob(path, "**/*.rbs")
|
|
676
|
+
if collection.include?(path.to_s)
|
|
677
|
+
files = files.reject { |file| RbsCollectionDiscovery.hidden_file?(path, file) }
|
|
678
|
+
end
|
|
679
|
+
files.map { |p| File.expand_path(p) }
|
|
555
680
|
end.to_set
|
|
556
681
|
end
|
|
557
682
|
|
|
@@ -569,9 +694,16 @@ module Rigor
|
|
|
569
694
|
# @param standdowns — issue #1075 — `{signature_path => Set[[class, method, kind]]}`, the members an
|
|
570
695
|
# inline declaration displaced because it was the more precise side of a consistent pair
|
|
571
696
|
# ({MemberConsistency::Resolution#signature_standdowns}). They are removed before the file is added.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
697
|
+
# @param subordinate_paths — issue #2978 — the subset loaded after the rest, planned against them as a deferred
|
|
698
|
+
# file is ({.add_deferred_signatures}) with every one of them as `own`: a member any of them declares stands
|
|
699
|
+
# down from the subordinate file instead of failing the class's definition build. A gem's `rbs collection`
|
|
700
|
+
# copy sits behind the gem's own `sig/` this way, so `Bar#a` in both binds the gem's, and `Bar#b` in the
|
|
701
|
+
# copy alone still loads.
|
|
702
|
+
def add_project_signatures(env, signature_paths, deferred_paths = [], standdowns: {}, subordinate_paths: [],
|
|
703
|
+
collection_paths: [])
|
|
704
|
+
subordinate = project_sig_files(subordinate_paths, collection_paths) - project_sig_files(deferred_paths)
|
|
705
|
+
eager = project_sig_files(signature_paths, collection_paths) - project_sig_files(deferred_paths) - subordinate
|
|
706
|
+
eager.sort.each do |file|
|
|
575
707
|
parsed = parse_signature_file(file)
|
|
576
708
|
next if parsed.nil? # quarantined (unparseable) or unreadable — skip so the env survives
|
|
577
709
|
|
|
@@ -579,7 +711,7 @@ module Rigor
|
|
|
579
711
|
strip_members!(decls, standdowns[file]) if standdowns.key?(file)
|
|
580
712
|
add_project_parsed_decls(env, buffer, directives, decls)
|
|
581
713
|
end
|
|
582
|
-
add_deferred_signatures(env,
|
|
714
|
+
add_deferred_signatures(env, subordinate, eager, standdowns: standdowns)
|
|
583
715
|
end
|
|
584
716
|
|
|
585
717
|
# Issue #777 — add one project signature transactionally. `RBS::Environment#add_source` appends to
|
|
@@ -617,16 +749,26 @@ module Rigor
|
|
|
617
749
|
# (rigor-activerecord's is `relation.rbs`, declaring exactly `Relation`), so dropping the file drops
|
|
618
750
|
# exactly the conflicting declaration. Splitting a plugin file that mixes a colliding class with
|
|
619
751
|
# innocent ones is the change to make if that ever stops being true.
|
|
620
|
-
|
|
752
|
+
#
|
|
753
|
+
# Issue #2422 — at equal arity the plugin reopens the class, and a method both sources declare fails the
|
|
754
|
+
# definition build (`DuplicatedMethodDefinitionError`) for the class and everything under it; a constant
|
|
755
|
+
# both declare raises `DuplicatedDeclarationError` and drops the plugin's whole file. So the file is planned
|
|
756
|
+
# against the env before it is added ({DeferredSignaturePlan}): a colliding method composes with another
|
|
757
|
+
# source's `def` as an overload continuation, or stands down when the other source is the project's own
|
|
758
|
+
# (`own`, buffer names) or cannot be continued; a colliding declaration stands down on its own; the rest
|
|
759
|
+
# of the file loads. {.deferred_member_standdowns} reads what stood down back off the built env.
|
|
760
|
+
#
|
|
761
|
+
# @param standdowns — issue #2978 — {.add_project_signatures}' members to remove before a file is planned.
|
|
762
|
+
def add_deferred_signatures(env, deferred_files, own = Set.new, standdowns: {})
|
|
621
763
|
deferred_files.sort.each do |file|
|
|
622
764
|
parsed = parse_signature_file(file)
|
|
623
765
|
next if parsed.nil?
|
|
624
766
|
|
|
625
767
|
buffer, directives, decls = parsed
|
|
768
|
+
strip_members!(decls, standdowns[file]) if standdowns.key?(file)
|
|
626
769
|
next if generic_arity_conflict(env, decls)
|
|
627
770
|
|
|
628
|
-
|
|
629
|
-
add_project_parsed_decls(env, buffer, directives, decls)
|
|
771
|
+
add_project_parsed_decls(env, buffer, directives, DeferredSignaturePlan.plan(decls, env, own))
|
|
630
772
|
end
|
|
631
773
|
end
|
|
632
774
|
|
|
@@ -648,6 +790,24 @@ module Rigor
|
|
|
648
790
|
end
|
|
649
791
|
end
|
|
650
792
|
|
|
793
|
+
# Issue #2422 — the members {.add_deferred_signatures} left out of each deferred file it loaded, as
|
|
794
|
+
# `[absolute_path, declaration, nil, nil, cause_file, :member]` — `declaration` reading `Object#acts_like?`
|
|
795
|
+
# or `Time.at`, `cause_file` the buffer of the source that declares it, or nil when that cannot be named.
|
|
796
|
+
# Derived from the built env for {.deferred_standdowns}' reason; a file that stood down whole is
|
|
797
|
+
# {.deferred_standdowns}' row, and one the env does not hold has nothing to read back.
|
|
798
|
+
def deferred_member_standdowns(env, deferred_paths)
|
|
799
|
+
return [] if env.nil? || deferred_paths.nil? || deferred_paths.empty?
|
|
800
|
+
|
|
801
|
+
loaded = loaded_signature_buffers(env)&.to_set { |buffer| buffer.name.to_s } || Set.new
|
|
802
|
+
project_sig_files(deferred_paths).sort.flat_map do |file|
|
|
803
|
+
parsed = loaded.include?(file) ? parse_signature_file(file) : nil
|
|
804
|
+
next [] if parsed.nil? || generic_arity_conflict(env, parsed[2])
|
|
805
|
+
|
|
806
|
+
rows = DeferredSignaturePlan.standdown_rows(env, file, parsed[2]).sort_by(&:first)
|
|
807
|
+
rows.map { |label, cause| [file, label, nil, nil, cause, :member] }
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
|
|
651
811
|
# The first `[class_name, existing_arity, incoming_arity, existing_file]` where `decls` would
|
|
652
812
|
# re-declare a class the env already has with a DIFFERENT number of type parameters, or nil when
|
|
653
813
|
# they agree everywhere. Equal arity is not a conflict: RBS reopens the class, which is the ordinary
|
|
@@ -690,9 +850,9 @@ module Rigor
|
|
|
690
850
|
decls.each do |decl|
|
|
691
851
|
case decl
|
|
692
852
|
when ::RBS::AST::Declarations::Module
|
|
693
|
-
each_declared_class(decl.members,
|
|
853
|
+
each_declared_class(decl.members, declared_class_path(decl, prefix), &block)
|
|
694
854
|
when ::RBS::AST::Declarations::Class
|
|
695
|
-
inner = prefix
|
|
855
|
+
inner = declared_class_path(decl, prefix)
|
|
696
856
|
block.call("::#{inner.join('::')}", decl.type_params.size)
|
|
697
857
|
each_declared_class(decl.members, inner, &block)
|
|
698
858
|
end
|
|
@@ -733,8 +893,8 @@ module Rigor
|
|
|
733
893
|
# {.add_project_signatures} so the warning fires even on a cache hit (where the env was already built
|
|
734
894
|
# with the file quarantined). Cheap: it only re-parses the user's own (usually small) `sig/` set, and
|
|
735
895
|
# returns empty immediately when there is no `signature_paths:`.
|
|
736
|
-
def quarantined_project_signatures(signature_paths)
|
|
737
|
-
project_sig_files(signature_paths).sort.filter_map do |file|
|
|
896
|
+
def quarantined_project_signatures(signature_paths, collection_paths = [])
|
|
897
|
+
project_sig_files(signature_paths, collection_paths).sort.filter_map do |file|
|
|
738
898
|
# The note carries the path itself because the warn composer prints only this element — a
|
|
739
899
|
# `ParsingError` message embeds its `path:line:` prefix, so the composer never adds one.
|
|
740
900
|
content = File.read(file, encoding: "UTF-8")
|
|
@@ -765,7 +925,7 @@ module Rigor
|
|
|
765
925
|
COLLISION_QUARANTINE_NOTE =
|
|
766
926
|
"duplicated declaration against bundled RBS — quarantined so the rest of the RBS env still loads"
|
|
767
927
|
|
|
768
|
-
def collision_quarantined_project_signatures(env, signature_paths)
|
|
928
|
+
def collision_quarantined_project_signatures(env, signature_paths, collection_paths = [])
|
|
769
929
|
return [] if env.nil?
|
|
770
930
|
|
|
771
931
|
present = env.buffers.to_set do |buffer|
|
|
@@ -777,7 +937,7 @@ module Rigor
|
|
|
777
937
|
name.to_s
|
|
778
938
|
end
|
|
779
939
|
buffers_by_basename = env.buffers.group_by { |buffer| File.basename(buffer.name.to_s) }
|
|
780
|
-
project_sig_files(signature_paths).sort.filter_map do |file|
|
|
940
|
+
project_sig_files(signature_paths, collection_paths).sort.filter_map do |file|
|
|
781
941
|
next if present.include?(file)
|
|
782
942
|
next if parse_signature_file(file).nil? # parse / encoding quarantine owns these
|
|
783
943
|
next if project_signature_loaded_under_other_path?(file, buffers_by_basename)
|
|
@@ -1133,11 +1293,12 @@ module Rigor
|
|
|
1133
1293
|
end
|
|
1134
1294
|
|
|
1135
1295
|
# `[owners, shadowable]`. `owners` is `{[class_name, method_name, kind] => [signature_path, member,
|
|
1136
|
-
# visibility]}` for every method-shaped member the project's own
|
|
1137
|
-
# the inline sources also name; the first file in sorted order
|
|
1138
|
-
# (that pair fails the definition build anyway). `shadowable` is
|
|
1139
|
-
# module name the inline sources and the signature files declare
|
|
1140
|
-
# might resolve to inside an enclosing namespace
|
|
1296
|
+
# visibility]}` for every method-shaped or instance-variable member the project's own
|
|
1297
|
+
# `signature_paths:` files declare and the inline sources also name; the first file in sorted order
|
|
1298
|
+
# owns a key two `.rbs` files declare (that pair fails the definition build anyway). `shadowable` is
|
|
1299
|
+
# every `::` segment of every class / module name the inline sources and the signature files declare
|
|
1300
|
+
# — the names a relative reference might resolve to inside an enclosing namespace
|
|
1301
|
+
# ({MemberConsistency::Comparator#faithful?}).
|
|
1141
1302
|
#
|
|
1142
1303
|
# The parse is scoped by a substring pre-filter over each file's text: a declaration has to spell its
|
|
1143
1304
|
# own name, so a signature file mentioning none of the names the inline sources declare cannot
|
|
@@ -1163,14 +1324,14 @@ module Rigor
|
|
|
1163
1324
|
def index_signature_members(index, file, decls)
|
|
1164
1325
|
return if decls.nil?
|
|
1165
1326
|
|
|
1166
|
-
each_declared_member(decls) do |class_name, member, visibility|
|
|
1167
|
-
|
|
1327
|
+
each_declared_member(decls) do |class_name, member, visibility, nesting|
|
|
1328
|
+
member_keys(member, nesting).each do |method_name, kind|
|
|
1168
1329
|
index[[class_name, method_name, kind]] ||= [file, member, visibility].freeze
|
|
1169
1330
|
end
|
|
1170
1331
|
end
|
|
1171
1332
|
end
|
|
1172
1333
|
|
|
1173
|
-
# The directive heads a refinement {MemberConsistency
|
|
1334
|
+
# The directive heads a refinement {MemberConsistency::Comparator#refinement_conflicts} checks, used to skip
|
|
1174
1335
|
# parsing an inline source that carries none when no `.rbs` overlaps it. Narrower than `rigor:v1:` on
|
|
1175
1336
|
# purpose: rbs-inline's output marks every skeleton `%a{rigor:v1:inferred-return}`, so that prefix is
|
|
1176
1337
|
# in nearly every synthesized source.
|
|
@@ -1190,49 +1351,65 @@ module Rigor
|
|
|
1190
1351
|
decls = parse_signature_source(name, content)
|
|
1191
1352
|
next if decls.nil?
|
|
1192
1353
|
|
|
1193
|
-
each_declared_member(decls) do |class_name, member, visibility|
|
|
1194
|
-
entries << [name.to_s, class_name, member, visibility]
|
|
1354
|
+
each_declared_member(decls) do |class_name, member, visibility, nesting|
|
|
1355
|
+
entries << [name.to_s, class_name, member, visibility, nesting]
|
|
1195
1356
|
end
|
|
1196
1357
|
end
|
|
1197
1358
|
end
|
|
1198
1359
|
|
|
1199
|
-
# Removes, in place, every member contributing a key in `keys` (a Set of `[class, method, kind]
|
|
1200
|
-
# Nested declarations are descended into after the
|
|
1201
|
-
#
|
|
1202
|
-
def strip_members!(decls, keys, prefix = [])
|
|
1360
|
+
# Removes, in place, every member contributing a key in `keys` (a Set of `[class, method, kind]`,
|
|
1361
|
+
# instance-variable keys included, #2440). Nested declarations are descended into after the
|
|
1362
|
+
# rejection, and a declaration member contributes no key, so nesting is never itself removed.
|
|
1363
|
+
def strip_members!(decls, keys, prefix = [], written = [])
|
|
1203
1364
|
Array(decls).each do |decl|
|
|
1204
1365
|
next unless declaration_with_members?(decl)
|
|
1205
1366
|
|
|
1206
|
-
inner = prefix
|
|
1367
|
+
inner = declared_class_path(decl, prefix)
|
|
1368
|
+
nesting = written + [decl.name.to_s]
|
|
1207
1369
|
class_name = inner.join("::")
|
|
1208
1370
|
decl.members.reject! do |member|
|
|
1209
|
-
|
|
1371
|
+
member_keys(member, nesting.join("/")).any? do |method_name, kind|
|
|
1372
|
+
keys.include?([class_name, method_name, kind])
|
|
1373
|
+
end
|
|
1210
1374
|
end
|
|
1211
|
-
strip_members!(decl.members, keys, inner)
|
|
1375
|
+
strip_members!(decl.members, keys, inner, nesting)
|
|
1212
1376
|
end
|
|
1213
1377
|
end
|
|
1214
1378
|
|
|
1215
|
-
#
|
|
1216
|
-
#
|
|
1217
|
-
#
|
|
1218
|
-
#
|
|
1219
|
-
#
|
|
1220
|
-
|
|
1221
|
-
|
|
1379
|
+
# The class path a declaration contributes to member keys: its name `::`-stripped, under the enclosing
|
|
1380
|
+
# path unless the name is absolute. A leading `::` resets the path, as RBS resolves `module A; class
|
|
1381
|
+
# ::Abs` to the top-level `Abs` (issue #2474), and so do the children of that declaration. The
|
|
1382
|
+
# `nesting` a mixin is keyed by is not reset: it keeps the names as written, which is the lexical
|
|
1383
|
+
# scope a relative mixin name resolves through.
|
|
1384
|
+
def declared_class_path(decl, prefix)
|
|
1385
|
+
base = decl.name.to_s.start_with?("::") ? [] : prefix
|
|
1386
|
+
base + [decl.name.to_s.delete_prefix("::")]
|
|
1387
|
+
end
|
|
1388
|
+
|
|
1389
|
+
# Yields `[class_name, member, visibility, nesting]` for every member of every class / module
|
|
1390
|
+
# declaration in `decls`, descending through nesting. The enclosing path is accumulated and the name
|
|
1391
|
+
# rendered `::`-stripped, so a `module Foo; class Bar` and a `class Foo::Bar` elsewhere key the same —
|
|
1392
|
+
# which is what makes a `sig/` declaration and an inline one comparable at all. `visibility` is the
|
|
1393
|
+
# member's own (`private def x`) or else the one the enclosing `private` / `public` section sets; a
|
|
1394
|
+
# singleton method takes no section, as in Ruby. `nesting` is the declaration names exactly as
|
|
1395
|
+
# written, outermost first, joined by `/` (`Foo/Bar` against `Foo::Bar`): a mixin's relative name
|
|
1396
|
+
# resolves through it, so {MemberConsistency.member_keys} keys a mixin by it (#2440).
|
|
1397
|
+
def each_declared_member(decls, prefix = [], written = [], &block)
|
|
1222
1398
|
Array(decls).each do |decl|
|
|
1223
1399
|
next unless declaration_with_members?(decl)
|
|
1224
1400
|
|
|
1225
|
-
inner = prefix
|
|
1401
|
+
inner = declared_class_path(decl, prefix)
|
|
1402
|
+
nesting = written + [decl.name.to_s]
|
|
1226
1403
|
class_name = inner.join("::")
|
|
1227
1404
|
section = :public
|
|
1228
1405
|
decl.members.each do |member|
|
|
1229
1406
|
case member
|
|
1230
1407
|
when ::RBS::AST::Members::Private then section = :private
|
|
1231
1408
|
when ::RBS::AST::Members::Public then section = :public
|
|
1232
|
-
else block.call(class_name, member, member_visibility(member, section))
|
|
1409
|
+
else block.call(class_name, member, member_visibility(member, section), nesting.join("/"))
|
|
1233
1410
|
end
|
|
1234
1411
|
end
|
|
1235
|
-
each_declared_member(decl.members, inner, &block)
|
|
1412
|
+
each_declared_member(decl.members, inner, nesting, &block)
|
|
1236
1413
|
end
|
|
1237
1414
|
end
|
|
1238
1415
|
|
|
@@ -1254,6 +1431,12 @@ module Rigor
|
|
|
1254
1431
|
MemberConsistency.member_method_keys(member)
|
|
1255
1432
|
end
|
|
1256
1433
|
|
|
1434
|
+
# {MemberConsistency.member_keys} — the method keys plus an instance variable's and a mixin's (#2440),
|
|
1435
|
+
# which is what the inline-vs-`sig/` consistency rule indexes and strips by.
|
|
1436
|
+
def member_keys(member, nesting = nil)
|
|
1437
|
+
MemberConsistency.member_keys(member, nesting)
|
|
1438
|
+
end
|
|
1439
|
+
|
|
1257
1440
|
# A class / module declaration header, as the pre-filter in {.signature_member_index} reads it out of
|
|
1258
1441
|
# synthesized RBS text.
|
|
1259
1442
|
INLINE_DECLARATION_NAME = /^[ \t]*(?:class|module)[ \t]+([A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*)/
|
|
@@ -1402,7 +1585,8 @@ module Rigor
|
|
|
1402
1585
|
# add self-contained declarations) and stay unconditional.
|
|
1403
1586
|
LIBRARY_SUPPLEMENT_CORE_OVERLAYS = {
|
|
1404
1587
|
"resolv.rbs" => "resolv",
|
|
1405
|
-
"string_scanner.rbs" => "strscan"
|
|
1588
|
+
"string_scanner.rbs" => "strscan",
|
|
1589
|
+
"bigdecimal.rbs" => "bigdecimal"
|
|
1406
1590
|
}.freeze
|
|
1407
1591
|
private_constant :LIBRARY_SUPPLEMENT_CORE_OVERLAYS
|
|
1408
1592
|
|
|
@@ -1428,8 +1612,12 @@ module Rigor
|
|
|
1428
1612
|
#
|
|
1429
1613
|
# @param loaded_library_names — libraries that actually resolved on this loader.
|
|
1430
1614
|
# @param gated_dirs — the {RequiredFeatures}-gated directory basenames this library list activates.
|
|
1431
|
-
|
|
1615
|
+
#
|
|
1616
|
+
# @param planned — issue #2470 — the {VendoredSignaturePlan} directories, which load from the plan instead.
|
|
1617
|
+
def add_bundled_signatures(rbs_loader, loaded_library_names, gated_dirs = [], planned: [])
|
|
1432
1618
|
vendored_gem_sig_paths.each do |path|
|
|
1619
|
+
next if planned.include?(path)
|
|
1620
|
+
|
|
1433
1621
|
rbs_loader.add(path: path) if vendored_dir_loads?(path, loaded_library_names, gated_dirs)
|
|
1434
1622
|
end
|
|
1435
1623
|
core_overlay_sig_paths.each do |dir|
|
|
@@ -1445,6 +1633,18 @@ module Rigor
|
|
|
1445
1633
|
end
|
|
1446
1634
|
end
|
|
1447
1635
|
|
|
1636
|
+
# Issue #2470 — the ungated vendored directories this build loads, planned against the signature files
|
|
1637
|
+
# that outrank them ({VendoredSignaturePlan}). The gated ones have their own plan (#1713).
|
|
1638
|
+
#
|
|
1639
|
+
# @param signature_files — the `signature_paths:` files a bundled plugin did not contribute: the project's
|
|
1640
|
+
# own, a collection's and a gem's. A plugin's files are planned against the env instead (#2422).
|
|
1641
|
+
def vendored_signature_plan(loaded_library_names, signature_files, excluded = Set.new)
|
|
1642
|
+
roots = vendored_gem_sig_paths.select do |path|
|
|
1643
|
+
!RequiredFeatures.gated_dir?(path.basename.to_s) && vendored_dir_loads?(path, loaded_library_names, [])
|
|
1644
|
+
end
|
|
1645
|
+
VendoredSignaturePlan.build(roots, signature_files, excluded)
|
|
1646
|
+
end
|
|
1647
|
+
|
|
1448
1648
|
# Issue #1700 — the {RequiredFeatures}-gated directories `libraries` activates, less one whose feature
|
|
1449
1649
|
# resolved as an RBS library here (`libraries: [prime]` on a host whose `prime` gem ships its `sig/`),
|
|
1450
1650
|
# whose own copy is then already loaded. {.build_env_for} then keeps a directory that joined only if the
|
|
@@ -1487,17 +1687,19 @@ module Rigor
|
|
|
1487
1687
|
end
|
|
1488
1688
|
|
|
1489
1689
|
# Rigor-owned per-gem RBS overlays (`data/gem_overlay/<gem>/`), ADR-72. Unlike the unconditional
|
|
1490
|
-
# `core_overlay`, each gem's overlay is loaded
|
|
1491
|
-
# Gemfile.lock
|
|
1492
|
-
#
|
|
1493
|
-
#
|
|
1690
|
+
# `core_overlay`, each gem's overlay is loaded only when that gem is locked in the project's
|
|
1691
|
+
# Gemfile.lock AND {Environment.for_project} finds its coverage source overlay-eligible
|
|
1692
|
+
# ({Environment::GEM_OVERLAY_COVERAGE_SOURCES} — `:missing` for every overlay but addressable's
|
|
1693
|
+
# `:rbs_collection` correction, #2857) with no reachable plugin twin; this method receives the
|
|
1694
|
+
# already-filtered gem-name set. One directory per gem name keeps the membership check a cheap
|
|
1695
|
+
# `File.directory?`.
|
|
1494
1696
|
GEM_OVERLAY_SIGS_ROOT = File.expand_path(
|
|
1495
1697
|
"../../../data/gem_overlay",
|
|
1496
1698
|
__dir__
|
|
1497
1699
|
).freeze
|
|
1498
1700
|
|
|
1499
|
-
# @param gem_names — overlay-eligible Gemfile.lock gem names (the caller filters
|
|
1500
|
-
#
|
|
1701
|
+
# @param gem_names — overlay-eligible Gemfile.lock gem names (the caller filters to the
|
|
1702
|
+
# coverage-source-eligible, no-conflicting-plugin set).
|
|
1501
1703
|
# @return the bundled overlay directory for each gem that ships one; empty when
|
|
1502
1704
|
# none match or the overlay root is absent.
|
|
1503
1705
|
def gem_overlay_sig_paths(gem_names)
|
|
@@ -1511,13 +1713,16 @@ module Rigor
|
|
|
1511
1713
|
|
|
1512
1714
|
# @param path — typically an entry from an {RbsLoader} instance's
|
|
1513
1715
|
# {#signature_paths}.
|
|
1514
|
-
# @return whether `path` sits under the bundled gem-overlay root —
|
|
1515
|
-
#
|
|
1516
|
-
#
|
|
1517
|
-
#
|
|
1518
|
-
#
|
|
1519
|
-
#
|
|
1520
|
-
#
|
|
1716
|
+
# @return whether `path` sits under the bundled gem-overlay root — the prefix half of
|
|
1717
|
+
# "this run loaded a `data/gem_overlay/` directory". Callers needing ONE gem's overlay ask
|
|
1718
|
+
# {.gem_overlay_sig_paths} for that gem's directory instead: `signature_standdowns` uses this
|
|
1719
|
+
# to recognise overlay rows across every gem, while `CheckRules#gem_overlay_loaded?` keys on
|
|
1720
|
+
# the activesupport overlay alone (#3041 — a project can load the addressable overlay without
|
|
1721
|
+
# the activesupport one, and only the latter may open `ActiveSupport::Duration` /
|
|
1722
|
+
# `ActiveSupport::Concern`). `GEM_OVERLAY_SIGS_ROOT` is defined inside this `class << self`
|
|
1723
|
+
# block, so it is reachable from here directly but NOT as `RbsLoader::GEM_OVERLAY_SIGS_ROOT`
|
|
1724
|
+
# from outside — this method is the public seam callers outside this class use instead of
|
|
1725
|
+
# reaching for the constant themselves.
|
|
1521
1726
|
def under_gem_overlay_root?(path)
|
|
1522
1727
|
path.to_s.start_with?("#{GEM_OVERLAY_SIGS_ROOT}/")
|
|
1523
1728
|
end
|
|
@@ -1632,7 +1837,8 @@ module Rigor
|
|
|
1632
1837
|
end
|
|
1633
1838
|
end
|
|
1634
1839
|
|
|
1635
|
-
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs, :deferred_signature_paths
|
|
1840
|
+
attr_reader :libraries, :signature_paths, :cache_store, :virtual_rbs, :deferred_signature_paths,
|
|
1841
|
+
:project_signature_paths, :core_root, :subordinate_signature_paths, :collection_signature_paths
|
|
1636
1842
|
|
|
1637
1843
|
# @param libraries — stdlib library names to load on top of core (e.g.,
|
|
1638
1844
|
# `["pathname", "json"]`). Empty by default. Each entry MUST correspond to a directory under the
|
|
@@ -1645,7 +1851,7 @@ module Rigor
|
|
|
1645
1851
|
# `RbsEnvironment`, `RbsKnownClassNames`, `RbsConstantTable`, `RbsClassAncestorTable`, and
|
|
1646
1852
|
# `RbsClassTypeParamNames` producers. Pass `nil` (the default) to skip caching; the runner threads
|
|
1647
1853
|
# its own Store through here when enabled.
|
|
1648
|
-
# @param virtual_rbs —
|
|
1854
|
+
# @param virtual_rbs — ADR-32 WD4 — `[virtual_filename, rbs_source]` pairs
|
|
1649
1855
|
# synthesised from project source by a plugin's `Manifest#source_rbs_synthesizer`. Merged into the
|
|
1650
1856
|
# env after `signature_paths:` and the vendored stubs. Pass `[]` (the default) when no
|
|
1651
1857
|
# synthesizer-emitting plugin is loaded.
|
|
@@ -1653,15 +1859,22 @@ module Rigor
|
|
|
1653
1859
|
# contributed (issue #610): loaded last and allowed to stand down against a colliding generic
|
|
1654
1860
|
# arity. Read back by the env-cache producer, which rebuilds the env from the loader's readers on
|
|
1655
1861
|
# a miss — a list it cannot read is a stand-down that runs only on a `--no-cache` build.
|
|
1656
|
-
|
|
1657
|
-
|
|
1862
|
+
# @param core_root — issue #2517 — a directory loaded in place of the `rbs` gem's `core/`, or nil (the
|
|
1863
|
+
# default) for that core. Validated here, so a bad selection fails at construction ({CoreRoot.validate!}).
|
|
1864
|
+
# @param subordinate_signature_paths — issue #2978 — the subset of `signature_paths:` that stands down member by
|
|
1865
|
+
# member to the rest of it (a bundle gem's `rbs collection` copy, behind the gem's own `sig/`).
|
|
1866
|
+
# @param collection_signature_paths — issue #3030 — the subset of `signature_paths:` that are `rbs collection`
|
|
1867
|
+
# gem directories, read without the files below their `_`-prefixed directories ({.project_sig_files}).
|
|
1868
|
+
def initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: [], # rubocop:disable Metrics/ParameterLists
|
|
1869
|
+
deferred_signature_paths: [], project_signature_paths: [], required_features: [], core_root: nil,
|
|
1870
|
+
subordinate_signature_paths: [], collection_signature_paths: [])
|
|
1871
|
+
@core_root = core_root.nil? ? nil : CoreRoot.validate!(core_root)
|
|
1872
|
+
CoreRoot.reject_overlap!(@core_root, project_signature_paths) if @core_root
|
|
1658
1873
|
@libraries = libraries.map(&:to_s).freeze
|
|
1659
1874
|
@signature_paths = signature_paths.map { |p| Pathname(p) }.freeze
|
|
1660
1875
|
assign_reopen_inputs(project_signature_paths, required_features)
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
# is also in `@signature_paths`, so the env cache descriptor already digests these files.
|
|
1664
|
-
@deferred_signature_paths = deferred_signature_paths.map { |p| Pathname(p) }.freeze
|
|
1876
|
+
assign_partition(deferred_signature_paths, subordinate_signature_paths)
|
|
1877
|
+
@collection_signature_paths = collection_signature_paths.map { |p| Pathname(p) }.freeze
|
|
1665
1878
|
@cache_store = cache_store
|
|
1666
1879
|
@virtual_rbs = virtual_rbs.map { |name, content| [name.to_s.dup.freeze, content.to_s.dup.freeze].freeze }.freeze
|
|
1667
1880
|
# Per-loader memoization bucket. Held as a single mutable Hash so the loader instance itself can be
|
|
@@ -1702,9 +1915,9 @@ module Rigor
|
|
|
1702
1915
|
# @return empty when every `signature_paths:` file parses.
|
|
1703
1916
|
def quarantined_signatures
|
|
1704
1917
|
@state[:quarantined] ||= begin
|
|
1705
|
-
parse_quarantined = self.class.quarantined_project_signatures(@signature_paths)
|
|
1918
|
+
parse_quarantined = self.class.quarantined_project_signatures(@signature_paths, @collection_signature_paths)
|
|
1706
1919
|
collision_quarantined = self.class.collision_quarantined_project_signatures(
|
|
1707
|
-
@state[:env], @signature_paths
|
|
1920
|
+
@state[:env], @signature_paths, @collection_signature_paths
|
|
1708
1921
|
)
|
|
1709
1922
|
parse_paths = parse_quarantined.to_set { |path, _note| path }
|
|
1710
1923
|
# Issue #610 — a deferred file that STOOD DOWN is absent from the env by design, which is exactly
|
|
@@ -1725,8 +1938,17 @@ module Rigor
|
|
|
1725
1938
|
# plugin sig collides, which is every project that has not run `rbs collection install` for a gem a
|
|
1726
1939
|
# bundled plugin also declares. Reported as `rbs.coverage.plugin-signature-stood-down` (`:info`); the
|
|
1727
1940
|
# same file MUST NOT also appear in {#quarantined_signatures}.
|
|
1941
|
+
#
|
|
1942
|
+
# Issue #2422 — followed by the members a deferred file that did load left out because another source
|
|
1943
|
+
# declares them ({.deferred_member_standdowns}), one row each.
|
|
1944
|
+
#
|
|
1945
|
+
# Issue #2974 — a gem overlay's rows are left out: the overlay is Rigor's own default, deferred so it stands
|
|
1946
|
+
# down to any source that declares the same thing, and that is not news to report (#2470's reasoning).
|
|
1728
1947
|
def signature_standdowns
|
|
1729
|
-
@state[:signature_standdowns] ||=
|
|
1948
|
+
@state[:signature_standdowns] ||= (
|
|
1949
|
+
self.class.deferred_standdowns(env, @deferred_signature_paths) +
|
|
1950
|
+
self.class.deferred_member_standdowns(env, @deferred_signature_paths)
|
|
1951
|
+
).reject { |row| self.class.under_gem_overlay_root?(row.first) }.freeze
|
|
1730
1952
|
end
|
|
1731
1953
|
|
|
1732
1954
|
# Issue #1713 — what stood down from each {RequiredFeatures}-gated vendored directory this loader's library
|
|
@@ -1745,7 +1967,7 @@ module Rigor
|
|
|
1745
1967
|
end
|
|
1746
1968
|
return [] if roots.empty?
|
|
1747
1969
|
|
|
1748
|
-
GatedSignaturePlan.standdowns(env, roots,
|
|
1970
|
+
GatedSignaturePlan.standdowns(env, roots, signature_files)
|
|
1749
1971
|
end
|
|
1750
1972
|
private :derive_vendored_standdowns
|
|
1751
1973
|
|
|
@@ -1781,7 +2003,7 @@ module Rigor
|
|
|
1781
2003
|
environment = env
|
|
1782
2004
|
return Set.new.freeze if environment.nil?
|
|
1783
2005
|
|
|
1784
|
-
project_files =
|
|
2006
|
+
project_files = signature_files
|
|
1785
2007
|
virtual_names = @virtual_rbs.to_set(&:first)
|
|
1786
2008
|
return Set.new.freeze if project_files.empty? && virtual_names.empty?
|
|
1787
2009
|
|
|
@@ -1851,6 +2073,22 @@ module Rigor
|
|
|
1851
2073
|
# Issue #2174 — the subset of `signature_paths:` the user configured (every entry is also in
|
|
1852
2074
|
# `@signature_paths`), and the features the project's source requires: read only by
|
|
1853
2075
|
# {#ruby_library_reopen?}.
|
|
2076
|
+
# Issue #610 — the subset of `signature_paths:` a bundled PLUGIN contributed. Loaded last and allowed to stand
|
|
2077
|
+
# down against a colliding generic arity ({.add_deferred_signatures}). Issue #2978 — and the subset that stands
|
|
2078
|
+
# down member by member to the rest. Every entry of either is also in `@signature_paths`, so the env cache
|
|
2079
|
+
# descriptor already digests these files.
|
|
2080
|
+
# Issue #3030 — every `.rbs` file this loader reads from its `signature_paths:`, the `_`-prefixed directories of
|
|
2081
|
+
# its `rbs collection` gem directories left out ({.project_sig_files}).
|
|
2082
|
+
def signature_files
|
|
2083
|
+
self.class.project_sig_files(@signature_paths, @collection_signature_paths)
|
|
2084
|
+
end
|
|
2085
|
+
private :signature_files
|
|
2086
|
+
|
|
2087
|
+
def assign_partition(deferred_signature_paths, subordinate_signature_paths)
|
|
2088
|
+
@deferred_signature_paths = deferred_signature_paths.map { |p| Pathname(p) }.freeze
|
|
2089
|
+
@subordinate_signature_paths = subordinate_signature_paths.map { |p| Pathname(p) }.freeze
|
|
2090
|
+
end
|
|
2091
|
+
|
|
1854
2092
|
def assign_reopen_inputs(project_signature_paths, required_features)
|
|
1855
2093
|
@project_signature_paths = project_signature_paths.map { |p| Pathname(p) }.freeze
|
|
1856
2094
|
@required_features = required_features.map { |feature| feature.to_s.dup.freeze }.freeze
|
|
@@ -1858,6 +2096,62 @@ module Rigor
|
|
|
1858
2096
|
private :assign_reopen_inputs
|
|
1859
2097
|
private :ruby_library_roots
|
|
1860
2098
|
|
|
2099
|
+
# Issue #2438 — true when every RBS declaration of `class_name` is the project's own
|
|
2100
|
+
# ({#project_authored_classes}) and none of them declares a member of the class itself: no method, attribute,
|
|
2101
|
+
# alias or mixin, only nested class / module / constant / type declarations (and variable or visibility
|
|
2102
|
+
# lines, which add no method). `rigor sig-gen --write` writes exactly this for a namespace whose source
|
|
2103
|
+
# lists no methods of its own (`module NS; class Foo; …; end; end`), while the namespace's methods live
|
|
2104
|
+
# where the generator cannot see them: a file outside `paths:`, a mixin, a hook. Such a declaration
|
|
2105
|
+
# names the class without describing its surface. Bundled (core, stdlib, gem) declarations never count.
|
|
2106
|
+
def memberless_project_class?(class_name)
|
|
2107
|
+
memberless_project_classes.key?(class_name.to_s.delete_prefix("::"))
|
|
2108
|
+
end
|
|
2109
|
+
|
|
2110
|
+
# The superclass a member-less project declaration of `class_name` writes (`class Poll < ::ApplicationRecord`),
|
|
2111
|
+
# as RBS spells it (rooted), or nil when none of its declarations writes one or the class is not member-less.
|
|
2112
|
+
def memberless_project_superclass(class_name)
|
|
2113
|
+
memberless_project_classes[class_name.to_s.delete_prefix("::")]
|
|
2114
|
+
end
|
|
2115
|
+
|
|
2116
|
+
def memberless_project_classes
|
|
2117
|
+
@state[:memberless_project_classes] ||= build_memberless_project_classes
|
|
2118
|
+
end
|
|
2119
|
+
private :memberless_project_classes
|
|
2120
|
+
|
|
2121
|
+
SURFACE_MEMBERS = [
|
|
2122
|
+
::RBS::AST::Members::MethodDefinition, ::RBS::AST::Members::Attribute,
|
|
2123
|
+
::RBS::AST::Members::Alias, ::RBS::AST::Members::Mixin
|
|
2124
|
+
].freeze
|
|
2125
|
+
private_constant :SURFACE_MEMBERS
|
|
2126
|
+
|
|
2127
|
+
def build_memberless_project_classes
|
|
2128
|
+
authored = project_authored_classes
|
|
2129
|
+
environment = env
|
|
2130
|
+
return {}.freeze if authored.empty? || environment.nil?
|
|
2131
|
+
|
|
2132
|
+
environment.class_decls.each_with_object({}) do |(rbs_name, entry), acc|
|
|
2133
|
+
name = rbs_name.to_s.delete_prefix("::")
|
|
2134
|
+
next unless authored.include?(name)
|
|
2135
|
+
|
|
2136
|
+
decls = self.class.entry_declarations(entry)
|
|
2137
|
+
acc[name] = declared_superclass_name(decls) if decls.none? { |decl| surface_member?(decl) }
|
|
2138
|
+
end.freeze
|
|
2139
|
+
rescue ::RBS::BaseError
|
|
2140
|
+
{}.freeze
|
|
2141
|
+
end
|
|
2142
|
+
|
|
2143
|
+
def surface_member?(decl)
|
|
2144
|
+
members = decl.respond_to?(:members) ? decl.members : []
|
|
2145
|
+
members.any? { |member| SURFACE_MEMBERS.any? { |kind| member.is_a?(kind) } }
|
|
2146
|
+
end
|
|
2147
|
+
private :surface_member?
|
|
2148
|
+
|
|
2149
|
+
def declared_superclass_name(decls)
|
|
2150
|
+
decls.filter_map { |decl| decl.respond_to?(:super_class) && decl.super_class&.name&.to_s }.first&.freeze
|
|
2151
|
+
end
|
|
2152
|
+
private :declared_superclass_name
|
|
2153
|
+
private :build_memberless_project_classes
|
|
2154
|
+
|
|
1861
2155
|
# The class / module names every one of whose declarations is the project's own
|
|
1862
2156
|
# ({#project_authored_entry?}, which {#inline_only_entry?} asks behind its inline-only gate).
|
|
1863
2157
|
def project_authored_classes
|
|
@@ -1907,17 +2201,33 @@ module Rigor
|
|
|
1907
2201
|
# the interpreter implements, as opposed to a stdlib library written in Ruby (`Logger` rewrites `@level` in
|
|
1908
2202
|
# its `initialize`). Same attribution, same cache behaviour.
|
|
1909
2203
|
def core_class?(class_name)
|
|
1910
|
-
(@state[:core_classes] ||= build_core_stdlib_classes(
|
|
2204
|
+
(@state[:core_classes] ||= build_core_stdlib_classes(core_roots)).include?(class_name.to_s.delete_prefix("::"))
|
|
2205
|
+
end
|
|
2206
|
+
|
|
2207
|
+
# Issue #2517 — the `core/` prefixes a declaration's buffer name is tested against: the selected
|
|
2208
|
+
# {#core_root} when there is one, which is what the environment loaded core from, else the `rbs` gem's.
|
|
2209
|
+
def core_roots
|
|
2210
|
+
@core_root ? ["#{@core_root}/".freeze].freeze : CORE_ROOTS
|
|
1911
2211
|
end
|
|
2212
|
+
private :core_roots
|
|
2213
|
+
|
|
2214
|
+
# {CORE_STDLIB_ROOTS} with {#core_roots} in place of the `rbs` gem's `core/`.
|
|
2215
|
+
def core_stdlib_roots
|
|
2216
|
+
return CORE_STDLIB_ROOTS unless @core_root
|
|
2217
|
+
return [].freeze if CORE_STDLIB_ROOTS.empty?
|
|
2218
|
+
|
|
2219
|
+
(core_roots + (CORE_STDLIB_ROOTS - CORE_ROOTS)).freeze
|
|
2220
|
+
end
|
|
2221
|
+
private :core_stdlib_roots
|
|
1912
2222
|
|
|
1913
2223
|
CORE_ROOTS = begin
|
|
1914
2224
|
[::RBS::EnvironmentLoader::DEFAULT_CORE_ROOT].compact.map { |root| "#{root}/".freeze }.freeze
|
|
1915
2225
|
rescue StandardError
|
|
1916
2226
|
[].freeze
|
|
1917
|
-
end
|
|
2227
|
+
end.freeze
|
|
1918
2228
|
private_constant :CORE_ROOTS
|
|
1919
2229
|
|
|
1920
|
-
def build_core_stdlib_classes(roots =
|
|
2230
|
+
def build_core_stdlib_classes(roots = core_stdlib_roots)
|
|
1921
2231
|
environment = env
|
|
1922
2232
|
return Set.new.freeze if environment.nil? || roots.empty?
|
|
1923
2233
|
|
|
@@ -1941,6 +2251,96 @@ module Rigor
|
|
|
1941
2251
|
core_stdlib_classes.include?(class_name.to_s.delete_prefix("::"))
|
|
1942
2252
|
end
|
|
1943
2253
|
|
|
2254
|
+
# Issue #2884 — true when every declaration of `method_definition` (an `RBS::Definition::Method`) is in a named
|
|
2255
|
+
# buffer outside the `rbs` gem's `core/` and `stdlib/` trees: a project, gem or collection signature wrote it.
|
|
2256
|
+
# Attributed by buffer name, as {#core_or_stdlib_class?} is; a declaration whose buffer name is lost (an old
|
|
2257
|
+
# environment blob's `<cached>` sentinel) answers false, which switches the caller's feature off.
|
|
2258
|
+
def declared_outside_core_stdlib?(method_definition)
|
|
2259
|
+
roots = core_stdlib_roots
|
|
2260
|
+
type_defs = method_definition.defs
|
|
2261
|
+
return false if roots.empty? || type_defs.empty?
|
|
2262
|
+
|
|
2263
|
+
type_defs.all? do |type_def|
|
|
2264
|
+
name = self.class.declaration_buffer_name(type_def.member)
|
|
2265
|
+
!name.nil? && roots.none? { |root| name.start_with?(root) }
|
|
2266
|
+
end
|
|
2267
|
+
end
|
|
2268
|
+
|
|
2269
|
+
# Issue #2884 — true when `method_definition`, a class's singleton `new`, is one `X.new` answers through: every
|
|
2270
|
+
# definition is an explicit `def self.new` (not the `new` `RBS::DefinitionBuilder` derives from `initialize`,
|
|
2271
|
+
# which returns the declaring class), written outside core / stdlib ({#declared_outside_core_stdlib?}), on a
|
|
2272
|
+
# class core / stdlib does not declare (a project signature reopening `String` does not change `String.new`).
|
|
2273
|
+
def honoured_singleton_new?(method_definition)
|
|
2274
|
+
method_definition.defs.all? { |type_def| explicit_singleton_new_member?(type_def.member) } &&
|
|
2275
|
+
declared_outside_core_stdlib?(method_definition) &&
|
|
2276
|
+
!core_or_stdlib_class?(method_definition.defined_in.to_s)
|
|
2277
|
+
end
|
|
2278
|
+
|
|
2279
|
+
def explicit_singleton_new_member?(member)
|
|
2280
|
+
member.is_a?(::RBS::AST::Members::MethodDefinition) && member.name == :new && member.kind != :instance
|
|
2281
|
+
end
|
|
2282
|
+
private :explicit_singleton_new_member?
|
|
2283
|
+
|
|
2284
|
+
# Issue #2884 — true when `method_definition`, a singleton `new`, answers what `Nominal[receiver]` answers for
|
|
2285
|
+
# every receiver that inherits it: each overload returns `instance`, untyped, `top`, `bot` or `void`, or the
|
|
2286
|
+
# declaring class itself, compared by its resolved full name. `self` is the receiver's class object, not its
|
|
2287
|
+
# instance, so it does not count. The dispatcher answers `Nominal[receiver]` for an inherited declaration this
|
|
2288
|
+
# holds of, and {#singleton_new_departs_from_instance?} is built from the same test, so the two cannot drift.
|
|
2289
|
+
def answers_instance_for_subclass?(method_definition)
|
|
2290
|
+
owner = method_definition.defined_in.to_s.delete_prefix("::")
|
|
2291
|
+
method_definition.method_types.all? do |method_type|
|
|
2292
|
+
returned = method_type.type.return_type
|
|
2293
|
+
INSTANCE_LIKE_RETURNS.any? { |base| returned.is_a?(base) } ||
|
|
2294
|
+
(returned.is_a?(::RBS::Types::ClassInstance) && returned.name.to_s.delete_prefix("::") == owner)
|
|
2295
|
+
end
|
|
2296
|
+
end
|
|
2297
|
+
|
|
2298
|
+
INSTANCE_LIKE_RETURNS = [
|
|
2299
|
+
::RBS::Types::Bases::Instance, ::RBS::Types::Bases::Any, ::RBS::Types::Bases::Top,
|
|
2300
|
+
::RBS::Types::Bases::Bottom, ::RBS::Types::Bases::Void
|
|
2301
|
+
].freeze
|
|
2302
|
+
private_constant :INSTANCE_LIKE_RETURNS
|
|
2303
|
+
|
|
2304
|
+
# Issue #2884 — true when some class declares a {#honoured_singleton_new?} `new` that is not
|
|
2305
|
+
# {#answers_instance_for_subclass?}: only such a declaration answers a Ruby subclass with something other than
|
|
2306
|
+
# the `Nominal[subclass]` the dispatcher gives without one, so the dispatcher walks a subclass's ancestry, and
|
|
2307
|
+
# files that walk's dependency edges, only when this holds. Memoised per loader: one pass over `class_decls` for
|
|
2308
|
+
# the classes whose own non-core declaration writes `def self.new`, then their definitions.
|
|
2309
|
+
def singleton_new_departs_from_instance?
|
|
2310
|
+
return @state[:singleton_new_departs] if @state.key?(:singleton_new_departs)
|
|
2311
|
+
|
|
2312
|
+
@state[:singleton_new_departs] = scan_singleton_new_departs
|
|
2313
|
+
end
|
|
2314
|
+
|
|
2315
|
+
def scan_singleton_new_departs
|
|
2316
|
+
during_internal_demand do
|
|
2317
|
+
singleton_new_declarers.any? do |class_name|
|
|
2318
|
+
definition = singleton_method(class_name: class_name, method_name: :new)
|
|
2319
|
+
definition && honoured_singleton_new?(definition) && !answers_instance_for_subclass?(definition)
|
|
2320
|
+
end
|
|
2321
|
+
end
|
|
2322
|
+
rescue ::RBS::BaseError
|
|
2323
|
+
true
|
|
2324
|
+
end
|
|
2325
|
+
private :scan_singleton_new_departs
|
|
2326
|
+
|
|
2327
|
+
def singleton_new_declarers
|
|
2328
|
+
environment = env
|
|
2329
|
+
roots = core_stdlib_roots
|
|
2330
|
+
return [] if environment.nil? || roots.empty?
|
|
2331
|
+
|
|
2332
|
+
environment.class_decls.filter_map do |type_name, entry|
|
|
2333
|
+
declares = self.class.entry_declarations(entry).any? do |decl|
|
|
2334
|
+
name = self.class.declaration_buffer_name(decl)
|
|
2335
|
+
next false if name.nil? || roots.any? { |root| name.start_with?(root) }
|
|
2336
|
+
|
|
2337
|
+
decl.members.any? { |member| explicit_singleton_new_member?(member) }
|
|
2338
|
+
end
|
|
2339
|
+
type_name.to_s if declares
|
|
2340
|
+
end
|
|
2341
|
+
end
|
|
2342
|
+
private :singleton_new_declarers
|
|
2343
|
+
|
|
1944
2344
|
# True when `class_name`'s RBS declaration is one the project wrote (see {#project_declared_classes}).
|
|
1945
2345
|
def project_declared_class?(class_name)
|
|
1946
2346
|
project_declared_classes.include?(class_name.to_s.delete_prefix("::"))
|
|
@@ -1977,7 +2377,7 @@ module Rigor
|
|
|
1977
2377
|
next if type.is_a?(Type::Bot)
|
|
1978
2378
|
|
|
1979
2379
|
path = self.class.declaration_buffer_name(entry.decl)
|
|
1980
|
-
acc[name.to_sym] = [type, !path.nil? &&
|
|
2380
|
+
acc[name.to_sym] = [type, !path.nil? && core_stdlib_roots.any? { |root| path.start_with?(root) }].freeze
|
|
1981
2381
|
end
|
|
1982
2382
|
table.freeze
|
|
1983
2383
|
rescue ::RBS::BaseError
|
|
@@ -2001,6 +2401,20 @@ module Rigor
|
|
|
2001
2401
|
@state[:env_build_failure]
|
|
2002
2402
|
end
|
|
2003
2403
|
|
|
2404
|
+
# Issue #2517 — builds the environment now when a core is selected, so a core it does not build from raises
|
|
2405
|
+
# its {CoreRootError} at the caller rather than at the first query inside analysis. No-op for the default core.
|
|
2406
|
+
def verify_core_root!
|
|
2407
|
+
env if @core_root
|
|
2408
|
+
nil
|
|
2409
|
+
end
|
|
2410
|
+
|
|
2411
|
+
# Issue #2883 — builds the environment now, for the default core as {#verify_core_root!} does for a selected one,
|
|
2412
|
+
# so no `type_of` builds it lazily (ADR-129 G1': the core-root Frame the build reads is never read inside typing).
|
|
2413
|
+
def prepare_for_typing!
|
|
2414
|
+
env
|
|
2415
|
+
nil
|
|
2416
|
+
end
|
|
2417
|
+
|
|
2004
2418
|
# Issue #696 — the PER-CLASS sibling of {#env_build_failure}, one tier quieter in consequence: an
|
|
2005
2419
|
# `RBS::DefinitionBuilder` failure ({#build_instance_definition} / {#build_singleton_definition}'s
|
|
2006
2420
|
# rescue) leaves the class KNOWN but with no method surface, so every call on it — real methods and
|
|
@@ -2086,7 +2500,8 @@ module Rigor
|
|
|
2086
2500
|
def member_consistency(proof: nil)
|
|
2087
2501
|
key = proof ? :member_consistency_proven : :member_consistency
|
|
2088
2502
|
@state[key] ||= self.class.member_consistency_for(
|
|
2089
|
-
self.class.project_sig_files(@
|
|
2503
|
+
signature_files - self.class.project_sig_files(@deferred_signature_paths),
|
|
2504
|
+
@virtual_rbs, proof: proof
|
|
2090
2505
|
).records
|
|
2091
2506
|
end
|
|
2092
2507
|
|
|
@@ -2214,6 +2629,77 @@ module Rigor
|
|
|
2214
2629
|
end
|
|
2215
2630
|
private :mixin_parent
|
|
2216
2631
|
|
|
2632
|
+
# Issue #2681 — every method name declared, on either side, by `name`'s RBS declarations and by the classes and
|
|
2633
|
+
# modules they inherit, include, prepend or extend, `::`-stripped names in `skip` excepted. Read from the
|
|
2634
|
+
# declarations as {#declared_ancestry} reads them, not through the definition builder: a definition that fails to
|
|
2635
|
+
# build reports a coverage diagnostic each time it is asked for. A superset of what the built definitions hold
|
|
2636
|
+
# is harmless to the one reader, a gate in front of ADR-43's override walk. Empty when the environment is
|
|
2637
|
+
# unavailable; the walk stops at 64 owners.
|
|
2638
|
+
def declared_method_names(name, skip: [])
|
|
2639
|
+
return Set.new if env.nil?
|
|
2640
|
+
|
|
2641
|
+
names = Set.new
|
|
2642
|
+
pending = [name.to_s.delete_prefix("::")]
|
|
2643
|
+
seen = Set.new
|
|
2644
|
+
until pending.empty? || seen.size > 64
|
|
2645
|
+
owner = pending.shift
|
|
2646
|
+
next if seen.include?(owner) || skip.include?(owner)
|
|
2647
|
+
|
|
2648
|
+
seen << owner
|
|
2649
|
+
entry = env.class_decls[parse_type_name(owner)]
|
|
2650
|
+
self.class.entry_declarations(entry).each { |decl| read_declared_methods(decl, names, pending) } if entry
|
|
2651
|
+
end
|
|
2652
|
+
names
|
|
2653
|
+
rescue ::RBS::BaseError
|
|
2654
|
+
Set.new
|
|
2655
|
+
end
|
|
2656
|
+
|
|
2657
|
+
def read_declared_methods(decl, names, pending)
|
|
2658
|
+
superclass = decl.respond_to?(:super_class) ? decl.super_class : nil
|
|
2659
|
+
pending << superclass.name.to_s.delete_prefix("::") if superclass
|
|
2660
|
+
return unless decl.respond_to?(:members)
|
|
2661
|
+
|
|
2662
|
+
decl.members.each do |member|
|
|
2663
|
+
case member
|
|
2664
|
+
when ::RBS::AST::Members::MethodDefinition, ::RBS::AST::Members::AttrReader
|
|
2665
|
+
names << member.name
|
|
2666
|
+
when ::RBS::AST::Members::AttrAccessor, ::RBS::AST::Members::AttrWriter
|
|
2667
|
+
names << member.name << :"#{member.name}="
|
|
2668
|
+
when ::RBS::AST::Members::Alias
|
|
2669
|
+
names << member.new_name
|
|
2670
|
+
when ::RBS::AST::Members::Include, ::RBS::AST::Members::Prepend, ::RBS::AST::Members::Extend
|
|
2671
|
+
pending << member.name.to_s.delete_prefix("::") unless member.name.interface?
|
|
2672
|
+
end
|
|
2673
|
+
end
|
|
2674
|
+
end
|
|
2675
|
+
private :read_declared_methods
|
|
2676
|
+
|
|
2677
|
+
# Issue #2321 — every class name an RBS class declaration names as its superclass, `::`-stripped: a class
|
|
2678
|
+
# absent from the set has no RBS-declared subclass. `nil` when the environment is unavailable or fails to read,
|
|
2679
|
+
# so a caller cannot mistake an unread environment for one without subclasses. Memoised per loader.
|
|
2680
|
+
def superclass_names
|
|
2681
|
+
return @state[:superclass_names] if @state.key?(:superclass_names)
|
|
2682
|
+
|
|
2683
|
+
@state[:superclass_names] = build_superclass_names&.freeze
|
|
2684
|
+
end
|
|
2685
|
+
|
|
2686
|
+
def build_superclass_names
|
|
2687
|
+
return nil if env.nil?
|
|
2688
|
+
|
|
2689
|
+
names = Set.new
|
|
2690
|
+
|
|
2691
|
+
env.class_decls.each_value do |entry|
|
|
2692
|
+
self.class.entry_declarations(entry).each do |decl|
|
|
2693
|
+
superclass = decl.respond_to?(:super_class) ? decl.super_class : nil
|
|
2694
|
+
names << superclass.name.to_s.delete_prefix("::") if superclass
|
|
2695
|
+
end
|
|
2696
|
+
end
|
|
2697
|
+
names
|
|
2698
|
+
rescue ::RBS::BaseError
|
|
2699
|
+
nil
|
|
2700
|
+
end
|
|
2701
|
+
private :build_superclass_names
|
|
2702
|
+
|
|
2217
2703
|
# Yields every known class / module / alias name (top-level prefixed) currently loaded into the
|
|
2218
2704
|
# environment. The cache producer that materialises the known-name set uses this so it never recurses
|
|
2219
2705
|
# back through {#class_known?}.
|
|
@@ -2230,7 +2716,7 @@ module Rigor
|
|
|
2230
2716
|
end
|
|
2231
2717
|
|
|
2232
2718
|
# Yields every type-alias declaration loaded into the environment (`type foo = ...`).
|
|
2233
|
-
# Yields `
|
|
2719
|
+
# Yields `[type_name, decl_entry]` pairs.
|
|
2234
2720
|
def each_type_alias_decl
|
|
2235
2721
|
return enum_for(:each_type_alias_decl) unless block_given?
|
|
2236
2722
|
return unless env
|
|
@@ -2288,7 +2774,7 @@ module Rigor
|
|
|
2288
2774
|
# fail-soft: see #each_class_decl_annotation.
|
|
2289
2775
|
end
|
|
2290
2776
|
|
|
2291
|
-
# Returns a frozen
|
|
2777
|
+
# Returns a frozen mapping from each loaded class / module name (top-level prefixed)
|
|
2292
2778
|
# to the file path of its FIRST declaration's RBS source. Used by {Rigor::Analysis::RunStats} to
|
|
2293
2779
|
# attribute the type universe between "project sig/" (paths under the configured `signature_paths`)
|
|
2294
2780
|
# and "bundled" (everything else — RBS core, stdlib libraries, gem-bundled RBS). Each value is a frozen
|
|
@@ -2408,6 +2894,34 @@ module Rigor
|
|
|
2408
2894
|
nil
|
|
2409
2895
|
end
|
|
2410
2896
|
|
|
2897
|
+
# #775 — the memo {Inference::RbsTypeTranslator} keeps, per loader, for the translated form of a closed alias
|
|
2898
|
+
# expansion (keyed by the expansion). It lives here because the translation reads this loader's environment
|
|
2899
|
+
# through any alias nested in the expansion ({#expand_type_alias}): another loader's `b` may differ under an
|
|
2900
|
+
# equal `a`, and the entry goes with the loader. The translator is its only reader and writer.
|
|
2901
|
+
def closed_alias_translations
|
|
2902
|
+
@state[:closed_alias_translations] ||= {}
|
|
2903
|
+
end
|
|
2904
|
+
|
|
2905
|
+
# An independent resolver for a read-only consumer. Its memo belongs to the caller, never the loader cache.
|
|
2906
|
+
def type_name_resolver
|
|
2907
|
+
environment = env
|
|
2908
|
+
return nil if environment.nil?
|
|
2909
|
+
|
|
2910
|
+
klass = ::RBS::Resolver::TypeNameResolver
|
|
2911
|
+
if klass.respond_to?(:build)
|
|
2912
|
+
klass.build(environment)
|
|
2913
|
+
else
|
|
2914
|
+
# Older RBS 3.x takes the environment instead of the current constructor's name tables.
|
|
2915
|
+
klass.new(environment) # rigor:disable call.wrong-arity
|
|
2916
|
+
end
|
|
2917
|
+
end
|
|
2918
|
+
|
|
2919
|
+
# The resolved alias declaration, without substituting type arguments or translating its AST.
|
|
2920
|
+
def type_alias_declaration(name)
|
|
2921
|
+
declarations = env&.type_alias_decls
|
|
2922
|
+
declarations&.[](name)&.decl
|
|
2923
|
+
end
|
|
2924
|
+
|
|
2411
2925
|
# @return the resolved singleton (class object) definition for `class_name`. The
|
|
2412
2926
|
# methods on this definition are the *class methods* of `class_name`, including those inherited from
|
|
2413
2927
|
# `Class` and `Module` for class types. Returns nil for unknown names and on RBS build errors
|
|
@@ -2509,7 +3023,7 @@ module Rigor
|
|
|
2509
3023
|
# singleton methods (e.g., `Array.new`) parameterize over the same `Elem` as instance methods.
|
|
2510
3024
|
#
|
|
2511
3025
|
# Returns an empty array for non-generic classes and for unknown names (the loader stays fail-soft).
|
|
2512
|
-
# NOTE: in the `rbs` gem, `RBS::Definition#type_params` returns
|
|
3026
|
+
# NOTE: in the `rbs` gem, `RBS::Definition#type_params` returns the names directly, not the AST
|
|
2513
3027
|
# `TypeParam` object (those live on the AST level).
|
|
2514
3028
|
#
|
|
2515
3029
|
# When `cache_store` is set, the loader fetches the entire type-parameter-name table once (per
|
|
@@ -2539,6 +3053,31 @@ module Rigor
|
|
|
2539
3053
|
NO_TYPE_PARAMS = [].freeze
|
|
2540
3054
|
private_constant :NO_TYPE_PARAMS
|
|
2541
3055
|
|
|
3056
|
+
# Issue #2436 — the `RBS::AST::TypeParam` list a loaded declaration of `class_name` spells (`[unchecked out
|
|
3057
|
+
# E]` for `Array`), read off the declarations rather than through {#class_type_param_names}: a definition
|
|
3058
|
+
# build validates the entry and raises on one whose declarations already disagree, which is the env sig-gen
|
|
3059
|
+
# is asked to repair. The first declaration answers, as it is the one rbs compares every other against
|
|
3060
|
+
# ({.generic_arity_conflict}). Empty for a non-generic or unknown name, and when the environment did not
|
|
3061
|
+
# build.
|
|
3062
|
+
def class_declaration_type_params(class_name)
|
|
3063
|
+
memo = (@state[:declaration_type_params] ||= {})
|
|
3064
|
+
key = class_name.to_s.delete_prefix("::")
|
|
3065
|
+
memo.fetch(key) { memo[key] = read_declaration_type_params(key) }
|
|
3066
|
+
end
|
|
3067
|
+
|
|
3068
|
+
def read_declaration_type_params(key)
|
|
3069
|
+
rbs_name = parse_type_name(key)
|
|
3070
|
+
entry = rbs_name && env&.class_decls&.[](rbs_name)
|
|
3071
|
+
return NO_TYPE_PARAMS if entry.nil?
|
|
3072
|
+
|
|
3073
|
+
first = self.class.entry_declarations(entry).first
|
|
3074
|
+
params = first.respond_to?(:type_params) ? first.type_params : nil
|
|
3075
|
+
params.nil? || params.empty? ? NO_TYPE_PARAMS : params.dup.freeze
|
|
3076
|
+
rescue ::RBS::BaseError
|
|
3077
|
+
NO_TYPE_PARAMS
|
|
3078
|
+
end
|
|
3079
|
+
private :read_declaration_type_params
|
|
3080
|
+
|
|
2542
3081
|
def class_ordering(lhs, rhs)
|
|
2543
3082
|
@hierarchy.class_ordering(lhs, rhs)
|
|
2544
3083
|
end
|
|
@@ -2811,7 +3350,7 @@ module Rigor
|
|
|
2811
3350
|
producer.send(:compute, self)
|
|
2812
3351
|
end
|
|
2813
3352
|
|
|
2814
|
-
# ADR-15 Phase 2b —
|
|
3353
|
+
# ADR-15 Phase 2b — the normalised ancestor chain of each class. Consumes
|
|
2815
3354
|
# the existing `RbsClassAncestorTable` producer when `cache_store` is set; falls back to the
|
|
2816
3355
|
# producer's `compute` otherwise. Used by {#reflection}.
|
|
2817
3356
|
def ancestor_names_table
|
|
@@ -2846,6 +3385,7 @@ module Rigor
|
|
|
2846
3385
|
# per AST node touched during analysis, multiplying per-file analysis cost by ~100x. Failures
|
|
2847
3386
|
# short-circuit to `nil` here and are surfaced to the user via `warn_about_env_build_failure_once`.
|
|
2848
3387
|
def env
|
|
3388
|
+
raise @state[:core_root_error] if @state[:core_root_error]
|
|
2849
3389
|
return @state[:env] if @state[:env_loaded]
|
|
2850
3390
|
|
|
2851
3391
|
@state[:env_loaded] = true
|
|
@@ -2854,11 +3394,24 @@ module Rigor
|
|
|
2854
3394
|
warn_about_virtual_rbs_collisions
|
|
2855
3395
|
@state[:env]
|
|
2856
3396
|
rescue ::RBS::BaseError => e
|
|
3397
|
+
# Issue #2517 — an environment that does not build from an explicitly selected core is not analysed
|
|
3398
|
+
# against at all: the degraded `Dynamic[top]` env would report the very APIs the selection removes as
|
|
3399
|
+
# fine. Memoised like the nil env, so every later reader raises the same error instead of rebuilding.
|
|
3400
|
+
raise(@state[:core_root_error] = core_root_build_error(e)) if @core_root
|
|
3401
|
+
|
|
2857
3402
|
record_env_build_failure(e)
|
|
2858
3403
|
warn_about_env_build_failure_once(e)
|
|
2859
3404
|
@state[:env] = nil
|
|
2860
3405
|
end
|
|
2861
3406
|
|
|
3407
|
+
def core_root_build_error(error)
|
|
3408
|
+
CoreRootError.new(
|
|
3409
|
+
"core_root: #{@core_root.inspect} did not build an RBS environment " \
|
|
3410
|
+
"(#{error.class.name}: #{error.message.lines.first.to_s.strip})"
|
|
3411
|
+
)
|
|
3412
|
+
end
|
|
3413
|
+
private :core_root_build_error
|
|
3414
|
+
|
|
2862
3415
|
# Tell the user, once per run, which of their `signature_paths:` `.rbs` files were skipped because they
|
|
2863
3416
|
# do not parse ({RbsLoader.add_project_signatures} quarantines them so the rest of the env survives). A
|
|
2864
3417
|
# skipped file's types are absent, so calls into them read `Dynamic[top]` — silently, without this. This
|
|
@@ -2966,7 +3519,7 @@ module Rigor
|
|
|
2966
3519
|
Cache::RbsEnvironment.fetch(loader: self, store: cache_store)
|
|
2967
3520
|
end
|
|
2968
3521
|
|
|
2969
|
-
# Full `
|
|
3522
|
+
# Full tables of `RBS::Definition` per class for the {#prewarm} / {#reflection} consumers (ADR-15
|
|
2970
3523
|
# Phase 2b/4b.x), which need every definition materialised up front. Built from the (cached) env via
|
|
2971
3524
|
# `RBS::DefinitionBuilder` — ADR-54 WD1 retired the disk blobs these used to `Marshal.load` (building
|
|
2972
3525
|
# all definitions from a cached env is faster), so the eager-table cost is now a compute, not a load.
|
|
@@ -3007,7 +3560,11 @@ module Rigor
|
|
|
3007
3560
|
libraries: @libraries,
|
|
3008
3561
|
signature_paths: @signature_paths,
|
|
3009
3562
|
virtual_rbs: @virtual_rbs,
|
|
3010
|
-
deferred_signature_paths: @deferred_signature_paths
|
|
3563
|
+
deferred_signature_paths: @deferred_signature_paths,
|
|
3564
|
+
project_signature_paths: @project_signature_paths,
|
|
3565
|
+
core_root: @core_root,
|
|
3566
|
+
subordinate_signature_paths: @subordinate_signature_paths,
|
|
3567
|
+
collection_signature_paths: @collection_signature_paths
|
|
3011
3568
|
)
|
|
3012
3569
|
end
|
|
3013
3570
|
|
|
@@ -3034,6 +3591,15 @@ module Rigor
|
|
|
3034
3591
|
rbs_name = canonical_module_name(rbs_name)
|
|
3035
3592
|
return nil unless env.class_decls.key?(rbs_name)
|
|
3036
3593
|
|
|
3594
|
+
# rbs's `AncestorBuilder#singleton_ancestors` has no cycle check: a class whose superclass chain loops
|
|
3595
|
+
# (`class S < ::S`, which a collection copy of the same class used to hide, #2441) recurses until the stack
|
|
3596
|
+
# runs out and aborts the run, where `#instance_ancestors` raises `RBS::RecursiveAncestorError`. Walking the
|
|
3597
|
+
# instance side first turns the loop into that error, so it is this class's build failure. Memoised by the
|
|
3598
|
+
# builder, so a class whose instance side was already built pays nothing. Any other stack exhaustion stays
|
|
3599
|
+
# fatal: it depends on how deep the analysis already was, and must not be memoised as a build failure.
|
|
3600
|
+
if env.class_decls[rbs_name].is_a?(::RBS::Environment::ClassEntry)
|
|
3601
|
+
builder.ancestor_builder.instance_ancestors(rbs_name)
|
|
3602
|
+
end
|
|
3037
3603
|
builder.build_singleton(rbs_name)
|
|
3038
3604
|
rescue ::RBS::BaseError => e
|
|
3039
3605
|
store_definition_build_detail(class_name, e)
|
|
@@ -3063,7 +3629,8 @@ module Rigor
|
|
|
3063
3629
|
key = class_name.to_s.delete_prefix("::")
|
|
3064
3630
|
details = ((@state[:definition_build_details] ||= {})[key] ||= [])
|
|
3065
3631
|
details << [key, error.class.name.to_s, definition_build_member(error),
|
|
3066
|
-
definition_build_conflict_buffers(error), unresolved_type_name_detail(error)
|
|
3632
|
+
definition_build_conflict_buffers(error, key), unresolved_type_name_detail(error),
|
|
3633
|
+
variable_mixin_count(error, key)].freeze
|
|
3067
3634
|
end
|
|
3068
3635
|
|
|
3069
3636
|
# Issue #997 — the `RBS::NoTypeFoundError` twin of {#definition_build_member}: `nil` for every other
|
|
@@ -3214,7 +3781,7 @@ module Rigor
|
|
|
3214
3781
|
end
|
|
3215
3782
|
|
|
3216
3783
|
# The third twin of {#warn_about_quarantined_signatures} / {#warn_about_virtual_rbs_collisions}: name,
|
|
3217
|
-
# once per class per
|
|
3784
|
+
# once per class per loader, a `RBS::DefinitionBuilder` failure caught in
|
|
3218
3785
|
# {#build_instance_definition} / {#build_singleton_definition}'s rescue. Without it, `class_known?`
|
|
3219
3786
|
# stays true (it only consults {#known_class_names_set}, never a definition build), so every call on
|
|
3220
3787
|
# the class — real methods and typos alike — silently degrades to `Dynamic[top]`.
|
|
@@ -3251,14 +3818,14 @@ module Rigor
|
|
|
3251
3818
|
|
|
3252
3819
|
def definition_build_failure_warning(class_name, error)
|
|
3253
3820
|
first_line = error.message.to_s.lines.first.to_s.strip
|
|
3254
|
-
buffers = definition_build_conflict_buffers(error)
|
|
3821
|
+
buffers = definition_build_conflict_buffers(error, class_name)
|
|
3255
3822
|
collisions =
|
|
3256
3823
|
if buffers.empty?
|
|
3257
3824
|
""
|
|
3258
3825
|
else
|
|
3259
3826
|
listed = buffers.first(QUARANTINE_WARN_LIMIT)
|
|
3260
3827
|
more = buffers.size - listed.size
|
|
3261
|
-
lines = listed.map { |name| " - #{name}" }
|
|
3828
|
+
lines = listed.map { |name| " - #{signature_source_label(name)}" }
|
|
3262
3829
|
lines << " … and #{more} more" if more.positive?
|
|
3263
3830
|
"\n Colliding declaration(s):\n#{lines.join("\n")}"
|
|
3264
3831
|
end
|
|
@@ -3267,6 +3834,14 @@ module Rigor
|
|
|
3267
3834
|
"`Dynamic[top]` — real methods and typos alike — instead of resolving normally.#{collisions}"
|
|
3268
3835
|
end
|
|
3269
3836
|
|
|
3837
|
+
# Issue #2440 — a `virtual:<plugin>:<path>` buffer named by the `.rb` file it was synthesized from.
|
|
3838
|
+
def signature_source_label(name)
|
|
3839
|
+
prefix, plugin_id, path = name.to_s.split(":", 3)
|
|
3840
|
+
return name.to_s unless prefix == "virtual" && plugin_id && path && !path.empty?
|
|
3841
|
+
|
|
3842
|
+
"#{path} (inline RBS via #{plugin_id})"
|
|
3843
|
+
end
|
|
3844
|
+
|
|
3270
3845
|
def warn_about_deferred_definition_build_failures
|
|
3271
3846
|
return if @state[:definition_build_summary_warned]
|
|
3272
3847
|
|
|
@@ -3305,10 +3880,11 @@ module Rigor
|
|
|
3305
3880
|
# `InvalidOverloadMethodError` carry `#members` (each with a `#location`);
|
|
3306
3881
|
# `DuplicatedInterfaceMethodDefinitionError` carries a single `#member`; most others
|
|
3307
3882
|
# (`NoSuperclassFoundError`, `UnknownMethodAliasError`, `RecursiveAncestorError`, …) carry only a bare
|
|
3308
|
-
# `#location`.
|
|
3309
|
-
#
|
|
3310
|
-
|
|
3311
|
-
|
|
3883
|
+
# `#location`. A duplicated instance variable carries one location and a `SuperclassMismatchError` none,
|
|
3884
|
+
# so both read the class entry's declarations for the other side (#2440). Falls back to an empty list —
|
|
3885
|
+
# the warning still names the class and the exception's own message — for the remainder.
|
|
3886
|
+
def definition_build_conflict_buffers(error, class_name = nil)
|
|
3887
|
+
definition_build_error_locations(error, class_name)
|
|
3312
3888
|
.compact.filter_map { |loc| loc.buffer&.name }.map(&:to_s)
|
|
3313
3889
|
.reject { |name| name == CACHED_LOCATION_BUFFER_NAME }.uniq.freeze
|
|
3314
3890
|
rescue ::RBS::BaseError, StandardError
|
|
@@ -3317,14 +3893,100 @@ module Rigor
|
|
|
3317
3893
|
|
|
3318
3894
|
# The `RBS::Location`s an error carries, by whichever accessor its class happens to expose (see the
|
|
3319
3895
|
# shape survey above). Split out so the buffer-name extraction reads as one pipeline.
|
|
3320
|
-
def definition_build_error_locations(error)
|
|
3896
|
+
def definition_build_error_locations(error, class_name = nil)
|
|
3321
3897
|
return Array(error.members).map(&:location) if error.respond_to?(:members) && error.members
|
|
3322
3898
|
return [error.member.location] if error.respond_to?(:member) && error.member
|
|
3899
|
+
# `VariableDuplicationError` is younger than the rbs floor the gemspec allows.
|
|
3900
|
+
if defined?(::RBS::VariableDuplicationError) && error.is_a?(::RBS::VariableDuplicationError)
|
|
3901
|
+
return variable_duplication_locations(error, class_name)
|
|
3902
|
+
end
|
|
3903
|
+
return superclass_mismatch_locations(error) if error.is_a?(::RBS::SuperclassMismatchError)
|
|
3323
3904
|
return [error.location] if error.respond_to?(:location)
|
|
3324
3905
|
|
|
3325
3906
|
[]
|
|
3326
3907
|
end
|
|
3327
3908
|
|
|
3909
|
+
# Issue #2440 — a duplicated `@x` / `self.@x` carries the location of ONE of its two declarations, so
|
|
3910
|
+
# the file list named only that side (typically the `virtual:rbs-inline:` buffer, leaving out the
|
|
3911
|
+
# committed `.rbs` the user can actually edit). The class entry's declarations name the rest: every
|
|
3912
|
+
# declaration of the class that declares the same variable, of the same kind. When the variable's
|
|
3913
|
+
# owner is a module the failing class mixes in (rbs counts it once per declaration that does), the
|
|
3914
|
+
# declarations of the failing class that mix that module in are named too: they are the duplicate.
|
|
3915
|
+
def variable_duplication_locations(error, class_name = nil)
|
|
3916
|
+
[error.location, *variable_declarations(error).map(&:location), *mixin_locations(class_name, error.type_name)]
|
|
3917
|
+
end
|
|
3918
|
+
|
|
3919
|
+
# Every declaration of the variable the error names, of the error's kind, in its owner's entry.
|
|
3920
|
+
def variable_declarations(error)
|
|
3921
|
+
member_class =
|
|
3922
|
+
if error.is_a?(::RBS::ClassInstanceVariableDuplicationError)
|
|
3923
|
+
::RBS::AST::Members::ClassInstanceVariable
|
|
3924
|
+
else
|
|
3925
|
+
::RBS::AST::Members::InstanceVariable
|
|
3926
|
+
end
|
|
3927
|
+
class_entry_decls(error.type_name).flat_map do |decl|
|
|
3928
|
+
decl_members(decl).select { |m| m.instance_of?(member_class) && m.name == error.variable_name }
|
|
3929
|
+
end
|
|
3930
|
+
end
|
|
3931
|
+
|
|
3932
|
+
# Issue #2440 — how many times `class_name`'s own declarations mix in the module that owns the
|
|
3933
|
+
# duplicated variable, when that is what duplicated it: the owner is another module, it declares the
|
|
3934
|
+
# variable exactly once, and the class mixes it in at least twice (in one declaration or across
|
|
3935
|
+
# several). nil otherwise — a variable duplicated in the owner itself and inherited (`Sub < Base`)
|
|
3936
|
+
# is not a double mixin. Carried in the failure tuple's sixth slot so the advice can tell the two
|
|
3937
|
+
# apart on a replayed tuple too.
|
|
3938
|
+
def variable_mixin_count(error, class_name)
|
|
3939
|
+
return nil unless defined?(::RBS::VariableDuplicationError) && error.is_a?(::RBS::VariableDuplicationError)
|
|
3940
|
+
|
|
3941
|
+
owner = parse_type_name(class_name)
|
|
3942
|
+
return nil if owner.nil? || owner == error.type_name
|
|
3943
|
+
return nil unless variable_declarations(error).size == 1
|
|
3944
|
+
|
|
3945
|
+
count = class_entry_decls(owner).sum do |decl|
|
|
3946
|
+
decl_members(decl).count { |m| mixin_of?(m, error.type_name) }
|
|
3947
|
+
end
|
|
3948
|
+
count >= 2 ? count : nil
|
|
3949
|
+
rescue ::RBS::BaseError, StandardError
|
|
3950
|
+
nil
|
|
3951
|
+
end
|
|
3952
|
+
|
|
3953
|
+
MIXIN_MEMBERS = [::RBS::AST::Members::Include, ::RBS::AST::Members::Extend,
|
|
3954
|
+
::RBS::AST::Members::Prepend].freeze
|
|
3955
|
+
private_constant :MIXIN_MEMBERS
|
|
3956
|
+
|
|
3957
|
+
# `resolve_type_names` has made every mixin name absolute.
|
|
3958
|
+
def mixin_of?(member, module_name)
|
|
3959
|
+
MIXIN_MEMBERS.any? { |klass| member.is_a?(klass) } && member.name == module_name
|
|
3960
|
+
end
|
|
3961
|
+
|
|
3962
|
+
def mixin_locations(class_name, module_name)
|
|
3963
|
+
return [] if class_name.nil?
|
|
3964
|
+
|
|
3965
|
+
owner = parse_type_name(class_name)
|
|
3966
|
+
return [] if owner.nil? || owner == module_name
|
|
3967
|
+
|
|
3968
|
+
class_entry_decls(owner).filter_map do |decl|
|
|
3969
|
+
decl.location if decl_members(decl).any? { |m| mixin_of?(m, module_name) }
|
|
3970
|
+
end
|
|
3971
|
+
end
|
|
3972
|
+
|
|
3973
|
+
def decl_members(decl)
|
|
3974
|
+
decl.respond_to?(:members) ? Array(decl.members) : []
|
|
3975
|
+
end
|
|
3976
|
+
|
|
3977
|
+
# Issue #2440 — `SuperclassMismatchError` carries the class entry and no location of its own: the
|
|
3978
|
+
# colliding declarations are the ones that name a superclass. {.entry_declarations} reads the entry on
|
|
3979
|
+
# both rbs majors (`each_decl` on 4.x, `MultiEntry::D` wrappers on 3.x).
|
|
3980
|
+
def superclass_mismatch_locations(error)
|
|
3981
|
+
decls = self.class.entry_declarations(error.entry)
|
|
3982
|
+
decls.select { |decl| decl.respond_to?(:super_class) && decl.super_class }.map(&:location)
|
|
3983
|
+
end
|
|
3984
|
+
|
|
3985
|
+
def class_entry_decls(type_name)
|
|
3986
|
+
entry = env&.class_decls&.[](type_name)
|
|
3987
|
+
entry.nil? ? [] : self.class.entry_declarations(entry)
|
|
3988
|
+
end
|
|
3989
|
+
|
|
3328
3990
|
# The member the failure is ABOUT, taken off the error object rather than parsed out of its message.
|
|
3329
3991
|
#
|
|
3330
3992
|
# Issue #696 — the message cannot serve here. It is built from `RBS::Location`s, and the ADR-54 env
|