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,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
|
+
require_relative "../typing_state"
|
|
5
6
|
require_relative "project_patched_methods"
|
|
6
7
|
require_relative "project_patched_definedness_guard"
|
|
7
8
|
require_relative "project_patched_visibility"
|
|
@@ -45,6 +46,38 @@ module Rigor
|
|
|
45
46
|
end
|
|
46
47
|
private_constant :GuardedDef
|
|
47
48
|
|
|
49
|
+
# A `module_function` singleton copy a `def`-form produced (`module_function def x`, or a `def` under a
|
|
50
|
+
# bare `module_function`): `entry` is the copy to bind and `def_entry` the instance entry it was copied
|
|
51
|
+
# from, so {resolve_aliases} records which def the singleton binding answers — a later named
|
|
52
|
+
# `module_function :x` keeps the copy only while that def is proved unchanged (issue #3056).
|
|
53
|
+
SingletonCopy = Data.define(:entry, :def_entry) do
|
|
54
|
+
def source_path = entry.source_path
|
|
55
|
+
end
|
|
56
|
+
private_constant :SingletonCopy
|
|
57
|
+
|
|
58
|
+
# A statement the walk does not read that may rebind a method: any call other than a visibility call, a
|
|
59
|
+
# literal-named `alias_method`, or a receiverless `include` / `extend`, plus an `alias` with a computed
|
|
60
|
+
# name and an `undef`. `define_method(:x)`, `class_eval "def x"`, `send(:define_method, ...)` and
|
|
61
|
+
# `attr_reader :x` all make Ruby's next `module_function :x` copy a fresh public body, so after one
|
|
62
|
+
# {resolve_aliases} no longer assumes a copy is unchanged (issue #3056).
|
|
63
|
+
Unread = Data.define(:source_path)
|
|
64
|
+
private_constant :Unread
|
|
65
|
+
|
|
66
|
+
# The receiverless calls the walk reads as not rebinding a method of the body's own class.
|
|
67
|
+
MIXIN_CALLS = %i[include extend].freeze
|
|
68
|
+
private_constant :MIXIN_CALLS
|
|
69
|
+
|
|
70
|
+
# ADR-129 WD1 — the two per-file inputs of the walk, each a Frame {.walk_file} installs for the file's walk and
|
|
71
|
+
# restores on the way out: the `def`s on the undefined side of a definedness guard, and each `def`'s visibility.
|
|
72
|
+
# Both are frozen once built and read only by {.walk_node}'s callees.
|
|
73
|
+
#
|
|
74
|
+
# G1′: the walk is a Prism traversal that never types, so no `type_of` runs while either is installed.
|
|
75
|
+
GUARDED_DEFS = TypingState.frame(:patched_scanner_guarded_defs, reason: "installed per scanned file's walk",
|
|
76
|
+
typing_unreachable: true)
|
|
77
|
+
DEF_VISIBILITIES = TypingState.frame(:patched_scanner_def_visibilities, reason: "installed per scanned file",
|
|
78
|
+
typing_unreachable: true)
|
|
79
|
+
private_constant :GUARDED_DEFS, :DEF_VISIBILITIES
|
|
80
|
+
|
|
48
81
|
module_function
|
|
49
82
|
|
|
50
83
|
# @param paths — absolute paths to the pre-eval files. The runner has already
|
|
@@ -61,11 +94,13 @@ module Rigor
|
|
|
61
94
|
paths.each { |path| scan_file(path, collected, diagnostics, buffer, census) }
|
|
62
95
|
pending_mixins, collected = collected.partition { |item| item.is_a?(Mixins::Pending) }
|
|
63
96
|
overrides = {}
|
|
64
|
-
|
|
97
|
+
singleton_copies = Set.new
|
|
98
|
+
entries = resolve_aliases(collected, overrides, singleton_copies)
|
|
65
99
|
diagnostics.concat(duplicate_declaration_diagnostics(entries))
|
|
66
100
|
Result.new(
|
|
67
101
|
registry: ProjectPatchedMethods.new(entries: entries, write_census: census.freeze, overrides: overrides,
|
|
68
|
-
mixins: Mixins.resolve(pending_mixins, entries)
|
|
102
|
+
mixins: Mixins.resolve(pending_mixins, entries),
|
|
103
|
+
singleton_copies: singleton_copies),
|
|
69
104
|
diagnostics: diagnostics
|
|
70
105
|
)
|
|
71
106
|
end
|
|
@@ -114,23 +149,37 @@ module Rigor
|
|
|
114
149
|
# A {Visibility::Pending} (issue #1708) changes the visibility of the binding its name has when it runs, in the
|
|
115
150
|
# file that bound it, so it is not a second declaration; a name no earlier patch binds on the class is one it
|
|
116
151
|
# inherits, recorded in `overrides` ({Visibility.apply}). `module_function :x` also binds the public singleton
|
|
117
|
-
# copy.
|
|
118
|
-
|
|
152
|
+
# copy: an entry {.applied} yields for a bound name, or a `[class_name, name]` pair in `singleton_copies`
|
|
153
|
+
# (issue #2108) for an inherited one, whose body the scan cannot name.
|
|
154
|
+
#
|
|
155
|
+
# `bound_defs` holds each key's def / alias-produced entry — the def identity a visibility change does not
|
|
156
|
+
# move — and `copy_sources` each singleton key's `module_function` copy source, the pair
|
|
157
|
+
# {Visibility.apply} reads to keep a repeated `module_function :x` from re-publicing a copy
|
|
158
|
+
# `private_class_method` made private (issue #3056).
|
|
159
|
+
def resolve_aliases(collected, overrides = {}, singleton_copies = Set.new)
|
|
119
160
|
bound = {}
|
|
120
161
|
homes = {}
|
|
162
|
+
bound_defs = {}
|
|
163
|
+
copy_sources = {}
|
|
121
164
|
per_file = collected.group_by(&:source_path).values.map do |items|
|
|
122
165
|
final = {}
|
|
123
166
|
items.each do |item|
|
|
124
167
|
if item.is_a?(Visibility::Pending)
|
|
125
|
-
next Visibility.apply(item, bound, homes, overrides
|
|
168
|
+
next Visibility.apply(item, bound, homes, overrides, singleton_copies, bound_defs, copy_sources) do |copy|
|
|
169
|
+
bind(copy, bound, homes, final)
|
|
170
|
+
end
|
|
126
171
|
end
|
|
172
|
+
next if copy_provenance_item?(item, bound, homes, final, copy_sources)
|
|
127
173
|
|
|
128
174
|
entry = item.is_a?(PendingAlias) ? alias_entry(item, bound) : item
|
|
129
175
|
entry = entry.entry if entry.is_a?(GuardedDef)
|
|
130
176
|
key = [entry.class_name, entry.method_name, entry.kind]
|
|
131
|
-
|
|
177
|
+
# A guarded def dropped here may still run (`method_defined?` ignores a private method), so the
|
|
178
|
+
# copy it would rebind is no longer known to be unchanged.
|
|
179
|
+
next copy_sources.delete([key[0], key[1], :singleton]) if item.is_a?(GuardedDef) && bound.key?(key)
|
|
132
180
|
|
|
133
181
|
bind(entry, bound, homes, final)
|
|
182
|
+
record_binding(key, entry, bound_defs, copy_sources)
|
|
134
183
|
end
|
|
135
184
|
final
|
|
136
185
|
end
|
|
@@ -138,6 +187,33 @@ module Rigor
|
|
|
138
187
|
end
|
|
139
188
|
private_class_method :resolve_aliases
|
|
140
189
|
|
|
190
|
+
# The bookkeeping {resolve_aliases} keeps beside `bound`: `bound_defs` holds the def / alias
|
|
191
|
+
# entry `key` binds — the def identity a visibility change does not move, which
|
|
192
|
+
# {Visibility.apply}'s `module_function` re-copy check compares — and a singleton-side binding
|
|
193
|
+
# clears the copy provenance, since a `def self.x` between two `module_function :x` calls makes
|
|
194
|
+
# the next copy fresh (issue #3056).
|
|
195
|
+
def record_binding(key, entry, bound_defs, copy_sources)
|
|
196
|
+
bound_defs[key] = entry
|
|
197
|
+
copy_sources.delete([entry.class_name, entry.method_name, :singleton]) if entry.kind == :singleton
|
|
198
|
+
end
|
|
199
|
+
private_class_method :record_binding
|
|
200
|
+
|
|
201
|
+
# The two items that only move the `module_function` copy provenance {Visibility.apply}'s re-copy
|
|
202
|
+
# check compares (issue #3056), handled here and answered true: a {SingletonCopy} binds the copy and
|
|
203
|
+
# records which instance def entry it was made from, and an {Unread} statement forgets every record,
|
|
204
|
+
# since it may have rebound any name.
|
|
205
|
+
def copy_provenance_item?(item, bound, homes, final, copy_sources)
|
|
206
|
+
case item
|
|
207
|
+
when SingletonCopy
|
|
208
|
+
bind(item.entry, bound, homes, final)
|
|
209
|
+
copy_sources[[item.entry.class_name, item.entry.method_name, :singleton]] = item.def_entry
|
|
210
|
+
when Unread then copy_sources.clear
|
|
211
|
+
else return false
|
|
212
|
+
end
|
|
213
|
+
true
|
|
214
|
+
end
|
|
215
|
+
private_class_method :copy_provenance_item?
|
|
216
|
+
|
|
141
217
|
def bind(entry, bound, homes, final)
|
|
142
218
|
key = [entry.class_name, entry.method_name, entry.kind]
|
|
143
219
|
bound[key] = entry
|
|
@@ -184,18 +260,16 @@ module Rigor
|
|
|
184
260
|
"#{e.class}: #{e.message}. Pre-evaluation skipped for this file; " \
|
|
185
261
|
"the rest of the run proceeds."
|
|
186
262
|
)
|
|
187
|
-
ensure
|
|
188
|
-
Thread.current[:rigor_guarded_defs] = nil
|
|
189
|
-
Thread.current[:rigor_def_visibilities] = nil
|
|
190
263
|
end
|
|
191
264
|
private_class_method :scan_file
|
|
192
265
|
|
|
193
266
|
# Walks one parsed file with its definedness guards and `def` visibilities (issue #1708) in reach of
|
|
194
267
|
# {record_def_node}.
|
|
195
268
|
def walk_file(root, path, entries)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
269
|
+
TypingState.current.with_pair(GUARDED_DEFS, DefinednessGuard.defs(root).freeze,
|
|
270
|
+
DEF_VISIBILITIES, Visibility.defs(root).freeze) do
|
|
271
|
+
walk_node(root, [], false, path, entries)
|
|
272
|
+
end
|
|
199
273
|
end
|
|
200
274
|
private_class_method :walk_file
|
|
201
275
|
|
|
@@ -231,17 +305,10 @@ module Rigor
|
|
|
231
305
|
descend_singleton_class(node, qualified_prefix, source_path, entries)
|
|
232
306
|
when Prism::DefNode
|
|
233
307
|
record_def_node(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
234
|
-
when Prism::AliasMethodNode
|
|
235
|
-
|
|
236
|
-
source_path, entries)
|
|
308
|
+
when Prism::AliasMethodNode, Prism::UndefNode
|
|
309
|
+
record_keyword(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
237
310
|
when Prism::CallNode
|
|
238
|
-
|
|
239
|
-
in_singleton_class, source_path, entries)
|
|
240
|
-
if Thread.current[:rigor_def_visibilities]&.[](node).equal?(Visibility::STATEMENT)
|
|
241
|
-
pending = Visibility.pending(node, qualified_prefix, in_singleton_class, source_path)
|
|
242
|
-
entries << pending if pending
|
|
243
|
-
entries.concat(Mixins.pending(node, qualified_prefix, in_singleton_class))
|
|
244
|
-
end
|
|
311
|
+
record_call(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
245
312
|
walk_children(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
246
313
|
else
|
|
247
314
|
walk_children(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
@@ -249,6 +316,34 @@ module Rigor
|
|
|
249
316
|
end
|
|
250
317
|
private_class_method :walk_node
|
|
251
318
|
|
|
319
|
+
# An `alias` with literal names binds as {record_alias} reads it; a computed-name `alias` or an `undef`
|
|
320
|
+
# is {Unread}.
|
|
321
|
+
def record_keyword(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
322
|
+
names = node.is_a?(Prism::AliasMethodNode) ? Source::AliasNames.keyword_names(node) : nil
|
|
323
|
+
entries << Unread.new(source_path: source_path) if names.nil?
|
|
324
|
+
record_alias(node, names, qualified_prefix, in_singleton_class, source_path, entries)
|
|
325
|
+
end
|
|
326
|
+
private_class_method :record_keyword
|
|
327
|
+
|
|
328
|
+
def record_call(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
329
|
+
names = Source::AliasNames.alias_method_call_names(node)
|
|
330
|
+
entries << Unread.new(source_path: source_path) if names.nil? && unread_call?(node)
|
|
331
|
+
record_alias(node, names, qualified_prefix, in_singleton_class, source_path, entries)
|
|
332
|
+
return unless TypingState.current[DEF_VISIBILITIES]&.[](node).equal?(Visibility::STATEMENT)
|
|
333
|
+
|
|
334
|
+
pending = Visibility.pending(node, qualified_prefix, in_singleton_class, source_path)
|
|
335
|
+
entries << pending if pending
|
|
336
|
+
entries.concat(Mixins.pending(node, qualified_prefix, in_singleton_class))
|
|
337
|
+
end
|
|
338
|
+
private_class_method :record_call
|
|
339
|
+
|
|
340
|
+
def unread_call?(node)
|
|
341
|
+
return false if Visibility.call(node)
|
|
342
|
+
|
|
343
|
+
!(MIXIN_CALLS.include?(node.name) && (node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)))
|
|
344
|
+
end
|
|
345
|
+
private_class_method :unread_call?
|
|
346
|
+
|
|
252
347
|
def walk_children(node, qualified_prefix, in_singleton_class, source_path, entries)
|
|
253
348
|
node.rigor_each_child do |child|
|
|
254
349
|
walk_node(child, qualified_prefix, in_singleton_class, source_path, entries)
|
|
@@ -288,9 +383,9 @@ module Rigor
|
|
|
288
383
|
source_path: source_path, source_line: line,
|
|
289
384
|
return_type: return_type
|
|
290
385
|
)
|
|
291
|
-
entry, *copies = Visibility.applied(entry,
|
|
292
|
-
entries << (
|
|
293
|
-
entries.concat(copies)
|
|
386
|
+
entry, *copies = Visibility.applied(entry, TypingState.current[DEF_VISIBILITIES]&.[](node) || :public)
|
|
387
|
+
entries << (TypingState.current[GUARDED_DEFS]&.key?(node) ? GuardedDef.new(entry: entry) : entry)
|
|
388
|
+
entries.concat(copies.map { |copy| SingletonCopy.new(entry: copy, def_entry: entry) })
|
|
294
389
|
end
|
|
295
390
|
private_class_method :record_def_node
|
|
296
391
|
|
|
@@ -98,20 +98,56 @@ module Rigor
|
|
|
98
98
|
# second declaration. A name the class does not bind is one it inherits (`public :kp` in `class Integer` over
|
|
99
99
|
# `module Kernel; private def kp`), which Ruby copies onto the class with the new visibility: `overrides` gets
|
|
100
100
|
# `key => visibility` for {ProjectPatchedMethods#lookup_through_ancestors} to apply to the body it finds farther
|
|
101
|
-
# on.
|
|
101
|
+
# on. `module_function :x` also binds the public singleton copy: for a name the class binds, {.applied} yields
|
|
102
|
+
# it to bind here; for an inherited one (issue #2108) `singleton_copies` records `[class_name, name]`, which
|
|
103
|
+
# {ProjectPatchedMethods#singleton_copy_entry} resolves through the owner's instance ancestors at call time
|
|
104
|
+
# (an approximation: Ruby snapshots the body when module_function runs)
|
|
105
|
+
# — the copy's body is whichever method the name reaches there, which the scan cannot name.
|
|
106
|
+
# Yields each copy that binds (`module_function :x`'s singleton), at the call's line, for the caller to
|
|
102
107
|
# bind.
|
|
103
|
-
|
|
108
|
+
#
|
|
109
|
+
# `bound_defs` maps each bound key to the entry a `def` / `alias` last produced for it — the def
|
|
110
|
+
# identity a visibility change (`private :x`) does not move — and `copy_sources` maps a singleton key
|
|
111
|
+
# to the instance def entry its `module_function` copy was made from. Ruby re-copies the instance
|
|
112
|
+
# method onto the singleton only when that def changed since the last copy (issue #3056): a
|
|
113
|
+
# `private_class_method :x` between two `module_function :x` calls keeps the copy private, while a
|
|
114
|
+
# `def x` redefinition, or any statement the scan does not read, makes the next copy public again.
|
|
115
|
+
def apply(pending, bound, homes, overrides, singleton_copies, bound_defs, copy_sources)
|
|
104
116
|
pending.names.each do |name|
|
|
105
117
|
key = [pending.class_name, name, pending.kind]
|
|
106
118
|
entry = bound[key]
|
|
107
|
-
|
|
119
|
+
if entry.nil?
|
|
120
|
+
overrides[key] = pending.visibility
|
|
121
|
+
singleton_copies << [pending.class_name, name] if pending.visibility == :module_function
|
|
122
|
+
next
|
|
123
|
+
end
|
|
108
124
|
|
|
109
125
|
changed, *copies = applied(entry, pending.visibility)
|
|
126
|
+
copies = [] if unchanged_module_function_copy?(pending, key, bound_defs, copy_sources)
|
|
110
127
|
bound[key] = homes[key][key] = changed
|
|
111
128
|
copies.each { |copy| yield copy.with(source_path: pending.source_path, source_line: pending.source_line) }
|
|
112
129
|
end
|
|
113
130
|
end
|
|
114
131
|
|
|
132
|
+
# Issue #3056 — true only when `pending` is a `module_function` named call whose singleton copy is
|
|
133
|
+
# proved to answer the same instance def: the copy's recorded source is still the def / alias
|
|
134
|
+
# binding `key` has. `resolve_aliases` drops that record whenever something may have rebound the
|
|
135
|
+
# name (a singleton-side `def`, a guarded def it dropped, or any statement the walk does not read),
|
|
136
|
+
# and then Ruby's re-copy is public, the reading that refuses no call Ruby runs. `bound_defs[key]` is
|
|
137
|
+
# the def entry `key` binds — a visibility change does not move it — and `copy_sources[skey]` the
|
|
138
|
+
# instance def entry the singleton's current copy was made from; either way the source is
|
|
139
|
+
# (re)recorded for the next call.
|
|
140
|
+
def unchanged_module_function_copy?(pending, key, bound_defs, copy_sources)
|
|
141
|
+
return false unless pending.visibility == :module_function
|
|
142
|
+
|
|
143
|
+
skey = [key[0], key[1], :singleton]
|
|
144
|
+
def_entry = bound_defs[key]
|
|
145
|
+
source = copy_sources[skey]
|
|
146
|
+
copy_sources[skey] = def_entry
|
|
147
|
+
!source.nil? && source.equal?(def_entry)
|
|
148
|
+
end
|
|
149
|
+
private_class_method :unchanged_module_function_copy?
|
|
150
|
+
|
|
115
151
|
# `entry` as `visibility` leaves it, then any copy that binds: `module_function` makes an instance method
|
|
116
152
|
# private and binds a public singleton copy, as Ruby does.
|
|
117
153
|
def applied(entry, visibility)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "../type"
|
|
6
|
+
require_relative "block_run_count"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module Inference
|
|
@@ -12,10 +13,12 @@ module Rigor
|
|
|
12
13
|
# counted as running the block once, and a `return` in the block that only a later run reaches (`each { return
|
|
13
14
|
# false if (seen += 1) > 3 }`) never joined the method's return type.
|
|
14
15
|
#
|
|
15
|
-
# Only the later-run `return` pass asks
|
|
16
|
-
# entry scopes and the ADR-56 write-back keep
|
|
17
|
-
# return type, so the rule here errs toward
|
|
18
|
-
#
|
|
16
|
+
# Only the later-run `return` pass asks {.may_run_block_repeatedly?}
|
|
17
|
+
# (`StatementEvaluator#collect_repeated_block_returns`); the block's entry scopes and the ADR-56 write-back keep
|
|
18
|
+
# {BlockRepetition}'s rule. That pass only widens the method's return type, so the rule here errs toward
|
|
19
|
+
# "repeats". The expression-position block-value pass asks {.block_value_may_repeat?} instead, whose rule
|
|
20
|
+
# ({BlockRunCount}) errs the other way. The callee repeats when its body, outside a nested `def` (a `yield` in a
|
|
21
|
+
# class body is a syntax error inside a method):
|
|
19
22
|
#
|
|
20
23
|
# - holds two or more sites that run the block (`yield`, or `call` / `yield` / `===` / `[]` on the `&blk`
|
|
21
24
|
# parameter), or one inside a loop, a block or a lambda, which may run it many times; or
|
|
@@ -41,26 +44,17 @@ module Rigor
|
|
|
41
44
|
false
|
|
42
45
|
end
|
|
43
46
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
# times, so the caller's block is typed as one that may repeat. Answers false where the callee does not resolve.
|
|
48
|
-
def hands_block_on?(call_node, receiver_type, scope)
|
|
47
|
+
# Issues #2301 and #2303 — whether the callee may run the block more than once on one path, for the
|
|
48
|
+
# expression-position block-value pass ({BlockRunCount}). Answers false where the callee does not resolve.
|
|
49
|
+
def block_value_may_repeat?(call_node, receiver_type, scope)
|
|
49
50
|
def_node = callee_def(call_node, receiver_type, scope)
|
|
50
51
|
return false unless def_node.is_a?(Prism::DefNode) && def_node.body
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
BlockRunCount.may_repeat?(def_node, scope)
|
|
53
54
|
rescue StandardError
|
|
54
55
|
false
|
|
55
56
|
end
|
|
56
57
|
|
|
57
|
-
def hand_off_in?(node, block_name)
|
|
58
|
-
return false if node.is_a?(Prism::DefNode)
|
|
59
|
-
return true if node.is_a?(Prism::BlockArgumentNode) && forwards_block?(node, block_name)
|
|
60
|
-
|
|
61
|
-
node.compact_child_nodes.any? { |child| hand_off_in?(child, block_name) }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
58
|
# The `def` an instance call on a project class, a singleton call on one, or an implicit-self call to a
|
|
65
59
|
# top-level method resolves to.
|
|
66
60
|
def callee_def(call_node, receiver_type, scope)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "rbs"
|
|
4
4
|
|
|
5
5
|
require_relative "../type"
|
|
6
|
+
require_relative "../typing_state"
|
|
7
|
+
require_relative "../memo_audit"
|
|
6
8
|
|
|
7
9
|
module Rigor
|
|
8
10
|
module Inference
|
|
@@ -36,8 +38,9 @@ module Rigor
|
|
|
36
38
|
# `untyped`. `Type::Intersection` stays out of this path: it is the same-base refinement composite
|
|
37
39
|
# carrier, and the dispatch tiers do not accept it as a receiver.
|
|
38
40
|
#
|
|
39
|
-
# Interface types
|
|
40
|
-
#
|
|
41
|
+
# Interface types degrade to `Dynamic[Top]`, except an interface in the `Rigor::Plugin` namespace, which
|
|
42
|
+
# translates to `Dynamic[Type::Interface]` (ADR-130 WD1/WD4): `untyped` to every rule, and resolvable only by a
|
|
43
|
+
# call of a member it declares (`RbsDispatch`).
|
|
41
44
|
#
|
|
42
45
|
# The optional `self_type:` and `instance_type:` arguments are the Rigor counterparts of RBS's
|
|
43
46
|
# `self` and `instance` tokens:
|
|
@@ -78,7 +81,7 @@ module Rigor
|
|
|
78
81
|
RBS::Types::Alias => :translate_alias,
|
|
79
82
|
RBS::Types::Intersection => :translate_intersection,
|
|
80
83
|
RBS::Types::Variable => :translate_variable,
|
|
81
|
-
RBS::Types::Interface => :
|
|
84
|
+
RBS::Types::Interface => :translate_interface
|
|
82
85
|
}.freeze
|
|
83
86
|
private_constant :TRANSLATORS
|
|
84
87
|
|
|
@@ -106,14 +109,12 @@ module Rigor
|
|
|
106
109
|
# repeat with one frozen String.
|
|
107
110
|
@relative_names = {}
|
|
108
111
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# scratch; every repeat now shares one Union.
|
|
116
|
-
@closed_alias_translations = ObjectSpace::WeakKeyMap.new
|
|
112
|
+
# ADR-130 WD2 — the erasure baseline's flag, installed by {.with_interface_erasure}.
|
|
113
|
+
INTERFACE_ERASURE = TypingState.frame(
|
|
114
|
+
:interface_erasure, reason: "a flag installed around an erasure-baseline run (the parity harness)",
|
|
115
|
+
witness: "spec/fixtures/typing_state_witnesses/interface_erasure.rb"
|
|
116
|
+
)
|
|
117
|
+
private_constant :INTERFACE_ERASURE
|
|
117
118
|
|
|
118
119
|
class << self
|
|
119
120
|
# @param self_type — substitute for `Bases::Self`.
|
|
@@ -130,6 +131,20 @@ module Rigor
|
|
|
130
131
|
translate_in(rbs_type, Context.new(self_type, instance_type, type_vars, alias_expander, 0))
|
|
131
132
|
end
|
|
132
133
|
|
|
134
|
+
# ADR-130 WD2 — the erasure baseline. Inside the block every RBS interface translates to `untyped`, as it did
|
|
135
|
+
# before ADR-130, whatever carrier the default engine has; the behavioural parity gate
|
|
136
|
+
# (`spec/integration/interface_parity_spec.rb`) runs its baseline here, so the baseline never comes from the
|
|
137
|
+
# default engine. {translate_interface} reads {interface_erasure?}, and {translate_alias} bypasses the loader's
|
|
138
|
+
# closed-alias memo under it, so a translation made in one mode is never served in the other (ADR-130 WD4). The
|
|
139
|
+
# switch lives in the calling thread's (and fiber's) `TypingState`, so a run under it is sequential: a pooled
|
|
140
|
+
# run raises (`PoolCoordinator`).
|
|
141
|
+
def with_interface_erasure(&)
|
|
142
|
+
MemoAudit.check_run_level_install(:interface_erasure)
|
|
143
|
+
TypingState.current.with(INTERFACE_ERASURE, true, &)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def interface_erasure? = TypingState.current[INTERFACE_ERASURE] == true
|
|
147
|
+
|
|
133
148
|
private
|
|
134
149
|
|
|
135
150
|
def translate_in(rbs_type, context)
|
|
@@ -151,6 +166,28 @@ module Rigor
|
|
|
151
166
|
Type::Combinator.untyped
|
|
152
167
|
end
|
|
153
168
|
|
|
169
|
+
# ADR-130 WD4 — the namespace whose interfaces carry the facet: `::Rigor::Plugin::_Scope` and its siblings.
|
|
170
|
+
PLUGIN_INTERFACE_PATH = %i[Rigor Plugin].freeze
|
|
171
|
+
private_constant :PLUGIN_INTERFACE_PATH
|
|
172
|
+
|
|
173
|
+
# ADR-130 WD1/WD4 — a `Rigor::Plugin` interface is `Dynamic[Interface]`; every other interface, rbs core's
|
|
174
|
+
# conversion aliases (`int`, `string`, ...) included, is `untyped` as before. Which one is a pure function of
|
|
175
|
+
# the `TypeName` (its namespace path, read without allocating), so a closed alias translates alike whichever
|
|
176
|
+
# file or worker reaches it first. Under {with_interface_erasure} every interface is `untyped`.
|
|
177
|
+
def translate_interface(rbs_type, context)
|
|
178
|
+
name = rbs_type.name
|
|
179
|
+
return Type::Combinator.untyped unless name.namespace.path == PLUGIN_INTERFACE_PATH
|
|
180
|
+
return Type::Combinator.untyped if interface_erasure?
|
|
181
|
+
|
|
182
|
+
carrier =
|
|
183
|
+
if rbs_type.args.empty?
|
|
184
|
+
Type::Interface.new(relative_name(name))
|
|
185
|
+
else
|
|
186
|
+
Type::Interface.new(relative_name(name), rbs_type.args.map { |arg| translate_in(arg, context) })
|
|
187
|
+
end
|
|
188
|
+
Type::Combinator.dynamic(carrier)
|
|
189
|
+
end
|
|
190
|
+
|
|
154
191
|
def translate_nil(_rbs_type, _context)
|
|
155
192
|
Type::Combinator.constant_of(nil)
|
|
156
193
|
end
|
|
@@ -251,18 +288,34 @@ module Rigor
|
|
|
251
288
|
|
|
252
289
|
expanded = expander.expand_type_alias(rbs_type)
|
|
253
290
|
return Type::Combinator.untyped if expanded.nil?
|
|
291
|
+
|
|
254
292
|
# A nested expansion is translated under its parent's budget, and `rigor trace` records every
|
|
255
|
-
# union merge as it happens, so neither consults the memo.
|
|
256
|
-
|
|
293
|
+
# union merge as it happens, so neither consults the memo. Nor does the erasure baseline (ADR-130 WD4): the
|
|
294
|
+
# memo holds the default engine's translations, where a `Rigor::Plugin` interface keeps its facet.
|
|
295
|
+
memo = closed_translations_of(expander)
|
|
296
|
+
unless memo && context.alias_depth.zero? && !FlowTracer.active? && !interface_erasure?
|
|
297
|
+
return translate_in(expanded, context.deeper)
|
|
298
|
+
end
|
|
257
299
|
|
|
258
|
-
cached =
|
|
300
|
+
cached = memo[expanded]
|
|
259
301
|
return cached if cached
|
|
260
302
|
|
|
261
303
|
translated = translate_in(expanded, context.deeper)
|
|
262
|
-
|
|
304
|
+
memo[expanded] = translated if closed_type?(expanded)
|
|
263
305
|
translated
|
|
264
306
|
end
|
|
265
307
|
|
|
308
|
+
# #775 — the translated form of a CLOSED alias expansion, keyed by the expansion, lives with the expander.
|
|
309
|
+
# Closed means no `self`, `instance` or type variable anywhere inside, so the caller's context cannot change
|
|
310
|
+
# the answer; at expansion depth 0 the budget cannot either. But a nested alias inside the expansion is
|
|
311
|
+
# expanded through the expander (`type a = b | Integer` holds `b`), so the translation reads the expander's
|
|
312
|
+
# environment: a memo shared by every expander answered a second loader, whose `b` differs, with the first
|
|
313
|
+
# loader's translation (equal expansions are equal keys). The expander owns its memo, so it dies with it. An
|
|
314
|
+
# expander that offers none (anything answering only `expand_type_alias`) is translated afresh each time.
|
|
315
|
+
def closed_translations_of(expander)
|
|
316
|
+
expander.closed_alias_translations if expander.respond_to?(:closed_alias_translations)
|
|
317
|
+
end
|
|
318
|
+
|
|
266
319
|
# True when nothing in `rbs_type`'s tree reads the translation context: no `self`, no `instance`,
|
|
267
320
|
# no type variable. Every rbs type answers `each_type` (the leaves through `EmptyEachType`).
|
|
268
321
|
def closed_type?(rbs_type)
|
|
@@ -85,6 +85,26 @@ module Rigor
|
|
|
85
85
|
candidates(first, depth + 1) + candidates(second, depth + 1)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
# Calls whose result hands its caller the receiver's own element objects (issue #2287): `h.values` the hash's
|
|
89
|
+
# values, `a.dup` / `a.clone` a shallow copy sharing them, and the blockless enumerator spellings (`a.each`,
|
|
90
|
+
# `a.map`, `a.each_with_index`, `e.with_index`) the elements themselves.
|
|
91
|
+
ELEMENT_EXPOSING_CALLS = %i[values dup clone each map each_with_index with_index].to_set.freeze
|
|
92
|
+
private_constant :ELEMENT_EXPOSING_CALLS
|
|
93
|
+
|
|
94
|
+
# The variables whose elements a loop over `receiver` is handed: {.candidates} of `receiver` once every
|
|
95
|
+
# blockless {ELEMENT_EXPOSING_CALLS} call is peeled off, so `h.values.each { |v| v << 2 }` and
|
|
96
|
+
# `a.each_with_index.map { |x, i| x << i }` name `h` and `a`. Only a loop's element widening reads it; an
|
|
97
|
+
# in-place mutation OF such a result (`h.values << 1`, `a.dup << 1`) changes a new object, not the variable.
|
|
98
|
+
def element_sources(receiver)
|
|
99
|
+
depth = 0
|
|
100
|
+
while receiver.is_a?(Prism::CallNode) && receiver.block.nil? && ELEMENT_EXPOSING_CALLS.include?(receiver.name)
|
|
101
|
+
return [] if (depth += 1) > WALK_DEPTH_CAP
|
|
102
|
+
|
|
103
|
+
receiver = receiver.receiver
|
|
104
|
+
end
|
|
105
|
+
candidates(receiver)
|
|
106
|
+
end
|
|
107
|
+
|
|
88
108
|
# The variable reads an in-place mutation of `receiver` changes: {.candidates}' locals and instance
|
|
89
109
|
# variables, or the class variable or global the receiver reads or writes directly, parenthesised or not
|
|
90
110
|
# (`(@@c ||= []) << x` mutates `@@c`, as `(buf ||= []) << x` mutates `buf`). A class variable or global counts
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
# Issue #2488 — a writer a class gains from a call on an explicit receiver outside its body: `E.attr_writer :w`,
|
|
8
|
+
# `E.send(:attr_accessor, :y)`, `E.define_method(:set) { @v = … }` (and `define_method` through `send`). Ruby made
|
|
9
|
+
# `attr_*` and `define_method` public in 2.5, so each is the receiver class's own declaration, exactly as the same
|
|
10
|
+
# call in a `E.class_eval { … }` block is. Both ivar censuses read the calls through here: the per-file class-ivar
|
|
11
|
+
# walk ({ScopeIndexer}) and the cross-file {IvarWriterCensus}. The receiver's name is the caller's to resolve, the
|
|
12
|
+
# same way each resolves a `class_eval` receiver.
|
|
13
|
+
module ReceiverWriterCall
|
|
14
|
+
SEND_CALLS = %i[send __send__ public_send].to_set.freeze
|
|
15
|
+
WRITER_MACROS = %i[attr_writer attr_accessor].to_set.freeze
|
|
16
|
+
# The names a direct call spells; a `send` reaches any of them through its literal first argument.
|
|
17
|
+
CALLS = (WRITER_MACROS | SEND_CALLS | %i[define_method]).freeze
|
|
18
|
+
NO_ARGUMENTS = [].freeze
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# The receiver of `node` when it is one of these calls on a receiver other than `self`, else nil.
|
|
23
|
+
def receiver(node)
|
|
24
|
+
return nil unless node.is_a?(Prism::CallNode) && CALLS.include?(node.name)
|
|
25
|
+
|
|
26
|
+
recv = node.receiver
|
|
27
|
+
recv unless recv.nil? || recv.is_a?(Prism::SelfNode)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The ivar names an explicit-receiver `attr_writer` / `attr_accessor` declares (`[:@w]`), or an empty Array.
|
|
31
|
+
def writer_ivars(node)
|
|
32
|
+
name, arguments = called(node)
|
|
33
|
+
return NO_ARGUMENTS unless WRITER_MACROS.include?(name)
|
|
34
|
+
|
|
35
|
+
arguments.filter_map do |argument|
|
|
36
|
+
text = literal_name(argument)
|
|
37
|
+
:"@#{text}" if text
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The literal block an explicit-receiver `define_method` installs as an instance method, or nil.
|
|
42
|
+
def define_method_block(node)
|
|
43
|
+
name, = called(node)
|
|
44
|
+
block = node.block
|
|
45
|
+
block if name == :define_method && block.is_a?(Prism::BlockNode)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The method `node` calls and its arguments, seen through a `send` whose first argument spells the name.
|
|
49
|
+
def called(node)
|
|
50
|
+
return [nil, NO_ARGUMENTS] if receiver(node).nil?
|
|
51
|
+
|
|
52
|
+
arguments = node.arguments&.arguments || NO_ARGUMENTS
|
|
53
|
+
return [node.name, arguments] unless SEND_CALLS.include?(node.name)
|
|
54
|
+
|
|
55
|
+
sent = literal_name(arguments.first)
|
|
56
|
+
sent ? [sent, arguments.drop(1)] : [nil, NO_ARGUMENTS]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def literal_name(argument)
|
|
60
|
+
return unless argument.is_a?(Prism::SymbolNode) || argument.is_a?(Prism::StringNode)
|
|
61
|
+
|
|
62
|
+
text = argument.unescaped
|
|
63
|
+
text.to_sym if text.valid_encoding?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|