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
|
@@ -20,7 +20,6 @@ module Rigor
|
|
|
20
20
|
#
|
|
21
21
|
# Centralising both here keeps the receiver-shape and constant-unwrap knowledge in one place: if
|
|
22
22
|
# `Type::Singleton` or `Type::Constant` ever changes shape, there is a single edit rather than nine.
|
|
23
|
-
# The per-tier fold bodies stay where they are — only the shared gate moves.
|
|
24
23
|
module SingletonFolding
|
|
25
24
|
module_function
|
|
26
25
|
|
|
@@ -20,8 +20,9 @@ module Rigor
|
|
|
20
20
|
# The rule is exact rather than heuristic — the receiver must be a `Singleton[C]` whose `C` the project
|
|
21
21
|
# pre-pass recorded as including `Singleton` — so a class that does not include the mixin is untouched,
|
|
22
22
|
# and a project that defines its own `Singleton` module is the one case that would misfire. Guarded by
|
|
23
|
-
# {
|
|
24
|
-
#
|
|
23
|
+
# {own_singleton_include?}: when the included name resolves to a project-declared class or module (a
|
|
24
|
+
# top-level `Singleton`, or `M::Singleton` inside `module M`), the tier declines, because then the name
|
|
25
|
+
# means something else entirely.
|
|
25
26
|
module SingletonMixinDispatch
|
|
26
27
|
MIXIN_NAMES = Ractor.make_shareable(Set["Singleton", "::Singleton"])
|
|
27
28
|
private_constant :MIXIN_NAMES
|
|
@@ -31,30 +32,62 @@ module Rigor
|
|
|
31
32
|
|
|
32
33
|
module_function
|
|
33
34
|
|
|
34
|
-
# @return `
|
|
35
|
+
# @return the instance type of `C` for `C.instance`, or nil to decline.
|
|
35
36
|
def try_dispatch(context)
|
|
36
|
-
return nil unless context.method_name == SELECTOR
|
|
37
|
-
return nil unless context.args.empty?
|
|
38
|
-
|
|
39
37
|
receiver = context.receiver
|
|
40
|
-
return nil unless receiver.
|
|
41
|
-
|
|
42
|
-
scope = context.scope
|
|
43
|
-
return nil if scope.nil?
|
|
44
|
-
return nil unless includes_singleton_mixin?(scope, receiver.class_name)
|
|
45
|
-
return nil if project_singleton_shadow?(scope)
|
|
38
|
+
return nil unless answers?(context.scope, receiver, context.method_name, context.args.empty?)
|
|
46
39
|
|
|
47
40
|
Type::Combinator.nominal_of(receiver.class_name)
|
|
48
41
|
end
|
|
49
42
|
|
|
43
|
+
# Issue #2438 — the one predicate behind {try_dispatch} and `CheckRules#undefined_method_diagnostic`, so the
|
|
44
|
+
# typer and the `call.undefined-method` rule cannot disagree about `C.instance`: a call this tier types is
|
|
45
|
+
# never reported as undefined, whatever a project sidecar of `C` lists.
|
|
46
|
+
def answers?(scope, receiver, method_name, no_arguments)
|
|
47
|
+
return false unless method_name == SELECTOR && no_arguments
|
|
48
|
+
return false unless receiver.is_a?(Type::Singleton)
|
|
49
|
+
return false if scope.nil?
|
|
50
|
+
|
|
51
|
+
includes_singleton_mixin?(scope, receiver.class_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# True when `class_name`'s instance ancestry carries the stdlib `Singleton` as an `include` written in the
|
|
55
|
+
# class itself or in a project class up its superclass chain (Ruby's `Singleton.included` also installs an
|
|
56
|
+
# `inherited` hook, so `class D < C` answers `instance` with its own instance). Read off
|
|
57
|
+
# `Scope::ResolutionChain` (the `:constants` flavor, so an empty project `module Singleton` is a project
|
|
58
|
+
# entry), where the stdlib module is an external entry. Issue #2438 — what is not the mixin:
|
|
59
|
+
#
|
|
60
|
+
# - `prepend Singleton`, which runs `prepend_features` instead of the `included` hook and so defines no
|
|
61
|
+
# `instance`; a prepended entry sits ahead of its owner on the chain;
|
|
62
|
+
# - an `include Singleton` in a MODULE the class includes: `Singleton.included` raises `TypeError` for a
|
|
63
|
+
# module, so that code never runs;
|
|
64
|
+
# - a top-level project `Singleton`, which both `Singleton` and `::Singleton` name.
|
|
65
|
+
#
|
|
66
|
+
# A NESTED project `N::Singleton` the name resolved to is read as the stdlib mixin: the discovery tables
|
|
67
|
+
# drop the leading `::` of a mixin argument, so `include ::Singleton` and `include Singleton` inside
|
|
68
|
+
# `module N` record the same name, and the stdlib reading is the one that types the rooted spelling and
|
|
69
|
+
# never reports a correct call.
|
|
50
70
|
def includes_singleton_mixin?(scope, class_name)
|
|
51
|
-
|
|
71
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s.delete_prefix("::"), :instance, :constants)
|
|
72
|
+
chain.record(scope)
|
|
73
|
+
classes = chain.level_classes.compact
|
|
74
|
+
chain.entries.each_with_index.any? { |entry, index| singleton_include?(chain, classes, entry, index) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def singleton_include?(chain, classes, entry, index)
|
|
78
|
+
if entry.external?
|
|
79
|
+
!entry.superclass_edge && MIXIN_NAMES.include?(entry.raw.to_s) && classes.include?(entry.owner) &&
|
|
80
|
+
index > chain.index_of(entry.owner)
|
|
81
|
+
else
|
|
82
|
+
entry.name.end_with?("::Singleton") && included_by_level_class?(chain, index)
|
|
83
|
+
end
|
|
52
84
|
end
|
|
53
85
|
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
86
|
+
# The entry sits after its level's class, so it was included into that level rather than prepended.
|
|
87
|
+
def included_by_level_class?(chain, index)
|
|
88
|
+
level = chain.level_starts.rindex { |start| start <= index }
|
|
89
|
+
klass = level && chain.level_classes[level]
|
|
90
|
+
!klass.nil? && index > chain.index_of(klass)
|
|
58
91
|
end
|
|
59
92
|
end
|
|
60
93
|
end
|
|
@@ -14,8 +14,8 @@ module Rigor
|
|
|
14
14
|
#
|
|
15
15
|
# A `Struct` instance is mutable (`s.x = v`, `s[:x] = v`, escape), so a member map bound to a
|
|
16
16
|
# variable can be invalidated by a later write. A member read folds off a **fresh** instance (the
|
|
17
|
-
# transient receiver of a `.new(...).x`
|
|
18
|
-
#
|
|
17
|
+
# transient receiver of a `.new(...).x` chain, unmutatable between materialisation and the read —
|
|
18
|
+
# slices 1+2), off a **fold-safe stored local** the {Inference::StructFoldSafety} scan
|
|
19
19
|
# proved is never mutated / aliased / escaped (slice 3), or off a **setter-mutated fold-safe local**
|
|
20
20
|
# whose bindings {#apply_setter_writeback} keeps current (slice 4). Any other stored receiver degrades
|
|
21
21
|
# to `Dynamic[top]` rather than fold a possibly-stale value.
|
|
@@ -255,8 +255,8 @@ module Rigor
|
|
|
255
255
|
end
|
|
256
256
|
|
|
257
257
|
# A member read is foldable when the receiver is either FRESH (the transient result of a
|
|
258
|
-
# `.new(...)
|
|
259
|
-
#
|
|
258
|
+
# `.new(...)` chain, which cannot have been mutated between materialisation and this read) or a
|
|
259
|
+
# FOLD-SAFE stored local (ADR-48 slice 3 — `StructFoldSafety` proved the binding is
|
|
260
260
|
# never mutated / aliased / escaped in its scope).
|
|
261
261
|
def foldable_receiver?(context)
|
|
262
262
|
fresh_receiver?(context) || fold_safe_local_receiver?(context) ||
|
|
@@ -308,7 +308,7 @@ module Rigor
|
|
|
308
308
|
end
|
|
309
309
|
|
|
310
310
|
# A fresh receiver is the result of a chained call that MATERIALISED the struct — `Point.new(1, 2).x`,
|
|
311
|
-
# `Point[1, 2].x
|
|
311
|
+
# `Point[1, 2].x`. Not merely a chained call: slices 1-2 read "chained" as
|
|
312
312
|
# "cannot have been mutated between materialisation and the read", and a method that hands back its
|
|
313
313
|
# own receiver falsifies that (issue #595):
|
|
314
314
|
#
|
|
@@ -31,12 +31,10 @@ module Rigor
|
|
|
31
31
|
# nodes; the bound exists so a pathological body cannot turn a per-call-site gate into a walk.
|
|
32
32
|
FACTORY_BODY_SCAN_BUDGET = 200
|
|
33
33
|
|
|
34
|
-
# Issue #595 / #525 — the ONE materialisation test
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# one answer. A `.with` call is never a materialisation: the composed `x.dup_self.with(indent: 9)`
|
|
39
|
-
# shape once slipped through as one, and `Struct#with` no longer exists to fold (#1335).
|
|
34
|
+
# Issue #595 / #525 — the ONE materialisation test, shared by {StructFolding#fresh_receiver?} and
|
|
35
|
+
# `ExpressionTyper`'s `:self`-grant arm so the two gates cannot fork. A `.with` call is never a
|
|
36
|
+
# materialisation: the composed `x.dup_self.with(indent: 9)` shape once slipped through as one, and
|
|
37
|
+
# `Struct#with` no longer exists (#1335).
|
|
40
38
|
#
|
|
41
39
|
# @param node — the receiver EXPRESSION.
|
|
42
40
|
# @param scope — the scope the factory and ancestor lookups resolve against.
|
|
@@ -55,7 +55,7 @@ module Rigor
|
|
|
55
55
|
|
|
56
56
|
# A form long enough to suggest generated or accumulated data rather than a literal a reader wrote;
|
|
57
57
|
# past it the folded `Constant[String]` stops being useful in a diagnostic and only costs memory.
|
|
58
|
-
#
|
|
58
|
+
# Counts pairs where `ShapeDispatch`'s `tuple_join` fold caps bytes, for the same reason.
|
|
59
59
|
FORM_PAIR_LIMIT = 64
|
|
60
60
|
private_constant :FORM_PAIR_LIMIT
|
|
61
61
|
|
|
@@ -41,6 +41,7 @@ require_relative "method_dispatcher/universal_object_dispatch"
|
|
|
41
41
|
require_relative "method_dispatcher/singleton_mixin_dispatch"
|
|
42
42
|
require_relative "method_dispatcher/method_folding"
|
|
43
43
|
require_relative "method_dispatcher/optional_count"
|
|
44
|
+
require_relative "../run_scoped_slots"
|
|
44
45
|
|
|
45
46
|
module Rigor
|
|
46
47
|
module Inference
|
|
@@ -204,8 +205,8 @@ module Rigor
|
|
|
204
205
|
# user-authored RBS overrides substrate synthesis) and ABOVE the dependency-source
|
|
205
206
|
# inference tier so a plugin's declared emit table beats the generic gem-source fallback
|
|
206
207
|
# for the same class. Slice 6a-TierB (origin_module dispatch) lands precise return types
|
|
207
|
-
# for Tier B emissions; Tier C emissions
|
|
208
|
-
#
|
|
208
|
+
# for Tier B emissions; Tier C emissions return their manifest `return_type` when the environment
|
|
209
|
+
# names that class (slice 6b), and `Dynamic[T]` otherwise.
|
|
209
210
|
synthetic_result = try_synthetic_method(
|
|
210
211
|
receiver_type, method_name, arg_types, block_type, environment
|
|
211
212
|
)
|
|
@@ -535,10 +536,9 @@ module Rigor
|
|
|
535
536
|
# The question is asked HERE, of the same two tiers `resolve` consults and in the same order, rather
|
|
536
537
|
# than reconstructed in the veto: a name this reports as answered is a name the dispatch really would
|
|
537
538
|
# resolve, and no plugin knowledge moves into the engine's veto. The tiers are the gated
|
|
538
|
-
# `dynamic_return` walk and the ADR-16 synthetic-method index (Tier C `heredoc_templates` emissions
|
|
539
|
-
#
|
|
540
|
-
#
|
|
541
|
-
# this predicate in production yet — it will when #476 lands, with no change here).
|
|
539
|
+
# `dynamic_return` walk and the ADR-16 synthetic-method index (Tier C `heredoc_templates` emissions and
|
|
540
|
+
# the Tier B `Plugin::Macro::TraitRegistry` explosions, which share that table; since #476 the pre-pass
|
|
541
|
+
# explodes a trait module whenever the project's RBS declares it).
|
|
542
542
|
#
|
|
543
543
|
# It is a predicate, not a type: the veto's job is to stop the top-level bind, after which the call
|
|
544
544
|
# falls through to the ordinary dispatch chain and the tier that answered here answers there too. The
|
|
@@ -916,8 +916,7 @@ module Rigor
|
|
|
916
916
|
result || EMPTY_CONTRIBUTIONS
|
|
917
917
|
end
|
|
918
918
|
|
|
919
|
-
# Runs the precision tiers
|
|
920
|
-
# order and returns the first non-nil answer. Each tier owns its own receiver/argument
|
|
919
|
+
# Runs the precision tiers in order and returns the first non-nil answer. Each tier owns its own receiver/argument
|
|
921
920
|
# shape checks; a tier that does not recognise the receiver returns nil so the next tier
|
|
922
921
|
# can try. The RBS tier sits below this chain and is invoked by the outer `dispatch`
|
|
923
922
|
# method.
|
|
@@ -926,7 +925,7 @@ module Rigor
|
|
|
926
925
|
# (`try_dispatch(CallContext) -> Type?`). Order is significant: ConstantFolding's
|
|
927
926
|
# exact-value folds win first, the stdlib singleton folders sit in the middle (each
|
|
928
927
|
# gates on a distinct `Singleton` receiver, so their relative order is immaterial), and
|
|
929
|
-
# BlockFolding runs
|
|
928
|
+
# BlockFolding runs near the end because its rules only apply to block-taking calls — the cheaper
|
|
930
929
|
# arity folds above it filter the common cases first. Adding a precise tier is a one-line
|
|
931
930
|
# append here rather than another link in a hand-written `||` ladder.
|
|
932
931
|
PRECISE_TIERS_HEAD = Ractor.make_shareable([
|
|
@@ -1031,6 +1030,8 @@ module Rigor
|
|
|
1031
1030
|
|
|
1032
1031
|
fallback_receiver = user_class_fallback_receiver(receiver_type, environment)
|
|
1033
1032
|
return nil if fallback_receiver.nil?
|
|
1033
|
+
return nil if declared_by_external_ancestor?(receiver_type, environment, context)
|
|
1034
|
+
return nil if project_singleton_definer?(receiver_type, context)
|
|
1034
1035
|
|
|
1035
1036
|
# Preserve the ORIGINAL receiver type as the `self` substitution so `Kernel#dup: () ->
|
|
1036
1037
|
# self` and other `self`-returning methods route through Object's RBS while still
|
|
@@ -1055,6 +1056,92 @@ module Rigor
|
|
|
1055
1056
|
)
|
|
1056
1057
|
end
|
|
1057
1058
|
|
|
1059
|
+
# Issue #2196 — a project class whose ancestry reaches a core or stdlib class before `Object` (`class FL <
|
|
1060
|
+
# Array`) answers a name that class declares from that class, not from `Object` / `Kernel`: Ruby dispatches
|
|
1061
|
+
# `select { … }` on an `FL` to `Array#select`, never to the private `Kernel#select` (`-> Array[String]`). The
|
|
1062
|
+
# ancestor tiers decline such a name where they cannot type it (`RbsDispatch#returns_the_walked_ancestry?`),
|
|
1063
|
+
# and the `Object` fallback then fabricated `Kernel#select`'s answer, so `fields.detect { |f| f.errors }` read
|
|
1064
|
+
# `f` as `String`. The fallback declines the name instead, which leaves the call `Dynamic[top]`.
|
|
1065
|
+
# {ExternalAncestorResolution.resolve} answers nil for a name only `Object`, `Kernel` or `BasicObject` declares,
|
|
1066
|
+
# so `puts` and `raise` keep resolving.
|
|
1067
|
+
def declared_by_external_ancestor?(receiver_type, environment, context)
|
|
1068
|
+
return false unless receiver_type.is_a?(Type::Nominal)
|
|
1069
|
+
|
|
1070
|
+
scope = context.scope
|
|
1071
|
+
return false if scope.nil?
|
|
1072
|
+
|
|
1073
|
+
memo = external_ancestor_memo(scope, environment)
|
|
1074
|
+
key = [receiver_type.class_name, context.method_name]
|
|
1075
|
+
return memo[key] if memo.key?(key)
|
|
1076
|
+
|
|
1077
|
+
memo[key] = !ExternalAncestorResolution.resolve(receiver_type.class_name, context.method_name, :instance,
|
|
1078
|
+
scope: scope, environment: environment,
|
|
1079
|
+
record_dependencies: false).nil?
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
# Issue #2284 — a class object whose singleton chain holds a project definer of the name (its own `def
|
|
1083
|
+
# self.name`, one a project superclass wrote, an `extend`ed module's `def name`) answers from that definer,
|
|
1084
|
+
# not from `Module#name` / `Class#…` on the `Singleton[Object]` fallback: Ruby's singleton ancestry puts
|
|
1085
|
+
# every project entry ahead of `Class`. The fallback declines so the typer's singleton-body inference
|
|
1086
|
+
# answers the definer's return, or `Dynamic` where the slot is contested and the def may not be the one
|
|
1087
|
+
# that runs.
|
|
1088
|
+
#
|
|
1089
|
+
# The chain walk files the class edge of every entry it passes, so it runs only for a name some project file
|
|
1090
|
+
# defines at all ({BlockCallTiming.project_defines_anywhere?}, which files the `defines:<name>` edge either way,
|
|
1091
|
+
# #1459). Without it every class-body `private` / `attr_reader` call on a project class would depend on its
|
|
1092
|
+
# whole ancestry. The by-name answer is memoised per discovery index; the edge is filed on every call.
|
|
1093
|
+
def project_singleton_definer?(receiver_type, context)
|
|
1094
|
+
return false unless receiver_type.is_a?(Type::Singleton)
|
|
1095
|
+
|
|
1096
|
+
scope = context.scope
|
|
1097
|
+
return false if scope.nil?
|
|
1098
|
+
return false unless project_defines_name?(scope, context.method_name)
|
|
1099
|
+
|
|
1100
|
+
!scope.singleton_def_through_ancestors(receiver_type.class_name, context.method_name).last.nil?
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def project_defines_name?(scope, method_name)
|
|
1104
|
+
memo = defines_name_memo(scope)
|
|
1105
|
+
if memo.key?(method_name)
|
|
1106
|
+
Analysis::DependencyRecorder.read_missing(:defines, method_name) if Analysis::DependencyRecorder.active?
|
|
1107
|
+
return memo[method_name]
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1110
|
+
memo[method_name] = BlockCallTiming.project_defines_anywhere?(method_name, scope)
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
DEFINES_NAME_MEMO_KEY = RunScopedSlots.register(:__rigor_user_class_fallback_defines_name__)
|
|
1114
|
+
private_constant :DEFINES_NAME_MEMO_KEY
|
|
1115
|
+
|
|
1116
|
+
# One slot per discovery index and environment, as {#external_ancestor_memo} keeps its own: the answer reads
|
|
1117
|
+
# only the frozen discovery tables and the environment's `pre_eval:` patches.
|
|
1118
|
+
def defines_name_memo(scope)
|
|
1119
|
+
discovery = scope.discovery
|
|
1120
|
+
environment = scope.environment
|
|
1121
|
+
slot = Thread.current[DEFINES_NAME_MEMO_KEY]
|
|
1122
|
+
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
|
|
1123
|
+
slot = [discovery, environment, {}]
|
|
1124
|
+
Thread.current[DEFINES_NAME_MEMO_KEY] = slot
|
|
1125
|
+
end
|
|
1126
|
+
slot[2]
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
EXTERNAL_ANCESTOR_MEMO_KEY = RunScopedSlots.register(:__rigor_user_class_fallback_external_ancestor__)
|
|
1130
|
+
private_constant :EXTERNAL_ANCESTOR_MEMO_KEY
|
|
1131
|
+
|
|
1132
|
+
# One slot per discovery index and environment, as `RbsDispatch#ancestor_memo_slot` keeps its own: the answer
|
|
1133
|
+
# reads only the frozen discovery tables and the RBS environment, and its walk is withheld from dependency
|
|
1134
|
+
# recording, so it may be served again for the same pair.
|
|
1135
|
+
def external_ancestor_memo(scope, environment)
|
|
1136
|
+
discovery = scope.discovery
|
|
1137
|
+
slot = Thread.current[EXTERNAL_ANCESTOR_MEMO_KEY]
|
|
1138
|
+
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
|
|
1139
|
+
slot = [discovery, environment, {}]
|
|
1140
|
+
Thread.current[EXTERNAL_ANCESTOR_MEMO_KEY] = slot
|
|
1141
|
+
end
|
|
1142
|
+
slot[2]
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1058
1145
|
# True when the call node carries an explicit receiver that is not the literal `self`.
|
|
1059
1146
|
# Such a call cannot legally dispatch to a private method, so the user-class fallback must
|
|
1060
1147
|
# skip private signatures rather than return a confidently-wrong type. Returns false for
|
|
@@ -1099,12 +1186,14 @@ module Rigor
|
|
|
1099
1186
|
#
|
|
1100
1187
|
# `RbsDispatch` already distributes over a `Union` (and erases a `Difference` to its base, #533),
|
|
1101
1188
|
# but it does so INSIDE one tier, so a member that needs a LOWER tier — a user class with no RBS,
|
|
1102
|
-
# a discovered `def`, an opt-in gem — makes the whole union decline. That
|
|
1103
|
-
# rigor-actionpack
|
|
1104
|
-
#
|
|
1105
|
-
# `#to_s` read opaque on every Rails app. Measured by toggling this file alone: the
|
|
1106
|
-
# census
|
|
1107
|
-
# diagnostics on both arms across eight survey targets.
|
|
1189
|
+
# a discovered `def`, an opt-in gem — makes the whole union decline. That was the corpus shape before
|
|
1190
|
+
# #2430: rigor-actionpack typed `params[:k]` as the bare `ActionController::Parameters | nil` union, the
|
|
1191
|
+
# `Parameters` arm resolved only through the user-class fallback, and so `params[:k].present?` / `#==`
|
|
1192
|
+
# / `#blank?` / `#to_s` read opaque on every Rails app. Measured by toggling this file alone: the
|
|
1193
|
+
# named-pair census drained 137 sites on redmine and 56 on mastodon, and `rigor check` reported the
|
|
1194
|
+
# same diagnostics on both arms across eight survey targets. The plugin now answers a `Dynamic`
|
|
1195
|
+
# carrier, which this projection does not reach, so those off-chain calls read `Dynamic[top]` until
|
|
1196
|
+
# the carrier is projected here too (#2448).
|
|
1108
1197
|
#
|
|
1109
1198
|
# **Why the bottom and not next to `RbsDispatch`.** Sitting here, the tier can only ever replace a
|
|
1110
1199
|
# `Dynamic[top]` the typer was about to produce — the same ADR-5 argument {UniversalObjectDispatch}
|
|
@@ -1257,7 +1346,7 @@ module Rigor
|
|
|
1257
1346
|
range_lift = range_new_lift(receiver_type.class_name, arg_types)
|
|
1258
1347
|
return range_lift if range_lift
|
|
1259
1348
|
|
|
1260
|
-
set_lift = set_new_lift(receiver_type.class_name, arg_types)
|
|
1349
|
+
set_lift = set_new_lift(receiver_type.class_name, arg_types, context)
|
|
1261
1350
|
return set_lift if set_lift
|
|
1262
1351
|
|
|
1263
1352
|
hash_lift = hash_new_lift(receiver_type.class_name, arg_types, context&.call_node)
|
|
@@ -1307,8 +1396,8 @@ module Rigor
|
|
|
1307
1396
|
# class is representable as the parent's singleton type — its singleton-method surface is
|
|
1308
1397
|
# the parent's (plus whatever the block defines, which we do not statically track here),
|
|
1309
1398
|
# so `Singleton[Parent]` lets downstream `klass.some_class_method` resolve. No parent →
|
|
1310
|
-
# `singleton(Object)
|
|
1311
|
-
# back to `Nominal[Class]` via the surrounding `meta_new` tail.
|
|
1399
|
+
# `singleton(Object)` (or the block's anonymous class, #319 below). Anything else (dynamic parent, more
|
|
1400
|
+
# than one positional, …) falls back to `Nominal[Class]` via the surrounding `meta_new` tail.
|
|
1312
1401
|
# #319 — a `Class.new do ... end` with no parent is NOT `Object`: the block body is a class body, so the
|
|
1313
1402
|
# class owns whatever that body defines. Answering `Singleton[Object]` handed the call site `Object`'s
|
|
1314
1403
|
# zero-arity `new` and `Object`'s method surface, so an `initialize(bucket)` written right there produced
|
|
@@ -1613,19 +1702,10 @@ module Rigor
|
|
|
1613
1702
|
# are given or the single argument is a `Tuple` whose every element is a `Constant[T]`.
|
|
1614
1703
|
# Mirrors `SetFolding#fold_new` but lives here so the `:new` path in
|
|
1615
1704
|
# `try_meta_introspection` can reach it before the RBS tier answers `Nominal[Set]`.
|
|
1616
|
-
def set_new_lift(class_name, arg_types)
|
|
1705
|
+
def set_new_lift(class_name, arg_types, context = nil)
|
|
1617
1706
|
return nil unless class_name == "Set"
|
|
1618
|
-
return Type::Combinator.constant_of(::Set.new) if arg_types.empty?
|
|
1619
|
-
return nil if arg_types.size > 1
|
|
1620
|
-
|
|
1621
|
-
arg = arg_types.first
|
|
1622
|
-
return nil unless arg.is_a?(Type::Tuple)
|
|
1623
|
-
return nil unless arg.elements.all?(Type::Constant)
|
|
1624
1707
|
|
|
1625
|
-
|
|
1626
|
-
Type::Combinator.constant_of(::Set.new(values))
|
|
1627
|
-
rescue StandardError
|
|
1628
|
-
nil
|
|
1708
|
+
SetFolding.fold_new(arg_types, context&.call_node, context&.block_type)
|
|
1629
1709
|
end
|
|
1630
1710
|
|
|
1631
1711
|
# `Regexp.new(pattern)` / `Regexp.new(pattern, opts)` — folds to `Constant[Regexp]` when
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "../../source/literals"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module ModuleFunctionState
|
|
9
|
+
# Issue #2209 — the `module_function` spellings other than the receiverless call that the deferred-ranges
|
|
10
|
+
# prescan ({ModuleFunctionState.prescan_deferred}) reads: `self.module_function(...)`, and
|
|
11
|
+
# `send(:module_function, ...)` or `__send__(:module_function, ...)` on no receiver or `self`, which Ruby runs
|
|
12
|
+
# as the receiverless call. `public_send` raises on a private method such as `module_function`, so it is not
|
|
13
|
+
# one.
|
|
14
|
+
#
|
|
15
|
+
# A literal name gives the `:singleton` row and a `def` argument the `:both` or `:singleton` row a receiverless
|
|
16
|
+
# call gives. The bare form, and an argument that is neither (`*names`, a local), gives an {ANY_ROW_NAME} row
|
|
17
|
+
# over the call: the prescan reads neither the toggle these spellings set nor such a name, so every singleton
|
|
18
|
+
# copy of the owner is uncertain (`DefinerResolution.module_function_row?`).
|
|
19
|
+
module Spellings
|
|
20
|
+
SEND_CALLS = %i[send __send__].freeze
|
|
21
|
+
private_constant :SEND_CALLS
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# The arguments the spelling `node` passes `module_function`, or nil for any other call.
|
|
26
|
+
def arguments(node)
|
|
27
|
+
arguments = node.arguments&.arguments || []
|
|
28
|
+
return arguments if node.name == :module_function && node.receiver.is_a?(Prism::SelfNode)
|
|
29
|
+
return nil unless SEND_CALLS.include?(node.name)
|
|
30
|
+
return nil unless node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)
|
|
31
|
+
return nil unless Source::Literals.symbol_or_string(arguments.first) == :module_function
|
|
32
|
+
|
|
33
|
+
arguments.drop(1)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Appends the rows of the spelling `node` with `arguments` ({.arguments}) to `ranges`, and prescans the rest
|
|
37
|
+
# as {ModuleFunctionState.prescan_deferred} prescans a receiverless call's.
|
|
38
|
+
def prescan(node, arguments, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
39
|
+
owner = qualified_prefix.empty? ? nil : qualified_prefix.join("::")
|
|
40
|
+
call_span = [node.location.start_offset, node.location.end_offset]
|
|
41
|
+
any = arguments.empty?
|
|
42
|
+
arguments.each do |arg|
|
|
43
|
+
if arg.is_a?(Prism::DefNode)
|
|
44
|
+
kind = ScopeIndexer.def_singleton?(arg, qualified_prefix, in_singleton_class) ? :singleton : :both
|
|
45
|
+
ranges << [arg.location.start_offset, arg.location.end_offset, arg.name, kind, owner]
|
|
46
|
+
elsif (name = Source::Literals.symbol_or_string(arg))
|
|
47
|
+
ranges << [*call_span, name, :singleton, owner]
|
|
48
|
+
else
|
|
49
|
+
any = true
|
|
50
|
+
nested_prescan(arg, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
ranges << [*call_span, ANY_ROW_NAME, :singleton, owner] if any
|
|
54
|
+
nested_prescan(node.block, qualified_prefix, in_singleton_class, offsets, ranges) if node.block
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def nested_prescan(node, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
58
|
+
ModuleFunctionState.prescan_deferred(node, qualified_prefix, in_singleton_class, offsets, ranges,
|
|
59
|
+
nested: true)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -76,6 +76,17 @@ module Rigor
|
|
|
76
76
|
(node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode))
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
# The `def` nodes a receiverless `public`, `private` or `protected` call takes as arguments (`private def x`,
|
|
80
|
+
# #2254). Ruby evaluates each one before the call, so it runs whenever the call's statement does. A named call
|
|
81
|
+
# with a symbol takes no def, and a call with a receiver is not read. A `module_function def` is not read here:
|
|
82
|
+
# its singleton copy is not modelled, so that gap stays as it was.
|
|
83
|
+
def visibility_call_defs(node)
|
|
84
|
+
return [] unless node.is_a?(Prism::CallNode) && node.receiver.nil?
|
|
85
|
+
return [] unless %i[public private protected].include?(node.name)
|
|
86
|
+
|
|
87
|
+
node.arguments&.arguments&.grep(Prism::DefNode) || []
|
|
88
|
+
end
|
|
89
|
+
|
|
79
90
|
# The mode after `stmt`, a direct statement of a body, for the readers that walk the statements: `mode`
|
|
80
91
|
# is `true`, `false` or `:uncertain` before it. A {.visibility_reset?} the statement runs ends the mode,
|
|
81
92
|
# and one it may not run (`:maybe_off`, below {.prescan_deferred}) makes an on mode uncertain. A
|
|
@@ -318,10 +329,20 @@ module Rigor
|
|
|
318
329
|
end
|
|
319
330
|
end
|
|
320
331
|
|
|
332
|
+
# Issue #2209 — the row name of a deferred-ranges row that stands for every name of its owner
|
|
333
|
+
# ({Spellings}).
|
|
334
|
+
ANY_ROW_NAME = :*
|
|
335
|
+
|
|
336
|
+
# Issue #2209 — the deferred-ranges row names that make `method_name` a module function on their owner: its
|
|
337
|
+
# own and {ANY_ROW_NAME}.
|
|
338
|
+
def row_names(method_name) = [method_name.to_sym, ANY_ROW_NAME]
|
|
339
|
+
|
|
321
340
|
# The prescan's arms for a call; true when the arm took the call and its subtree.
|
|
322
341
|
def prescanned_call?(node, qualified_prefix, in_singleton_class, offsets, ranges, nested)
|
|
323
342
|
if call?(node)
|
|
324
343
|
prescan_call(node, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
344
|
+
elsif (arguments = Spellings.arguments(node))
|
|
345
|
+
Spellings.prescan(node, arguments, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
325
346
|
elsif visibility_reset?(node)
|
|
326
347
|
offsets << [node.location.start_offset, nested ? :maybe_off : :off]
|
|
327
348
|
elsif ScopeIndexer.receiver_eval_call?(node) || (node.block && ScopeIndexer.meta_new_constant_rvalue?(node))
|
|
@@ -332,7 +353,6 @@ module Rigor
|
|
|
332
353
|
true
|
|
333
354
|
end
|
|
334
355
|
|
|
335
|
-
# The prescan's call arm.
|
|
336
356
|
def prescan_call(node, qualified_prefix, in_singleton_class, offsets, ranges)
|
|
337
357
|
if bare?(node)
|
|
338
358
|
offsets << [node.location.start_offset, :on]
|
|
@@ -374,3 +394,5 @@ module Rigor
|
|
|
374
394
|
end
|
|
375
395
|
end
|
|
376
396
|
end
|
|
397
|
+
|
|
398
|
+
require_relative "module_function_state/spellings"
|
|
@@ -12,7 +12,7 @@ module Rigor
|
|
|
12
12
|
#
|
|
13
13
|
# `Rigor::Inference::MultiTargetBinder` decomposes a tuple- or array-shaped right-hand side type
|
|
14
14
|
# against a Prism multi-target tree and produces a `name -> Rigor::Type` binding map. The
|
|
15
|
-
# binder is shared between
|
|
15
|
+
# binder is shared between five surfaces:
|
|
16
16
|
#
|
|
17
17
|
# 1. `Rigor::Inference::StatementEvaluator#eval_multi_write` for the statement-level `a, b =
|
|
18
18
|
# rhs` form (`Prism::MultiWriteNode`).
|