rigortype 0.3.7 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- 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 +14 -1
- data/docs/handbook/11-sig-gen.md +96 -15
- 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 +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -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 +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- 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/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- 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/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +69 -13
- 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 +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +100 -29
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -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 +2 -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 +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- 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 +316 -40
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +70 -0
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- 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 +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- 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 +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- 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 +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -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 +31 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -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 +10 -6
- 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 +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -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/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- 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 +28 -0
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- 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 +613 -113
- data/lib/rigor/environment.rb +189 -27
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- 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/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -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/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 +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- 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/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- 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 +4 -5
- 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 +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- 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 +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -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/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 +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- 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 +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- 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/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- 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 +31 -12
- 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 +23 -1
- data/lib/rigor/plugin/registry.rb +67 -14
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- 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 +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- 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 +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +181 -102
- 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/observation_collector.rb +17 -15
- 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 +17 -11
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -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/writer.rb +23 -6
- 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 +2 -5
- 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 +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -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/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.rb +1 -0
- 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 +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -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.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -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 +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- 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 +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- 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 +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -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 +1 -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.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- 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 +0 -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 +1 -0
- 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 +1 -0
- 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 +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -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 +1 -0
- 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.rb +128 -3
- 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 +1 -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 +1 -0
- 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 +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +11 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +28 -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
|
|
|
@@ -16,7 +16,7 @@ module Rigor
|
|
|
16
16
|
# Each rule mirrors Ruby's actual iteration semantics:
|
|
17
17
|
#
|
|
18
18
|
# - `n.times { |i| … }` yields `i ∈ [0, n-1]` when `n > 0`, nothing otherwise. The block-param type is
|
|
19
|
-
# therefore `
|
|
19
|
+
# therefore `Integer[0..n-1]` for a `Constant<Integer>` receiver, `Integer[0..upper-1]` for a finite
|
|
20
20
|
# `IntegerRange`, and `non_negative_int` for any unbounded-above shape.
|
|
21
21
|
# - `a.upto(b) { |i| … }` yields `i ∈ [a, b]` when `a <= b`. Lower bound from the receiver, upper
|
|
22
22
|
# bound from the argument.
|
|
@@ -25,7 +25,7 @@ module Rigor
|
|
|
25
25
|
module IteratorDispatch
|
|
26
26
|
module_function
|
|
27
27
|
|
|
28
|
-
# @return
|
|
28
|
+
# @return block-param types, or
|
|
29
29
|
# nil to fall through to the next tier.
|
|
30
30
|
def block_param_types(context)
|
|
31
31
|
receiver = context.receiver
|
|
@@ -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
|
|
@@ -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
|