rigortype 0.3.8 → 0.4.0
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/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -4,6 +4,8 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../../type"
|
|
6
6
|
require_relative "../../source/constant_path"
|
|
7
|
+
require_relative "../../source/node_children"
|
|
8
|
+
require_relative "../struct_fold_safety"
|
|
7
9
|
|
|
8
10
|
module Rigor
|
|
9
11
|
module Inference
|
|
@@ -25,6 +27,10 @@ module Rigor
|
|
|
25
27
|
module StructMaterialization
|
|
26
28
|
module_function
|
|
27
29
|
|
|
30
|
+
# Nodes one factory-body scan may visit before it gives up and refuses. A factory is a handful of
|
|
31
|
+
# nodes; the bound exists so a pathological body cannot turn a per-call-site gate into a walk.
|
|
32
|
+
FACTORY_BODY_SCAN_BUDGET = 200
|
|
33
|
+
|
|
28
34
|
# Issue #595 / #525 — the ONE materialisation test. Both places that must answer "was this receiver
|
|
29
35
|
# expression's struct newly built?" go through it: this module's direct member-read gate above, and
|
|
30
36
|
# `ExpressionTyper`'s caller-side `:self`-grant arm. They had forked answers once (the grant arm was
|
|
@@ -32,20 +38,95 @@ module Rigor
|
|
|
32
38
|
# composed `x.dup_self.with(indent: 9).shout` shape survived — a correctly whitelisted `.with` fold
|
|
33
39
|
# firing off a receiver the other gate should never have called fresh.
|
|
34
40
|
#
|
|
35
|
-
#
|
|
36
|
-
# @param
|
|
37
|
-
# @param receiver [Rigor::Type, nil] the carrier the expression produced.
|
|
38
|
-
# @param scope [Rigor::Scope, nil]
|
|
41
|
+
# @param node — the receiver EXPRESSION.
|
|
42
|
+
# @param receiver — the carrier the expression produced.
|
|
39
43
|
def materialization_call?(node, receiver, scope)
|
|
40
44
|
return false unless node.is_a?(Prism::CallNode)
|
|
41
45
|
|
|
42
46
|
case node.name
|
|
43
47
|
when :new, :[] then struct_class_expression?(node.receiver, scope)
|
|
44
48
|
when :with then !hand_written_with?(receiver, scope)
|
|
49
|
+
else fresh_factory_call?(node, scope)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Issue #599 — the FACTORY-METHOD idiom, the one shape #595's whitelist knowingly paid for as a
|
|
54
|
+
# missed error: `def build = Pair.new("hi", [1, 2])` then `build.items`. The whitelist could not see
|
|
55
|
+
# through `build`, so the chain declined and a typo on it went unreported.
|
|
56
|
+
#
|
|
57
|
+
# It is recovered by asking the callee, under two bounds that keep the answer cheap and fail-closed.
|
|
58
|
+
#
|
|
59
|
+
# 1. RESOLUTION is one table read on a shape whose target cannot be an arbitrary object: a
|
|
60
|
+
# receiverless send resolved through the confidence-gated top-level def table, or `Const.name`
|
|
61
|
+
# resolved through the singleton-side ancestor walk. An INSTANCE-side receiver is refused
|
|
62
|
+
# outright — `x.dup_self` is the #595 bug shape, and what `x` holds at the call is exactly what
|
|
63
|
+
# this gate has no way to know.
|
|
64
|
+
# 2. ACCEPTANCE is decided on the callee's RETURN POSITION, not on a self-alias scan of its body.
|
|
65
|
+
# "Cannot return `self`" is not the property freshness needs: the factory's `self` is the module
|
|
66
|
+
# or `main`, never the struct, while `def get = GLOBAL` over a mutated constant returns a
|
|
67
|
+
# long-lived instance with no `self` anywhere in it. What the gate needs is that the returned
|
|
68
|
+
# object was built BY THIS CALL, so the body's tail must itself be a materialisation
|
|
69
|
+
# ({#fresh_materialization_tail?}) and no `return` may hand back anything else. That subsumes the
|
|
70
|
+
# self-alias refusal — a body returning `self` has a `SelfNode` tail, not a `.new` — while
|
|
71
|
+
# admitting the idiom the issue is about.
|
|
72
|
+
def fresh_factory_call?(node, scope)
|
|
73
|
+
return false if scope.nil?
|
|
74
|
+
|
|
75
|
+
body = factory_def_body(node, scope)
|
|
76
|
+
return false unless body.is_a?(Prism::StatementsNode)
|
|
77
|
+
|
|
78
|
+
fresh_materialization_tail?(body.body.last, scope) && !early_return?(body)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The two CHEAPLY RESOLVABLE callee shapes, each a single table read against the frozen discovery
|
|
82
|
+
# index. `bindable_top_level_def_for` is the confidence-gated accessor — the only one the inference
|
|
83
|
+
# may bind through — so a call inside a block whose `self` is unmodelled declines here as it does
|
|
84
|
+
# everywhere else. Anything else, an instance-side receiver above all, resolves to nil.
|
|
85
|
+
def factory_def_body(node, scope)
|
|
86
|
+
def_node =
|
|
87
|
+
case node.receiver
|
|
88
|
+
when nil then scope.bindable_top_level_def_for(node.name)
|
|
89
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
90
|
+
owner = Source::ConstantPath.qualified_name_or_nil(node.receiver)
|
|
91
|
+
owner && scope.singleton_def_through_ancestors(owner, node.name).first
|
|
92
|
+
end
|
|
93
|
+
def_node&.body
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A materialisation written in the CALLEE's body, judged syntactically. Deliberately narrower than
|
|
97
|
+
# {#struct_class_expression?}: that predicate's local-variable arm reads the binding off the scope it
|
|
98
|
+
# is handed, which is the CALLER's — a local of the same name in the callee body is a different
|
|
99
|
+
# binding entirely, so the arm is dropped rather than answered from the wrong scope.
|
|
100
|
+
def fresh_materialization_tail?(node, scope)
|
|
101
|
+
return false unless node.is_a?(Prism::CallNode)
|
|
102
|
+
return false unless %i[new []].include?(node.name)
|
|
103
|
+
|
|
104
|
+
case node.receiver
|
|
105
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
106
|
+
name = Source::ConstantPath.qualified_name_or_nil(node.receiver)
|
|
107
|
+
!name.nil? && !scope.struct_member_layout(name).nil?
|
|
108
|
+
when Prism::CallNode then inline_struct_factory?(node.receiver)
|
|
45
109
|
else false
|
|
46
110
|
end
|
|
47
111
|
end
|
|
48
112
|
|
|
113
|
+
# An explicit `return` anywhere in the body hands back an expression the tail check never saw, so the
|
|
114
|
+
# body is refused rather than scanned arm by arm. Nested `def` / `class` / `module` bodies are a
|
|
115
|
+
# different method's returns; a block's `return` is this method's, so the walk descends into it. The
|
|
116
|
+
# visit budget is the fail-closed bound: a body too large to scan is not proven, and unproven is not
|
|
117
|
+
# fresh.
|
|
118
|
+
def early_return?(node, budget = [FACTORY_BODY_SCAN_BUDGET])
|
|
119
|
+
return true if node.is_a?(Prism::ReturnNode)
|
|
120
|
+
return false if Inference::StructFoldSafety.scope_boundary?(node)
|
|
121
|
+
|
|
122
|
+
budget[0] -= 1
|
|
123
|
+
return true if budget[0].negative?
|
|
124
|
+
|
|
125
|
+
found = false
|
|
126
|
+
node.rigor_each_child { |child| found ||= early_return?(child, budget) }
|
|
127
|
+
found
|
|
128
|
+
end
|
|
129
|
+
|
|
49
130
|
# `.with` copies the receiver into a new instance — unless the struct wrote its own, which is free to
|
|
50
131
|
# return `self` and would reopen the very hole above.
|
|
51
132
|
#
|
|
@@ -68,8 +68,7 @@ module Rigor
|
|
|
68
68
|
|
|
69
69
|
module_function
|
|
70
70
|
|
|
71
|
-
# @
|
|
72
|
-
# @return [Rigor::Type, nil] the receiver-independent return type, or nil to decline.
|
|
71
|
+
# @return the receiver-independent return type, or nil to decline.
|
|
73
72
|
def try_dispatch(context)
|
|
74
73
|
return nil unless context.receiver.is_a?(Type::Dynamic)
|
|
75
74
|
|
|
@@ -21,11 +21,15 @@ require_relative "method_dispatcher/iterator_dispatch"
|
|
|
21
21
|
require_relative "method_dispatcher/reduce_folding"
|
|
22
22
|
require_relative "method_dispatcher/block_folding"
|
|
23
23
|
require_relative "method_dispatcher/array_to_h_folding"
|
|
24
|
+
require_relative "method_dispatcher/hash_transform_keys_folding"
|
|
24
25
|
require_relative "method_dispatcher/file_folding"
|
|
25
26
|
require_relative "method_dispatcher/shellwords_folding"
|
|
26
27
|
require_relative "method_dispatcher/math_folding"
|
|
28
|
+
require_relative "method_dispatcher/random_folding"
|
|
27
29
|
require_relative "method_dispatcher/time_folding"
|
|
28
30
|
require_relative "method_dispatcher/regexp_folding"
|
|
31
|
+
require_relative "method_dispatcher/match_data_folding"
|
|
32
|
+
require_relative "method_dispatcher/process_folding"
|
|
29
33
|
require_relative "method_dispatcher/cgi_folding"
|
|
30
34
|
require_relative "method_dispatcher/uri_folding"
|
|
31
35
|
require_relative "method_dispatcher/set_folding"
|
|
@@ -54,19 +58,17 @@ module Rigor
|
|
|
54
58
|
module MethodDispatcher # rubocop:disable Metrics/ModuleLength
|
|
55
59
|
module_function
|
|
56
60
|
|
|
57
|
-
# @param receiver_type
|
|
61
|
+
# @param receiver_type — type of the receiver expression, or
|
|
58
62
|
# `nil` for an implicit-self call.
|
|
59
|
-
# @param
|
|
60
|
-
# @param arg_types [Array<Rigor::Type>] positional argument types.
|
|
61
|
-
# @param block_type [Rigor::Type, nil] inferred return type of the
|
|
63
|
+
# @param block_type — inferred return type of the
|
|
62
64
|
# accompanying `do ... end` / `{ ... }` block (Slice 6 phase C
|
|
63
65
|
# sub-phase 2). When non-nil, the dispatcher prefers an
|
|
64
66
|
# overload that declares a block, and binds the method's
|
|
65
67
|
# block-return type variable to `block_type` so a return type
|
|
66
68
|
# like `Array[U]` resolves to `Array[block_type]`.
|
|
67
|
-
# @param environment
|
|
69
|
+
# @param environment — required for
|
|
68
70
|
# RBS-backed dispatch; when nil only constant folding can fire.
|
|
69
|
-
# @return
|
|
71
|
+
# @return inferred result type, or `nil` for "no rule".
|
|
70
72
|
def dispatch(receiver_type:, method_name:, arg_types:,
|
|
71
73
|
block_type: nil, environment: nil,
|
|
72
74
|
call_node: nil, scope: nil)
|
|
@@ -94,9 +96,12 @@ module Rigor
|
|
|
94
96
|
result
|
|
95
97
|
end
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
# `plugin_typed_sink` is internal bookkeeping for the composite-receiver tier below: when it is an
|
|
100
|
+
# Array, an answering plugin tier PUSHES the call node instead of recording it on the scope, and
|
|
101
|
+
# the tier commits the buffer only if it goes on to answer. See {#try_composite_receiver}.
|
|
102
|
+
def resolve(receiver_type:, method_name:, arg_types:, # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ParameterLists
|
|
98
103
|
block_type: nil, environment: nil,
|
|
99
|
-
call_node: nil, scope: nil)
|
|
104
|
+
call_node: nil, scope: nil, plugin_typed_sink: nil)
|
|
100
105
|
return nil if receiver_type.nil?
|
|
101
106
|
|
|
102
107
|
# Build the call context once and thread it — unchanged — through every tier
|
|
@@ -126,7 +131,22 @@ module Rigor
|
|
|
126
131
|
# RBS's, and `call.undefined-method` reads that record instead of re-deciding from the receiver's
|
|
127
132
|
# (possibly partial) signature. Recorded for EVERY plugin answer, dynamic or precise: the
|
|
128
133
|
# incoherence the record closes is about which subsystem typed the call, not about the type.
|
|
129
|
-
|
|
134
|
+
#
|
|
135
|
+
# Under a composite-receiver projection the record is BUFFERED instead (#1101). It is the one
|
|
136
|
+
# recording in this method that is set-only — `Scope#plugin_typed_calls` has no removal, and
|
|
137
|
+
# `CheckRules#call_site_exempt?` / `#source_arity_envelope` read it as an exemption — so a
|
|
138
|
+
# member a plugin answers beside a member that declines would exempt a call this dispatcher
|
|
139
|
+
# never typed, silencing `call.undefined-method` and `call.wrong-arity` on a firing that stands.
|
|
140
|
+
# `call_site_exempt?` runs ahead of the receiver-shape branch, so the union rule is covered by
|
|
141
|
+
# it too and the suppression is reachable end to end — the arms are measured in
|
|
142
|
+
# `spec/integration/composite_receiver_plugin_typed_suppression_spec.rb`. The
|
|
143
|
+
# `record_dynamic_origin` writes below and in the tiers that follow need no such care: that
|
|
144
|
+
# table is last-wins and the caller's fail-soft widening overwrites it.
|
|
145
|
+
if plugin_typed_sink
|
|
146
|
+
plugin_typed_sink << call_node
|
|
147
|
+
else
|
|
148
|
+
scope&.record_plugin_typed_call(call_node)
|
|
149
|
+
end
|
|
130
150
|
if plugin_result.is_a?(Type::Dynamic)
|
|
131
151
|
scope&.record_dynamic_origin(call_node, DynamicOrigin::FRAMEWORK_DSL_BOUNDARY)
|
|
132
152
|
end
|
|
@@ -255,7 +275,13 @@ module Rigor
|
|
|
255
275
|
if fallback_result.is_a?(Type::Dynamic)
|
|
256
276
|
scope&.record_dynamic_origin(call_node, DynamicOrigin::INFERRED_RETURN_UNTYPED)
|
|
257
277
|
end
|
|
258
|
-
fallback_result
|
|
278
|
+
return fallback_result if fallback_result
|
|
279
|
+
|
|
280
|
+
# Issue #1101 — composite-receiver projection, the last tier. Every tier above needs a
|
|
281
|
+
# receiver it can name; a `Union` or a `Difference` is not one, so a composite receiver whose
|
|
282
|
+
# members WOULD each resolve still lands `Dynamic[top]`. Retry per projected member through
|
|
283
|
+
# this same chain. See {#try_composite_receiver} for the projection rule and why it sits here.
|
|
284
|
+
try_composite_receiver(context)
|
|
259
285
|
end
|
|
260
286
|
|
|
261
287
|
# ADR-100 WD4 — records the transitive `-> void` provenance when the called method is a
|
|
@@ -335,10 +361,14 @@ module Rigor
|
|
|
335
361
|
[receiver_type.class_name, :instance]
|
|
336
362
|
when Type::Singleton, Type::StructClass, Type::DataClass
|
|
337
363
|
[receiver_type.class_name, :singleton]
|
|
338
|
-
else
|
|
364
|
+
else NO_DISCOVERED_LOOKUP
|
|
339
365
|
end
|
|
340
366
|
end
|
|
341
367
|
|
|
368
|
+
# The shared "no discovered-method key" pair; callers destructure it (#775).
|
|
369
|
+
NO_DISCOVERED_LOOKUP = [nil, nil].freeze
|
|
370
|
+
private_constant :NO_DISCOVERED_LOOKUP
|
|
371
|
+
|
|
342
372
|
# ADR-5 robustness — returns `Dynamic[Top]` when the receiver is an instance or singleton
|
|
343
373
|
# of a type Rigor synthesized (a missing-namespace module or a referenced-type stub). The
|
|
344
374
|
# stub has no methods, so the call would otherwise reach the user-class fallback and
|
|
@@ -447,6 +477,52 @@ module Rigor
|
|
|
447
477
|
FlowContribution::Merger.merge(contributions).return_type
|
|
448
478
|
end
|
|
449
479
|
|
|
480
|
+
# Issue #963 item 2 — the MEMBERSHIP half of the two plugin tiers, for #618's own-method veto.
|
|
481
|
+
#
|
|
482
|
+
# `ExpressionTyper#try_local_def_dispatch` has to decide whether the call's own `self` answers the
|
|
483
|
+
# name before a same-named top-level `def` may bind, and a plugin-supplied member is an answer Ruby
|
|
484
|
+
# really dispatches to: a model's column reader, an association, a scope, a macro-synthesised
|
|
485
|
+
# reader. The veto asked neither tier, so a top-level `def name` bound ahead of the member and
|
|
486
|
+
# typed the read as the def's `nil` — #618's false positive at a source the veto could not see.
|
|
487
|
+
#
|
|
488
|
+
# The question is asked HERE, of the same two tiers `resolve` consults and in the same order, rather
|
|
489
|
+
# than reconstructed in the veto: a name this reports as answered is a name the dispatch really would
|
|
490
|
+
# resolve, and no plugin knowledge moves into the engine's veto. The tiers are the gated
|
|
491
|
+
# `dynamic_return` walk and the ADR-16 synthetic-method index (Tier C `heredoc_templates` emissions
|
|
492
|
+
# today; the Tier B `Plugin::Macro::TraitRegistry` explosions share that table but cannot populate it
|
|
493
|
+
# until #476 threads a real environment into the pre-pass that builds it, so no trait member reaches
|
|
494
|
+
# this predicate in production yet — it will when #476 lands, with no change here).
|
|
495
|
+
#
|
|
496
|
+
# It is a predicate, not a type: the veto's job is to stop the top-level bind, after which the call
|
|
497
|
+
# falls through to the ordinary dispatch chain and the tier that answered here answers there too. The
|
|
498
|
+
# walk is not on the hot path — it runs only once `Scope#bindable_top_level_def_for` has already
|
|
499
|
+
# produced a candidate, which needs a project top-level `def` of the same name.
|
|
500
|
+
def plugin_member_answers?(call_node:, scope:, receiver_type:, method_name:)
|
|
501
|
+
return false if receiver_type.nil?
|
|
502
|
+
return true unless try_plugin_contribution(call_node, scope, receiver_type).nil?
|
|
503
|
+
|
|
504
|
+
synthetic_method_member?(receiver_type, method_name, scope&.environment)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# The synthetic-method half of {#plugin_member_answers?} — the same `(class_name, method_name, kind)`
|
|
508
|
+
# probe {#try_synthetic_method} makes, without the return-type promotion the veto has no use for.
|
|
509
|
+
def synthetic_method_member?(receiver_type, method_name, environment)
|
|
510
|
+
index = environment&.synthetic_method_index
|
|
511
|
+
return false if index.nil? || index.empty?
|
|
512
|
+
|
|
513
|
+
class_name = synthetic_method_class_name(receiver_type)
|
|
514
|
+
return false if class_name.nil?
|
|
515
|
+
|
|
516
|
+
matches = if receiver_type.is_a?(Type::Singleton)
|
|
517
|
+
index.lookup_singleton(class_name, method_name)
|
|
518
|
+
else
|
|
519
|
+
index.lookup_instance(class_name, method_name)
|
|
520
|
+
end
|
|
521
|
+
!matches.empty?
|
|
522
|
+
rescue StandardError
|
|
523
|
+
false
|
|
524
|
+
end
|
|
525
|
+
|
|
450
526
|
# ADR-16 synthetic-method tier. Slice 2b shipped the floor — a match short-circuits at the
|
|
451
527
|
# right precedence (above dep-source / discovered / user-class-fallback; below RBS) and
|
|
452
528
|
# returns `Dynamic[T]`. Slice 6 (precision promotion):
|
|
@@ -759,8 +835,10 @@ module Rigor
|
|
|
759
835
|
"File" => FileFolding,
|
|
760
836
|
"Shellwords" => ShellwordsFolding,
|
|
761
837
|
"Math" => MathFolding,
|
|
838
|
+
"Random" => RandomFolding,
|
|
762
839
|
"Time" => TimeFolding,
|
|
763
840
|
"Regexp" => RegexpFolding,
|
|
841
|
+
"Process" => ProcessFolding,
|
|
764
842
|
"CGI" => CGIFolding,
|
|
765
843
|
"URI" => URIFolding,
|
|
766
844
|
"Set" => SetFolding,
|
|
@@ -769,7 +847,7 @@ module Rigor
|
|
|
769
847
|
private_constant :STDLIB_SINGLETON_FOLDERS
|
|
770
848
|
|
|
771
849
|
PRECISE_TIERS_TAIL = Ractor.make_shareable([
|
|
772
|
-
MethodFolding, ReduceFolding, ArrayToHFolding, BlockFolding
|
|
850
|
+
MethodFolding, ReduceFolding, ArrayToHFolding, HashTransformKeysFolding, BlockFolding
|
|
773
851
|
].freeze)
|
|
774
852
|
private_constant :PRECISE_TIERS_TAIL
|
|
775
853
|
|
|
@@ -797,11 +875,8 @@ module Rigor
|
|
|
797
875
|
return result if result
|
|
798
876
|
end
|
|
799
877
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
result = folder.try_dispatch(context)
|
|
803
|
-
return result if result
|
|
804
|
-
end
|
|
878
|
+
folder_result = dispatch_receiver_keyed_folder(context)
|
|
879
|
+
return folder_result if folder_result
|
|
805
880
|
|
|
806
881
|
kernel_result = dispatch_kernel_intrinsic(context)
|
|
807
882
|
return kernel_result if kernel_result
|
|
@@ -813,6 +888,19 @@ module Rigor
|
|
|
813
888
|
nil
|
|
814
889
|
end
|
|
815
890
|
|
|
891
|
+
# The folders the receiver's class selects, at most one per call: a stdlib singleton folder, or for a
|
|
892
|
+
# `MatchData` receiver the slice fold of the proven `$~` (issue #1381). Every other receiver skips them without
|
|
893
|
+
# a trial.
|
|
894
|
+
def dispatch_receiver_keyed_folder(context)
|
|
895
|
+
receiver = context.receiver
|
|
896
|
+
folder =
|
|
897
|
+
case receiver
|
|
898
|
+
when Type::Singleton then STDLIB_SINGLETON_FOLDERS[receiver.class_name]
|
|
899
|
+
when Type::Nominal then MatchDataFolding if receiver.class_name == "MatchData"
|
|
900
|
+
end
|
|
901
|
+
folder&.try_dispatch(context)
|
|
902
|
+
end
|
|
903
|
+
|
|
816
904
|
# ADR-91 WD1 — the single dispatcher-held ownership gate for the Kernel module-function surface
|
|
817
905
|
# (gate-by-held-key, the same move STDLIB_SINGLETON_FOLDERS made above). The tier runs only when
|
|
818
906
|
# the method name is in the compiled `INTRINSIC_NAMES` table AND the call is attributable to
|
|
@@ -890,6 +978,123 @@ module Rigor
|
|
|
890
978
|
end
|
|
891
979
|
end
|
|
892
980
|
|
|
981
|
+
# Issue #1101 — the composite-receiver projection tier, the LAST thing `resolve` tries.
|
|
982
|
+
#
|
|
983
|
+
# Every tier above needs a receiver it can *name*: `receiver_descriptor`, the discovered-method
|
|
984
|
+
# lookup, the dependency-source and synthesized-stub tiers and the user-class fallback all switch
|
|
985
|
+
# on `Nominal` / `Singleton` / a member carrier. A `Union` or a `Difference` is none of those, so a
|
|
986
|
+
# composite receiver whose members would each resolve perfectly well still fell through the whole
|
|
987
|
+
# chain and landed `Dynamic[top]`.
|
|
988
|
+
#
|
|
989
|
+
# `RbsDispatch` already distributes over a `Union` (and erases a `Difference` to its base, #533),
|
|
990
|
+
# but it does so INSIDE one tier, so a member that needs a LOWER tier — a user class with no RBS,
|
|
991
|
+
# a discovered `def`, an opt-in gem — makes the whole union decline. That is the corpus shape:
|
|
992
|
+
# rigor-actionpack types `params[:k]` as `ActionController::Parameters | nil`, the `Parameters` arm
|
|
993
|
+
# resolves only through the user-class fallback, and so `params[:k].present?` / `#==` / `#blank?` /
|
|
994
|
+
# `#to_s` read opaque on every Rails app. Measured by toggling this file alone: the named-pair
|
|
995
|
+
# census drains 137 sites on redmine and 56 on mastodon, and `rigor check` reports the same
|
|
996
|
+
# diagnostics on both arms across eight survey targets.
|
|
997
|
+
#
|
|
998
|
+
# **Why the bottom and not next to `RbsDispatch`.** Sitting here, the tier can only ever replace a
|
|
999
|
+
# `Dynamic[top]` the typer was about to produce — the same ADR-5 argument {UniversalObjectDispatch}
|
|
1000
|
+
# makes. Hoisting the projection above `RbsDispatch` would change answers that resolve today, which
|
|
1001
|
+
# is a different change with a different measurement.
|
|
1002
|
+
#
|
|
1003
|
+
# **The projection rule** (`receiver_projection` below; the normative statement lives in
|
|
1004
|
+
# [`inference-engine.md`](docs/internal-spec/inference-engine.md) § "Composite receivers"):
|
|
1005
|
+
#
|
|
1006
|
+
# - `Union[A, B, …]` projects to its members. Each is dispatched through this same chain and the
|
|
1007
|
+
# results are unioned; if ANY member declines, the whole tier declines. Never a partial answer —
|
|
1008
|
+
# a union whose members disagree about whether the method exists is exactly the case where an
|
|
1009
|
+
# answer would be a guess.
|
|
1010
|
+
# - `Difference[base, nil]` projects to `base` with its nil-valued members removed. Removing `nil`
|
|
1011
|
+
# from a receiver cannot change method resolution at all: `NilClass` is nobody's ancestor, so
|
|
1012
|
+
# every remaining inhabitant looks a method up exactly as a plain `base` inhabitant does, and the
|
|
1013
|
+
# carrier is value-set-equal to the projection. This is `T.must(x)` (rigor-sorbet's
|
|
1014
|
+
# `strip_nil`), which builds `Difference[inferred(x), Constant[nil]]` — over a `T?` local that
|
|
1015
|
+
# base IS a union, which is why the two arms compose rather than being separate features.
|
|
1016
|
+
# - Any other `Difference` DECLINES. `A − B` for a real `B` is a claim about which inhabitants
|
|
1017
|
+
# survive, and the projection has nowhere to put it: dispatching on `A` re-admits exactly the
|
|
1018
|
+
# overrides `B` contributed, and over a union base (`(String | Integer) − Integer` is Strings
|
|
1019
|
+
# only) it re-admits a member the difference eliminated and unions in its answer — a WIDER type
|
|
1020
|
+
# than today's decline, on a shape no corpus target produces. Subtracting a class from a union
|
|
1021
|
+
# needs an algebra the `Difference` carrier does not have (it removes a value set, not a member),
|
|
1022
|
+
# so the honest answer is to keep declining until something demands it. `RbsDispatch`'s #533
|
|
1023
|
+
# erasure is untouched and still answers these for an RBS-known base.
|
|
1024
|
+
#
|
|
1025
|
+
# `self` binds to the PROJECTED receiver, not to the composite: the projection is the exact set of
|
|
1026
|
+
# inhabitants that can reach the method, so a `-> self` method (`dup`, `itself`, `tap`) answers per
|
|
1027
|
+
# member and the union reassembles them — `(String | Symbol)#itself` is `String | Symbol`. Nothing is
|
|
1028
|
+
# lost on the `Difference` arm because the projection is value-set-equal to the carrier there. This
|
|
1029
|
+
# falls out of re-entering `resolve` rather than needing a `self_type_override`.
|
|
1030
|
+
def try_composite_receiver(context)
|
|
1031
|
+
members = receiver_projection(context.receiver)
|
|
1032
|
+
return nil if members.nil? || members.size > COMPOSITE_PROJECTION_MEMBERS
|
|
1033
|
+
|
|
1034
|
+
plugin_typed = []
|
|
1035
|
+
results = members.map do |member|
|
|
1036
|
+
resolve(
|
|
1037
|
+
receiver_type: member, method_name: context.method_name, arg_types: context.args,
|
|
1038
|
+
block_type: context.block_type, environment: context.environment,
|
|
1039
|
+
call_node: context.call_node, scope: context.scope, plugin_typed_sink: plugin_typed
|
|
1040
|
+
)
|
|
1041
|
+
end
|
|
1042
|
+
return nil if results.any?(&:nil?)
|
|
1043
|
+
|
|
1044
|
+
plugin_typed.uniq.each { |node| context.scope&.record_plugin_typed_call(node) }
|
|
1045
|
+
Type::Combinator.union(*results)
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
# An upper bound on the fan-out, because this tier re-runs the FULL dispatch chain per member.
|
|
1049
|
+
# Real unions are two or three members wide; a receiver wider than this is a join the engine has
|
|
1050
|
+
# already lost precision on, and paying N chain walks to union N answers is not worth it.
|
|
1051
|
+
COMPOSITE_PROJECTION_MEMBERS = 8
|
|
1052
|
+
private_constant :COMPOSITE_PROJECTION_MEMBERS
|
|
1053
|
+
|
|
1054
|
+
# The receivers each member of a composite carrier is dispatched as, or nil when the carrier is not
|
|
1055
|
+
# projectable. Every member returned MUST itself be non-composite: that is what bounds the re-entry
|
|
1056
|
+
# into `resolve` at one level, by construction rather than by a counter — the nested call reaches
|
|
1057
|
+
# this tier with a receiver it declines immediately. A normalized `Union` never holds a `Union`, so
|
|
1058
|
+
# the shapes the guard turns away are a `Difference` over a `Difference` (`T.must(T.must(x))`) and a
|
|
1059
|
+
# `Union` holding one (`c ? T.must(y) : Bar.new`, which normalizes to `(A - nil) | Bar`). Both
|
|
1060
|
+
# decline exactly as they did before this tier existed; neither appears in the survey corpus.
|
|
1061
|
+
def receiver_projection(receiver)
|
|
1062
|
+
members =
|
|
1063
|
+
case receiver
|
|
1064
|
+
when Type::Union then receiver.members
|
|
1065
|
+
when Type::Difference then difference_projection(receiver)
|
|
1066
|
+
end
|
|
1067
|
+
return nil if members.nil? || members.empty?
|
|
1068
|
+
return nil if members.any? { |member| member.is_a?(Type::Union) || member.is_a?(Type::Difference) }
|
|
1069
|
+
|
|
1070
|
+
members
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
# `Difference[base, nil]` projects to `base` minus its nil-valued members; every other difference
|
|
1074
|
+
# declines. The nil filter runs over a scalar base as well as a union one, so the rule stays total:
|
|
1075
|
+
# `Combinator.difference` does not normalize `NilClass - nil` (or `nil - nil`) to `Bot`, and this
|
|
1076
|
+
# tier must not project `[NilClass]` and answer a NilClass method for a carrier with no inhabitant.
|
|
1077
|
+
# Nothing observable turns on it today — `RbsDispatch`'s #533 erasure sits above this tier and
|
|
1078
|
+
# already answers a nil-only base from the base's own descriptor — which is why the guard is here
|
|
1079
|
+
# rather than in `Combinator`, where it would be a normalization change with its own blast radius.
|
|
1080
|
+
def difference_projection(difference)
|
|
1081
|
+
return nil unless nil_valued_type?(difference.removed)
|
|
1082
|
+
|
|
1083
|
+
base = difference.base
|
|
1084
|
+
members = base.is_a?(Type::Union) ? base.members : [base]
|
|
1085
|
+
retained = members.reject { |member| nil_valued_type?(member) }
|
|
1086
|
+
return nil if retained.empty?
|
|
1087
|
+
|
|
1088
|
+
retained
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
# `nil` as either carrier spells it: the value-pinned `Constant[nil]` the engine normally produces,
|
|
1092
|
+
# and the `Nominal[NilClass]` a widened or RBS-sourced position carries.
|
|
1093
|
+
def nil_valued_type?(type)
|
|
1094
|
+
(type.is_a?(Type::Constant) && type.value.nil?) ||
|
|
1095
|
+
(type.is_a?(Type::Nominal) && type.class_name == "NilClass")
|
|
1096
|
+
end
|
|
1097
|
+
|
|
893
1098
|
# Slice 7 phase 8 — meta-introspection shortcuts. The default `Object#class` RBS return
|
|
894
1099
|
# type is `Class`, but for a receiver of known nominal identity we can do better:
|
|
895
1100
|
# `instance_of(Foo).class` is `Singleton[Foo]` (the class object itself), which downstream
|
|
@@ -1350,18 +1555,13 @@ module Rigor
|
|
|
1350
1555
|
# method definition, or selected overload does not provide statically declared block
|
|
1351
1556
|
# parameter types. Callers MUST treat the empty array as "no information"; the binder
|
|
1352
1557
|
# falls back to `Dynamic[Top]` for every parameter slot in that case.
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
# @param method_name [Symbol]
|
|
1356
|
-
# @param arg_types [Array<Rigor::Type>]
|
|
1357
|
-
# @param environment [Rigor::Environment, nil]
|
|
1358
|
-
# @return [Array<Rigor::Type>]
|
|
1359
|
-
def expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil)
|
|
1558
|
+
def expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil,
|
|
1559
|
+
scope: nil)
|
|
1360
1560
|
return [] if receiver_type.nil?
|
|
1361
1561
|
|
|
1362
1562
|
context = CallContext.build(
|
|
1363
1563
|
receiver: receiver_type, method_name: method_name,
|
|
1364
|
-
args: arg_types, environment: environment
|
|
1564
|
+
args: arg_types, environment: environment, scope: scope
|
|
1365
1565
|
)
|
|
1366
1566
|
iterator_result = IteratorDispatch.block_param_types(context)
|
|
1367
1567
|
return iterator_result if iterator_result
|
|
@@ -55,14 +55,13 @@ module Rigor
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
class MethodParameterBinder
|
|
58
|
-
# @param
|
|
59
|
-
# @param class_path [String, nil] the qualified name of the class the method is defined in
|
|
58
|
+
# @param class_path — the qualified name of the class the method is defined in
|
|
60
59
|
# (e.g., `"Foo::Bar"`), or `nil` for a top-level `def` outside any class. When `nil` (or
|
|
61
60
|
# when the class is unknown to RBS), every parameter falls back to `Dynamic[Top]`.
|
|
62
|
-
# @param singleton
|
|
61
|
+
# @param singleton — `true` when the def is a singleton method (either `def
|
|
63
62
|
# self.foo` or a `def foo` inside `class << self`); routes the lookup through
|
|
64
63
|
# `RbsLoader#singleton_method`.
|
|
65
|
-
# @param source_path
|
|
64
|
+
# @param source_path — the project-relative path of the file the method is
|
|
66
65
|
# defined in. Used to match ADR-28 path-scoped protocol contracts; `nil` (the default
|
|
67
66
|
# for synthetic / probe scopes) disables the contract tier.
|
|
68
67
|
def initialize(environment:, class_path:, singleton:, source_path: nil)
|
|
@@ -72,8 +71,7 @@ module Rigor
|
|
|
72
71
|
@source_path = source_path
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
# @
|
|
76
|
-
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type.
|
|
74
|
+
# @return ordered map from parameter name to bound type.
|
|
77
75
|
# Anonymous parameters (`*` and `**` without a name) are skipped.
|
|
78
76
|
def bind(def_node)
|
|
79
77
|
slots = collect_slots(def_node.parameters)
|
|
@@ -200,7 +198,14 @@ module Rigor
|
|
|
200
198
|
# both `()` and `(int)` overloads — only the second matches a `def first(n)` redefinition)
|
|
201
199
|
# are silently skipped, so the binder defaults to the most informative type the RBS
|
|
202
200
|
# signature provides without having to know which overload the runtime will pick.
|
|
201
|
+
#
|
|
202
|
+
# An overload declared `(?)` (`RBS::Types::UntypedFunction`, issue #1430) has no parameter lists and
|
|
203
|
+
# accepts any argument in every slot, so no slot can be narrowed below `Dynamic[Top]`: binding a typed
|
|
204
|
+
# sibling overload's parameter type would read a value that overload legitimately passes as a
|
|
205
|
+
# contradiction inside the body. Every slot keeps its default; the declared return type still applies.
|
|
203
206
|
def apply_rbs_overloads(types, slots, method_types)
|
|
207
|
+
return unless method_types.all? { |mt| mt.type.respond_to?(:required_positionals) }
|
|
208
|
+
|
|
204
209
|
slots.each do |slot|
|
|
205
210
|
next if slot.name.nil?
|
|
206
211
|
|
|
@@ -260,9 +265,7 @@ module Rigor
|
|
|
260
265
|
return nil if contracts.empty?
|
|
261
266
|
|
|
262
267
|
singleton = def_node.receiver.is_a?(Prism::SelfNode) || @singleton
|
|
263
|
-
contracts.find
|
|
264
|
-
contract.method_name == def_node.name && contract.singleton == singleton
|
|
265
|
-
end
|
|
268
|
+
contracts.find { |contract| contract.method_name == def_node.name && contract.singleton == singleton }
|
|
266
269
|
end
|
|
267
270
|
|
|
268
271
|
def positional_slot_at(slots, index)
|