rigortype 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- 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/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +60 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- 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 +27 -21
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- 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 +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- 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 +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -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 +28 -9
- 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 +98 -4
- 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 +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +96 -2
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- 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/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -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 +7 -6
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- 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 +5 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
|
+
require_relative "../analysis/dependency_recorder"
|
|
5
6
|
require_relative "../source/node_children"
|
|
6
7
|
require_relative "../source/parameter_envelope"
|
|
7
8
|
require_relative "../type"
|
|
8
9
|
require_relative "guard_rebinding"
|
|
9
10
|
require_relative "project_method_ownership"
|
|
10
11
|
require_relative "refined_dispatch/own_method"
|
|
12
|
+
require_relative "../run_scoped_slots"
|
|
11
13
|
|
|
12
14
|
module Rigor
|
|
13
15
|
module Inference
|
|
@@ -84,7 +86,9 @@ module Rigor
|
|
|
84
86
|
:to_r, :to_c, :to_proc, :to_path, :to_sym, :to_io, :coerce, :exception, :divmod, :each, :respond_to?,
|
|
85
87
|
:respond_to_missing?, :method_missing, :initialize_copy, :marshal_dump, :pretty_print
|
|
86
88
|
].freeze
|
|
87
|
-
|
|
89
|
+
# Issue #2203 — the classes and module every project class's instances reach past its own chain.
|
|
90
|
+
IMPLICIT_ROOTS = %w[Object Kernel BasicObject].freeze
|
|
91
|
+
private_constant :UNPROVABLE_NODES, :LITERAL_CLASSES, :SELF_SAFE_NAMES, :PROTOCOL_NAMES, :IMPLICIT_ROOTS
|
|
88
92
|
|
|
89
93
|
# Raised inside a proof to abandon it.
|
|
90
94
|
class Unprovable < StandardError
|
|
@@ -112,6 +116,32 @@ module Rigor
|
|
|
112
116
|
nil
|
|
113
117
|
end
|
|
114
118
|
|
|
119
|
+
DEFINITION_COUNTS_KEY = RunScopedSlots.register(:__rigor_self_call_ivar_writes_definition_counts__)
|
|
120
|
+
private_constant :DEFINITION_COUNTS_KEY
|
|
121
|
+
|
|
122
|
+
# `{[owner, name] => the number of instance `def` rows of name the project records for owner}`, over every
|
|
123
|
+
# file of `deferred_ranges` (a `discovered_deferred_ranges` table). Built once per table and kept in ONE
|
|
124
|
+
# thread slot keyed by the table's identity (#2300): the table is never written in place, a file whose rows
|
|
125
|
+
# equal the seed's keeps the seed's table, and a scan of every file's rows per query was the cost once the
|
|
126
|
+
# table held the whole project. The slot holds one table and its counts, the next table replaces them, and the
|
|
127
|
+
# run that filled it releases it when it ends ({RunScopedSlots}, #2399).
|
|
128
|
+
def definition_counts(deferred_ranges)
|
|
129
|
+
slot = Thread.current[DEFINITION_COUNTS_KEY]
|
|
130
|
+
return slot[1] if slot && slot[0].equal?(deferred_ranges)
|
|
131
|
+
|
|
132
|
+
counts = {}
|
|
133
|
+
deferred_ranges.each_value do |rows|
|
|
134
|
+
rows.each do |row|
|
|
135
|
+
next unless row[3] == :instance
|
|
136
|
+
|
|
137
|
+
key = [row[4], row[2]]
|
|
138
|
+
counts[key] = counts.fetch(key, 0) + 1
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
Thread.current[DEFINITION_COUNTS_KEY] = [deferred_ranges, counts]
|
|
142
|
+
counts
|
|
143
|
+
end
|
|
144
|
+
|
|
115
145
|
# One proof's state: the instance variables read code writes, and the `def` bodies already read.
|
|
116
146
|
class Proof
|
|
117
147
|
attr_reader :written
|
|
@@ -127,7 +157,7 @@ module Rigor
|
|
|
127
157
|
# Reads the method an implicit-self or `self.` call to `call_node`'s name runs.
|
|
128
158
|
def self_call(call_node)
|
|
129
159
|
name = call_node.name
|
|
130
|
-
unprovable! if GuardRebinding.code_running_name?(name) || refined?(name)
|
|
160
|
+
unprovable! if GuardRebinding.code_running_name?(name) || refined?(name, on_self: true)
|
|
131
161
|
|
|
132
162
|
if ProjectMethodOwnership.defines?(@class_name, name, :instance, @scope)
|
|
133
163
|
read_def(proven_def(name))
|
|
@@ -184,10 +214,66 @@ module Rigor
|
|
|
184
214
|
|
|
185
215
|
private
|
|
186
216
|
|
|
187
|
-
# Whether a `refine` block
|
|
188
|
-
# read code sits may route the call to it (`refine Kernel do def warn …`)
|
|
189
|
-
|
|
190
|
-
|
|
217
|
+
# Whether a `refine` block in the project defines `name` where the call may reach it: a `using` in effect
|
|
218
|
+
# where the read code sits may route the call to it (`refine Kernel do def warn …`).
|
|
219
|
+
#
|
|
220
|
+
# Issue #2163 — the answer reads every file's refinements whole, and a refined class off the chain (`Kernel`,
|
|
221
|
+
# `IO`, `String`) is no `mark:` the chain read files, so it records `refinement:<name>` and `refinement:*`
|
|
222
|
+
# whichever way it answers, as `InEffectRefinements` consumers do: a file that starts or stops refining the
|
|
223
|
+
# name re-checks the reader.
|
|
224
|
+
#
|
|
225
|
+
# Issue #2203 — for a call on `self` (`on_self:`) whose reach {#self_reach} knows, a row of the name counts only
|
|
226
|
+
# on a class key in that reach, on the wildcard class key, or on a key the walk could not name
|
|
227
|
+
# (`InEffectRefinements.class_wildcard_key?`); a names-wildcard row (a computed `define_method`,
|
|
228
|
+
# `import_methods`) counts as the name, but only on a class key in the reach. A refinement of `String` leaves
|
|
229
|
+
# a `Holder` alone: dropping the proof is no safer, since the intervening-call reset restores the class-ivar
|
|
230
|
+
# seed and a class guard's narrowing then reports. Where the reach is unknown, and for a call on another
|
|
231
|
+
# receiver, a row of the name counts on any class and a names-wildcard row never does, as before the issue.
|
|
232
|
+
def refined?(name, on_self: false)
|
|
233
|
+
wildcard = Scope::DiscoveryIndex::REFINEMENT_WILDCARD
|
|
234
|
+
if Analysis::DependencyRecorder.active?
|
|
235
|
+
Analysis::DependencyRecorder.read_name(:refinement, name)
|
|
236
|
+
Analysis::DependencyRecorder.read_name(:refinement, wildcard)
|
|
237
|
+
end
|
|
238
|
+
reach = on_self ? self_reach : nil
|
|
239
|
+
@scope.discovered_refinements.any? do |refined, methods|
|
|
240
|
+
next methods.key?(name) if reach.nil?
|
|
241
|
+
|
|
242
|
+
if reach.include?(refined)
|
|
243
|
+
methods.key?(name) || methods.key?(wildcard)
|
|
244
|
+
else
|
|
245
|
+
methods.key?(name) && InEffectRefinements.class_wildcard_key?(@scope, refined)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Issue #2203 — the classes and modules on the instance chain of `self`'s class and of `Object`, `Kernel` and
|
|
251
|
+
# `BasicObject` (which holds the modules the project mixes into them), with `Object`'s RBS ancestors
|
|
252
|
+
# (`PP::ObjectMixin`); nil when a chain is cut, mixes in a module the tables cannot name, ends in a superclass
|
|
253
|
+
# they do not know (`class Holder < Struct.new(:a)`), or holds an external entry. A project subclass's chain
|
|
254
|
+
# needs no read: {#no_protocol_override} gives the proof up on a refined class or module anywhere on it. The
|
|
255
|
+
# implicit roots' chains are recorded, so a file that mixes a module into `Object` re-checks the reader;
|
|
256
|
+
# `self`'s own chain is recorded by {#no_protocol_override}'s read.
|
|
257
|
+
def self_reach
|
|
258
|
+
return @self_reach if defined?(@self_reach)
|
|
259
|
+
|
|
260
|
+
@self_reach = read_self_reach
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def read_self_reach
|
|
264
|
+
names = Set.new
|
|
265
|
+
[@class_name.to_s, *IMPLICIT_ROOTS].each do |class_name|
|
|
266
|
+
chain = Scope::ResolutionChain.for(@scope, class_name, :instance, :methods)
|
|
267
|
+
chain.record(@scope) if Analysis::DependencyRecorder.active?
|
|
268
|
+
return nil if chain.truncated? || chain.wildcard_mixin? || chain.opaque_tail?(@scope)
|
|
269
|
+
return nil if chain.entries.any?(&:external?)
|
|
270
|
+
|
|
271
|
+
chain.entries.each { |entry| names << entry.name }
|
|
272
|
+
end
|
|
273
|
+
loader = @scope.environment&.rbs_loader
|
|
274
|
+
return nil if loader.nil?
|
|
275
|
+
|
|
276
|
+
names.merge(loader.ancestor_names_for("Object"))
|
|
191
277
|
end
|
|
192
278
|
|
|
193
279
|
def call_receiver(node) = node.is_a?(Prism::CallNode) ? node.receiver : nil
|
|
@@ -240,12 +326,11 @@ module Rigor
|
|
|
240
326
|
# one file, with one `def` of the name there (a reopening or a `class_eval` body in the same file is a second
|
|
241
327
|
# one), and no `define_method`, `attr_*` or alias of it, nor a rewrite no literal names (`T.define_method`,
|
|
242
328
|
# `T.send(:define_method, …)`, `T.class_eval` with a computed name), nor a definition the walk could not prove
|
|
243
|
-
# runs (a possible or contested one, as `DefinerResolution` reads them).
|
|
244
|
-
# file.
|
|
329
|
+
# runs (a possible or contested one, as `DefinerResolution` reads them). `Scope#parameter_envelopes_of` files
|
|
330
|
+
# the class edge to every declaring file and, issue #2137, the `mark:` edge for a file that only marks it.
|
|
245
331
|
def single_definition(owner, name)
|
|
246
|
-
|
|
332
|
+
bucket = @scope.parameter_envelopes_of(owner)
|
|
247
333
|
unprovable! if possible_definition?(owner, name)
|
|
248
|
-
bucket = @scope.discovered_parameter_envelopes[owner] || {}
|
|
249
334
|
unprovable! if bucket[[:instance, name]] == Source::ParameterEnvelope::OPAQUE
|
|
250
335
|
unprovable! if (@scope.discovered_class_sources[owner]&.size || 0) > 1
|
|
251
336
|
unprovable! if definition_count(owner, name) > 1
|
|
@@ -259,11 +344,7 @@ module Rigor
|
|
|
259
344
|
end
|
|
260
345
|
|
|
261
346
|
def definition_count(owner, name)
|
|
262
|
-
|
|
263
|
-
@scope.discovered_deferred_ranges.each_value do |rows|
|
|
264
|
-
count += rows.count { |row| row[2] == name && row[3] == :instance && row[4] == owner }
|
|
265
|
-
end
|
|
266
|
-
count
|
|
347
|
+
SelfCallIvarWrites.definition_counts(@scope.discovered_deferred_ranges).fetch([owner, name], 0)
|
|
267
348
|
end
|
|
268
349
|
|
|
269
350
|
# Whether `chain`'s own order stands with no second world: no order mark and no fork. `ResolutionChain#settle`
|
|
@@ -279,9 +360,12 @@ module Rigor
|
|
|
279
360
|
end
|
|
280
361
|
|
|
281
362
|
# Whether the project rewrites `class_name`'s method table beyond the names it spells, or refines it.
|
|
363
|
+
#
|
|
364
|
+
# Issue #2137 — read through `Scope#parameter_envelopes_of`, which files the `mark:` edge: a `refine` of the
|
|
365
|
+
# class from a file whose declared classes do not change fires no class key, and adding or removing it must
|
|
366
|
+
# re-check the reader.
|
|
282
367
|
def rewritten?(class_name)
|
|
283
|
-
|
|
284
|
-
!bucket.nil? && Scope::DiscoveryIndex.rewritten_surface?(bucket)
|
|
368
|
+
Scope::DiscoveryIndex.rewritten_surface?(@scope.parameter_envelopes_of(class_name))
|
|
285
369
|
end
|
|
286
370
|
|
|
287
371
|
# The first project class or module on `class_name`'s settled instance chain that defines `name`, stopping
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../type"
|
|
4
|
+
require_relative "content_join"
|
|
5
|
+
require_relative "mutation_rejoin"
|
|
6
|
+
require_relative "rewrite_mutation"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Inference
|
|
10
|
+
# The Set half of {MutationWidening}'s seams (issue #2294), beside {StringMutation}.
|
|
11
|
+
#
|
|
12
|
+
# `f = Set["a"]; f.clear; f << 1; f.map { |v| v.fdiv(2) }` reported `undefined method 'fdiv' for String`
|
|
13
|
+
# on code that prints `[0.5]`: the mutation seams answered `Tuple`, `HashShape`, `Constant[String]` and the
|
|
14
|
+
# re-openable `Array` / `Hash` nominals, so a `Set` kept the elements it was built with through every
|
|
15
|
+
# `<<`, `add`, `merge` and `replace`.
|
|
16
|
+
#
|
|
17
|
+
# A Set has no slots and no arity, so the only thing a mutation falsifies is the element set. Both carriers
|
|
18
|
+
# this module answers end at the typed Set #2290 builds for a non-constant literal — `Set[E | Dynamic[top]]`:
|
|
19
|
+
#
|
|
20
|
+
# - a constant-folded `Constant[Set]` widens to that carrier, its members' value pins dropped
|
|
21
|
+
# ({MethodDispatcher.array_new_element}) and joined with the `Dynamic[top]` floor;
|
|
22
|
+
# - a typed `Set` whose element already carries a gradual arm re-grows the way
|
|
23
|
+
# {MutationRejoin.regrowable_carrier?} lets an `Array` / `Hash` re-grow;
|
|
24
|
+
# - a PRECISE `Set[String]` (an RBS return such as `Array#to_set`) re-grows too (issue #2361), as a precise
|
|
25
|
+
# `Array[String]` does ({MutationRejoin.widen_precise_array}): `f = ARGV.to_set; f.clear; f << 1` is
|
|
26
|
+
# `Set[Dynamic[top] | String]`.
|
|
27
|
+
#
|
|
28
|
+
# A gradual seed admits every added member as itself: the seed-admissibility gate has nothing to contradict on
|
|
29
|
+
# it. A precise seed admits only a member its element accepts ({MutationRejoin.precise_admitted}), any other
|
|
30
|
+
# taking the floor, and takes no {RewriteMutation} arm: `replace` joins its elements onto the seed, and `map!`
|
|
31
|
+
# leaves it as it was. Every join carries the `Dynamic[top]` floor, as the Array re-join's does. An adder whose
|
|
32
|
+
# arguments the block-capture seam did not type joins only that floor: the store happened, and a Set has no
|
|
33
|
+
# block content join to add it afterwards. An adder called with no arguments changes nothing.
|
|
34
|
+
module SetMutation
|
|
35
|
+
# Receiver-mutating Set methods. The non-bang siblings (`map` vs `map!`, `select` vs `select!`) and the
|
|
36
|
+
# algebra (`|`, `+`, `-`, `&`, `^`) return a new Set and stay precise.
|
|
37
|
+
MUTATORS = %i[
|
|
38
|
+
<< add add? merge replace
|
|
39
|
+
map! collect! flatten!
|
|
40
|
+
clear delete delete? delete_if keep_if select! filter! reject! subtract
|
|
41
|
+
].to_set.freeze
|
|
42
|
+
|
|
43
|
+
# The {MUTATORS} that put elements in. `<<` / `add` / `add?` store their argument; `merge` stores every
|
|
44
|
+
# argument's elements; `replace` its argument's elements, after {RewriteMutation} has dropped the old ones.
|
|
45
|
+
CONTENT_ADDERS = %i[<< add add? merge replace].to_set.freeze
|
|
46
|
+
|
|
47
|
+
module_function
|
|
48
|
+
|
|
49
|
+
def constant?(type)
|
|
50
|
+
type.is_a?(Type::Constant) && type.value.is_a?(::Set)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# True when `type` is a `Set` carrier of either kind, which {#widen} answers for alone.
|
|
54
|
+
def set?(type)
|
|
55
|
+
constant?(type) || (type.is_a?(Type::Nominal) && type.class_name == "Set")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# True when `type` is a `Set` the seams may grow: a constant-folded one, or a typed one carrying a gradual
|
|
59
|
+
# element arm.
|
|
60
|
+
def carrier?(type)
|
|
61
|
+
constant?(type) || (type.is_a?(Type::Nominal) && regrowable?(type))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# True when `type`, or a member of it, is a {#carrier?}: the only types a Set-only adder's floor can change.
|
|
65
|
+
def carrier_in?(type)
|
|
66
|
+
type.is_a?(Type::Union) ? type.members.any? { |member| carrier?(member) } : carrier?(type)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Every typed `Set[E]`, a precise one included since issue #2361.
|
|
70
|
+
def regrowable?(nominal)
|
|
71
|
+
nominal.class_name == "Set" && nominal.type_args.size == 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The carrier `type` holds after `method_name` with argument types `arg_types`, or `nil` when the call is not
|
|
75
|
+
# a Set mutator or `type` is not a carrier this seam grows. `nil` also answers a re-join that changed nothing,
|
|
76
|
+
# so a remover on an already-widened Set does not rebind it. That is a guard, not a behaviour: a Set has no
|
|
77
|
+
# `receiver[key] ||=` slot narrowings for a rebind to drop, so the identical carrier would read the same either
|
|
78
|
+
# way, and declining only saves rebuilding the scope.
|
|
79
|
+
#
|
|
80
|
+
# Issue #2555 L3 — `synthesised` says the binding carries the `:synthesised` construction fact: the engine built
|
|
81
|
+
# the Set itself (`s = Set[1]`, `Set.new(...)`), so no declaration made a claim about its elements, and the
|
|
82
|
+
# carrier stays re-growable for as long as the binding keeps the fact. Such a Set joins without the floor
|
|
83
|
+
# ({#floored}'s and {#join}'s) and without the precise seed's admissibility gate: `s = Set[1]; s << 2` reads
|
|
84
|
+
# `Set[Integer]`, and a read of its member reports what an Integer does not answer.
|
|
85
|
+
def widen(type, method_name, arg_types, unread_arguments: false, synthesised: false)
|
|
86
|
+
return nil unless MUTATORS.include?(method_name)
|
|
87
|
+
|
|
88
|
+
if constant?(type)
|
|
89
|
+
members = type.value.map { |v| Type::Combinator.constant_of(v) }
|
|
90
|
+
seed = synthesised && !members.empty? ? exact(members) : floored(members)
|
|
91
|
+
join(seed, method_name, arg_types, unread_arguments, synthesised:)
|
|
92
|
+
elsif type.is_a?(Type::Nominal) && regrowable?(type)
|
|
93
|
+
joined = join(type, method_name, arg_types, unread_arguments, synthesised:)
|
|
94
|
+
joined == type ? nil : joined
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Every join carries the `Dynamic[top]` floor, a precise seed's included (issue #2361): a later remover or
|
|
99
|
+
# rewriter (`subtract`, `delete`, `map!`) can take the stored members out again, and nothing narrows the
|
|
100
|
+
# carrier back, so a closed `Set[Integer | String]` would reject a declared `Set[String]` the program meets.
|
|
101
|
+
# A synthesised carrier (issue #2555) takes the rewrite arm a gradual one takes, admits every added member, and
|
|
102
|
+
# joins no floor: an untyped store joins `Dynamic[top]` as the one unknown member it is.
|
|
103
|
+
def join(carrier, method_name, arg_types, unread_arguments, synthesised: false)
|
|
104
|
+
seed = carrier.type_args.first
|
|
105
|
+
# A precise seed takes no rewrite arm, as a precise `Array` does not ({MutationRejoin.widen_precise_array}).
|
|
106
|
+
carrier = RewriteMutation.arm(carrier, method_name) if synthesised || MutationRejoin.gradual_parameter?(seed)
|
|
107
|
+
return carrier unless CONTENT_ADDERS.include?(method_name)
|
|
108
|
+
|
|
109
|
+
stored = added_elements(method_name, arg_types)
|
|
110
|
+
if stored.empty?
|
|
111
|
+
# An adder called with no arguments (`s.merge`) is a no-op; one whose arguments went untyped is a store.
|
|
112
|
+
return carrier unless unread_arguments
|
|
113
|
+
|
|
114
|
+
added = []
|
|
115
|
+
else
|
|
116
|
+
return carrier if !MutationRejoin.gradual_parameter?(seed) && MutationRejoin.already_held?(seed, stored)
|
|
117
|
+
|
|
118
|
+
added = admitted(seed, stored.map { |t| widened_member(t) }, synthesised:)
|
|
119
|
+
end
|
|
120
|
+
floor = synthesised && !added.empty? ? [] : [Type::Combinator.untyped]
|
|
121
|
+
element = Type::Combinator.union(carrier.type_args.first, *added, *floor)
|
|
122
|
+
Type::Combinator.nominal_of("Set", type_args: [element])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# `added` as {MutationRejoin.precise_admitted} admits it into a precise seed; a gradual or synthesised seed
|
|
126
|
+
# admits all.
|
|
127
|
+
def admitted(seed, added, synthesised: false)
|
|
128
|
+
return added if synthesised || MutationRejoin.gradual_parameter?(seed)
|
|
129
|
+
|
|
130
|
+
MutationRejoin.precise_admitted(seed, added)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The element types one content-adder call puts in, from the call's argument types.
|
|
134
|
+
def added_elements(method_name, arg_types)
|
|
135
|
+
return [] if arg_types.empty?
|
|
136
|
+
|
|
137
|
+
case method_name
|
|
138
|
+
when :merge, :replace then arg_types.flat_map { |t| element_types(t) }
|
|
139
|
+
else [arg_types.first || Type::Combinator.untyped]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# `Set[E]` over `members`, value pins dropped: a constant Set a synthesised binding holds (issue #2555).
|
|
144
|
+
def exact(members)
|
|
145
|
+
Type::Combinator.nominal_of("Set", type_args: [Type::Combinator.union(*members.map { |m| widened_member(m) })])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# `Set[E | Dynamic[top]]` over `members`, the #2290 typed-Set floor. An empty seed (`Set.new`) is
|
|
149
|
+
# `Set[Dynamic[top]]`.
|
|
150
|
+
def floored(members)
|
|
151
|
+
widened = members.map { |m| widened_member(m) }
|
|
152
|
+
Type::Combinator.nominal_of("Set", type_args: [Type::Combinator.union(*widened, Type::Combinator.untyped)])
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The element types an enumerable argument (`merge`'s, `replace`'s, the algebra's `_Each[A]`) yields: a
|
|
156
|
+
# `Tuple`'s elements, an `Array` / `Set` / `Range` nominal's element parameter, a constant Set's or Range's
|
|
157
|
+
# members. Anything else yields elements this seam cannot name, which are unknown rather than none.
|
|
158
|
+
def element_types(type)
|
|
159
|
+
case type
|
|
160
|
+
when Type::Union then type.members.flat_map { |m| element_types(m) }
|
|
161
|
+
when Type::Tuple then type.elements
|
|
162
|
+
when Type::Nominal
|
|
163
|
+
if %w[Array Set Range].include?(type.class_name) && type.type_args.size == 1
|
|
164
|
+
type.type_args
|
|
165
|
+
else
|
|
166
|
+
[Type::Combinator.untyped]
|
|
167
|
+
end
|
|
168
|
+
when Type::Constant then constant_elements(type)
|
|
169
|
+
else [Type::Combinator.untyped]
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def constant_elements(constant)
|
|
174
|
+
value = constant.value
|
|
175
|
+
if value.is_a?(::Set) then value.map { |v| Type::Combinator.constant_of(v) }
|
|
176
|
+
elsif value.is_a?(::Range) && value.begin.is_a?(Integer)
|
|
177
|
+
[Type::Combinator.nominal_of("Integer")]
|
|
178
|
+
else [Type::Combinator.untyped]
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# A stored member's value pin and literal shape are dropped, as an `Array.new` fill's are: the set holds the
|
|
183
|
+
# object, and the program keeps a reference to it.
|
|
184
|
+
def widened_member(type)
|
|
185
|
+
MethodDispatcher.array_new_element(type)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -105,8 +105,7 @@ module Rigor
|
|
|
105
105
|
node = scope.singleton_def_for(owner, method_name)
|
|
106
106
|
return nil if node.nil?
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
nesting&.first
|
|
108
|
+
scope.discovery.recorded_def_nesting(node)&.first
|
|
110
109
|
end
|
|
111
110
|
|
|
112
111
|
# Issue #1722 — the hook-capable test for one chain entry, asked by `call.undefined-method` of a project module
|