rigortype 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- 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 +77 -71
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +24 -31
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +31 -12
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +108 -102
- 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 +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +12 -8
- 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 +84 -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 +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +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 +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +480 -67
- 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 +41 -10
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +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/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +9 -13
- data/lib/rigor/plugin/registry.rb +3 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +15 -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 +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +371 -58
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +3 -5
- 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/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -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 +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-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 +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- 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 +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- 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/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +36 -3
|
@@ -15,6 +15,7 @@ require_relative "../cache/file_digest"
|
|
|
15
15
|
require_relative "../analysis/check_rules/published_constant_guard"
|
|
16
16
|
require_relative "anonymous_meta_class"
|
|
17
17
|
require_relative "def_handle"
|
|
18
|
+
require_relative "definers_index"
|
|
18
19
|
require_relative "fresh_frame_blocks"
|
|
19
20
|
require_relative "global_write_census"
|
|
20
21
|
require_relative "guard_rebinding"
|
|
@@ -45,6 +46,9 @@ require_relative "scope_indexer/certainty"
|
|
|
45
46
|
require_relative "scope_indexer/member_layouts_collector"
|
|
46
47
|
require_relative "scope_indexer/declared_ivar_types"
|
|
47
48
|
require_relative "scope_indexer/declared_param_writes"
|
|
49
|
+
require_relative "scope_indexer/constant_ivar_value"
|
|
50
|
+
require_relative "scope_indexer/ivar_contributions"
|
|
51
|
+
require_relative "scope_indexer/empty_seed"
|
|
48
52
|
|
|
49
53
|
module Rigor
|
|
50
54
|
module Inference
|
|
@@ -67,8 +71,8 @@ module Rigor
|
|
|
67
71
|
# ```
|
|
68
72
|
#
|
|
69
73
|
# Nodes that are not part of the program subtree (e.g. synthesised virtual nodes that the caller looks up after the
|
|
70
|
-
# fact) yield the `default_scope`. The returned Hash is mutable in principle but callers MUST treat it as
|
|
71
|
-
# the indexer itself never exposes a way to update it past construction.
|
|
74
|
+
# fact) yield the seeded `default_scope`. The returned Hash is mutable in principle but callers MUST treat it as
|
|
75
|
+
# read-only; the indexer itself never exposes a way to update it past construction.
|
|
72
76
|
# rubocop:disable-next Metrics/ModuleLength
|
|
73
77
|
module ScopeIndexer
|
|
74
78
|
# Issue #644 — the census descriptor for a constant write that publishes no value: an unfoldable or
|
|
@@ -109,13 +113,15 @@ module Rigor
|
|
|
109
113
|
# `Dynamic[top]`. Off for every other reader, so parameter inference
|
|
110
114
|
# and sig-gen observe the clause's plain entry.
|
|
111
115
|
# @return identity-comparing
|
|
112
|
-
# table whose default value is `default_scope`.
|
|
116
|
+
# table whose default value is the seeded `default_scope`.
|
|
113
117
|
# The no-op re-anchoring table (#722 residue 2): shared so the overwhelmingly common
|
|
114
118
|
# nothing-to-re-anchor walk allocates none.
|
|
115
119
|
EMPTY_RENAMES = {}.freeze
|
|
116
120
|
|
|
117
121
|
# No path whose contributions are all `possible` (issue #2021), the cross-file walks' default.
|
|
118
122
|
EMPTY_PATH_SET = Set.new.freeze
|
|
123
|
+
# The {IvarWriterCensus} entry kinds the project pre-pass tags with the file that records them.
|
|
124
|
+
PATH_TAGGED_KINDS = %i[own_write own_write_type].to_set.freeze
|
|
119
125
|
|
|
120
126
|
# `declared_ivar_types:` — whether an RBS-declared ivar type replaces the census's join of the writes (#1406).
|
|
121
127
|
# `rigor sig-gen` passes false: it proposes what the code says, and reading a declaration back would make a row
|
|
@@ -233,9 +239,9 @@ module Rigor
|
|
|
233
239
|
end
|
|
234
240
|
|
|
235
241
|
# Runs the combined methods/def-nodes descent (one walk of the file), seeds the discovered-methods existence table
|
|
236
|
-
# onto `seeded_scope` (merged UNDER the cross-file pre-pass seed `default_scope` carries), and returns
|
|
237
|
-
# file_def_nodes, file_envelopes]` so the caller can thread the def-node and issue #992
|
|
238
|
-
# {#merge_project_method_indexes} without walking the file a second time.
|
|
242
|
+
# onto `seeded_scope` (merged UNDER the cross-file pre-pass seed `default_scope` carries), and returns
|
|
243
|
+
# `[scope, [file_def_nodes, file_envelopes, file_siblings]]` so the caller can thread the def-node and issue #992
|
|
244
|
+
# envelope tables into {#merge_project_method_indexes} without walking the file a second time.
|
|
239
245
|
#
|
|
240
246
|
# ADR-119 WD3 — the file's `possible_discovered_methods` folds over the seed's by {#fold_possible_methods}, so a
|
|
241
247
|
# name the file defines certainly is no longer possible and a name only the file's possible contribution
|
|
@@ -306,8 +312,8 @@ module Rigor
|
|
|
306
312
|
siblings = per_file_siblings(seeded_scope.discovery.sibling_tables, file_tables[0], file_tables[2],
|
|
307
313
|
file_singleton, file_visibilities)
|
|
308
314
|
extends, methods_table, unpositioned =
|
|
309
|
-
merge_and_fold_extends(default_scope, root, def_nodes,
|
|
310
|
-
mixin_unpositioned)
|
|
315
|
+
merge_and_fold_extends(default_scope, root, [def_nodes, file_tables[0]],
|
|
316
|
+
[singleton_def_nodes, file_singleton[0]], seeded_scope, siblings, mixin_unpositioned)
|
|
311
317
|
|
|
312
318
|
seeded_scope.with_discovery(
|
|
313
319
|
seeded_scope.discovery.with(
|
|
@@ -421,13 +427,23 @@ module Rigor
|
|
|
421
427
|
# ADR-119 WD1 — the unpositioned union (the seed's, the mixin walks' and this file's extend side) is settled
|
|
422
428
|
# first, because the fold reads it to tell a certain extend edge from a listed one. `siblings` is written by
|
|
423
429
|
# the fold.
|
|
424
|
-
|
|
430
|
+
# `def_pair` and `singleton_pair` are `[merged table, this file's own]`; the merged tables are frozen once the
|
|
431
|
+
# fold, their last in-place writer, is done ({#freeze_def_tables}).
|
|
432
|
+
def merge_and_fold_extends(default_scope, root, def_pair, singleton_pair, seeded_scope, siblings,
|
|
425
433
|
mixin_unpositioned)
|
|
434
|
+
def_nodes = def_pair.first
|
|
435
|
+
singleton_def_nodes = singleton_pair.first
|
|
426
436
|
file_extends, extends, extend_unpositioned = merge_extend_tables(default_scope, root)
|
|
427
437
|
unpositioned = union_unpositioned(default_scope.discovery.unpositioned_mixins, *mixin_unpositioned,
|
|
428
438
|
extend_unpositioned)
|
|
429
439
|
methods_table = fold_per_file_extends(file_extends, def_nodes, singleton_def_nodes, seeded_scope, siblings,
|
|
430
440
|
unpositioned)
|
|
441
|
+
# The fold also writes the singleton hash of each class this file `extend`s, declared there or not.
|
|
442
|
+
file_extends.each_key { |owner| singleton_def_nodes[owner]&.freeze }
|
|
443
|
+
freeze_def_tables(def_pair, singleton_pair)
|
|
444
|
+
# #2300 — the override gate's definers index then inverts the seed once per run, not once per file.
|
|
445
|
+
DefinersIndex.record_layers(def_nodes, default_scope.discovered_def_nodes)
|
|
446
|
+
DefinersIndex.record_layers(singleton_def_nodes, default_scope.discovered_singleton_def_nodes)
|
|
431
447
|
[extends, methods_table, unpositioned]
|
|
432
448
|
end
|
|
433
449
|
|
|
@@ -458,6 +474,20 @@ module Rigor
|
|
|
458
474
|
walked.fetch(:header_nestings)).freeze]
|
|
459
475
|
end
|
|
460
476
|
|
|
477
|
+
# Issue #2330 — the per-file def table is read by identity: the callee-chain census memo is keyed by the
|
|
478
|
+
# {Scope::DiscoveryIndex} that carries it, which is only sound while the table is never written in place.
|
|
479
|
+
# Frozen after the extends fold, the last in-place writer; the seed's per-class hashes are frozen already.
|
|
480
|
+
#
|
|
481
|
+
# Only the per-class hashes of the classes this file declares are frozen here (the merge made or took
|
|
482
|
+
# them); a seed-only class's hash was frozen by `finalize_def_index`, so walking the whole table per file is
|
|
483
|
+
# needless.
|
|
484
|
+
def freeze_def_tables(*pairs)
|
|
485
|
+
pairs.each do |table, file_table|
|
|
486
|
+
file_table.each_key { |owner| table[owner].freeze }
|
|
487
|
+
table.freeze
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
461
491
|
# The instance-side def-node table and its issue #681 nesting twin, each merged over the cross-file seed.
|
|
462
492
|
# Returned as a pair so the two stay written together: a node the merge keeps must be the same object the
|
|
463
493
|
# nesting table keys, and they are only that if both halves take the same file's walk.
|
|
@@ -529,20 +559,28 @@ module Rigor
|
|
|
529
559
|
mutated_ivars = {}
|
|
530
560
|
read_before_write = {}
|
|
531
561
|
init_writes = {}
|
|
532
|
-
# WD3 — per
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
# by class.
|
|
562
|
+
# WD3 — per class, then per method, the ivars the method definitely assigns non-nil on every completing path,
|
|
563
|
+
# consulted by `dead_transient_nil_writes` so a ctor that reassigns `@x` indirectly through an unconditional
|
|
564
|
+
# same-class method call (`mask!`) credits the overwrite. Built once per program here, memoised by class.
|
|
536
565
|
method_assign_effects = build_method_assign_effects(root)
|
|
566
|
+
# #2291 — an `@x = LIT` write reads the file's constant table, built on the first such write.
|
|
567
|
+
accumulator[CONSTANT_IVAR_VALUES] = ConstantIvarValue.new(root, default_scope)
|
|
537
568
|
walk_class_ivars(root, [], default_scope, accumulator, mutated_ivars,
|
|
538
569
|
read_before_write, init_writes, method_assign_effects)
|
|
570
|
+
accumulator.delete(CONSTANT_IVAR_VALUES)
|
|
539
571
|
reflective_opaque = accumulator.delete(REFLECTIVE_OPAQUE_CLASSES)
|
|
540
572
|
exempt_class_body_writes!(init_writes, accumulator.delete(DEFINE_METHOD_WRITES) || {})
|
|
541
573
|
merge_held_ivar_writes!(accumulator)
|
|
542
574
|
record_aliased_ivar_mutations!(root, mutated_ivars)
|
|
543
575
|
widen_mutated_ivar_entries!(accumulator, mutated_ivars)
|
|
576
|
+
# #2170 — an ancestor's writes as this file's census types them, before its read-before-write `nil`.
|
|
577
|
+
writes = accumulator.transform_values(&:dup)
|
|
544
578
|
contribute_read_before_write_nil!(accumulator, read_before_write, init_writes)
|
|
545
579
|
make_reflective_classes_gradual!(accumulator, reflective_opaque)
|
|
580
|
+
# #2079 / #2170 — another file's writes, and the chain's modules' and superclasses', join existing entries.
|
|
581
|
+
IvarContributions.join!(accumulator, writes, default_scope)
|
|
582
|
+
# An in-place mutation the file observes widens a joined value too (a same-file ancestor's String literal).
|
|
583
|
+
widen_mutated_ivar_entries!(accumulator, mutated_ivars)
|
|
546
584
|
# #1406 — an RBS `@x: T` or `attr_* x: T` declaration replaces the census's join of the writes.
|
|
547
585
|
DeclaredIvarTypes.apply!(accumulator, default_scope) if declared_ivar_types
|
|
548
586
|
accumulator.transform_values(&:freeze).freeze
|
|
@@ -628,8 +666,8 @@ module Rigor
|
|
|
628
666
|
end
|
|
629
667
|
end
|
|
630
668
|
|
|
631
|
-
#
|
|
632
|
-
#
|
|
669
|
+
# Per class, then per method, the ivars each method hands back, dropping the empty sets so the caller's
|
|
670
|
+
# `next if per_class.nil?` skips a whole class in one lookup.
|
|
633
671
|
def returned_ivars_by_method(defs)
|
|
634
672
|
defs.each_with_object({}) do |(class_name, methods), acc|
|
|
635
673
|
methods.each do |method_name, def_node|
|
|
@@ -718,9 +756,8 @@ module Rigor
|
|
|
718
756
|
# The widening uses `MutationWidening.widen_for_mutator` — the same primitive
|
|
719
757
|
# `Inference::StatementEvaluator#eval_call` applies for per-method-body widening on a local / ivar receiver. The
|
|
720
758
|
# class-level pass extends that primitive's reach so a `Tuple`-seeded ivar in `initialize` is observed as
|
|
721
|
-
# `Nominal[Array]` at the entry of every OTHER method body in the class — closing the cross-method gap
|
|
722
|
-
#
|
|
723
|
-
# mutation"; Redmine 6.1.2 `Redmine::Views::Builders::Structure` is the canonical worked site).
|
|
759
|
+
# `Nominal[Array]` at the entry of every OTHER method body in the class — closing the cross-method gap
|
|
760
|
+
# (Redmine 6.1.2 `Redmine::Views::Builders::Structure` is the canonical worked site).
|
|
724
761
|
#
|
|
725
762
|
# Always-safe: the widening can only LOSE precision; the underlying nominal (`Array` / `Hash`) and the element
|
|
726
763
|
# union are preserved.
|
|
@@ -765,18 +802,27 @@ module Rigor
|
|
|
765
802
|
end
|
|
766
803
|
|
|
767
804
|
widen_member_for_lookup_mutators(member, observed_methods)
|
|
768
|
-
when Type::Constant
|
|
769
|
-
# `@s = +"ab"` in one method and `@s << "c"` in another: without this arm the seed stayed pinned at every
|
|
770
|
-
# other method's entry, and `@s == "ab"` folded always-truthy on a receiver that holds `"abc"`.
|
|
771
|
-
return member unless StringMutation.constant?(member) &&
|
|
772
|
-
observed_methods.any? { |m| StringMutation::MUTATORS.include?(m) }
|
|
773
|
-
|
|
774
|
-
Type::Combinator.nominal_of("String")
|
|
805
|
+
when Type::Constant then widen_constant_for_observed_mutators(member, observed_methods)
|
|
775
806
|
else
|
|
776
807
|
member
|
|
777
808
|
end
|
|
778
809
|
end
|
|
779
810
|
|
|
811
|
+
# `@s = +"ab"` in one method and `@s << "c"` in another: without this arm the seed stayed pinned at every other
|
|
812
|
+
# method's entry, and `@s == "ab"` folded always-truthy on a receiver that holds `"abc"`. A constant-folded Set
|
|
813
|
+
# (`@s = Set["a"]`, then `@s.clear; @s << 1` elsewhere — issue #2294) takes the untyped floor the Tuple arm takes.
|
|
814
|
+
def widen_constant_for_observed_mutators(member, observed_methods)
|
|
815
|
+
if SetMutation.constant?(member)
|
|
816
|
+
return member unless observed_methods.any? { |m| SetMutation::MUTATORS.include?(m) }
|
|
817
|
+
|
|
818
|
+
return Type::Combinator.nominal_of("Set", type_args: [Type::Combinator.untyped])
|
|
819
|
+
end
|
|
820
|
+
return member unless StringMutation.constant?(member) &&
|
|
821
|
+
observed_methods.any? { |m| StringMutation::MUTATORS.include?(m) }
|
|
822
|
+
|
|
823
|
+
Type::Combinator.nominal_of("String")
|
|
824
|
+
end
|
|
825
|
+
|
|
780
826
|
# A `HashShape` ivar seed some method gave a default, a default proc or identity keys, and no method stored
|
|
781
827
|
# into, removed from or rewrote: its pairs are the seed's own, so it takes the per-method {HashLookupMutation}
|
|
782
828
|
# widening rather than the untyped floor above — a present key keeps its value across methods, a missing one
|
|
@@ -1316,9 +1362,9 @@ module Rigor
|
|
|
1316
1362
|
# not `InstanceVariableWriteNode`s, so the generic descent below never records them as writes. Left unhandled,
|
|
1317
1363
|
# an ivar written only via massign in `initialize` stays absent from `init_writes`, and
|
|
1318
1364
|
# `contribute_read_before_write_nil!` then unions a spurious `nil` into its class-ivar seed — masking a
|
|
1319
|
-
# genuinely-typed `@m` (e.g. `Tuple[]`) as `T | nil` at every sibling read. The RHS runs before any
|
|
1320
|
-
# committed, so descend into `value` FIRST (an ivar read there is read-before-write), then mark every
|
|
1321
|
-
# target as written.
|
|
1365
|
+
# genuinely-typed `@m` (e.g. `Tuple[]`) as `T | nil` at every sibling read. The RHS runs before any
|
|
1366
|
+
# target is committed, so descend into `value` FIRST (an ivar read there is read-before-write), then mark every
|
|
1367
|
+
# ivar target as written.
|
|
1322
1368
|
if node.is_a?(Prism::MultiWriteNode)
|
|
1323
1369
|
detect_read_before_write(node.value, seen_writes, read_first) if node.value
|
|
1324
1370
|
detect_multi_write_target_writes(node, seen_writes, read_first)
|
|
@@ -1445,13 +1491,21 @@ module Rigor
|
|
|
1445
1491
|
method_name, receiver = mutation_target(node)
|
|
1446
1492
|
return if method_name.nil?
|
|
1447
1493
|
return unless receiver.is_a?(Prism::InstanceVariableReadNode)
|
|
1448
|
-
return unless
|
|
1494
|
+
return unless ivar_census_mutator?(method_name)
|
|
1449
1495
|
|
|
1450
1496
|
per_class = (mutated_ivars[class_name] ||= {})
|
|
1451
1497
|
per_ivar = (per_class[receiver.name] ||= Set.new)
|
|
1452
1498
|
per_ivar << method_name
|
|
1453
1499
|
end
|
|
1454
1500
|
|
|
1501
|
+
# The Set-only names (`add`, `merge`, … — issue #2294) are recorded here rather than in `SHAPE_MUTATORS`, which
|
|
1502
|
+
# every scan reads by name alone: there `merge` would make `Hash#merge`, which returns a new Hash, a mutation of
|
|
1503
|
+
# its receiver. The census is safe to over-collect, since {#widen_member_for_observed_mutators} reads each name
|
|
1504
|
+
# against the table of the carrier it widens.
|
|
1505
|
+
def ivar_census_mutator?(method_name)
|
|
1506
|
+
MutationWidening::SHAPE_MUTATORS.include?(method_name) || SetMutation::MUTATORS.include?(method_name)
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1455
1509
|
# `[mutator name, receiver node]` for a node that mutates its receiver, `nil` otherwise.
|
|
1456
1510
|
#
|
|
1457
1511
|
# `@h[k] ||= v` and its `&&=` / `+=` siblings store through `[]=` but are not `[]=` CallNodes; missing them left
|
|
@@ -1586,10 +1640,9 @@ module Rigor
|
|
|
1586
1640
|
SAME_CLASS_CALL_DEPTH_CAP = 3
|
|
1587
1641
|
private_constant :SAME_CLASS_CALL_DEPTH_CAP
|
|
1588
1642
|
|
|
1589
|
-
# WD3 — builds the per-class definite-assignment summary
|
|
1590
|
-
#
|
|
1591
|
-
#
|
|
1592
|
-
# `mask!`).
|
|
1643
|
+
# WD3 — builds the per-class definite-assignment summary: per class, then per method, the ivars assigned non-nil
|
|
1644
|
+
# on every completing path. Used so a ctor's `dead_transient_nil_writes` can credit an indirect overwrite through
|
|
1645
|
+
# an unconditionally-called same-class method (ipaddr's `initialize` reassigns `@mask_addr` via `mask!`).
|
|
1593
1646
|
#
|
|
1594
1647
|
# Each method's set is computed by the same suffix definite-assignment analysis used for the ctor seed, run from
|
|
1595
1648
|
# the method body's first statement for every ivar the method writes anywhere. Same-class calls inside a method
|
|
@@ -1976,10 +2029,11 @@ module Rigor
|
|
|
1976
2029
|
end
|
|
1977
2030
|
|
|
1978
2031
|
def record_ivar_write(node, scope, class_name, accumulator, guarded: false, declared: nil)
|
|
1979
|
-
rvalue_type = declared ||
|
|
2032
|
+
rvalue_type = declared || accumulator[CONSTANT_IVAR_VALUES]&.type_of(node.value, scope) ||
|
|
2033
|
+
scope.type_of(node.value)
|
|
1980
2034
|
|
|
1981
2035
|
# `@x = nil unless @x` / `@y = false unless @y` — follow-up to the polarity-aware defensive-init guard fix
|
|
1982
|
-
# (
|
|
2036
|
+
# (defensive ivar-init with a nil / false rvalue). When the rvalue is itself a falsey
|
|
1983
2037
|
# Constant, `union(rvalue, Constant[nil])` collapses (for `nil`) or doesn't widen the type's truthiness profile
|
|
1984
2038
|
# (for `false`) — the predicate `unless @x` then folds to a single `Constant[nil]` / `Constant[false]` and the
|
|
1985
2039
|
# `flow.always-truthy-condition` / `-always-falsey-` rule false-fires on the no-op-but-documented-default idiom.
|
|
@@ -1989,6 +2043,8 @@ module Rigor
|
|
|
1989
2043
|
# `lib/tdiary/configuration.rb:157` for the worked site.
|
|
1990
2044
|
return if guarded && falsey_constant?(rvalue_type)
|
|
1991
2045
|
|
|
2046
|
+
rvalue_type = EmptySeed.erase(node.value, rvalue_type)
|
|
2047
|
+
|
|
1992
2048
|
rvalue_type = Type::Combinator.union(rvalue_type, Type::Combinator.constant_of(nil)) if guarded
|
|
1993
2049
|
accumulate_ivar_type(accumulator, class_name, node.name, rvalue_type)
|
|
1994
2050
|
end
|
|
@@ -2018,7 +2074,7 @@ module Rigor
|
|
|
2018
2074
|
# `nil`, the guard bound its own ivar, and `ExpressionTyper#compound_write_value` read
|
|
2019
2075
|
# `truthy(nil) | bot` — an instance method that sig-gen declared `-> bot`.
|
|
2020
2076
|
def record_ivar_or_write(node, scope, class_name, accumulator)
|
|
2021
|
-
rvalue_type = scope.type_of(node.value)
|
|
2077
|
+
rvalue_type = EmptySeed.erase(node.value, scope.type_of(node.value))
|
|
2022
2078
|
return if Narrowing.narrow_truthy(rvalue_type).is_a?(Type::Bot)
|
|
2023
2079
|
|
|
2024
2080
|
accumulate_ivar_type(accumulator, class_name, node.name,
|
|
@@ -2182,7 +2238,9 @@ module Rigor
|
|
|
2182
2238
|
CLASS_BODY_WRITES = :class_body_writes
|
|
2183
2239
|
# The classes a computed-name `instance_variable_set` may write any ivar of ({#record_reflective_ivar_write}).
|
|
2184
2240
|
REFLECTIVE_OPAQUE_CLASSES = :reflective_opaque_classes
|
|
2185
|
-
|
|
2241
|
+
# The census's {ConstantIvarValue}, which {#record_ivar_write} reads.
|
|
2242
|
+
CONSTANT_IVAR_VALUES = :constant_ivar_values
|
|
2243
|
+
private_constant :DEFINE_METHOD_WRITES, :CLASS_BODY_WRITES, :REFLECTIVE_OPAQUE_CLASSES, :CONSTANT_IVAR_VALUES
|
|
2186
2244
|
|
|
2187
2245
|
# Unions `type` into the class-ivar accumulator for `(class_name, ivar_name)`. Shared by the single-write and
|
|
2188
2246
|
# multi-write (parallel-assignment) collectors.
|
|
@@ -2215,7 +2273,11 @@ module Rigor
|
|
|
2215
2273
|
|
|
2216
2274
|
rhs_type = scope.type_of(node.value)
|
|
2217
2275
|
ivars = MultiTargetBinder.bind_marked(node, rhs_type, scope: scope, soften_slots: false).ivars
|
|
2218
|
-
|
|
2276
|
+
# #2499 — `@a, @b = {}, []` erases each empty seed as a single write does.
|
|
2277
|
+
values = EmptySeed.multi_write_values(node)
|
|
2278
|
+
ivars.each do |name, type|
|
|
2279
|
+
accumulate_ivar_type(accumulator, class_name, name, EmptySeed.erase(values[name], type))
|
|
2280
|
+
end
|
|
2219
2281
|
end
|
|
2220
2282
|
|
|
2221
2283
|
def falsey_constant?(type)
|
|
@@ -2382,7 +2444,7 @@ module Rigor
|
|
|
2382
2444
|
end
|
|
2383
2445
|
|
|
2384
2446
|
def record_cvar_write(node, scope, class_name, accumulator)
|
|
2385
|
-
rvalue_type = scope.type_of(node.value)
|
|
2447
|
+
rvalue_type = EmptySeed.erase(node.value, scope.type_of(node.value))
|
|
2386
2448
|
accumulator[class_name] ||= {}
|
|
2387
2449
|
existing = accumulator[class_name][node.name]
|
|
2388
2450
|
accumulator[class_name][node.name] =
|
|
@@ -2504,7 +2566,7 @@ module Rigor
|
|
|
2504
2566
|
end
|
|
2505
2567
|
|
|
2506
2568
|
def record_global_write(node, scope, accumulator)
|
|
2507
|
-
rvalue_type = scope.type_of(node.value)
|
|
2569
|
+
rvalue_type = EmptySeed.erase(node.value, scope.type_of(node.value))
|
|
2508
2570
|
existing = accumulator[node.name]
|
|
2509
2571
|
accumulator[node.name] =
|
|
2510
2572
|
existing ? Type::Combinator.union(existing, rvalue_type) : rvalue_type
|
|
@@ -3269,7 +3331,7 @@ module Rigor
|
|
|
3269
3331
|
unless owner.empty?
|
|
3270
3332
|
body_scope = census_body_scope(default_scope, enclosing_prefix, Type::Combinator.singleton_of(owner))
|
|
3271
3333
|
end
|
|
3272
|
-
rvalue_type = meta_new_constant_type(node, full) || body_scope.type_of(node.value)
|
|
3334
|
+
rvalue_type = meta_new_constant_type(node, full) || EmptySeed.erase(node.value, body_scope.type_of(node.value))
|
|
3273
3335
|
existing = accumulator[full]
|
|
3274
3336
|
accumulator[full] = existing ? Type::Combinator.union(existing, rvalue_type) : rvalue_type
|
|
3275
3337
|
end
|
|
@@ -3567,7 +3629,8 @@ module Rigor
|
|
|
3567
3629
|
REFINEMENT_WILDCARD = Scope::DiscoveryIndex::REFINEMENT_WILDCARD
|
|
3568
3630
|
REFINE_WORD = /\brefine\b/
|
|
3569
3631
|
HEREDOC_OPENER = /<<[~-]?["'`A-Za-z_]/
|
|
3570
|
-
|
|
3632
|
+
COMPARISON_OPERATORS = Set[:==, :!=, :===].freeze
|
|
3633
|
+
private_constant :EMPTY_REFINEMENTS, :REFINEMENT_WILDCARD, :REFINE_WORD, :HEREDOC_OPENER, :COMPARISON_OPERATORS
|
|
3571
3634
|
|
|
3572
3635
|
NO_OFFSETS = [].freeze
|
|
3573
3636
|
private_constant :NO_OFFSETS
|
|
@@ -3660,7 +3723,8 @@ module Rigor
|
|
|
3660
3723
|
# `self`, charged to its module or the wildcard), `:literal` (A3's `:refine` / eval-string literal), `:nested` (a
|
|
3661
3724
|
# `refine` in a refine body, or in a singleton method of one), `:dsl` (where `self` is a class, #1689) and
|
|
3662
3725
|
# `:raises` (no block, a Proc as the block, another receiver, or a computed target where `self` is `main`), and
|
|
3663
|
-
# `:data` (a `:refine` literal where `self` is a plain class or one of its instances)
|
|
3726
|
+
# `:data` (a `:refine` literal where `self` is a plain class or one of its instances), and `:inert` (issue #2203:
|
|
3727
|
+
# a `:refine` Symbol that is a hash key or a comparison operand, which no call reads as a method name).
|
|
3664
3728
|
def refine_census(root)
|
|
3665
3729
|
census = []
|
|
3666
3730
|
refinements = build_methods_and_def_nodes(root, census: census)[3]
|
|
@@ -3839,6 +3903,8 @@ module Rigor
|
|
|
3839
3903
|
end
|
|
3840
3904
|
|
|
3841
3905
|
census_call(node, qualified_prefix, tables, refine_body)
|
|
3906
|
+
when Prism::AssocNode
|
|
3907
|
+
census_assoc_key(node, tables)
|
|
3842
3908
|
when Prism::SymbolNode
|
|
3843
3909
|
census_symbol(node, tables)
|
|
3844
3910
|
end
|
|
@@ -3894,6 +3960,8 @@ module Rigor
|
|
|
3894
3960
|
arguments = node.arguments&.arguments
|
|
3895
3961
|
return if arguments.nil?
|
|
3896
3962
|
|
|
3963
|
+
return census_compared_literal(arguments.first, tables) if COMPARISON_OPERATORS.include?(node.name)
|
|
3964
|
+
|
|
3897
3965
|
strings = RefineCensus.string_literal_call?(node)
|
|
3898
3966
|
arguments.each do |argument|
|
|
3899
3967
|
next unless RefineCensus.refine_symbol?(argument) || (strings && RefineCensus.refine_string?(argument))
|
|
@@ -3928,6 +3996,26 @@ module Rigor
|
|
|
3928
3996
|
|
|
3929
3997
|
def constant_shaped?(node) = node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
|
3930
3998
|
|
|
3999
|
+
# Issue #2203 — a `:refine` Symbol a comparison reads (`node.name == :refine`, `!=`, `===`) is a value compared,
|
|
4000
|
+
# never a method name a call runs: it is inert (`:inert`), and records no row.
|
|
4001
|
+
def census_compared_literal(argument, tables)
|
|
4002
|
+
return unless RefineCensus.refine_symbol?(argument) && !tables.literal_seen?(argument)
|
|
4003
|
+
|
|
4004
|
+
tables.see_literal(argument)
|
|
4005
|
+
tables.account(argument, :inert)
|
|
4006
|
+
end
|
|
4007
|
+
|
|
4008
|
+
# Issue #2203 — a `:refine` Symbol that is a hash key (`h(x, refine: true)`, `{ refine: 1 }`, `{ :refine => 1 }`)
|
|
4009
|
+
# names an option, not `Module#refine`: it is inert (`:inert`), and records no row. A `:refine` value stays a
|
|
4010
|
+
# literal.
|
|
4011
|
+
def census_assoc_key(node, tables)
|
|
4012
|
+
key = node.key
|
|
4013
|
+
return unless RefineCensus.refine_symbol?(key) && !tables.literal_seen?(key)
|
|
4014
|
+
|
|
4015
|
+
tables.see_literal(key)
|
|
4016
|
+
tables.account(key, :inert)
|
|
4017
|
+
end
|
|
4018
|
+
|
|
3931
4019
|
# A3 — a `:refine` Symbol anywhere else (`alias my_refine refine`, `%i[refine]`, a hash) names `Module#refine`
|
|
3932
4020
|
# for code the walk cannot follow, run on the module the in-effect walk says `self` is there; where `self` is a
|
|
3933
4021
|
# plain class or one of its instances it is data.
|
|
@@ -4199,6 +4287,8 @@ module Rigor
|
|
|
4199
4287
|
end
|
|
4200
4288
|
end
|
|
4201
4289
|
return
|
|
4290
|
+
when Prism::AssocNode
|
|
4291
|
+
census_assoc_key(node, methods_acc) if methods_acc.mentions_refine?
|
|
4202
4292
|
when Prism::SymbolNode
|
|
4203
4293
|
census_symbol(node, methods_acc) if methods_acc.mentions_refine?
|
|
4204
4294
|
when Prism::CallNode
|
|
@@ -10423,10 +10513,11 @@ module Rigor
|
|
|
10423
10513
|
|
|
10424
10514
|
# #1154 — tags each `[:own_write, class, ivar]` entry with the file that records it, so a reader of the merged
|
|
10425
10515
|
# census can tell a second file writing the same ivar of the same class (`UnseenIvarWriters`).
|
|
10516
|
+
# Issue #2079 — and each `[:own_write_type, class, ivar, value]` entry, which {IvarContributions} reads.
|
|
10426
10517
|
def attribute_own_writes(census, path)
|
|
10427
|
-
return census unless census.any? { |entry| entry.first
|
|
10518
|
+
return census unless census.any? { |entry| PATH_TAGGED_KINDS.include?(entry.first) }
|
|
10428
10519
|
|
|
10429
|
-
census.to_set { |entry| entry.first
|
|
10520
|
+
census.to_set { |entry| PATH_TAGGED_KINDS.include?(entry.first) ? [*entry, path].freeze : entry }
|
|
10430
10521
|
end
|
|
10431
10522
|
|
|
10432
10523
|
# Folds one file's Data + Struct member-layout tables into the cross-file accumulator (kept out of
|
|
@@ -10859,7 +10950,7 @@ module Rigor
|
|
|
10859
10950
|
# A {DYNAMIC_TARGET_PREFIX} key is not a name and never publishes; it is expanded by
|
|
10860
10951
|
# {#apply_dynamic_target_writes} into a retraction over the names it could have written.
|
|
10861
10952
|
#
|
|
10862
|
-
# @return
|
|
10953
|
+
# @return the value table and the table of source paths per name.
|
|
10863
10954
|
def finalize_constant_writes(writes)
|
|
10864
10955
|
values = {}
|
|
10865
10956
|
sources = {}
|
|
@@ -9,6 +9,7 @@ require_relative "../type"
|
|
|
9
9
|
require_relative "guard_rebinding"
|
|
10
10
|
require_relative "project_method_ownership"
|
|
11
11
|
require_relative "refined_dispatch/own_method"
|
|
12
|
+
require_relative "../run_scoped_slots"
|
|
12
13
|
|
|
13
14
|
module Rigor
|
|
14
15
|
module Inference
|
|
@@ -85,7 +86,9 @@ module Rigor
|
|
|
85
86
|
:to_r, :to_c, :to_proc, :to_path, :to_sym, :to_io, :coerce, :exception, :divmod, :each, :respond_to?,
|
|
86
87
|
:respond_to_missing?, :method_missing, :initialize_copy, :marshal_dump, :pretty_print
|
|
87
88
|
].freeze
|
|
88
|
-
|
|
89
|
+
# Issue #2203 — the classes and module every project class's instances reach past its own chain.
|
|
90
|
+
IMPLICIT_ROOTS = %w[Object Kernel BasicObject].freeze
|
|
91
|
+
private_constant :UNPROVABLE_NODES, :LITERAL_CLASSES, :SELF_SAFE_NAMES, :PROTOCOL_NAMES, :IMPLICIT_ROOTS
|
|
89
92
|
|
|
90
93
|
# Raised inside a proof to abandon it.
|
|
91
94
|
class Unprovable < StandardError
|
|
@@ -113,6 +116,32 @@ module Rigor
|
|
|
113
116
|
nil
|
|
114
117
|
end
|
|
115
118
|
|
|
119
|
+
DEFINITION_COUNTS_KEY = RunScopedSlots.register(:__rigor_self_call_ivar_writes_definition_counts__)
|
|
120
|
+
private_constant :DEFINITION_COUNTS_KEY
|
|
121
|
+
|
|
122
|
+
# `{[owner, name] => the number of instance `def` rows of name the project records for owner}`, over every
|
|
123
|
+
# file of `deferred_ranges` (a `discovered_deferred_ranges` table). Built once per table and kept in ONE
|
|
124
|
+
# thread slot keyed by the table's identity (#2300): the table is never written in place, a file whose rows
|
|
125
|
+
# equal the seed's keeps the seed's table, and a scan of every file's rows per query was the cost once the
|
|
126
|
+
# table held the whole project. The slot holds one table and its counts, the next table replaces them, and the
|
|
127
|
+
# run that filled it releases it when it ends ({RunScopedSlots}, #2399).
|
|
128
|
+
def definition_counts(deferred_ranges)
|
|
129
|
+
slot = Thread.current[DEFINITION_COUNTS_KEY]
|
|
130
|
+
return slot[1] if slot && slot[0].equal?(deferred_ranges)
|
|
131
|
+
|
|
132
|
+
counts = {}
|
|
133
|
+
deferred_ranges.each_value do |rows|
|
|
134
|
+
rows.each do |row|
|
|
135
|
+
next unless row[3] == :instance
|
|
136
|
+
|
|
137
|
+
key = [row[4], row[2]]
|
|
138
|
+
counts[key] = counts.fetch(key, 0) + 1
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
Thread.current[DEFINITION_COUNTS_KEY] = [deferred_ranges, counts]
|
|
142
|
+
counts
|
|
143
|
+
end
|
|
144
|
+
|
|
116
145
|
# One proof's state: the instance variables read code writes, and the `def` bodies already read.
|
|
117
146
|
class Proof
|
|
118
147
|
attr_reader :written
|
|
@@ -128,7 +157,7 @@ module Rigor
|
|
|
128
157
|
# Reads the method an implicit-self or `self.` call to `call_node`'s name runs.
|
|
129
158
|
def self_call(call_node)
|
|
130
159
|
name = call_node.name
|
|
131
|
-
unprovable! if GuardRebinding.code_running_name?(name) || refined?(name)
|
|
160
|
+
unprovable! if GuardRebinding.code_running_name?(name) || refined?(name, on_self: true)
|
|
132
161
|
|
|
133
162
|
if ProjectMethodOwnership.defines?(@class_name, name, :instance, @scope)
|
|
134
163
|
read_def(proven_def(name))
|
|
@@ -185,19 +214,66 @@ module Rigor
|
|
|
185
214
|
|
|
186
215
|
private
|
|
187
216
|
|
|
188
|
-
# Whether a `refine` block
|
|
189
|
-
# read code sits may route the call to it (`refine Kernel do def warn …`)
|
|
217
|
+
# Whether a `refine` block in the project defines `name` where the call may reach it: a `using` in effect
|
|
218
|
+
# where the read code sits may route the call to it (`refine Kernel do def warn …`).
|
|
190
219
|
#
|
|
191
220
|
# Issue #2163 — the answer reads every file's refinements whole, and a refined class off the chain (`Kernel`,
|
|
192
221
|
# `IO`, `String`) is no `mark:` the chain read files, so it records `refinement:<name>` and `refinement:*`
|
|
193
222
|
# whichever way it answers, as `InEffectRefinements` consumers do: a file that starts or stops refining the
|
|
194
223
|
# name re-checks the reader.
|
|
195
|
-
|
|
224
|
+
#
|
|
225
|
+
# Issue #2203 — for a call on `self` (`on_self:`) whose reach {#self_reach} knows, a row of the name counts only
|
|
226
|
+
# on a class key in that reach, on the wildcard class key, or on a key the walk could not name
|
|
227
|
+
# (`InEffectRefinements.class_wildcard_key?`); a names-wildcard row (a computed `define_method`,
|
|
228
|
+
# `import_methods`) counts as the name, but only on a class key in the reach. A refinement of `String` leaves
|
|
229
|
+
# a `Holder` alone: dropping the proof is no safer, since the intervening-call reset restores the class-ivar
|
|
230
|
+
# seed and a class guard's narrowing then reports. Where the reach is unknown, and for a call on another
|
|
231
|
+
# receiver, a row of the name counts on any class and a names-wildcard row never does, as before the issue.
|
|
232
|
+
def refined?(name, on_self: false)
|
|
233
|
+
wildcard = Scope::DiscoveryIndex::REFINEMENT_WILDCARD
|
|
196
234
|
if Analysis::DependencyRecorder.active?
|
|
197
235
|
Analysis::DependencyRecorder.read_name(:refinement, name)
|
|
198
|
-
Analysis::DependencyRecorder.read_name(:refinement,
|
|
236
|
+
Analysis::DependencyRecorder.read_name(:refinement, wildcard)
|
|
237
|
+
end
|
|
238
|
+
reach = on_self ? self_reach : nil
|
|
239
|
+
@scope.discovered_refinements.any? do |refined, methods|
|
|
240
|
+
next methods.key?(name) if reach.nil?
|
|
241
|
+
|
|
242
|
+
if reach.include?(refined)
|
|
243
|
+
methods.key?(name) || methods.key?(wildcard)
|
|
244
|
+
else
|
|
245
|
+
methods.key?(name) && InEffectRefinements.class_wildcard_key?(@scope, refined)
|
|
246
|
+
end
|
|
199
247
|
end
|
|
200
|
-
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Issue #2203 — the classes and modules on the instance chain of `self`'s class and of `Object`, `Kernel` and
|
|
251
|
+
# `BasicObject` (which holds the modules the project mixes into them), with `Object`'s RBS ancestors
|
|
252
|
+
# (`PP::ObjectMixin`); nil when a chain is cut, mixes in a module the tables cannot name, ends in a superclass
|
|
253
|
+
# they do not know (`class Holder < Struct.new(:a)`), or holds an external entry. A project subclass's chain
|
|
254
|
+
# needs no read: {#no_protocol_override} gives the proof up on a refined class or module anywhere on it. The
|
|
255
|
+
# implicit roots' chains are recorded, so a file that mixes a module into `Object` re-checks the reader;
|
|
256
|
+
# `self`'s own chain is recorded by {#no_protocol_override}'s read.
|
|
257
|
+
def self_reach
|
|
258
|
+
return @self_reach if defined?(@self_reach)
|
|
259
|
+
|
|
260
|
+
@self_reach = read_self_reach
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def read_self_reach
|
|
264
|
+
names = Set.new
|
|
265
|
+
[@class_name.to_s, *IMPLICIT_ROOTS].each do |class_name|
|
|
266
|
+
chain = Scope::ResolutionChain.for(@scope, class_name, :instance, :methods)
|
|
267
|
+
chain.record(@scope) if Analysis::DependencyRecorder.active?
|
|
268
|
+
return nil if chain.truncated? || chain.wildcard_mixin? || chain.opaque_tail?(@scope)
|
|
269
|
+
return nil if chain.entries.any?(&:external?)
|
|
270
|
+
|
|
271
|
+
chain.entries.each { |entry| names << entry.name }
|
|
272
|
+
end
|
|
273
|
+
loader = @scope.environment&.rbs_loader
|
|
274
|
+
return nil if loader.nil?
|
|
275
|
+
|
|
276
|
+
names.merge(loader.ancestor_names_for("Object"))
|
|
201
277
|
end
|
|
202
278
|
|
|
203
279
|
def call_receiver(node) = node.is_a?(Prism::CallNode) ? node.receiver : nil
|
|
@@ -268,11 +344,7 @@ module Rigor
|
|
|
268
344
|
end
|
|
269
345
|
|
|
270
346
|
def definition_count(owner, name)
|
|
271
|
-
|
|
272
|
-
@scope.discovered_deferred_ranges.each_value do |rows|
|
|
273
|
-
count += rows.count { |row| row[2] == name && row[3] == :instance && row[4] == owner }
|
|
274
|
-
end
|
|
275
|
-
count
|
|
347
|
+
SelfCallIvarWrites.definition_counts(@scope.discovered_deferred_ranges).fetch([owner, name], 0)
|
|
276
348
|
end
|
|
277
349
|
|
|
278
350
|
# Whether `chain`'s own order stands with no second world: no order mark and no fork. `ResolutionChain#settle`
|