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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# `Hash#transform_keys(mapping)` return type, with or without a block.
|
|
9
|
+
#
|
|
10
|
+
# CRuby's `transform_keys_hash_i` looks each old key up in the mapping with `rb_hash_lookup2`, which
|
|
11
|
+
# ignores a default. A hit takes the mapping's value as the new key and never reaches the block. A miss
|
|
12
|
+
# yields the old key to the block, or keeps it when no block is given. The new key type is therefore the
|
|
13
|
+
# mapping's value type joined with the block's return type, or with the receiver's key type when there is
|
|
14
|
+
# no block. The values are the receiver's, untouched.
|
|
15
|
+
#
|
|
16
|
+
# Neither RBS line says that. rbs 4.2 declares `[K2] (hash[_Key, K2] replacements) { (K old_key) -> K2 }
|
|
17
|
+
# -> Hash[K2, V]`, and {RbsDispatch} leaves `K2` untyped because the mapping and the block both decide it.
|
|
18
|
+
# The blockless `[K2] (hash[_Key, K2]) -> Hash[K | K2, V]` leaves `K2` unbound too. rbs 3.x declares no
|
|
19
|
+
# mapping overload at all; `data/core_overlay/hash_rbs3.rbs` supplies rbs 4.2's on that line, with the same gaps.
|
|
20
|
+
# This tier answers ahead of both, so the forms it accepts get one answer on either RBS line.
|
|
21
|
+
#
|
|
22
|
+
# A mapping the analysis cannot read contributes a `Dynamic[top]` key arm instead of nothing. That covers an
|
|
23
|
+
# untyped argument, a `to_hash`-convertible object, a Hash subclass, the unlisted entries of an open shape,
|
|
24
|
+
# and an empty closed shape, which is what a mapping filled through an alias the engine does not track still
|
|
25
|
+
# reads as. A block the call carries but the block pass could not type contributes `Dynamic[top]` too. A
|
|
26
|
+
# literal with a `**splat` entry needs no arm of its own: its type joins the splatted hash's pairs and a
|
|
27
|
+
# `Dynamic[top]` arm, which also keeps a copy the code writes into (`m = { **o }; m[:b] = :w`) growing. A
|
|
28
|
+
# non-empty shape filled through an alias still reads narrower than it is: that is a gap in the binding's
|
|
29
|
+
# own type.
|
|
30
|
+
#
|
|
31
|
+
# Declines, leaving the RBS answer, when:
|
|
32
|
+
#
|
|
33
|
+
# - the method is not `transform_keys`. `transform_keys!` returns `self` and stays with RBS.
|
|
34
|
+
# - the argument count is not one, or is unknown with no block. A `*splat`, a forwarded `...`, or a lone
|
|
35
|
+
# `**splat` may pass no argument, and with no block that call answers an `Enumerator`. With a block, the
|
|
36
|
+
# call is the block form or the mapping form, and a `Dynamic[top]` mapping arm covers both.
|
|
37
|
+
# - a receiver member is not a `Hash` carrier: a `HashShape` other than the empty closed one, a `Hash`
|
|
38
|
+
# nominal, or a difference over one (`non-empty-hash[K, V]`). A subclass may override the method
|
|
39
|
+
# (`ActiveSupport::HashWithIndifferentAccess` does), and an empty closed shape answers `{}` whatever the
|
|
40
|
+
# mapping says.
|
|
41
|
+
module HashTransformKeysFolding
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
def try_dispatch(context)
|
|
45
|
+
return nil unless context.method_name == :transform_keys
|
|
46
|
+
|
|
47
|
+
block = block_arm(context)
|
|
48
|
+
renamed = mapping_arm(context, block)
|
|
49
|
+
return nil if renamed.nil?
|
|
50
|
+
|
|
51
|
+
receiver = context.receiver
|
|
52
|
+
members = receiver.is_a?(Type::Union) ? receiver.members : [receiver]
|
|
53
|
+
answers = members.map { |member| answer_for(member, renamed, block) }
|
|
54
|
+
return nil if answers.any?(&:nil?)
|
|
55
|
+
|
|
56
|
+
Type::Combinator.union(*answers)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The mapping form's block parameter: the receiver's key type, as rbs 4.2's `{ (K old_key) -> K2 }`
|
|
60
|
+
# declares it, and as `data/core_overlay/hash_rbs3.rbs` restates it on the rbs 3.x line. The RBS probe
|
|
61
|
+
# does not bind it for a union of receiver shapes. Reached through {IteratorDispatch.block_param_types};
|
|
62
|
+
# nil falls through to the RBS probe.
|
|
63
|
+
def block_param_types(context)
|
|
64
|
+
return nil unless context.args.size == 1
|
|
65
|
+
|
|
66
|
+
receiver = context.receiver
|
|
67
|
+
members = receiver.is_a?(Type::Union) ? receiver.members : [receiver]
|
|
68
|
+
keys = members.map { |member| receiver_key_value(member)&.first }
|
|
69
|
+
return nil if keys.any?(&:nil?)
|
|
70
|
+
|
|
71
|
+
[Type::Combinator.union(*keys)]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# `Hash[unmapped | renamed, V]` for one receiver member, or nil when the member is not a `Hash` carrier.
|
|
75
|
+
def answer_for(receiver, renamed, block)
|
|
76
|
+
key_value = receiver_key_value(receiver)
|
|
77
|
+
return nil if key_value.nil?
|
|
78
|
+
|
|
79
|
+
key, value = key_value
|
|
80
|
+
new_key = Type::Combinator.union(block || key, renamed)
|
|
81
|
+
Type::Combinator.nominal_of("Hash", type_args: [new_key, value])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The receiver's `[K, V]`, projected the way {RbsDispatch} projects a shape, or nil when the receiver
|
|
85
|
+
# is not a `Hash` carrier. An open shape may hold entries it does not list, so its key and value take
|
|
86
|
+
# a `Dynamic[top]` arm.
|
|
87
|
+
def receiver_key_value(receiver)
|
|
88
|
+
case receiver
|
|
89
|
+
when Type::HashShape then shape_key_value(receiver)
|
|
90
|
+
when Type::Nominal then nominal_key_value(receiver)
|
|
91
|
+
when Type::Difference then receiver_key_value(receiver.base)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def shape_key_value(shape)
|
|
96
|
+
return nil if shape.pairs.empty? && shape.closed?
|
|
97
|
+
|
|
98
|
+
key = Type::Combinator.union(*shape.pairs.keys.map { |k| Type::Combinator.constant_of(k) })
|
|
99
|
+
value = Type::Combinator.union(*shape.pairs.values)
|
|
100
|
+
return [key, value] if shape.closed?
|
|
101
|
+
|
|
102
|
+
[Type::Combinator.union(key, unknown), Type::Combinator.union(value, unknown)]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def nominal_key_value(nominal)
|
|
106
|
+
return nil unless nominal.class_name == "Hash"
|
|
107
|
+
return [unknown, unknown] unless nominal.type_args.size == 2
|
|
108
|
+
|
|
109
|
+
nominal.type_args
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# The new keys the mapping supplies, or nil when the call is not one this tier answers (see the module
|
|
113
|
+
# comment for the argument-count rule).
|
|
114
|
+
def mapping_arm(context, block)
|
|
115
|
+
arguments = argument_nodes(context.call_node)
|
|
116
|
+
return block.nil? ? nil : unknown if unresolved_argument_count?(arguments)
|
|
117
|
+
return nil unless context.args.size == 1
|
|
118
|
+
|
|
119
|
+
mapping_value_type(context.args.first)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# The mapping's value type. Anything whose value type cannot be read contributes `Dynamic[top]`,
|
|
123
|
+
# including a member that is not a Hash at all (`to_hash` may still convert it, and a `TypeError` for one
|
|
124
|
+
# that cannot is not this tier's to report).
|
|
125
|
+
def mapping_value_type(mapping)
|
|
126
|
+
case mapping
|
|
127
|
+
when Type::HashShape then shape_value_type(mapping)
|
|
128
|
+
when Type::Nominal then nominal_value_type(mapping)
|
|
129
|
+
when Type::Difference then mapping_value_type(mapping.base)
|
|
130
|
+
when Type::Union then Type::Combinator.union(*mapping.members.map { |member| mapping_value_type(member) })
|
|
131
|
+
else unknown
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# An empty closed shape contributes `Dynamic[top]`, not `bot`: the engine records no aliasing, so
|
|
136
|
+
# `m = {}; m.tap { |x| x[:a] = :z }` still reads `{}` after the store, and a `bot` arm would drop `:z`.
|
|
137
|
+
# A non-empty shape filled through an alias has the same gap, which this rule does not close.
|
|
138
|
+
def shape_value_type(shape)
|
|
139
|
+
return unknown if shape.pairs.empty? && shape.closed?
|
|
140
|
+
|
|
141
|
+
value = Type::Combinator.union(*shape.pairs.values)
|
|
142
|
+
shape.closed? ? value : Type::Combinator.union(value, unknown)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def nominal_value_type(nominal)
|
|
146
|
+
return unknown unless nominal.class_name == "Hash" && nominal.type_args.size == 2
|
|
147
|
+
|
|
148
|
+
nominal.type_args[1]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The block's return type; `Dynamic[top]` when the call carries a block the block pass left untyped (it
|
|
152
|
+
# rescues to nil); nil when there is no block, so the unmapped keys keep the receiver's key type. `&nil`
|
|
153
|
+
# passes no block, whatever type the block pass gave it.
|
|
154
|
+
def block_arm(context)
|
|
155
|
+
block_node = context.call_node.is_a?(Prism::CallNode) ? context.call_node.block : nil
|
|
156
|
+
return nil if block_node.is_a?(Prism::BlockArgumentNode) && block_node.expression.is_a?(Prism::NilNode)
|
|
157
|
+
return context.block_type if context.block_type
|
|
158
|
+
|
|
159
|
+
block_node ? unknown : nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# The call's argument nodes, or an empty list for a nil or non-call node (an internal caller, whose
|
|
163
|
+
# argument types are authoritative).
|
|
164
|
+
def argument_nodes(call_node)
|
|
165
|
+
return EMPTY_ARGUMENTS unless call_node.is_a?(Prism::CallNode)
|
|
166
|
+
|
|
167
|
+
call_node.arguments&.arguments || EMPTY_ARGUMENTS
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
EMPTY_ARGUMENTS = [].freeze
|
|
171
|
+
private_constant :EMPTY_ARGUMENTS
|
|
172
|
+
|
|
173
|
+
# A `*splat`, a forwarded `...`, or keyword arguments made only of `**splat` entries: each may pass no
|
|
174
|
+
# argument at all (`transform_keys(**{})` passes none), so whether there is a mapping is not static.
|
|
175
|
+
def unresolved_argument_count?(arguments)
|
|
176
|
+
arguments.any? do |argument|
|
|
177
|
+
case argument
|
|
178
|
+
when Prism::SplatNode, Prism::ForwardingArgumentsNode then true
|
|
179
|
+
when Prism::KeywordHashNode then argument.elements.all?(Prism::AssocSplatNode)
|
|
180
|
+
else false
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def unknown
|
|
186
|
+
Type::Combinator.untyped
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../../type"
|
|
4
4
|
require_relative "singleton_folding"
|
|
5
|
+
require_relative "hash_transform_keys_folding"
|
|
5
6
|
|
|
6
7
|
module Rigor
|
|
7
8
|
module Inference
|
|
@@ -16,16 +17,18 @@ module Rigor
|
|
|
16
17
|
# Each rule mirrors Ruby's actual iteration semantics:
|
|
17
18
|
#
|
|
18
19
|
# - `n.times { |i| … }` yields `i ∈ [0, n-1]` when `n > 0`, nothing otherwise. The block-param type is
|
|
19
|
-
# therefore `
|
|
20
|
+
# therefore `Integer[0..n-1]` for a `Constant<Integer>` receiver, `Integer[0..upper-1]` for a finite
|
|
20
21
|
# `IntegerRange`, and `non_negative_int` for any unbounded-above shape.
|
|
21
22
|
# - `a.upto(b) { |i| … }` yields `i ∈ [a, b]` when `a <= b`. Lower bound from the receiver, upper
|
|
22
23
|
# bound from the argument.
|
|
23
24
|
# - `a.downto(b) { |i| … }` yields the same domain `[b, a]`, just iterated in reverse. Lower bound
|
|
24
25
|
# from the argument, upper bound from the receiver.
|
|
26
|
+
# - `h.transform_keys(mapping) { |k| … }` yields the receiver's keys, a rule the RBS probe does not state for
|
|
27
|
+
# a union of receiver shapes ({HashTransformKeysFolding.block_param_types}).
|
|
25
28
|
module IteratorDispatch
|
|
26
29
|
module_function
|
|
27
30
|
|
|
28
|
-
# @return
|
|
31
|
+
# @return block-param types, or
|
|
29
32
|
# nil to fall through to the next tier.
|
|
30
33
|
def block_param_types(context)
|
|
31
34
|
receiver = context.receiver
|
|
@@ -41,6 +44,7 @@ module Rigor
|
|
|
41
44
|
when :group_by, :partition then single_element_block_params(receiver)
|
|
42
45
|
when :each_slice, :each_cons then slice_block_params(receiver)
|
|
43
46
|
when :new then class_new_block_params(receiver, args)
|
|
47
|
+
when :transform_keys then HashTransformKeysFolding.block_param_types(context)
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../../type"
|
|
4
4
|
require_relative "constant_folding"
|
|
5
|
+
require_relative "random_folding"
|
|
5
6
|
|
|
6
7
|
module Rigor
|
|
7
8
|
module Inference
|
|
@@ -43,7 +44,7 @@ module Rigor
|
|
|
43
44
|
# every entry is spelling-parity checked. Public (not private_constant) and Ractor-shareable
|
|
44
45
|
# because the gate and the spec both read it.
|
|
45
46
|
INTRINSIC_NAMES = Ractor.make_shareable(
|
|
46
|
-
Set[:Array, :Integer, :Float, :Rational, :Complex, :p, :pp, :String, :Hash, :format, :sprintf]
|
|
47
|
+
Set[:Array, :Integer, :Float, :Rational, :Complex, :p, :pp, :String, :Hash, :format, :sprintf, :rand]
|
|
47
48
|
)
|
|
48
49
|
|
|
49
50
|
# `Kernel#Rational` / `Kernel#Complex` constructor folds. When every argument is a `Type::Constant`
|
|
@@ -101,6 +102,11 @@ module Rigor
|
|
|
101
102
|
return try_string(context) if method_name == :String
|
|
102
103
|
return try_hash(context) if method_name == :Hash
|
|
103
104
|
return try_format(context) if FORMAT_METHODS.include?(method_name)
|
|
105
|
+
# ADR-109 / #833 — `Kernel#rand(a..b)` lands in the literal range, a bounded carrier of the
|
|
106
|
+
# range's own class (never a constant: the generator advances). The other argument shapes
|
|
107
|
+
# stay unfolded on purpose; {RandomFolding} owns the `Random.rand` spelling and the reading.
|
|
108
|
+
# A splat still declines: the reading needs exactly one literal-Range argument.
|
|
109
|
+
return RandomFolding.interval_for_args(args) if method_name == :rand
|
|
104
110
|
|
|
105
111
|
nil
|
|
106
112
|
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
require_relative "regexp_folding"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module MethodDispatcher
|
|
9
|
+
# Issue #1381 — slice reads of the proven `$~`. On a proven-match edge the flow engine narrows `$~` to
|
|
10
|
+
# `MatchData` and each unconditional group's `$N` to `String` (see `Narrowing#regex_match_predicate_scopes`).
|
|
11
|
+
# The Integer overload of RBS `MatchData#[]` already reads `String`, but the Range and `(start, length)`
|
|
12
|
+
# overloads and `values_at` read `Array[String?]`, and destructuring that binds every slot `String?`.
|
|
13
|
+
# This tier answers those reads with a Tuple, one slot per group, using the rule
|
|
14
|
+
# {RegexpFolding.fold_last_match_group} applies to `Regexp.last_match(N)`: `String` when `$N` is bound,
|
|
15
|
+
# `String?` when it is not (an optional or alternation-reachable group is nil on a successful match).
|
|
16
|
+
# Index 0 is the whole match, always a `String`.
|
|
17
|
+
#
|
|
18
|
+
# Supported, all with compile-time constant Integer arguments:
|
|
19
|
+
#
|
|
20
|
+
# * `$~[a..b]` / `$~[a...b]`
|
|
21
|
+
# * `$~[start, length]`
|
|
22
|
+
# * `$~.values_at(i, j, ...)`
|
|
23
|
+
#
|
|
24
|
+
# The receiver must be the `$~` read itself or a no-argument `Regexp.last_match`: a local MatchData carries
|
|
25
|
+
# no group facts, and the scope's `$N` describe only the frame's current match.
|
|
26
|
+
#
|
|
27
|
+
# The scope does not record the regex's group count, and a slice past the last group is shorter than the
|
|
28
|
+
# range asked for (`$~[1..3]` on a one-group match is `["x"]`) while `values_at` pads with nil. The highest
|
|
29
|
+
# bound `$K` is a lower bound on the count — `Scope#join_bindings` keeps a key only when both sides bind
|
|
30
|
+
# it — so every index must be at most K. Declines (RBS answers) when:
|
|
31
|
+
#
|
|
32
|
+
# - `$~` is not proven non-nil, or no scope is threaded through,
|
|
33
|
+
# - no `$1`..`$9` narrows to `String` (every group optional, no group at all, `when /a/, /b/`),
|
|
34
|
+
# - any index is above K, negative, or not a constant Integer, or a Range bound is nil (endless /
|
|
35
|
+
# beginless),
|
|
36
|
+
# - the slice is empty.
|
|
37
|
+
#
|
|
38
|
+
# K is read from `$1`..`$9` only: `Scope#forget_match_globals` does not clear `$10` and above (#1384), so a
|
|
39
|
+
# binding there may belong to an earlier match.
|
|
40
|
+
module MatchDataFolding
|
|
41
|
+
MAX_GROUP_GLOBAL = 9
|
|
42
|
+
private_constant :MAX_GROUP_GLOBAL
|
|
43
|
+
|
|
44
|
+
module_function
|
|
45
|
+
|
|
46
|
+
# @return folded Tuple, or nil to defer.
|
|
47
|
+
def try_dispatch(context)
|
|
48
|
+
receiver = context.receiver
|
|
49
|
+
return nil unless receiver.is_a?(Type::Nominal) && receiver.class_name == "MatchData"
|
|
50
|
+
|
|
51
|
+
scope = context.scope
|
|
52
|
+
return nil if scope.nil?
|
|
53
|
+
return nil unless last_match_read?(context.call_node, scope)
|
|
54
|
+
return nil unless RegexpFolding.proven_match?(scope.global(:$~))
|
|
55
|
+
|
|
56
|
+
highest = highest_bound_group(scope)
|
|
57
|
+
return nil if highest.nil?
|
|
58
|
+
|
|
59
|
+
indices = slot_indices(context.method_name, context.args, highest)
|
|
60
|
+
return nil if indices.nil?
|
|
61
|
+
|
|
62
|
+
Type::Combinator.tuple_of(*indices.map { |index| slot_type(index, scope) })
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The receiver expression reads the frame's current match: `$~`, or `last_match` with no arguments and no block
|
|
66
|
+
# on an expression that types as the core `Regexp` class. A project class that happens to be named `Regexp`
|
|
67
|
+
# (`Foo::Regexp` read as `Regexp` inside `module Foo`) types as its own singleton and does not count.
|
|
68
|
+
def last_match_read?(call_node, scope)
|
|
69
|
+
return false unless call_node.is_a?(Prism::CallNode)
|
|
70
|
+
|
|
71
|
+
receiver = call_node.receiver
|
|
72
|
+
case receiver
|
|
73
|
+
when Prism::GlobalVariableReadNode
|
|
74
|
+
receiver.name == :$~
|
|
75
|
+
when Prism::CallNode
|
|
76
|
+
receiver.name == :last_match && receiver.arguments.nil? && receiver.block.nil? &&
|
|
77
|
+
regexp_class?(receiver.receiver, scope)
|
|
78
|
+
else
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def regexp_class?(node, scope)
|
|
84
|
+
return false if node.nil?
|
|
85
|
+
|
|
86
|
+
type = scope.type_of(node)
|
|
87
|
+
type.is_a?(Type::Singleton) && type.class_name == "Regexp"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The highest N in 1..9 whose `$N` is narrowed to `String`, or nil when none is.
|
|
91
|
+
def highest_bound_group(scope)
|
|
92
|
+
MAX_GROUP_GLOBAL.downto(1).find { |index| string_global?(scope.global(:"$#{index}")) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def string_global?(type)
|
|
96
|
+
type.is_a?(Type::Nominal) && type.class_name == "String"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The group indices the call reads, in result order, or nil when the call shape is not folded or any index
|
|
100
|
+
# falls outside `0..highest`.
|
|
101
|
+
def slot_indices(method_name, args, highest)
|
|
102
|
+
indices =
|
|
103
|
+
case method_name
|
|
104
|
+
when :[] then index_slice(args)
|
|
105
|
+
when :values_at then values_at_indices(args)
|
|
106
|
+
end
|
|
107
|
+
return nil if indices.nil? || indices.none?
|
|
108
|
+
return nil unless indices.all? { |index| index.between?(0, highest) }
|
|
109
|
+
|
|
110
|
+
indices.to_a
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# `[range]` or `[start, length]`. A single Integer index is left to RBS, which already reads `String`.
|
|
114
|
+
def index_slice(args)
|
|
115
|
+
case args.size
|
|
116
|
+
when 1 then range_indices(args.first)
|
|
117
|
+
when 2 then start_length_indices(*args)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def range_indices(arg)
|
|
122
|
+
range = constant_value(arg)
|
|
123
|
+
return nil unless range.is_a?(Range)
|
|
124
|
+
|
|
125
|
+
first = range.begin
|
|
126
|
+
last = range.end
|
|
127
|
+
return nil unless first.is_a?(Integer) && last.is_a?(Integer)
|
|
128
|
+
return nil if first.negative? || last.negative?
|
|
129
|
+
|
|
130
|
+
range
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def start_length_indices(start_arg, length_arg)
|
|
134
|
+
start = constant_value(start_arg)
|
|
135
|
+
length = constant_value(length_arg)
|
|
136
|
+
return nil unless start.is_a?(Integer) && length.is_a?(Integer)
|
|
137
|
+
return nil if start.negative? || length.negative?
|
|
138
|
+
|
|
139
|
+
start...(start + length)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def values_at_indices(args)
|
|
143
|
+
indices = args.map { |arg| constant_value(arg) }
|
|
144
|
+
indices.all?(Integer) ? indices : nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def constant_value(type)
|
|
148
|
+
type.is_a?(Type::Constant) ? type.value : nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def slot_type(index, scope)
|
|
152
|
+
return Type::Combinator.nominal_of("String") if index.zero?
|
|
153
|
+
|
|
154
|
+
RegexpFolding.fold_last_match_group(Type::Combinator.constant_of(index), scope)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -44,11 +44,23 @@ module Rigor
|
|
|
44
44
|
MATH_BINARY = Set[:atan2, :hypot, :ldexp].freeze
|
|
45
45
|
MATH_TUPLE_UNARY = Set[:frexp, :lgamma].freeze
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
# ADR-109 — the monotone non-decreasing functions, each with the inclusive lower edge of its
|
|
48
|
+
# domain (`nil` for every real). Over a bounded argument the image is the closed range between the
|
|
49
|
+
# images of the bounds: `Math.sqrt(Integer[0..])` is `Float[0.0..]`, `Math.exp(Float[..0.0])` is
|
|
50
|
+
# `Float[0.0..1.0]`. A bound below the domain edge raises at run time for the value that reaches
|
|
51
|
+
# it, so the fold declines. Not listed: the non-monotone functions (`sin`, `cos`, `tan`, `gamma`,
|
|
52
|
+
# `erfc` decreases, `acos` decreases) and the ones whose image needs a sign split.
|
|
53
|
+
MATH_MONOTONE_DOMAIN = {
|
|
54
|
+
sqrt: 0.0, cbrt: nil, exp: nil, expm1: nil,
|
|
55
|
+
log: 0.0, log2: 0.0, log10: 0.0, log1p: -1.0,
|
|
56
|
+
atan: nil, sinh: nil, tanh: nil, asinh: nil, erf: nil
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
private_constant :MATH_UNARY, :MATH_BINARY, :MATH_TUPLE_UNARY, :MATH_MONOTONE_DOMAIN
|
|
48
60
|
|
|
49
61
|
module_function
|
|
50
62
|
|
|
51
|
-
# @return
|
|
63
|
+
# @return folded result, or nil to defer.
|
|
52
64
|
def try_dispatch(context)
|
|
53
65
|
receiver = context.receiver
|
|
54
66
|
method_name = context.method_name
|
|
@@ -77,13 +89,38 @@ module Rigor
|
|
|
77
89
|
return nil unless args.size == 1
|
|
78
90
|
|
|
79
91
|
x = numeric_constant(args.first)
|
|
80
|
-
return
|
|
92
|
+
return fold_monotone_range(method_name, args.first) if x.nil?
|
|
81
93
|
|
|
82
94
|
fold_float_result(Math.public_send(method_name, x))
|
|
83
95
|
rescue Math::DomainError, RangeError
|
|
84
96
|
nil
|
|
85
97
|
end
|
|
86
98
|
|
|
99
|
+
# The bounded-argument arm of {fold_unary} / {fold_log}: an `IntegerRange` or `FloatRange`
|
|
100
|
+
# argument to a monotone function folds to the range between the images of its bounds.
|
|
101
|
+
def fold_monotone_range(method_name, arg)
|
|
102
|
+
return nil unless MATH_MONOTONE_DOMAIN.key?(method_name)
|
|
103
|
+
|
|
104
|
+
lower, upper = range_bounds(arg)
|
|
105
|
+
return nil if lower.nil?
|
|
106
|
+
|
|
107
|
+
edge = MATH_MONOTONE_DOMAIN[method_name]
|
|
108
|
+
return nil if edge && lower < edge
|
|
109
|
+
|
|
110
|
+
Type::Combinator.float_range(Math.public_send(method_name, lower), Math.public_send(method_name, upper))
|
|
111
|
+
rescue Math::DomainError, RangeError, ArgumentError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# `[lower, upper]` as doubles for a bounded numeric carrier, `nil` for anything else. An
|
|
116
|
+
# exclusive Float end reads as its canonical closed bound, one double below.
|
|
117
|
+
def range_bounds(arg)
|
|
118
|
+
case arg
|
|
119
|
+
when Type::FloatRange then [arg.min, arg.canonical_max]
|
|
120
|
+
when Type::IntegerRange then [arg.lower.to_f, arg.upper.to_f]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
87
124
|
def fold_binary(method_name, args)
|
|
88
125
|
return nil unless args.size == 2
|
|
89
126
|
|
|
@@ -101,6 +138,7 @@ module Rigor
|
|
|
101
138
|
return nil unless [1, 2].include?(args.size)
|
|
102
139
|
|
|
103
140
|
values = args.map { |a| numeric_constant(a) }
|
|
141
|
+
return fold_monotone_range(:log, args.first) if args.size == 1 && values.first.nil?
|
|
104
142
|
return nil if values.any?(&:nil?)
|
|
105
143
|
|
|
106
144
|
fold_float_result(Math.log(*values))
|
|
@@ -29,11 +29,8 @@ module Rigor
|
|
|
29
29
|
# `.method("name")` with a precisely-known Symbol / String argument. Declines on every other shape
|
|
30
30
|
# so the RBS tier still answers `Method` for non-folding cases.
|
|
31
31
|
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# the fold.
|
|
35
|
-
# @param args [Array<Rigor::Type>] caller's argument types in order. Only the single-argument case
|
|
36
|
-
# matches; other arities decline.
|
|
32
|
+
# Reads `context.receiver`, `context.method_name` (only `:method` triggers the fold) and `context.args`
|
|
33
|
+
# (only the single-argument case matches; other arities decline).
|
|
37
34
|
def try_dispatch(context)
|
|
38
35
|
receiver = context.receiver
|
|
39
36
|
method_name = context.method_name
|