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
|
@@ -235,7 +235,7 @@ module Rigor
|
|
|
235
235
|
# A subset of a multi-collector run that its nodes keep asking for, built the first time and reused:
|
|
236
236
|
# each collector alone (a pair in which one declines), the run without each collector (a larger run in
|
|
237
237
|
# which one declines), and the run split by `factory_block` and `lexical_prefix` variant where it mixes
|
|
238
|
-
# them. `
|
|
238
|
+
# them. `kind` names the table; `key` is the collector, or the rule's Symbol for a split.
|
|
239
239
|
def subset(kind, collector = nil)
|
|
240
240
|
table = (@subsets[kind] ||= {}.compare_by_identity)
|
|
241
241
|
key = collector || kind
|
|
@@ -5,3 +5,7 @@
|
|
|
5
5
|
# the indexer, which loads the walk. `ScopeIndexer` itself requires the traversal file rather than this one, which
|
|
6
6
|
# keeps the two free of a require cycle.
|
|
7
7
|
require_relative "scope_indexer"
|
|
8
|
+
|
|
9
|
+
# {Rigor::Reflection.class_declarations_in}'s collectors: the plugin-facing walk lives with `Reflection` but needs the
|
|
10
|
+
# walk above, so it is loaded here rather than by `reflection/declarations`, which the engine-free paths load.
|
|
11
|
+
require_relative "../reflection/declaration_collectors"
|
|
@@ -5,6 +5,7 @@ require_relative "../type"
|
|
|
5
5
|
require_relative "../source/node_walker"
|
|
6
6
|
require_relative "mutation_rejoin"
|
|
7
7
|
require_relative "receiver_alias"
|
|
8
|
+
require_relative "set_mutation"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
10
11
|
module Inference
|
|
@@ -54,11 +55,31 @@ module Rigor
|
|
|
54
55
|
# and so does `it` / `_1` wherever it is read, and an accumulator parameter (`inject([]) { |acc, e| acc << e }`):
|
|
55
56
|
# the rule is wider than "an element", which only loses precision. It does not follow an alias (`y = x`) or
|
|
56
57
|
# an element passed to a call (`add(x)`), which the straight-line widening does not either.
|
|
57
|
-
|
|
58
|
+
#
|
|
59
|
+
# A Set-only mutator (`add`, `merge`, `subtract`, … — {SetMutation::MUTATORS} less `SHAPE_MUTATORS`) counts
|
|
60
|
+
# only when `set_held` is given and answers true (issue #2363). The names are read without their receiver,
|
|
61
|
+
# and `merge` on a Hash element returns a new Hash, so the caller answers from the collection's type whether
|
|
62
|
+
# an element could be a Set at all ({holds_set?}). The block runs at the first such site, at most once.
|
|
63
|
+
def element_mutation?(body, names, &set_held)
|
|
58
64
|
return false if body.nil?
|
|
59
65
|
|
|
60
66
|
names |= nested_parameter_names(body)
|
|
61
|
-
Source::NodeWalker.each(body).any? { |node| element_mutation_site?(node, names) }
|
|
67
|
+
Source::NodeWalker.each(body).any? { |node| element_mutation_site?(node, names, set_held) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# True when `type` holds a `Set` at some depth: as itself, a union member, a `Tuple` element, a `HashShape`
|
|
71
|
+
# value or a type argument. A collection whose type holds none has no element a Set-only mutator can be
|
|
72
|
+
# mutating, so a `merge` on one of its elements is `Hash#merge`, which returns a new Hash.
|
|
73
|
+
def holds_set?(type)
|
|
74
|
+
case type
|
|
75
|
+
when Type::Constant then SetMutation.constant?(type)
|
|
76
|
+
when Type::Nominal then type.class_name == "Set" || type.type_args.any? { |arg| holds_set?(arg) }
|
|
77
|
+
when Type::Tuple then type.elements.any? { |element| holds_set?(element) }
|
|
78
|
+
when Type::HashShape then type.pairs.each_value.any? { |value| holds_set?(value) }
|
|
79
|
+
when Type::Union then type.members.any? { |member| holds_set?(member) }
|
|
80
|
+
when Type::Difference then holds_set?(type.base)
|
|
81
|
+
else false
|
|
82
|
+
end
|
|
62
83
|
end
|
|
63
84
|
|
|
64
85
|
# The names `parameters` (a block's parameter list, or a `for` index) binds.
|
|
@@ -80,9 +101,12 @@ module Rigor
|
|
|
80
101
|
end
|
|
81
102
|
end
|
|
82
103
|
|
|
83
|
-
def element_mutation_site?(node, names)
|
|
104
|
+
def element_mutation_site?(node, names, set_held)
|
|
84
105
|
if node.is_a?(Prism::CallNode)
|
|
85
|
-
|
|
106
|
+
return false unless element_rooted?(node.receiver, names)
|
|
107
|
+
|
|
108
|
+
MutationWidening::SHAPE_MUTATORS.include?(node.name) ||
|
|
109
|
+
(!set_held.nil? && SetMutation::MUTATORS.include?(node.name) && set_held.call)
|
|
86
110
|
else
|
|
87
111
|
ELEMENT_WRITE_NODES.include?(node.class) && element_rooted?(node.receiver, names)
|
|
88
112
|
end
|
|
@@ -101,15 +125,23 @@ module Rigor
|
|
|
101
125
|
# A block call hands each of its receiver's elements to the block's parameters, and a body that mutates one in
|
|
102
126
|
# place (`a.each { |x| x << 2 }`) changes the shape the receiver's binding still carries one level down
|
|
103
127
|
# (`[[1]]`). The receiver's variables drop their shapes at every depth ({widen}), as a `for` loop's
|
|
104
|
-
# value does ({StatementEvaluator#for_collection_after}).
|
|
128
|
+
# value does ({StatementEvaluator#for_collection_after}). A receiver that is a call exposing a variable's
|
|
129
|
+
# elements (`h.values`, `a.dup`, `a.each_with_index`) names that variable ({ReceiverAlias.element_sources}).
|
|
105
130
|
def widen_element_aliases(receiver, body, names, scope)
|
|
106
|
-
return scope unless element_mutation?(body, names)
|
|
131
|
+
return scope unless element_mutation?(body, names) { receiver_holds_set?(receiver, scope) }
|
|
107
132
|
|
|
108
133
|
widen_receiver(receiver, scope)
|
|
109
134
|
end
|
|
110
135
|
|
|
136
|
+
def receiver_holds_set?(receiver, scope)
|
|
137
|
+
ReceiverAlias.element_sources(receiver).any? do |read|
|
|
138
|
+
getter, = MutationWidening::ALIAS_ACCESSORS[read.class]
|
|
139
|
+
getter && holds_set?(scope.public_send(getter, ReceiverAlias.read_name(read)))
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
111
143
|
def widen_receiver(receiver, scope)
|
|
112
|
-
ReceiverAlias.
|
|
144
|
+
ReceiverAlias.element_sources(receiver).reduce(scope) { |acc, read| widen_read(read, acc) }
|
|
113
145
|
end
|
|
114
146
|
|
|
115
147
|
# `a.each(&:strip!)`: a block argument naming a mutator mutates every element it is handed.
|
|
@@ -8,7 +8,7 @@ module Rigor
|
|
|
8
8
|
# file's methods: the file was never parsed this run, so there is no live node to store. A handle records
|
|
9
9
|
# the four things needed to resolve, fingerprint, or scope it without the node:
|
|
10
10
|
#
|
|
11
|
-
# - `path` + `node_id` — the resolution key. The per-run parse memo ({
|
|
11
|
+
# - `path` + `node_id` — the resolution key. The per-run parse memo ({DefNodeResolver.resolve}) parses the
|
|
12
12
|
# file once and returns the `Prism::DefNode` whose `node_id` matches (Prism `node_id` is stable across
|
|
13
13
|
# repeated parses of identical bytes). `name` is the cross-check: a resolved node whose `name` differs
|
|
14
14
|
# from the handle's forces a fresh walk (bundle/source skew that the digest gate should make impossible —
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
require_relative "def_handle"
|
|
5
|
+
require_relative "../typing_state"
|
|
5
6
|
require_relative "../source/node_children"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
@@ -20,23 +21,35 @@ module Rigor
|
|
|
20
21
|
# file, and finally nil — conservative (the accessor then reports no user def, losing precision but never
|
|
21
22
|
# firing a wrong diagnostic), never silent.
|
|
22
23
|
#
|
|
23
|
-
# The memo is a
|
|
24
|
-
#
|
|
24
|
+
# The memo is a {TypingState} Frame installed for the duration of a run ({.with_run}) and restored on the way
|
|
25
|
+
# out: per thread, and a fork child inherits whatever is installed at the fork. Outside a run scope every
|
|
25
26
|
# resolution parses directly — a correct if un-memoised fallback for runner-less probes.
|
|
26
27
|
module DefNodeResolver
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
# ADR-129 WD1 — the run's memo is a Frame: {.with_run} installs it for the run and restores the previous one, and
|
|
29
|
+
# whether one is installed changes answers (outside a run every resolution parses afresh and no `Module.nesting`
|
|
30
|
+
# is recorded). The installed hash is frozen; the four tables in it are extensional caches its owner fills in
|
|
31
|
+
# place without a touch (Memos by kind, whose entry is fixed once written): `nodes` is keyed on
|
|
32
|
+
# `(path, node_id)`, `indexes` and `refinement_queries` on the file's path, and `nestings` on the identity of
|
|
33
|
+
# the node this module minted, holding the nesting the handle's bundle row (or the file's `refine` bodies)
|
|
34
|
+
# recorded for it.
|
|
35
|
+
RUN_MEMO = TypingState.frame(
|
|
36
|
+
:def_node_resolver_run,
|
|
37
|
+
memos: %i[nodes indexes nestings refinement_queries],
|
|
38
|
+
reason: "installed per run by with_run; its tables are extensional caches, keyed on (path, node_id), on the " \
|
|
39
|
+
"path, and (nestings) on the minted node's identity, filled in place",
|
|
40
|
+
# G1′ witness: an incremental recheck types a file against handles of the unchanged files, and
|
|
41
|
+
# {.resolve} reads the run's tables from inside `type_of` (#2874).
|
|
42
|
+
witness: "spec/fixtures/typing_state_witnesses/def_node_resolver_run.rb"
|
|
43
|
+
)
|
|
44
|
+
private_constant :RUN_MEMO
|
|
29
45
|
|
|
30
46
|
# Installs a fresh per-run resolution memo, restoring the previous one on exit (always, even on a raise).
|
|
31
47
|
# `nodes` caches the resolved node per `(path, node_id)`; `indexes` caches each file's parse index;
|
|
32
48
|
# `nestings` re-attaches each handle's recorded `Module.nesting` to the node this module minted for it
|
|
33
49
|
# ({.rehydrated_nesting}).
|
|
34
|
-
def self.with_run
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
yield
|
|
38
|
-
ensure
|
|
39
|
-
Thread.current[MEMO_KEY] = previous
|
|
50
|
+
def self.with_run(&)
|
|
51
|
+
memo = { nodes: {}, indexes: {}, nestings: {}.compare_by_identity, refinement_queries: {} }.freeze
|
|
52
|
+
TypingState.current.with(RUN_MEMO, memo, &)
|
|
40
53
|
end
|
|
41
54
|
|
|
42
55
|
# Whether a per-run memo scope is in force. The rehydrated `Module.nesting` ({.rehydrated_nesting}) is
|
|
@@ -44,7 +57,12 @@ module Rigor
|
|
|
44
57
|
# per-run memo scope" clause states — and lets a spec assert that no handle-consuming entry point
|
|
45
58
|
# resolves outside it, which is the difference between the peel fallback being a documented edge and
|
|
46
59
|
# being silently reachable from production.
|
|
47
|
-
def self.run_scope? = !
|
|
60
|
+
def self.run_scope? = !TypingState.current[RUN_MEMO].nil?
|
|
61
|
+
|
|
62
|
+
# Issue #3160 — the nodes this run's memo has minted so far, keyed `[path, node_id]` (nil outside a run scope).
|
|
63
|
+
# The return memo keys on them, so the runner keeps the table to find an unchanged file's def in the memo once
|
|
64
|
+
# the run scope has closed.
|
|
65
|
+
def self.resolved_nodes = TypingState.current[RUN_MEMO]&.fetch(:nodes)
|
|
48
66
|
|
|
49
67
|
# Resolves `handle` to a `Prism::DefNode` (the same object across the run for a given (path, node_id)), or
|
|
50
68
|
# nil. A non-handle argument is returned unchanged, so callers can pass a table value that is either a live
|
|
@@ -52,7 +70,7 @@ module Rigor
|
|
|
52
70
|
def self.resolve(handle)
|
|
53
71
|
return handle unless handle.is_a?(DefHandle)
|
|
54
72
|
|
|
55
|
-
memo =
|
|
73
|
+
memo = TypingState.current[RUN_MEMO]
|
|
56
74
|
return locate(handle, {}) if memo.nil?
|
|
57
75
|
|
|
58
76
|
key = [handle.path, handle.node_id].freeze
|
|
@@ -96,7 +114,7 @@ module Rigor
|
|
|
96
114
|
# def_node_resolver_spec.rb` pins that, so a new one added outside a run scope fails there rather than
|
|
97
115
|
# silently reverting to the peel.
|
|
98
116
|
def self.rehydrated_nesting(node)
|
|
99
|
-
memo =
|
|
117
|
+
memo = TypingState.current[RUN_MEMO]
|
|
100
118
|
memo && memo[:nestings][node]
|
|
101
119
|
end
|
|
102
120
|
|
|
@@ -104,7 +122,7 @@ module Rigor
|
|
|
104
122
|
# is recorded like any other: it is the bundle saying the def is written at the top level, and dropping
|
|
105
123
|
# it would leave the warm path peeling the caller's namespace where a cold run resolves at the top
|
|
106
124
|
# level. Only `nil` — a bundle that recorded nothing for the def — is skipped, and
|
|
107
|
-
# `
|
|
125
|
+
# the `SCHEMA` bump to 17 (`Cache::IncrementalSnapshot`) keeps a pre-#716 blob, where `nil` MEANT top level,
|
|
108
126
|
# from being read as one.
|
|
109
127
|
def self.record_nesting(memo, node, nesting)
|
|
110
128
|
return if node.nil? || nesting.nil?
|
|
@@ -117,7 +135,7 @@ module Rigor
|
|
|
117
135
|
# read: the typed refined arm reads another file's refine body through it. The bodies' `Module.nesting`
|
|
118
136
|
# is recorded as a handle's is, so the re-typed body reads its constants where it is written.
|
|
119
137
|
def self.refinement_query(path)
|
|
120
|
-
memo =
|
|
138
|
+
memo = TypingState.current[RUN_MEMO]
|
|
121
139
|
return build_refinement_query(path, nil) if memo.nil?
|
|
122
140
|
|
|
123
141
|
queries = memo[:refinement_queries]
|
|
@@ -35,7 +35,7 @@ module Rigor
|
|
|
35
35
|
body = def_node.body
|
|
36
36
|
return nil if body.nil?
|
|
37
37
|
|
|
38
|
-
last =
|
|
38
|
+
last = primary_value_expression(body)
|
|
39
39
|
return nil if last.nil?
|
|
40
40
|
|
|
41
41
|
inner_scope = scope_index[last] || scope_index[body] || scope_index[def_node]
|
|
@@ -46,11 +46,54 @@ module Rigor
|
|
|
46
46
|
|
|
47
47
|
lookup_context = keep_lookup_nil ? LookupContext.new(scope_index, truthy_guards(body)) : nil
|
|
48
48
|
last_type = with_lookup_nil(last_type, last, inner_scope, lookup_context) if lookup_context
|
|
49
|
-
|
|
49
|
+
arm_types = rescue_arm_types(body, inner_scope, scope_index, lookup_context)
|
|
50
|
+
return nil if arm_types.nil?
|
|
51
|
+
|
|
52
|
+
union_with_explicit_returns(body, Type::Combinator.union(last_type, *arm_types), scope_index,
|
|
53
|
+
lookup_context: lookup_context)
|
|
50
54
|
rescue StandardError
|
|
51
55
|
nil
|
|
52
56
|
end
|
|
53
57
|
|
|
58
|
+
# Issue #2410 — the value a def body ends on when no exception escapes its primary path. A def-level
|
|
59
|
+
# `rescue … else … end` body is a `BeginNode` whose else-clause, when present, replaces the body's value (the
|
|
60
|
+
# ensure-clause's value is discarded, so it never counts).
|
|
61
|
+
def primary_value_expression(body)
|
|
62
|
+
return body_last_expression(body) unless body.is_a?(Prism::BeginNode) && body.else_clause
|
|
63
|
+
|
|
64
|
+
body_last_expression(body.else_clause.statements)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Issue #2410 — the value of each arm of a def-level rescue chain, which the method returns when that arm
|
|
68
|
+
# completes. An empty arm returns `nil`; an arm ending in `raise`, `retry` or `return` types as bottom and so
|
|
69
|
+
# adds nothing here (a `return`'s value is collected with the other explicit returns). Nil when an arm cannot
|
|
70
|
+
# be typed: the body's value alone would be a narrower, wrong contract.
|
|
71
|
+
def rescue_arm_types(body, inner_scope, scope_index, lookup_context)
|
|
72
|
+
return [] unless body.is_a?(Prism::BeginNode)
|
|
73
|
+
|
|
74
|
+
types = []
|
|
75
|
+
arm = body.rescue_clause
|
|
76
|
+
while arm
|
|
77
|
+
type = rescue_arm_type(arm, inner_scope, scope_index, lookup_context)
|
|
78
|
+
return nil if type.nil?
|
|
79
|
+
|
|
80
|
+
types << type
|
|
81
|
+
arm = arm.subsequent
|
|
82
|
+
end
|
|
83
|
+
types
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def rescue_arm_type(arm, inner_scope, scope_index, lookup_context)
|
|
87
|
+
value = body_last_expression(arm.statements)
|
|
88
|
+
return Type::Combinator.constant_of(nil) if value.nil?
|
|
89
|
+
|
|
90
|
+
scope = scope_index[value] || scope_index[arm] || inner_scope
|
|
91
|
+
type = safe_type_of(scope, value)
|
|
92
|
+
return nil if type.nil?
|
|
93
|
+
|
|
94
|
+
lookup_context ? with_lookup_nil(type, value, scope, lookup_context) : type
|
|
95
|
+
end
|
|
96
|
+
|
|
54
97
|
# What {#optimistic_value?} reads beside a node's scope: the engine's per-node scopes, and for each lookup
|
|
55
98
|
# under a guard the source of the guarding predicates ({#truthy_guards}).
|
|
56
99
|
LookupContext = Data.define(:scope_index, :guards)
|
|
@@ -83,7 +126,7 @@ module Rigor
|
|
|
83
126
|
end
|
|
84
127
|
|
|
85
128
|
def optimistic_call?(node, scope, context)
|
|
86
|
-
if scope.
|
|
129
|
+
if scope.optimistic_origin(node)
|
|
87
130
|
return false if guarded_lookup?(node, context) || non_empty_end?(node, scope)
|
|
88
131
|
|
|
89
132
|
return true
|
|
@@ -227,8 +270,7 @@ module Rigor
|
|
|
227
270
|
|
|
228
271
|
scope = scope_index[return_node] || scope_index[args.first]
|
|
229
272
|
return if scope.nil?
|
|
230
|
-
# `return a, b` packs into a Tuple at runtime;
|
|
231
|
-
# returns contribute no type to keep the implementation focused.
|
|
273
|
+
# `return a, b` packs into a Tuple at runtime; only the single-value form is typed.
|
|
232
274
|
return unless args.size == 1
|
|
233
275
|
|
|
234
276
|
type = safe_type_of(scope, args.first)
|
|
@@ -25,8 +25,10 @@ module Rigor
|
|
|
25
25
|
# truth-tested: {UNKNOWN} and {ABSENT} are both truthy, so any other use folds a decline into a firing arm.
|
|
26
26
|
# `spec/rigor/inference/definer_resolution_case_in_spec.rb` fails on a call site of any other shape.
|
|
27
27
|
#
|
|
28
|
-
# It migrates one question at a time: `SourceArity` (C1b)
|
|
29
|
-
#
|
|
28
|
+
# It migrates one question at a time: `SourceArity` (C1b, C2-c) asks for the receiver's own side, instance or
|
|
29
|
+
# singleton; the override lints (C1c) and the C2 typing sites (`ExpressionTyper#resolve_typing_def_with_owner`,
|
|
30
|
+
# `StatementEvaluator#own_definer_node`) read the instance side; `RefinedDispatch::OwnMethod.certain_owner?` and
|
|
31
|
+
# `CheckRules#reached_definer_visibility` pass the side they hold.
|
|
30
32
|
module DefinerResolution
|
|
31
33
|
Known = Data.define(:answer, :owner)
|
|
32
34
|
UnknownResult = Data.define
|
|
@@ -80,11 +82,16 @@ module Rigor
|
|
|
80
82
|
# the entry's answer, as `[owner_name, value]` (the owner is part of the answer so that two worlds naming
|
|
81
83
|
# different parents disagree). It is the override lints' parent read: the nearest project ancestor after the
|
|
82
84
|
# class whose block answers.
|
|
85
|
+
#
|
|
86
|
+
# Issue #3156 — a class or module the project declares but whose Ruby body records no method, superclass or
|
|
87
|
+
# include (`module PMod; end`, `class AMid` / `end`) is an external entry on the chain, which only its RBS
|
|
88
|
+
# declaration speaks for. The override block answers from RBS, so such an entry is asked too, under the
|
|
89
|
+
# project name it resolves to (`ResolutionChain::Entry#declared_name`); an undeclared external is not.
|
|
83
90
|
def override_answer(scope)
|
|
84
91
|
lambda do |chain, from|
|
|
85
|
-
first_hit(chain, scope, from) do |
|
|
86
|
-
value = yield(
|
|
87
|
-
[
|
|
92
|
+
first_hit(chain, scope, from, askable_external: true) do |name|
|
|
93
|
+
value = yield(name)
|
|
94
|
+
[name, value] unless value.nil?
|
|
88
95
|
end
|
|
89
96
|
end
|
|
90
97
|
end
|
|
@@ -329,17 +336,20 @@ module Rigor
|
|
|
329
336
|
kind == :singleton ? [:singleton, method_name.to_sym] : method_name.to_sym
|
|
330
337
|
end
|
|
331
338
|
|
|
332
|
-
|
|
339
|
+
# The first entry at or after `from` whose block answers. The default answer functions are handed the project
|
|
340
|
+
# entry; the override one (`askable_external:`) is handed a name, and also asks an external entry the project
|
|
341
|
+
# declares.
|
|
342
|
+
def first_hit(chain, scope, from, askable_external: false)
|
|
333
343
|
index = from - 1
|
|
334
344
|
found = nil
|
|
335
345
|
chain.search(scope, from) do |entry|
|
|
336
346
|
index += 1
|
|
337
|
-
next
|
|
347
|
+
next unless (name = entry.external? ? askable_external && entry.declared_name(scope) : entry.name)
|
|
338
348
|
|
|
339
|
-
answer = yield(entry)
|
|
349
|
+
answer = yield(askable_external ? name : entry)
|
|
340
350
|
next if answer.nil?
|
|
341
351
|
|
|
342
|
-
found = Hit.new(answer,
|
|
352
|
+
found = Hit.new(answer, name, index, entry.side)
|
|
343
353
|
end
|
|
344
354
|
found
|
|
345
355
|
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../typing_state"
|
|
4
|
+
require_relative "../memo_audit"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
# The `method_name (Symbol) → [owner names]` inverted index the override gate reads
|
|
9
|
+
# (`ExpressionTyper#method_definers_index`), over one instance or singleton def table.
|
|
10
|
+
#
|
|
11
|
+
# Issue #2300 — a file's def table is its own `def`s merged over the cross-file seed
|
|
12
|
+
# (`ScopeIndexer#merge_def_node_tables`, `#merge_singleton_def_nodes`, then the #526 extend fold), a new Hash per
|
|
13
|
+
# file. Inverting it whole scanned every project `def` once per analysed file. {.record_layers} remembers the
|
|
14
|
+
# seed a final merged table was built over, and {.for} then inverts the seed ONCE per seed table and, per file,
|
|
15
|
+
# only the owners whose inner table is not the seed's own object ({Layered}).
|
|
16
|
+
#
|
|
17
|
+
# The two answers hold the same owners: an owner the merge left alone shares the seed's inner table, so its
|
|
18
|
+
# names are the seed's, and every other owner is read from the merged table itself. That holds only while the
|
|
19
|
+
# seed cannot change under the cached index, so a seed whose tables are not all frozen, or a merged table
|
|
20
|
+
# missing one of the seed's owners, is inverted whole, as is a table {.record_layers} was not told about. Only
|
|
21
|
+
# the order of a name's owners can differ, and the gate asks `any?` of the list.
|
|
22
|
+
module DefinersIndex
|
|
23
|
+
# These thread slots are bounded, like `ExpressionTyper#method_definers_index`'s two ways. Each is a fixed
|
|
24
|
+
# number of entries, replaced rather than accumulated: two `[merged, seed]` pairs (the last file's two def
|
|
25
|
+
# tables) and two seed indexes. The run that filled them releases them when it ends (run-scoped, #2399),
|
|
26
|
+
# so a finished run keeps none of its seed tables or their indexes alive.
|
|
27
|
+
#
|
|
28
|
+
# ADR-129 WD1 — all three are run-scoped Memos keyed on table identity. The layers say only which seed a merged
|
|
29
|
+
# table was built over, a shortcut: {.for} answers the same index owners, `Layered` or whole, so the answer is
|
|
30
|
+
# a function of the table alone. Each seed index is a pure function of its seed table.
|
|
31
|
+
#
|
|
32
|
+
# Each hit goes through `MemoAudit.check`: a `Layered` answer is compared, in {.canonical} form, with the table
|
|
33
|
+
# inverted whole, and a seed index with the seed inverted again. While an audit recomputation runs
|
|
34
|
+
# ({MemoAudit.suspended?}) the three are neither read nor written, so it inverts every table whole.
|
|
35
|
+
LAYERS = TypingState.memo(
|
|
36
|
+
:definers_index_layers,
|
|
37
|
+
run_scoped: true, audit: "definers index layers",
|
|
38
|
+
reason: "the seed each of the last two merged tables was built over; keyed on table identity"
|
|
39
|
+
)
|
|
40
|
+
SEED_INDEX = TypingState.memo(
|
|
41
|
+
:definers_index_seed,
|
|
42
|
+
run_scoped: true, audit: "definers index seed",
|
|
43
|
+
reason: "pure function of the seed table it keys on"
|
|
44
|
+
)
|
|
45
|
+
SEED_INDEX_ALT = TypingState.memo(
|
|
46
|
+
:definers_index_seed_alt,
|
|
47
|
+
run_scoped: true, audit: "definers index seed alt",
|
|
48
|
+
reason: "pure function of the seed table it keys on"
|
|
49
|
+
)
|
|
50
|
+
private_constant :LAYERS, :SEED_INDEX, :SEED_INDEX_ALT
|
|
51
|
+
|
|
52
|
+
# A name's owners: the seed's, less the owners the file replaced, plus the replaced owners' own.
|
|
53
|
+
class Layered
|
|
54
|
+
def initialize(seed_index, replaced, replaced_index)
|
|
55
|
+
@seed_index = seed_index
|
|
56
|
+
@replaced = replaced
|
|
57
|
+
@replaced_index = replaced_index
|
|
58
|
+
@answers = {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def [](method_name)
|
|
62
|
+
return @answers[method_name] if @answers.key?(method_name)
|
|
63
|
+
|
|
64
|
+
from_seed = @seed_index[method_name]
|
|
65
|
+
from_seed = from_seed.reject { |owner| @replaced.key?(owner) } if from_seed && !@replaced.empty?
|
|
66
|
+
from_file = @replaced_index[method_name]
|
|
67
|
+
owners = from_file.nil? ? from_seed : (from_seed || []) + from_file
|
|
68
|
+
@answers[method_name] = owners.nil? || owners.empty? ? nil : owners
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Every name some owner may define, for {DefinersIndex.canonical}.
|
|
72
|
+
def names = @seed_index.keys | @replaced_index.keys
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
module_function
|
|
76
|
+
|
|
77
|
+
# Records that the final `merged` table was built over `seed`. The last two are kept (a file merges an
|
|
78
|
+
# instance and a singleton table), replaced rather than accumulated.
|
|
79
|
+
def record_layers(merged, seed)
|
|
80
|
+
return if MemoAudit.enabled? && MemoAudit.suspended?
|
|
81
|
+
|
|
82
|
+
state = TypingState.current
|
|
83
|
+
previous = state[LAYERS]
|
|
84
|
+
state[LAYERS] = [[merged, seed], previous&.first].compact
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The definers index over `table`.
|
|
88
|
+
def for(table)
|
|
89
|
+
return build(table) if MemoAudit.suspended?
|
|
90
|
+
|
|
91
|
+
seed = TypingState.current[LAYERS]&.find { |entry| entry[0].equal?(table) }&.last
|
|
92
|
+
return build(table) if seed.nil? || !deeply_frozen?(seed)
|
|
93
|
+
|
|
94
|
+
replaced = {}
|
|
95
|
+
kept = 0
|
|
96
|
+
table.each do |owner, methods|
|
|
97
|
+
if methods.equal?(seed[owner])
|
|
98
|
+
kept += 1
|
|
99
|
+
else
|
|
100
|
+
replaced[owner] = methods
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
return build(table) unless kept + replaced.count { |owner, _| seed.key?(owner) } == seed.size
|
|
104
|
+
|
|
105
|
+
audit_layered(Layered.new(seed_index(seed), replaced, build(replaced)), table)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# `index`, after checking it against `table` inverted whole when the audit is on.
|
|
109
|
+
def audit_layered(index, table)
|
|
110
|
+
return index unless MemoAudit.enabled?
|
|
111
|
+
|
|
112
|
+
MemoAudit.check("definers index layers", canonical(index), table.size) { canonical(build(table)) }
|
|
113
|
+
index
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# `index` as a plain `name → sorted owners` Hash, so a `Layered` index and a whole one compare equal when they
|
|
117
|
+
# hold the same owners. Audit-only: it reads every name.
|
|
118
|
+
def canonical(index)
|
|
119
|
+
names = index.is_a?(Layered) ? index.names : index.keys
|
|
120
|
+
names.each_with_object({}) do |name, out|
|
|
121
|
+
owners = index[name]
|
|
122
|
+
out[name] = owners.sort if owners
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def deeply_frozen?(seed)
|
|
127
|
+
seed.frozen? && seed.each_value.all?(&:frozen?)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The seed's index, built once per seed table. Two ways, an instance and a singleton seed table.
|
|
131
|
+
def seed_index(seed)
|
|
132
|
+
state = TypingState.current
|
|
133
|
+
slot = state[SEED_INDEX]
|
|
134
|
+
if slot && slot[0].equal?(seed)
|
|
135
|
+
return MemoAudit.check("definers index seed", slot[1], seed.size) do
|
|
136
|
+
build(seed)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
slot = state[SEED_INDEX_ALT]
|
|
141
|
+
if slot && slot[0].equal?(seed)
|
|
142
|
+
return MemoAudit.check("definers index seed alt", slot[1], seed.size) { build(seed) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
index = MemoAudit.key_audit("definers index seed") { build(seed) }
|
|
146
|
+
state[SEED_INDEX_ALT] = state[SEED_INDEX]
|
|
147
|
+
state[SEED_INDEX] = [seed, index]
|
|
148
|
+
index
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def build(table)
|
|
152
|
+
index = {}
|
|
153
|
+
table.each do |class_name, methods|
|
|
154
|
+
next if class_name == ScopeIndexer::TOP_LEVEL_DEF_KEY
|
|
155
|
+
|
|
156
|
+
methods.each_key { |method_name| (index[method_name] ||= []) << class_name }
|
|
157
|
+
end
|
|
158
|
+
index
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|