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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
|
+
require_relative "../type"
|
|
6
|
+
|
|
5
7
|
module Rigor
|
|
6
8
|
module Inference
|
|
7
9
|
# Marks a value whose *nil-freeness* rests on Rigor's deliberate choice to ignore core RBS's
|
|
@@ -18,7 +20,7 @@ module Rigor
|
|
|
18
20
|
# that happens to have been produced optimistically.
|
|
19
21
|
#
|
|
20
22
|
# Issue #286: the `if` / `unless` branch elision is one consumer of `Narrowing.predicate_certainty`; the
|
|
21
|
-
# other two are `flow.always-truthy-condition` and
|
|
23
|
+
# other two are `flow.always-truthy-condition` and `StatementEvaluator#right_operand_dead?`, and the
|
|
22
24
|
# spec passage above binds all three. This channel is what lets a certainty judgment tell the two apart.
|
|
23
25
|
#
|
|
24
26
|
# Issue #313: the mark is attached to a *value* — the call node that produced it, or the local / ivar it
|
|
@@ -102,24 +104,147 @@ module Rigor
|
|
|
102
104
|
# `StatementEvaluator` and `AlwaysTruthyConditionCollector` all route here, so the three consumers the
|
|
103
105
|
# spec binds cannot drift apart.
|
|
104
106
|
#
|
|
105
|
-
# Resolution order — the mark recorded on the node itself, then the binding a bare local / ivar read
|
|
106
|
-
# a write in value position
|
|
107
|
-
#
|
|
107
|
+
# Resolution order — the mark recorded on the node itself, then the binding a bare local / ivar read resolves
|
|
108
|
+
# through, then a write in value position and a safe-navigation call and the predicate-fold derivation issue
|
|
109
|
+
# #313 added.
|
|
108
110
|
def resolve(node, scope)
|
|
109
111
|
return nil if node.nil? || scope.nil?
|
|
110
112
|
|
|
111
|
-
recorded = scope.
|
|
113
|
+
recorded = scope.optimistic_origin(node)
|
|
112
114
|
return recorded if recorded
|
|
113
115
|
|
|
116
|
+
resolve_node(node, scope)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The shape dispatch {.resolve} applies once the node-level mark is ruled out.
|
|
120
|
+
def resolve_node(node, scope)
|
|
114
121
|
case node
|
|
115
|
-
when Prism::LocalVariableReadNode
|
|
116
|
-
when Prism::InstanceVariableReadNode
|
|
122
|
+
when Prism::LocalVariableReadNode then scope.optimistic_local(node.name)
|
|
123
|
+
when Prism::InstanceVariableReadNode then scope.optimistic_ivar(node.name)
|
|
124
|
+
when Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode,
|
|
125
|
+
Prism::GlobalVariableWriteNode, Prism::ClassVariableWriteNode,
|
|
126
|
+
Prism::MultiWriteNode, Prism::LocalVariableOrWriteNode,
|
|
127
|
+
Prism::InstanceVariableOrWriteNode, Prism::ClassVariableOrWriteNode,
|
|
128
|
+
Prism::GlobalVariableOrWriteNode, Prism::LocalVariableAndWriteNode,
|
|
129
|
+
Prism::InstanceVariableAndWriteNode, Prism::ClassVariableAndWriteNode,
|
|
130
|
+
Prism::GlobalVariableAndWriteNode
|
|
131
|
+
resolve_write_form(node, scope)
|
|
117
132
|
when Prism::AndNode, Prism::OrNode then resolve(node.left, scope) || resolve(node.right, scope)
|
|
118
|
-
when Prism::CallNode
|
|
133
|
+
when Prism::CallNode
|
|
134
|
+
resolve_attribute_write(node, scope) || resolve_through_safe_navigation(node, scope) ||
|
|
135
|
+
resolve_through_predicate(node, scope)
|
|
119
136
|
when Prism::ParenthesesNode then resolve_through_parentheses(node, scope)
|
|
120
137
|
end
|
|
121
138
|
end
|
|
122
139
|
|
|
140
|
+
# The write forms in value position: the plain and multiple writes whose value is the
|
|
141
|
+
# right-hand side, and the `||=` / `&&=` spellings whose old binding participates.
|
|
142
|
+
def resolve_write_form(node, scope)
|
|
143
|
+
case node
|
|
144
|
+
when Prism::LocalVariableOrWriteNode, Prism::InstanceVariableOrWriteNode,
|
|
145
|
+
Prism::ClassVariableOrWriteNode, Prism::GlobalVariableOrWriteNode
|
|
146
|
+
resolve_or_write(node, scope)
|
|
147
|
+
when Prism::LocalVariableAndWriteNode, Prism::InstanceVariableAndWriteNode,
|
|
148
|
+
Prism::ClassVariableAndWriteNode, Prism::GlobalVariableAndWriteNode
|
|
149
|
+
resolve_and_write(node, scope)
|
|
150
|
+
else
|
|
151
|
+
resolve_write(node, scope)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Issue #2617 — a write in value position (`if (v = h[k])`) yields its right-hand side, so the write's own
|
|
156
|
+
# optimism is the RHS's. The binding lookup alone could not see it from the scope a certainty consumer
|
|
157
|
+
# reads: the `if`'s entry scope predates the predicate's evaluation, so `if (v = h[k])` found no mark for
|
|
158
|
+
# `v` there and `flow.always-truthy-condition` fired on the optimistic read. Resolving `node.value` reaches
|
|
159
|
+
# the mark recorded on the read itself, which lives in the side table every scope shares; the binding lookup
|
|
160
|
+
# stays as the fallback for the post-write scope `branch_certainty` passes.
|
|
161
|
+
def resolve_write(node, scope)
|
|
162
|
+
resolve(node.value, scope) || optimistic_write_binding(node, scope)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Only locals and ivars carry a binding mark; a global or class-variable write, and a
|
|
166
|
+
# multiple assignment's slots (marked through {.destructuring_marks} instead), have no
|
|
167
|
+
# `optimistic_*` table entry to fall back to, so they answer nil here.
|
|
168
|
+
def optimistic_write_binding(node, scope)
|
|
169
|
+
case node
|
|
170
|
+
when Prism::LocalVariableWriteNode then scope.optimistic_local(node.name)
|
|
171
|
+
when Prism::InstanceVariableWriteNode then scope.optimistic_ivar(node.name)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Issue #3054 — `v ||= rhs` yields the OLD binding whenever it is truthy, so the write's
|
|
176
|
+
# value is the right-hand side's only on the paths where the old value is falsey. When the
|
|
177
|
+
# old binding is provably always truthy — its type has no falsey inhabitant and it carries
|
|
178
|
+
# no optimistic mark — the right-hand side is never reached and the expression's verdict is
|
|
179
|
+
# the old value's own proof, so resolving through `node.value` would silence a correct
|
|
180
|
+
# always-truthy verdict (`v = "x"; if (v ||= h[k])` still folds to `"x"`). Otherwise the
|
|
181
|
+
# verdict, when one folds, rests on the right-hand side's bet.
|
|
182
|
+
def resolve_or_write(node, scope)
|
|
183
|
+
binding_type, binding_mark = or_write_binding(node, scope)
|
|
184
|
+
return nil if always_truthy_type?(binding_type) && binding_mark.nil?
|
|
185
|
+
|
|
186
|
+
resolve(node.value, scope)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Issue #3054 — `v &&= rhs` yields the right-hand side whenever the old binding is truthy;
|
|
190
|
+
# the old value itself wins only on its falsey edge, a result no always-truthy verdict can
|
|
191
|
+
# rest on. So the right-hand side's mark covers every constant verdict the expression folds
|
|
192
|
+
# to, joined by the old binding's own mark for the case where the miss — not the rvalue — is
|
|
193
|
+
# what makes the condition live (`v = h[k]; if (v &&= "lit")`).
|
|
194
|
+
def resolve_and_write(node, scope)
|
|
195
|
+
resolve(node.value, scope) || and_write_binding_mark(node, scope)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# The `[type, mark]` pair an `||=` reads its old value through: the scope binding and the
|
|
199
|
+
# optimistic mark it carries. Class- and global-variable or-writes have no mark table — the
|
|
200
|
+
# mark channel covers locals and ivars only — so their mark is always nil.
|
|
201
|
+
def or_write_binding(node, scope)
|
|
202
|
+
case node
|
|
203
|
+
when Prism::LocalVariableOrWriteNode
|
|
204
|
+
[scope.local(node.name), scope.optimistic_local(node.name)]
|
|
205
|
+
when Prism::InstanceVariableOrWriteNode
|
|
206
|
+
[scope.ivar(node.name), scope.optimistic_ivar(node.name)]
|
|
207
|
+
when Prism::ClassVariableOrWriteNode
|
|
208
|
+
[scope.cvar(node.name), nil]
|
|
209
|
+
when Prism::GlobalVariableOrWriteNode
|
|
210
|
+
[scope.global(node.name), nil]
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# The old binding's optimistic mark for an `&&=` — the same two tables {.or_write_binding}
|
|
215
|
+
# reads, with no type: `&&=` needs the mark alone.
|
|
216
|
+
def and_write_binding_mark(node, scope)
|
|
217
|
+
case node
|
|
218
|
+
when Prism::LocalVariableAndWriteNode then scope.optimistic_local(node.name)
|
|
219
|
+
when Prism::InstanceVariableAndWriteNode then scope.optimistic_ivar(node.name)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Issue #3054 — `recv.x = rhs` and `recv[i] = rhs` yield the assigned value, which Prism
|
|
224
|
+
# spells as the call's last argument (`x=` takes it alone, `[]=` trails the indices). The
|
|
225
|
+
# compound spellings never reach here: `+=` / `[]=` operators and `||=` / `&&=` on an
|
|
226
|
+
# attribute or index are `CallOperatorWriteNode` / `IndexOperatorWriteNode` /
|
|
227
|
+
# `CallOrWriteNode` / `CallAndWriteNode` / `IndexOrWriteNode` / `IndexAndWriteNode`, not
|
|
228
|
+
# `CallNode`, and their value is the old slot's, not the right-hand side's.
|
|
229
|
+
def resolve_attribute_write(node, scope)
|
|
230
|
+
resolve(node.arguments&.arguments&.last, scope) if node.attribute_write?
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Whether `type` proves every inhabitant truthy — the empty falsey fragment of
|
|
234
|
+
# `Narrowing.narrow_falsey`, mirrored carrier for carrier so this leaf module need not
|
|
235
|
+
# require `Narrowing` (which already requires this file). A `nil` binding (unbound) and
|
|
236
|
+
# every carrier the falsey fragment cannot empty (`Top`, `Dynamic`, ranges, refinements,
|
|
237
|
+
# differences) answer false: only a proof counts.
|
|
238
|
+
def always_truthy_type?(type)
|
|
239
|
+
case type
|
|
240
|
+
when Type::Constant then !(type.value.nil? || type.value == false)
|
|
241
|
+
when Type::Nominal then !%w[NilClass FalseClass].include?(type.class_name)
|
|
242
|
+
when Type::Union then type.members.all? { |member| always_truthy_type?(member) }
|
|
243
|
+
when Type::Singleton, Type::Tuple, Type::HashShape then true
|
|
244
|
+
else type.is_a?(Type::Bot)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
123
248
|
# `recv&.m` is `nil` exactly when `recv` is (or when `m` answers `nil`), so it restates `recv`'s presence
|
|
124
249
|
# and is as optimistic as `recv`. Only the call carrying the `&.` is derived: Ruby skips that one call and
|
|
125
250
|
# no more, so `recv&.m.n` sends `n` to the `nil` and raises (or answers `NilClass#n`), and neither it nor
|
|
@@ -169,7 +294,7 @@ module Rigor
|
|
|
169
294
|
# predicate's folded boolean only when the miss answers the other boolean (or cannot be told), so
|
|
170
295
|
# `!recv&.empty?` — `!false` on a hit, `!nil` on a miss — stays `true`, and so does `x = recv&.empty?; !x`.
|
|
171
296
|
def miss_answer(node, scope)
|
|
172
|
-
return nil if scope.
|
|
297
|
+
return nil if scope.optimistic_origin(node)
|
|
173
298
|
|
|
174
299
|
case node
|
|
175
300
|
when Prism::LocalVariableReadNode then scope.optimistic_local_miss(node.name)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "mutation_widening"
|
|
5
|
+
require_relative "receiver_alias"
|
|
6
|
+
require_relative "../type"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Inference
|
|
10
|
+
# Issue #2499 rule 3 (part of #2494) — the stdlib calls that fill a String the caller passes in. After
|
|
11
|
+
# `io.read(n, buf)`, `io.readpartial(n, buf)`, `io.sysread(n, buf)` and `io.read_nonblock(n, buf)` the buffer
|
|
12
|
+
# holds what was read, and after `StringIO.new(s)` every later write through the StringIO lands in `s`. The
|
|
13
|
+
# argument (a local, instance, class or global variable read) is widened as an in-place `replace` of it would
|
|
14
|
+
# widen it, so a `buf = +""` seed stops reading `""` after the call.
|
|
15
|
+
#
|
|
16
|
+
# A built-in catalogue keyed by method name and argument position, not a receiver-typed dispatch: a method of
|
|
17
|
+
# another class with the same name and a second positional argument loses only precision on that argument.
|
|
18
|
+
module OutBufferMutation
|
|
19
|
+
# Method name => the zero-based position of the buffer argument.
|
|
20
|
+
READERS = { read: 1, readpartial: 1, sysread: 1, read_nonblock: 1 }.freeze
|
|
21
|
+
private_constant :READERS
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# `scope` with the buffer argument of `call_node` widened, or `scope` itself when the call is no such call or the
|
|
26
|
+
# argument holds no String: `Rails.cache.read(key, opts)` passes an options Hash that keeps its shape.
|
|
27
|
+
def floor(call_node, scope)
|
|
28
|
+
buffer = buffer_argument(call_node)
|
|
29
|
+
return scope if buffer.nil?
|
|
30
|
+
|
|
31
|
+
getter, = MutationWidening::ALIAS_ACCESSORS[buffer.class]
|
|
32
|
+
return scope unless string_carrier?(scope.public_send(getter, ReceiverAlias.read_name(buffer)))
|
|
33
|
+
|
|
34
|
+
MutationWidening.widen_alias_read(:replace, buffer, scope)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# A `Constant<String>` or the `String` nominal, or a union of nothing else.
|
|
38
|
+
def string_carrier?(type)
|
|
39
|
+
case type
|
|
40
|
+
when Type::Constant then type.value.is_a?(String)
|
|
41
|
+
when Type::Nominal then type.class_name == "String"
|
|
42
|
+
when Type::Union then type.members.all? { |member| string_carrier?(member) }
|
|
43
|
+
else false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def buffer_argument(call_node)
|
|
48
|
+
arguments = call_node.arguments&.arguments
|
|
49
|
+
return nil if arguments.nil?
|
|
50
|
+
|
|
51
|
+
position = READERS[call_node.name]
|
|
52
|
+
position = 0 if position.nil? && string_io_new?(call_node)
|
|
53
|
+
return nil if position.nil?
|
|
54
|
+
|
|
55
|
+
argument = arguments[position]
|
|
56
|
+
MutationWidening::ALIAS_ACCESSORS.key?(argument.class) ? argument : nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def string_io_new?(call_node)
|
|
60
|
+
return false unless call_node.name == :new
|
|
61
|
+
|
|
62
|
+
receiver = call_node.receiver
|
|
63
|
+
case receiver
|
|
64
|
+
when Prism::ConstantReadNode then receiver.name == :StringIO
|
|
65
|
+
when Prism::ConstantPathNode then receiver.parent.nil? && receiver.name == :StringIO
|
|
66
|
+
else false
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
private_class_method :buffer_argument, :string_io_new?, :string_carrier?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -95,11 +95,10 @@ module Rigor
|
|
|
95
95
|
# may type in a later round once its own argument resolves). Unresolved call sites do not
|
|
96
96
|
# contribute.
|
|
97
97
|
#
|
|
98
|
-
# What it does NOT do yet
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# because the only consumer is the protection metric, which runs no diagnostics).
|
|
98
|
+
# What it does NOT do yet: typing keyword / optional / rest / block parameters themselves,
|
|
99
|
+
# inherited-method receivers, top-level helpers, and a rigorous closed-call-site-set proof
|
|
100
|
+
# (the union is optimistic over resolved sites; the `check` walk tolerates that because an
|
|
101
|
+
# inferred parameter is only a lower bound, which the in-body rules decline on, WD6b).
|
|
103
102
|
class ParameterInferenceCollector
|
|
104
103
|
EMPTY = {}.freeze
|
|
105
104
|
|
|
@@ -120,7 +119,7 @@ module Rigor
|
|
|
120
119
|
# @param files — project `.rb` paths to scan for call sites.
|
|
121
120
|
# @param target_ruby — Prism parse target.
|
|
122
121
|
# @param max_rounds — the WD5 fixpoint cap (1 = single-level).
|
|
123
|
-
# @return
|
|
122
|
+
# @return the inferred parameter table, frozen.
|
|
124
123
|
def self.collect(files:, environment:, target_ruby: nil, max_rounds: DEFAULT_ROUNDS, workers: 0)
|
|
125
124
|
new(files: files, environment: environment, target_ruby: target_ruby,
|
|
126
125
|
max_rounds: max_rounds, workers: workers).collect
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
#
|
|
14
14
|
# ADR-17 slice 2 gave `pre_eval:` a project-wide **method** registry ({ProjectPatchedMethods}). A
|
|
15
15
|
# constant declared in the same file stayed invisible across the file boundary: `Scope#in_source_constants`
|
|
16
|
-
#
|
|
16
|
+
# was per-file and kept out of `Runner#project_scope_seed_tables`, so only class-shaped
|
|
17
17
|
# constants (a `class` / `module` declaration, or one of the four meta-new forms `ScopeIndexer`'s
|
|
18
18
|
# `record_class_new_constant_decl` promotes) ever crossed. `TIMEOUT = 30` in a listed file read as
|
|
19
19
|
# `Dynamic[top]` everywhere else.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../type"
|
|
4
4
|
require_relative "external_ancestor_resolution"
|
|
5
|
+
require_relative "../typing_state"
|
|
6
|
+
require_relative "../memo_audit"
|
|
5
7
|
|
|
6
8
|
module Rigor
|
|
7
9
|
module Inference
|
|
@@ -19,8 +21,14 @@ module Rigor
|
|
|
19
21
|
NO_TARGETS = [].freeze
|
|
20
22
|
private_constant :NO_TARGETS
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
# ADR-129 WD1 — a run-scoped Memo keyed on the identity of the discovery index and the environment, the only
|
|
25
|
+
# inputs the buckets' answers read.
|
|
26
|
+
MEMO = TypingState.memo(
|
|
27
|
+
:project_method_ownership,
|
|
28
|
+
run_scoped: true, audit: ["project method defines", "block runs", "ancestry non-escaping"],
|
|
29
|
+
reason: "answer buckets keyed on the discovery index and environment identities"
|
|
30
|
+
)
|
|
31
|
+
private_constant :MEMO
|
|
24
32
|
|
|
25
33
|
module_function
|
|
26
34
|
|
|
@@ -61,9 +69,15 @@ module Rigor
|
|
|
61
69
|
return false if scope.nil?
|
|
62
70
|
|
|
63
71
|
by_method = (memo(scope)[kind][class_name] ||= {})
|
|
64
|
-
|
|
72
|
+
if by_method.key?(method_sym)
|
|
73
|
+
return MemoAudit.check("project method defines", by_method[method_sym], class_name, method_sym) do
|
|
74
|
+
compute_defines?(class_name, method_sym, kind, scope)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
65
77
|
|
|
66
|
-
by_method[method_sym] =
|
|
78
|
+
by_method[method_sym] = MemoAudit.key_audit("project method defines") do
|
|
79
|
+
compute_defines?(class_name, method_sym, kind, scope)
|
|
80
|
+
end
|
|
67
81
|
end
|
|
68
82
|
|
|
69
83
|
# One slot per thread, keyed on the identity of the scope's frozen discovery index and its environment —
|
|
@@ -71,15 +85,19 @@ module Rigor
|
|
|
71
85
|
# changes: the shape of `ExpressionTyper#class_graph_buckets`, whose rationale applies unchanged. A
|
|
72
86
|
# `Scope` merges each file's discovery with the project pre-pass, so the slot turns over per file, and
|
|
73
87
|
# an ADR-46 dependency the first lookup recorded belongs to the file every later hit serves. Pool
|
|
74
|
-
# workers are separate processes, and a Ractor has its own
|
|
75
|
-
# belongs to {ClosureEscapeAnalyzer}'s ancestry step.
|
|
88
|
+
# workers are separate processes, and a Ractor has its own {TypingState}. The `:ancestry` bucket
|
|
89
|
+
# belongs to {ClosureEscapeAnalyzer}'s ancestry step. The `:block_runs` bucket belongs to
|
|
90
|
+
# {BlockRunCount}.
|
|
76
91
|
def memo(scope)
|
|
92
|
+
return new_buckets if MemoAudit.enabled? && MemoAudit.suspended?
|
|
93
|
+
|
|
77
94
|
discovery = scope.discovery
|
|
78
95
|
environment = scope.environment
|
|
79
|
-
|
|
96
|
+
state = TypingState.current
|
|
97
|
+
slot = state[MEMO]
|
|
80
98
|
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
|
|
81
|
-
slot = [discovery, environment,
|
|
82
|
-
|
|
99
|
+
slot = [discovery, environment, new_buckets]
|
|
100
|
+
state[MEMO] = slot
|
|
83
101
|
end
|
|
84
102
|
slot[2]
|
|
85
103
|
end
|
|
@@ -87,6 +105,9 @@ module Rigor
|
|
|
87
105
|
class << self
|
|
88
106
|
private
|
|
89
107
|
|
|
108
|
+
# Empty buckets: a new slot's, and a throwaway set while an audit recomputation bypasses the memo.
|
|
109
|
+
def new_buckets = { instance: {}, singleton: {}, ancestry: {}, block_runs: {}.compare_by_identity }
|
|
110
|
+
|
|
90
111
|
def class_target(class_name, kind)
|
|
91
112
|
class_name.nil? ? nil : [[class_name.to_s, kind]]
|
|
92
113
|
end
|
|
@@ -36,6 +36,9 @@ module Rigor
|
|
|
36
36
|
# `farther` is set only on an entry {#lookup_through_ancestors} returns: the other definers of the name farther
|
|
37
37
|
# on the walk (entries, or {SOURCE_DEFINER} for a project-source `def`), which {#callable?} reads before it
|
|
38
38
|
# refuses the call.
|
|
39
|
+
#
|
|
40
|
+
# `kind` stays truthful on a copy {#singleton_copy_entry} returns: the `module_function` singleton copy is a
|
|
41
|
+
# `:singleton` method even though the body it copied is an instance `def`.
|
|
39
42
|
Entry = Data.define(:class_name, :method_name, :kind, :source_path, :source_line, :return_type, :alias_of,
|
|
40
43
|
:visibility, :farther) do
|
|
41
44
|
def initialize(class_name:, method_name:, kind:, source_path:, source_line:, return_type: nil, alias_of: nil,
|
|
@@ -49,7 +52,7 @@ module Rigor
|
|
|
49
52
|
# written, with no lexical resolution, as a patch's class name is.
|
|
50
53
|
Mixin = Data.define(:owner, :module_name, :kind)
|
|
51
54
|
|
|
52
|
-
attr_reader :by_key, :write_census, :mixins, :overrides
|
|
55
|
+
attr_reader :by_key, :write_census, :mixins, :overrides, :singleton_copies
|
|
53
56
|
|
|
54
57
|
# Each owner's prepended and included modules, the most recent first.
|
|
55
58
|
def self.mixins_by_owner(mixins)
|
|
@@ -69,7 +72,12 @@ module Rigor
|
|
|
69
72
|
# @param overrides — issue #1708: `[class_name, method_name, kind] => visibility` for a named visibility call
|
|
70
73
|
# (`public :kp` in `class Integer`) on a method the class inherits rather than binds, which Ruby copies onto
|
|
71
74
|
# the class with that visibility.
|
|
72
|
-
|
|
75
|
+
# @param singleton_copies — issue #2108: `[class_name, method_name]` pairs a `module_function :x` wrote for a
|
|
76
|
+
# name the class inherits rather than binds — Ruby's public singleton copy of the instance method the name
|
|
77
|
+
# resolves to. {#lookup_through_ancestors} answers a singleton-side call for one through
|
|
78
|
+
# {#singleton_copy_entry}.
|
|
79
|
+
def initialize(entries: [], write_census: GlobalWriteCensus::EMPTY, mixins: [], overrides: {},
|
|
80
|
+
singleton_copies: [])
|
|
73
81
|
@by_key = entries.each_with_object({}) do |entry, acc|
|
|
74
82
|
key = [entry.class_name, entry.method_name, entry.kind]
|
|
75
83
|
acc[key] = entry
|
|
@@ -78,6 +86,7 @@ module Rigor
|
|
|
78
86
|
@mixins = mixins.dup.freeze
|
|
79
87
|
@mixins_of = ProjectPatchedMethods.mixins_by_owner(@mixins)
|
|
80
88
|
@overrides = overrides.dup.freeze
|
|
89
|
+
@singleton_copies = singleton_copies.to_set.freeze
|
|
81
90
|
freeze
|
|
82
91
|
end
|
|
83
92
|
|
|
@@ -121,10 +130,18 @@ module Rigor
|
|
|
121
130
|
end
|
|
122
131
|
|
|
123
132
|
entry = @by_key[[owner, method_name, side]]
|
|
133
|
+
if entry.nil? && found.nil? && side == :singleton
|
|
134
|
+
copy = singleton_copy_entry(owner, method_name, rbs_loader, source_defines)
|
|
135
|
+
# Issue #2108 — a singleton copy is a real method on `#<Class:owner>` at this exact ancestor
|
|
136
|
+
# position: Ruby's lookup ends there, so nothing farther on the walk can answer `owner.x`.
|
|
137
|
+
# Return it now — collecting `farther` would only make {#callable?} decline the refusal Ruby
|
|
138
|
+
# makes of a private or protected copy.
|
|
139
|
+
return apply_override(copy, override) if copy
|
|
140
|
+
end
|
|
124
141
|
if found
|
|
125
142
|
farther << entry if entry
|
|
126
143
|
elsif entry
|
|
127
|
-
found =
|
|
144
|
+
found = apply_override(entry, override)
|
|
128
145
|
elsif (visibility = @overrides[[owner, method_name, side]])
|
|
129
146
|
override ||= [owner, visibility]
|
|
130
147
|
end
|
|
@@ -137,9 +154,12 @@ module Rigor
|
|
|
137
154
|
|
|
138
155
|
# Issue #1708 — whether Ruby lets a call reach `entry`. A public method answers any call; a private one only a
|
|
139
156
|
# call with no receiver or with `self` as it (`explicit` is false for those); a protected one also a call from
|
|
140
|
-
# a method whose `self` is an instance of the entry's owner
|
|
141
|
-
#
|
|
142
|
-
#
|
|
157
|
+
# a method whose `self` is an instance of the entry's owner — for a `:singleton` entry, the owner itself or a
|
|
158
|
+
# class whose singleton chain holds it (`Kernel.a1` under `class << self; protected :a1` is refused from
|
|
159
|
+
# `main`, an Object, and from another class's singleton method, and answered from Kernel's own and from a
|
|
160
|
+
# `module_function` def of Kernel's, whose self is typed as an instance of Kernel though the copy runs with
|
|
161
|
+
# self = Kernel). `self_type` is the caller's `self` (`nil` at the top level, where it is `main`, an Object);
|
|
162
|
+
# a `self` this cannot name (the default) may be one, so a protected entry answers it.
|
|
143
163
|
#
|
|
144
164
|
# It never refuses a call some other definer of the name may answer: the walk's order is an approximation
|
|
145
165
|
# (a module two owners mix in is spliced at the nearer one, where Ruby keeps the first include and skips the
|
|
@@ -152,7 +172,7 @@ module Rigor
|
|
|
152
172
|
allowed =
|
|
153
173
|
case entry.visibility
|
|
154
174
|
when :private, :module_function then false
|
|
155
|
-
when :protected then self_kind_of?(self_type, entry.class_name, rbs_loader)
|
|
175
|
+
when :protected then self_kind_of?(self_type, entry.class_name, entry.kind, rbs_loader)
|
|
156
176
|
else true
|
|
157
177
|
end
|
|
158
178
|
return true if allowed
|
|
@@ -171,18 +191,50 @@ module Rigor
|
|
|
171
191
|
|
|
172
192
|
private
|
|
173
193
|
|
|
174
|
-
# Whether `self_type` may
|
|
175
|
-
# with the patches' mixins, do not hold
|
|
176
|
-
|
|
177
|
-
|
|
194
|
+
# Whether `self_type` may reach `owner`'s `entry_kind` method: false only when the ancestors of a class RBS
|
|
195
|
+
# knows, with the patches' mixins, do not hold `[owner, entry_kind]` — an instance of the owner for an
|
|
196
|
+
# instance method, the owner itself or a class whose singleton chain holds it for a singleton one. For a
|
|
197
|
+
# singleton entry a self whose class IS the owner answers too: a `module_function` / `extend self` def is
|
|
198
|
+
# typed `Nominal[owner]` though its singleton copy runs with self = owner, so `name == owner` may be the
|
|
199
|
+
# owner object itself (issue #3056).
|
|
200
|
+
def self_kind_of?(self_type, owner, entry_kind, rbs_loader)
|
|
201
|
+
name, side =
|
|
178
202
|
if self_type.nil? then ["Object", :instance]
|
|
179
203
|
elsif self_type.respond_to?(:class_name) && self_type.class_name
|
|
180
204
|
[self_type.class_name.to_s, self_type.is_a?(Type::Singleton) ? :singleton : :instance]
|
|
181
205
|
end
|
|
182
206
|
return true if name.nil? || rbs_loader.nil?
|
|
183
207
|
|
|
184
|
-
keys = ancestor_keys(name,
|
|
185
|
-
keys == [[name,
|
|
208
|
+
keys = ancestor_keys(name, side, rbs_loader)
|
|
209
|
+
keys == [[name, side]] || keys.include?([owner, entry_kind]) ||
|
|
210
|
+
(entry_kind == :singleton && side == :instance && name == owner)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# `entry` as the walk's recorded `override` ([owner, visibility], from a nearer row's named
|
|
214
|
+
# visibility call) leaves it: the override's owner as `class_name` and its visibility — or `entry`
|
|
215
|
+
# itself when the walk passed no override on the way.
|
|
216
|
+
def apply_override(entry, override)
|
|
217
|
+
override ? entry.with(class_name: override.first, visibility: override.last) : entry
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Issue #2108 — the entry a `module_function :x` on a name `owner` inherits answers `owner.x` with. Ruby
|
|
221
|
+
# copies the instance method the name resolves to onto the owner's singleton as a public method, so the
|
|
222
|
+
# copy is the entry this walk finds for the name on the owner's INSTANCE ancestors, re-attributed to the
|
|
223
|
+
# owner, re-kinded `:singleton` and made public — unless a later named visibility call (`private_class_method
|
|
224
|
+
# :x`, or `private :x` in `class << self`) recorded the pair's singleton-side override, which Ruby applies to
|
|
225
|
+
# the copy. The instance walk's `farther` definers are dropped: the copy is a real method on
|
|
226
|
+
# `#<Class:owner>` that ends the singleton-side lookup, so no instance-chain definer can answer `owner.x`,
|
|
227
|
+
# and keeping them would only make {#callable?} decline a refusal Ruby makes. nil when no copy is
|
|
228
|
+
# recorded for `[owner, method_name]`, or when the instance walk finds no patch entry — an RBS or
|
|
229
|
+
# project-source method the registry does not hold, where the singleton walk then answers whatever else
|
|
230
|
+
# it reaches, as before.
|
|
231
|
+
def singleton_copy_entry(owner, method_name, rbs_loader, source_defines)
|
|
232
|
+
return nil unless @singleton_copies.include?([owner, method_name])
|
|
233
|
+
|
|
234
|
+
copy = lookup_through_ancestors(class_name: owner, method_name: method_name, kind: :instance,
|
|
235
|
+
rbs_loader: rbs_loader, source_defines: source_defines)
|
|
236
|
+
copy&.with(class_name: owner, kind: :singleton, farther: nil,
|
|
237
|
+
visibility: @overrides[[owner, method_name, :singleton]] || :public)
|
|
186
238
|
end
|
|
187
239
|
|
|
188
240
|
def ancestor_keys(class_name, kind, rbs_loader)
|