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
|
@@ -214,7 +214,6 @@ module Rigor
|
|
|
214
214
|
# survives the call.
|
|
215
215
|
CONSTANT_SELF_RETURNERS = %i[freeze itself dup clone].to_set.freeze
|
|
216
216
|
|
|
217
|
-
# @return [Rigor::Type::Constant, Rigor::Type::Union, Rigor::Type::IntegerRange, nil]
|
|
218
217
|
def try_dispatch(context)
|
|
219
218
|
receiver = context.receiver
|
|
220
219
|
method_name = context.method_name
|
|
@@ -234,15 +233,8 @@ module Rigor
|
|
|
234
233
|
first_arg = args.first
|
|
235
234
|
return nil unless first_arg.is_a?(Type::Constant) && first_arg.value.is_a?(Symbol)
|
|
236
235
|
end
|
|
237
|
-
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
# v0.0.7 — `String#%` against a `Tuple` / `HashShape` argument runs Ruby's format-string engine
|
|
241
|
-
# when both sides are statically constant. The standard `numeric_set_of` path bails on Tuple /
|
|
242
|
-
# HashShape arguments because they are not scalar-Constant carriers, so the special-case sits
|
|
243
|
-
# ahead of the numeric path.
|
|
244
|
-
format_lift = try_fold_string_format(receiver, method_name, args)
|
|
245
|
-
return format_lift if format_lift
|
|
236
|
+
ahead = try_fold_ahead_of_numeric_path(receiver, method_name, args)
|
|
237
|
+
return ahead if ahead
|
|
246
238
|
|
|
247
239
|
receiver_set = numeric_set_of(receiver)
|
|
248
240
|
return nil unless receiver_set
|
|
@@ -253,6 +245,20 @@ module Rigor
|
|
|
253
245
|
dispatch_by_arity(receiver_set, method_name, arg_sets)
|
|
254
246
|
end
|
|
255
247
|
|
|
248
|
+
# The folds that must answer before `numeric_set_of` gets a say, because the receiver or an
|
|
249
|
+
# argument is a carrier it declines:
|
|
250
|
+
#
|
|
251
|
+
# - the self-returners, whose receiver may be any `Constant`;
|
|
252
|
+
# - v0.0.7's `String#%` against a `Tuple` / `HashShape` argument, neither a scalar-Constant
|
|
253
|
+
# carrier, which runs Ruby's own format-string engine;
|
|
254
|
+
# - #861's `clamp` on a plain `Integer` / `Float` receiver, a `Nominal` that `numeric_set_of`
|
|
255
|
+
# declines outright.
|
|
256
|
+
def try_fold_ahead_of_numeric_path(receiver, method_name, args)
|
|
257
|
+
try_fold_self_return(receiver, method_name, args) ||
|
|
258
|
+
try_fold_string_format(receiver, method_name, args) ||
|
|
259
|
+
try_fold_unbounded_clamp(receiver, method_name, args)
|
|
260
|
+
end
|
|
261
|
+
|
|
256
262
|
# `freeze` / `itself` / `dup` / `clone` on a value-pinned receiver return the receiver carrier.
|
|
257
263
|
#
|
|
258
264
|
# This sits AHEAD of the catalogue path and deliberately bypasses its purity gate, because the gate
|
|
@@ -344,11 +350,13 @@ module Rigor
|
|
|
344
350
|
return type.members.map(&:value) if type.members.all?(Type::Constant)
|
|
345
351
|
|
|
346
352
|
# A union that mixes `Constant<Integer>` and `IntegerRange` members (e.g. an accumulator's
|
|
347
|
-
# running fixpoint assumption `1 |
|
|
353
|
+
# running fixpoint assumption `1 | Integer[1..6]`) folds as the bounding interval. The
|
|
348
354
|
# range-arithmetic path (`try_fold_binary_range`) then keeps the result an `IntegerRange`
|
|
349
355
|
# instead of bailing to Dynamic.
|
|
350
356
|
union_integer_bounds(type)
|
|
351
|
-
|
|
357
|
+
# ADR-109 — a bounded Float folds through its own arms (`try_fold_unary_float_range`,
|
|
358
|
+
# `try_fold_ternary_float_range`, `try_fold_clamp_range`); every other arm declines it.
|
|
359
|
+
when Type::IntegerRange, Type::FloatRange then type
|
|
352
360
|
end
|
|
353
361
|
end
|
|
354
362
|
|
|
@@ -390,11 +398,15 @@ module Rigor
|
|
|
390
398
|
case set
|
|
391
399
|
when Array then try_fold_unary_set(set, method_name)
|
|
392
400
|
when Type::IntegerRange then try_fold_unary_range(set, method_name)
|
|
401
|
+
when Type::FloatRange then try_fold_unary_float_range(set, method_name)
|
|
393
402
|
end
|
|
394
403
|
end
|
|
395
404
|
|
|
396
405
|
def try_fold_binary(left, method_name, right)
|
|
397
406
|
return try_fold_divmod(left, right) if method_name == :divmod
|
|
407
|
+
return try_fold_clamp_range(left, right) if method_name == :clamp && bounded_range?(left)
|
|
408
|
+
# ADR-109 — no Float-range arithmetic yet; a bounded Float on either side declines here.
|
|
409
|
+
return nil if left.is_a?(Type::FloatRange) || right.is_a?(Type::FloatRange)
|
|
398
410
|
|
|
399
411
|
if left.is_a?(Type::IntegerRange) || right.is_a?(Type::IntegerRange)
|
|
400
412
|
try_fold_binary_range(left, method_name, right)
|
|
@@ -403,6 +415,133 @@ module Rigor
|
|
|
403
415
|
end
|
|
404
416
|
end
|
|
405
417
|
|
|
418
|
+
def bounded_range?(type)
|
|
419
|
+
type.is_a?(Type::IntegerRange) || type.is_a?(Type::FloatRange)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# #834 — `clamp(range)` on a bounded receiver, the one-argument twin of `range_clamp` /
|
|
423
|
+
# `float_range_clamp`. An exclusive end raises at run time (`cannot clamp with an exclusive
|
|
424
|
+
# range`), so it declines rather than folding a call that does not return; a missing end keeps
|
|
425
|
+
# the receiver's own bound on that side. The bracket's endpoints must be literals of the
|
|
426
|
+
# receiver's class.
|
|
427
|
+
def try_fold_clamp_range(receiver, right)
|
|
428
|
+
bracket = clamp_bracket(right)
|
|
429
|
+
return nil if bracket.nil?
|
|
430
|
+
|
|
431
|
+
lower, upper = bracket
|
|
432
|
+
case receiver
|
|
433
|
+
when Type::IntegerRange
|
|
434
|
+
return nil unless clamp_endpoints_are?(Integer, lower, upper)
|
|
435
|
+
|
|
436
|
+
range_clamp(receiver, lower || receiver.lower, upper || receiver.upper)
|
|
437
|
+
when Type::FloatRange
|
|
438
|
+
return nil unless clamp_endpoints_are?(Numeric, lower, upper)
|
|
439
|
+
|
|
440
|
+
float_range_clamp(receiver, lower, upper)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# `[lower, upper]` of a single literal, closed, non-reversed `Range` argument (either side may be
|
|
445
|
+
# `nil` for an open range); `nil` for any other shape.
|
|
446
|
+
def clamp_bracket(right)
|
|
447
|
+
return nil unless right.is_a?(Array) && right.size == 1 && right.first.is_a?(Range)
|
|
448
|
+
|
|
449
|
+
bracket = right.first
|
|
450
|
+
return nil if bracket.exclude_end? && !bracket.end.nil?
|
|
451
|
+
|
|
452
|
+
lower = bracket.begin
|
|
453
|
+
upper = bracket.end
|
|
454
|
+
return nil if lower && upper && lower > upper
|
|
455
|
+
|
|
456
|
+
[lower, upper]
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def clamp_endpoints_are?(klass, lower, upper)
|
|
460
|
+
(lower.nil? || lower.is_a?(klass)) && (upper.nil? || upper.is_a?(klass))
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# #861 — `clamp` on a receiver that carries no bracket of its own. `Comparable#clamp` returns a
|
|
464
|
+
# value inside the bracket whatever the receiver's bounds are, so a plain `Integer` / `Float`
|
|
465
|
+
# receiver takes the bracket entire: `i.clamp(1..9)` and `i.clamp(1, 9)` are `Integer[1..9]`
|
|
466
|
+
# where the RBS tier's `self | A` could only say `Integer` (resp. `1 | 9 | Integer`).
|
|
467
|
+
#
|
|
468
|
+
# Endpoints must be literals of the receiver's own class. A mixed bracket is `Comparable`'s
|
|
469
|
+
# business rather than the fold's: `1.clamp(0.5, 2.5)` returns the receiver OR a bound, so the
|
|
470
|
+
# run-time class is a union across `Integer` and `Float`, and it declines here.
|
|
471
|
+
#
|
|
472
|
+
# `Float` covers `NaN` while a `FloatRange` never does — sound in this direction, because
|
|
473
|
+
# `clamp` on a NaN receiver raises (`comparison of Float with Float failed`) and the call that
|
|
474
|
+
# returns is always inside the bracket.
|
|
475
|
+
#
|
|
476
|
+
# The two run-time raisers `clamp_bracket` already declines — an exclusive end (`cannot clamp
|
|
477
|
+
# with an exclusive range`) and a reversed bracket — keep declining here.
|
|
478
|
+
def try_fold_unbounded_clamp(receiver, method_name, args)
|
|
479
|
+
return nil unless method_name == :clamp
|
|
480
|
+
|
|
481
|
+
klass = unbounded_clamp_receiver_class(receiver)
|
|
482
|
+
return nil unless klass
|
|
483
|
+
|
|
484
|
+
case args.size
|
|
485
|
+
when 1 then fold_unbounded_clamp_range(klass, args.first)
|
|
486
|
+
when 2 then fold_unbounded_clamp_pair(klass, args)
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# `Integer` / `Float` for a bare nominal receiver of that class; `nil` for everything else,
|
|
491
|
+
# the bounded carriers included — those keep their own intersecting folds.
|
|
492
|
+
def unbounded_clamp_receiver_class(receiver)
|
|
493
|
+
return nil unless receiver.is_a?(Type::Nominal) && receiver.type_args.empty?
|
|
494
|
+
|
|
495
|
+
case receiver.class_name
|
|
496
|
+
when "Integer" then Integer
|
|
497
|
+
when "Float" then Float
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def fold_unbounded_clamp_range(klass, arg)
|
|
502
|
+
bracket = clamp_bracket(numeric_set_of(arg))
|
|
503
|
+
return nil if bracket.nil?
|
|
504
|
+
|
|
505
|
+
lower, upper = bracket
|
|
506
|
+
return nil unless clamp_endpoints_are?(klass, lower, upper)
|
|
507
|
+
return nil if clamp_bound_nan?(lower) || clamp_bound_nan?(upper)
|
|
508
|
+
|
|
509
|
+
unbounded_clamp_result(klass, lower, upper)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def fold_unbounded_clamp_pair(klass, args)
|
|
513
|
+
values = args.map { |arg| single_clamp_bound(klass, numeric_set_of(arg)) }
|
|
514
|
+
lower, upper = values
|
|
515
|
+
return nil if lower.nil? || upper.nil?
|
|
516
|
+
return nil if lower > upper
|
|
517
|
+
|
|
518
|
+
unbounded_clamp_result(klass, lower, upper)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# The two-argument form takes no `nil` bound: `clamp(nil, 9)` is `Comparable`'s own
|
|
522
|
+
# one-sided form, which the catalogue does not route here.
|
|
523
|
+
def single_clamp_bound(klass, values)
|
|
524
|
+
return nil unless values.is_a?(Array) && values.size == 1
|
|
525
|
+
|
|
526
|
+
value = values.first
|
|
527
|
+
return nil unless value.is_a?(klass)
|
|
528
|
+
|
|
529
|
+
clamp_bound_nan?(value) ? nil : value
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# A NaN bound orders against nothing, so `clamp` raises for it — no bracket to fold to.
|
|
533
|
+
def clamp_bound_nan?(bound)
|
|
534
|
+
bound.is_a?(Float) && bound.nan?
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def unbounded_clamp_result(klass, lower, upper)
|
|
538
|
+
if klass == Integer
|
|
539
|
+
build_integer_range(lower || -Float::INFINITY, upper || Float::INFINITY)
|
|
540
|
+
else
|
|
541
|
+
Type::Combinator.float_range(lower || -Float::INFINITY, upper || Float::INFINITY)
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
406
545
|
# `Integer#divmod` and `Float#divmod` return a 2-element array `[quotient, remainder]`. We project
|
|
407
546
|
# that into `Tuple[Constant[q], Constant[r]]` so downstream rules see the precise element types.
|
|
408
547
|
# Union/range receivers are widened per-position: each tuple slot carries the union of quotients
|
|
@@ -870,16 +1009,70 @@ module Rigor
|
|
|
870
1009
|
# could not reach.
|
|
871
1010
|
#
|
|
872
1011
|
# v0.0.6 — IntegerRange-shaped receivers participate in `Comparable#between?` and
|
|
873
|
-
# `Comparable#clamp` folds. `
|
|
874
|
-
# bounds against scalar args; `
|
|
1012
|
+
# `Comparable#clamp` folds. `Integer[a..b].between?(min, max)` decides three-valued via the receiver's
|
|
1013
|
+
# bounds against scalar args; `Integer[a..b].clamp` narrows the receiver's bounds against the bracket.
|
|
875
1014
|
# Other ternary methods over IntegerRange operands still decline.
|
|
876
1015
|
def try_fold_ternary(receiver_set, method_name, arg_sets)
|
|
877
1016
|
return try_fold_ternary_range(receiver_set, method_name, arg_sets) if receiver_set.is_a?(Type::IntegerRange)
|
|
878
|
-
|
|
1017
|
+
if receiver_set.is_a?(Type::FloatRange)
|
|
1018
|
+
return try_fold_ternary_float_range(receiver_set, method_name, arg_sets)
|
|
1019
|
+
end
|
|
1020
|
+
return nil if arg_sets.any? { |s| bounded_range?(s) }
|
|
879
1021
|
|
|
880
1022
|
try_fold_ternary_set(receiver_set, method_name, arg_sets)
|
|
881
1023
|
end
|
|
882
1024
|
|
|
1025
|
+
# ADR-109 — receiver `FloatRange` + two scalar numeric args: `between?` decides three-valued
|
|
1026
|
+
# over the bracket, `clamp` narrows to the bracket (NaN never inhabits the receiver, so the
|
|
1027
|
+
# comparison that would raise on NaN cannot).
|
|
1028
|
+
def try_fold_ternary_float_range(range, method_name, arg_sets)
|
|
1029
|
+
return nil unless arg_sets.all?(Array)
|
|
1030
|
+
|
|
1031
|
+
min_arg = single_real_arg(arg_sets[0])
|
|
1032
|
+
max_arg = single_real_arg(arg_sets[1])
|
|
1033
|
+
return nil if min_arg.nil? || max_arg.nil?
|
|
1034
|
+
return nil if min_arg > max_arg
|
|
1035
|
+
|
|
1036
|
+
case method_name
|
|
1037
|
+
when :between? then float_range_between(range, min_arg, max_arg)
|
|
1038
|
+
when :clamp then float_range_clamp(range, min_arg, max_arg)
|
|
1039
|
+
end
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
def single_real_arg(values)
|
|
1043
|
+
return nil unless values.is_a?(Array) && values.size == 1
|
|
1044
|
+
|
|
1045
|
+
v = values.first
|
|
1046
|
+
return nil unless v.is_a?(Integer) || (v.is_a?(Float) && !v.nan?)
|
|
1047
|
+
|
|
1048
|
+
v
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
def float_range_between(range, min_arg, max_arg)
|
|
1052
|
+
return Type::Combinator.constant_of(false) if range.canonical_max < min_arg || range.min > max_arg
|
|
1053
|
+
return Type::Combinator.constant_of(true) if range.min >= min_arg && range.canonical_max <= max_arg
|
|
1054
|
+
|
|
1055
|
+
bool_union
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
# `Float[a..b].clamp(lo, hi)` (either bound may be `nil` for the one-argument `clamp(lo..)` /
|
|
1059
|
+
# `clamp(..hi)` forms). Keeps the receiver's own end, exclusivity included, where it is the
|
|
1060
|
+
# tighter one. When the bracket lies wholly outside the receiver every value snaps to one bound
|
|
1061
|
+
# and the result is a point Rigor would have to invent, so it declines like the Integer twin.
|
|
1062
|
+
def float_range_clamp(range, min_arg, max_arg)
|
|
1063
|
+
new_min = min_arg.nil? ? range.min : [range.min, min_arg.to_f].max
|
|
1064
|
+
if max_arg.nil? || max_arg.to_f >= range.canonical_max
|
|
1065
|
+
new_max = range.max
|
|
1066
|
+
exclusive = range.exclude_end?
|
|
1067
|
+
else
|
|
1068
|
+
new_max = max_arg.to_f
|
|
1069
|
+
exclusive = false
|
|
1070
|
+
end
|
|
1071
|
+
return nil if new_min > (exclusive ? new_max.prev_float : new_max)
|
|
1072
|
+
|
|
1073
|
+
Type::Combinator.float_range(new_min, new_max, exclude_end: exclusive)
|
|
1074
|
+
end
|
|
1075
|
+
|
|
883
1076
|
# Receiver IntegerRange + two scalar `Constant<Integer>` args — the only IntegerRange-aware ternary
|
|
884
1077
|
# fold today. `between?` returns Trinary truthiness over the bracket; `clamp` returns the
|
|
885
1078
|
# intersected IntegerRange (or a collapsed Constant if the result pins a single point).
|
|
@@ -904,7 +1097,7 @@ module Rigor
|
|
|
904
1097
|
v.is_a?(Integer) ? v : nil
|
|
905
1098
|
end
|
|
906
1099
|
|
|
907
|
-
# `
|
|
1100
|
+
# `Integer[a..b].between?(min, max)`:
|
|
908
1101
|
# - Constant[true] when [a,b] ⊆ [min,max] (and finite).
|
|
909
1102
|
# - Constant[false] when [a,b] ∩ [min,max] is empty.
|
|
910
1103
|
# - bool union otherwise.
|
|
@@ -916,7 +1109,7 @@ module Rigor
|
|
|
916
1109
|
bool_union
|
|
917
1110
|
end
|
|
918
1111
|
|
|
919
|
-
# `
|
|
1112
|
+
# `Integer[a..b].clamp(min, max)`:
|
|
920
1113
|
# - new_lower = max(a, min), new_upper = min(b, max).
|
|
921
1114
|
# - When new_lower > new_upper the bracket excluded the range entirely; the call still returns one
|
|
922
1115
|
# of the bracket bounds at runtime, but Rigor is strictly less precise here than Ruby — decline
|
|
@@ -1112,8 +1305,8 @@ module Rigor
|
|
|
1112
1305
|
end
|
|
1113
1306
|
|
|
1114
1307
|
# Range % Range. Only the `(any range) % (positive constant n)` and `(any range) % (negative
|
|
1115
|
-
# constant n)` cases are folded precisely — the former narrows to `
|
|
1116
|
-
# `
|
|
1308
|
+
# constant n)` cases are folded precisely — the former narrows to `Integer[0..n-1]`, the latter to
|
|
1309
|
+
# `Integer[n+1..0]`. Other shapes fall back to nil.
|
|
1117
1310
|
def range_modulo(_left, right)
|
|
1118
1311
|
return nil unless right.finite? && right.min == right.max
|
|
1119
1312
|
|
|
@@ -1220,6 +1413,99 @@ module Rigor
|
|
|
1220
1413
|
}.freeze
|
|
1221
1414
|
private_constant :UNARY_RANGE_DIRECT
|
|
1222
1415
|
|
|
1416
|
+
# ADR-109 — unary folds over a bounded Float. No bounded range contains NaN, so the predicates
|
|
1417
|
+
# that exist to detect it are decided outright, and the monotone conversions map the two bounds.
|
|
1418
|
+
# An exclusive end is dropped where negation would turn it into an exclusive begin, a shape
|
|
1419
|
+
# Ruby's literal cannot spell: the result is the closed envelope, a superset.
|
|
1420
|
+
FLOAT_RANGE_UNARY = {
|
|
1421
|
+
abs: :float_range_abs,
|
|
1422
|
+
magnitude: :float_range_abs,
|
|
1423
|
+
"-@": :float_range_negate,
|
|
1424
|
+
"+@": :float_range_identity,
|
|
1425
|
+
to_f: :float_range_identity,
|
|
1426
|
+
nan?: :float_range_never_nan,
|
|
1427
|
+
finite?: :float_range_finite,
|
|
1428
|
+
zero?: :float_range_zero,
|
|
1429
|
+
positive?: :float_range_positive,
|
|
1430
|
+
negative?: :float_range_negative,
|
|
1431
|
+
floor: :float_range_to_integer,
|
|
1432
|
+
ceil: :float_range_to_integer,
|
|
1433
|
+
round: :float_range_to_integer,
|
|
1434
|
+
truncate: :float_range_to_integer,
|
|
1435
|
+
to_i: :float_range_to_integer
|
|
1436
|
+
}.freeze
|
|
1437
|
+
private_constant :FLOAT_RANGE_UNARY
|
|
1438
|
+
|
|
1439
|
+
def try_fold_unary_float_range(range, method_name)
|
|
1440
|
+
handler = FLOAT_RANGE_UNARY[method_name]
|
|
1441
|
+
return nil if handler.nil?
|
|
1442
|
+
|
|
1443
|
+
send(handler, range, method_name)
|
|
1444
|
+
end
|
|
1445
|
+
|
|
1446
|
+
def float_range_abs(range, _method_name)
|
|
1447
|
+
lower = range.min
|
|
1448
|
+
upper = range.canonical_max
|
|
1449
|
+
return range if lower >= 0.0
|
|
1450
|
+
return Type::Combinator.float_range(-upper, -lower) if upper <= 0.0
|
|
1451
|
+
|
|
1452
|
+
Type::Combinator.float_range(0.0, [-lower, upper].max)
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
def float_range_negate(range, _method_name)
|
|
1456
|
+
Type::Combinator.float_range(-range.canonical_max, -range.min)
|
|
1457
|
+
end
|
|
1458
|
+
|
|
1459
|
+
def float_range_identity(range, _method_name)
|
|
1460
|
+
range
|
|
1461
|
+
end
|
|
1462
|
+
|
|
1463
|
+
def float_range_never_nan(_range, _method_name)
|
|
1464
|
+
Type::Combinator.constant_of(false)
|
|
1465
|
+
end
|
|
1466
|
+
|
|
1467
|
+
def float_range_finite(range, _method_name)
|
|
1468
|
+
lower = range.min
|
|
1469
|
+
upper = range.canonical_max
|
|
1470
|
+
return Type::Combinator.constant_of(true) if lower.finite? && upper.finite?
|
|
1471
|
+
return Type::Combinator.constant_of(false) if lower == upper
|
|
1472
|
+
|
|
1473
|
+
bool_union
|
|
1474
|
+
end
|
|
1475
|
+
|
|
1476
|
+
def float_range_zero(range, _method_name)
|
|
1477
|
+
return Type::Combinator.constant_of(false) unless range.covers?(0.0)
|
|
1478
|
+
return Type::Combinator.constant_of(true) if range.min.zero? && range.canonical_max.zero?
|
|
1479
|
+
|
|
1480
|
+
bool_union
|
|
1481
|
+
end
|
|
1482
|
+
|
|
1483
|
+
# `Float#positive?` is `self > 0`, `negative?` is `self < 0`.
|
|
1484
|
+
def float_range_positive(range, _method_name)
|
|
1485
|
+
return Type::Combinator.constant_of(true) if range.min > 0.0
|
|
1486
|
+
return Type::Combinator.constant_of(false) if range.canonical_max <= 0.0
|
|
1487
|
+
|
|
1488
|
+
bool_union
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1491
|
+
def float_range_negative(range, _method_name)
|
|
1492
|
+
return Type::Combinator.constant_of(true) if range.canonical_max < 0.0
|
|
1493
|
+
return Type::Combinator.constant_of(false) if range.min >= 0.0
|
|
1494
|
+
|
|
1495
|
+
bool_union
|
|
1496
|
+
end
|
|
1497
|
+
|
|
1498
|
+
# `floor` / `ceil` / `round` / `truncate` / `to_i` are monotone, so the image of the range is the
|
|
1499
|
+
# integer range between the images of its bounds. An infinite bound raises `FloatDomainError` at
|
|
1500
|
+
# run time for the value that reaches it, so the fold declines and the RBS `Integer` stands.
|
|
1501
|
+
def float_range_to_integer(range, method_name)
|
|
1502
|
+
lower = range.min
|
|
1503
|
+
upper = range.canonical_max
|
|
1504
|
+
return nil if lower.infinite? || upper.infinite?
|
|
1505
|
+
|
|
1506
|
+
build_integer_range(lower.public_send(method_name), upper.public_send(method_name))
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1223
1509
|
def try_fold_unary_range(range, method_name)
|
|
1224
1510
|
return range_unary_predicate(range, method_name) if RANGE_UNARY_PREDICATES.include?(method_name)
|
|
1225
1511
|
return range_unary_shift(range, method_name) if RANGE_UNARY_SHIFTS.include?(method_name)
|
|
@@ -31,7 +31,7 @@ module Rigor
|
|
|
31
31
|
# and the reader-redefinition guard are shared with {StructFolding}.
|
|
32
32
|
extend MemberShapeProjection
|
|
33
33
|
|
|
34
|
-
# @return
|
|
34
|
+
# @return the folded result, or nil to defer.
|
|
35
35
|
def try_dispatch(context)
|
|
36
36
|
receiver = context.receiver
|
|
37
37
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
require_relative "../../rbs_extended"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module MethodDispatcher
|
|
9
|
+
# Issue #1350 — how `OverloadSelector` reads a `Dynamic[T]` argument. The internal spec keeps a `Dynamic` whose
|
|
10
|
+
# static facet is not `top` out of the imprecise arguments because its facet discriminates, but acceptance
|
|
11
|
+
# short-circuits on the wrapper and takes it against any parameter, so the receiver-affinity pre-sort's first arm
|
|
12
|
+
# won: `Money#add(Integer(v))` typed `Money` and `.even?` reported `call.undefined-method`.
|
|
13
|
+
#
|
|
14
|
+
# Selection therefore reads a narrow facet's members ({.select}), leaving out `nil`: a facet's `nil` is usually
|
|
15
|
+
# the join of an overload that returns it for some untyped input (`Integer(v)`'s `exception:` form), and chosen
|
|
16
|
+
# by it `Integer | nil` fell through to the only arm that takes `nil` and `Complex(Integer(v), 1)` turned
|
|
17
|
+
# `Complex?`. One member left stands in for the argument, pass 0 included, and the return stays unwrapped; two
|
|
18
|
+
# are selected one by one, and the distinct picks come back together for the dispatch layer's join, wrapped in
|
|
19
|
+
# `Dynamic` when their returns differ (#521).
|
|
20
|
+
#
|
|
21
|
+
# Only sealed members are read ({.sealed?}): a literal, or a plain `Integer`, `Float`, `Rational`, `Complex` or
|
|
22
|
+
# `Symbol`, core classes whose `new` and `allocate` are undefined. Such a member's runtime value is of exactly its
|
|
23
|
+
# class, where any other member's may be of a subclass and take an arm the member itself skips: `Numeric` in
|
|
24
|
+
# `2 ** v`'s `Complex | Numeric` skipped a project `(real) -> Float`, whose `real` alias names `Integer`, for a
|
|
25
|
+
# catch-all `(untyped) -> nil`, and `.floor` on the call reported `call.undefined-method`. Scanning the parameters
|
|
26
|
+
# for a subclass of the member caught a plain class name and missed aliases, `instance`, type variables,
|
|
27
|
+
# intersections, singletons and modules.
|
|
28
|
+
#
|
|
29
|
+
# Even a sealed member is read only where acceptance can rule it out ({.provable?}). Acceptance reads class
|
|
30
|
+
# relations from the analyzer's own process, so it answers `no` for `Integer` against a `(Printable)` that project
|
|
31
|
+
# RBS or source includes into `Integer` (#1352). A plain argument shares that, but the wrapper hid it from a
|
|
32
|
+
# `Dynamic` one: with `(Printable) -> String | (Integer) -> Integer`, reading the member typed
|
|
33
|
+
# `fmt(Integer(v)).upcase` as `Integer` and fired `call.undefined-method` where master read `String`. So every
|
|
34
|
+
# overload's positional parameters must be spelled as a class RBS declares, `untyped`, `top` or `nil`, through `?`
|
|
35
|
+
# and `|`, which leaves acceptance only a sealed class's fixed ancestry to read. A literal or `bool` parameter
|
|
36
|
+
# asks for a value, which a class member cannot prove it holds: `Symbol` skipped `(:json) -> String` for
|
|
37
|
+
# `(untyped) -> nil`, and a `TrueClass` member was refused by `bool` itself. Anything else, a module, a stubbed
|
|
38
|
+
# name, an alias, an interface, a type variable, `instance`, `self`, an intersection, a singleton or an untyped
|
|
39
|
+
# `(?)`, keeps the wrapper too. The list names what is provable rather than what is not, so a form it has not met
|
|
40
|
+
# falls back to master's reading.
|
|
41
|
+
#
|
|
42
|
+
# A wider facet keeps the wrapper and the receiver's arm, as before. It is usually itself a #521 join
|
|
43
|
+
# (`Dynamic[BigDecimal | Complex | Float | Integer | Rational]` from `n * untyped`), and read member by member it
|
|
44
|
+
# joined every arm again: 256 call sites across the survey corpus lost a precise type to `Dynamic`, and a
|
|
45
|
+
# recursion's fuel-exhausted `n * of(n - 1)` stopped reading `Integer`.
|
|
46
|
+
module FacetDistribution
|
|
47
|
+
# The most facet members, `nil` aside, that selection reads one by one.
|
|
48
|
+
MEMBER_LIMIT = 2
|
|
49
|
+
# Member-wise argument lists beyond this keep their wrappers.
|
|
50
|
+
CAP = 8
|
|
51
|
+
# Core classes whose `new` and `allocate` are undefined, so plain Ruby cannot make an instance of a subclass
|
|
52
|
+
# (`Integer`, `Float` and `Symbol` have no allocator at all; a `Rational` or `Complex` subclass instance takes
|
|
53
|
+
# `Marshal.load` or a rebound `Class#allocate`). `NilClass` leaves with the facet's `nil`, and `true` and
|
|
54
|
+
# `false` are read only as literals.
|
|
55
|
+
SEALED_CLASSES = %w[Integer Float Rational Complex Symbol].freeze
|
|
56
|
+
|
|
57
|
+
module_function
|
|
58
|
+
|
|
59
|
+
# Whether any argument is a `Dynamic` whose facet selection reads; the cheap guard of the hot path.
|
|
60
|
+
def faceted?(arg_types) = arg_types.any? { |arg| arg.is_a?(Type::Dynamic) && facet_members(arg) }
|
|
61
|
+
|
|
62
|
+
# Selects through the block, called as `yield(arg_types, member)`. It yields once per member-wise list, one
|
|
63
|
+
# member standing in for each faceted argument, with `member` true so the block answers only a genuine match.
|
|
64
|
+
# It yields once with the arguments as given instead when the overloads are not {.provable?}, when an argument
|
|
65
|
+
# has two members and the caller is the singular `select` (`member_wise` false, whose one answer a member order
|
|
66
|
+
# would decide), when there are more than {CAP} lists, or when any list matches nothing.
|
|
67
|
+
def select(arg_types, definition, member_wise:, environment:, &)
|
|
68
|
+
choices = arg_types.map { |arg| facet_members(arg) || [arg] }
|
|
69
|
+
return yield(arg_types, false) unless member_wise || choices.all? { |members| members.size == 1 }
|
|
70
|
+
return yield(arg_types, false) unless provable?(definition, environment)
|
|
71
|
+
|
|
72
|
+
picks = picks_by_member(choices, &)
|
|
73
|
+
picks.empty? ? yield(arg_types, false) : picks
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The distinct overloads the member-wise lists pick; none past {CAP} or when any list picks nothing.
|
|
77
|
+
def picks_by_member(choices)
|
|
78
|
+
return [] if choices.reduce(1) { |count, members| count * members.size } > CAP
|
|
79
|
+
|
|
80
|
+
lists = choices.first.product(*choices.drop(1)).map { |combination| yield(combination, true) }
|
|
81
|
+
lists.any?(&:empty?) ? [] : lists.flatten(1).uniq(&:object_id)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Whether every overload's positional parameters, rest and trailing included, are in a provable form
|
|
85
|
+
# ({.provable_param?}), where acceptance's answer for a sealed member rests only on the member class's fixed
|
|
86
|
+
# ancestry; a `rigor:v1:param:` override proves nothing either.
|
|
87
|
+
def provable?(definition, environment)
|
|
88
|
+
loader = environment&.rbs_loader
|
|
89
|
+
return false if loader.nil?
|
|
90
|
+
return false unless RbsExtended.param_type_override_map(definition, environment: environment).empty?
|
|
91
|
+
|
|
92
|
+
definition.method_types.all? do |method_type|
|
|
93
|
+
fun = method_type.type
|
|
94
|
+
next false unless fun.respond_to?(:required_positionals)
|
|
95
|
+
|
|
96
|
+
params = fun.required_positionals + fun.optional_positionals + fun.trailing_positionals
|
|
97
|
+
params += [fun.rest_positionals] if fun.rest_positionals
|
|
98
|
+
params.all? { |param| provable_param?(param.type, loader) }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def provable_param?(rbs_type, loader)
|
|
103
|
+
case rbs_type
|
|
104
|
+
when RBS::Types::ClassInstance then declared_class?(rbs_type.name.to_s.delete_prefix("::"), loader)
|
|
105
|
+
when RBS::Types::Optional then provable_param?(rbs_type.type, loader)
|
|
106
|
+
when RBS::Types::Union then rbs_type.types.all? { |member| provable_param?(member, loader) }
|
|
107
|
+
when RBS::Types::Bases::Any, RBS::Types::Bases::Top, RBS::Types::Bases::Nil then true
|
|
108
|
+
else false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# A class RBS declares, not a module and not a name Rigor stubbed because no RBS declares it: a class cannot be
|
|
113
|
+
# included, and a sealed member's class has a fixed ancestry, so acceptance's answer against it holds.
|
|
114
|
+
def declared_class?(name, loader)
|
|
115
|
+
loader.class_known?(name) && !loader.rbs_module?(name) && !loader.synthesized_type_names.include?(name)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# A `Dynamic` argument's facet members other than `nil`, or nil when selection keeps the wrapper: not a
|
|
119
|
+
# `Dynamic`, the untyped carrier, a facet that is only `nil`, one wider than {MEMBER_LIMIT}, or one with a
|
|
120
|
+
# member that is not sealed, an untyped member included.
|
|
121
|
+
def facet_members(arg)
|
|
122
|
+
return nil unless arg.is_a?(Type::Dynamic) && !arg.static_facet.is_a?(Type::Top)
|
|
123
|
+
|
|
124
|
+
facet = arg.static_facet
|
|
125
|
+
members = (facet.is_a?(Type::Union) ? facet.members : [facet]).reject { |member| nil_value?(member) }
|
|
126
|
+
members if members.size.between?(1, MEMBER_LIMIT) && members.all? { |member| sealed?(member) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# A member whose runtime value is of exactly its class: a `Constant`, which is a value of that exact class, or a
|
|
130
|
+
# plain instance of {SEALED_CLASSES}.
|
|
131
|
+
def sealed?(member)
|
|
132
|
+
case member
|
|
133
|
+
when Type::Constant then true
|
|
134
|
+
when Type::Nominal then member.type_args.empty? && SEALED_CLASSES.include?(member.class_name)
|
|
135
|
+
else false
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def nil_value?(type)
|
|
140
|
+
return type.value.nil? if type.is_a?(Type::Constant)
|
|
141
|
+
|
|
142
|
+
type.is_a?(Type::Nominal) && type.class_name == "NilClass"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|