rigortype 0.3.8 → 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 +72 -1
- 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 +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- 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 +8 -24
- 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 +81 -22
- 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 +177 -26
- 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 +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- 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 +2 -4
- 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_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- 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 +2 -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 +2 -2
- 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 +388 -59
- data/lib/rigor/environment.rb +105 -20
- 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 +4 -7
- 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 +42 -12
- 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 +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- 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 +115 -22
- 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 +4 -5
- 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 +5 -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 +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -22,25 +22,19 @@ module Rigor
|
|
|
22
22
|
class OutOfRangeError < StandardError; end
|
|
23
23
|
|
|
24
24
|
class << self
|
|
25
|
-
# @param
|
|
26
|
-
# @param
|
|
27
|
-
# @param line [Integer] 1-indexed line number
|
|
28
|
-
# @param column [Integer] 1-indexed column number (byte index within the line)
|
|
29
|
-
# @return [Prism::Node, nil]
|
|
25
|
+
# @param line — 1-indexed line number
|
|
26
|
+
# @param column — 1-indexed column number (byte index within the line)
|
|
30
27
|
def at_position(source:, root:, line:, column:)
|
|
31
28
|
new(source: source, root: root).at_position(line: line, column: column)
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
# @param
|
|
35
|
-
# @param offset [Integer] 0-indexed byte offset
|
|
36
|
-
# @return [Prism::Node, nil]
|
|
31
|
+
# @param offset — 0-indexed byte offset
|
|
37
32
|
def at_offset(root:, offset:)
|
|
38
33
|
new(source: nil, root: root).at_offset(offset)
|
|
39
34
|
end
|
|
40
35
|
end
|
|
41
36
|
|
|
42
|
-
# @param source
|
|
43
|
-
# @param root [Prism::Node]
|
|
37
|
+
# @param source — used by `#at_position`; may be omitted when only `#at_offset` is needed.
|
|
44
38
|
def initialize(source:, root:)
|
|
45
39
|
@source = source
|
|
46
40
|
@root = root
|
|
@@ -48,7 +42,7 @@ module Rigor
|
|
|
48
42
|
|
|
49
43
|
# Resolve a `(line, column)` pair (1-indexed) to the deepest enclosing node.
|
|
50
44
|
#
|
|
51
|
-
# @raise
|
|
45
|
+
# @raise OutOfRangeError — if the line or column falls outside the source buffer.
|
|
52
46
|
def at_position(line:, column:)
|
|
53
47
|
offset = position_to_offset(line, column)
|
|
54
48
|
at_offset(offset)
|
|
@@ -25,8 +25,7 @@ module Rigor
|
|
|
25
25
|
module NodeWalker
|
|
26
26
|
module_function
|
|
27
27
|
|
|
28
|
-
# @
|
|
29
|
-
# @return [Enumerator] when no block is given.
|
|
28
|
+
# @return when no block is given.
|
|
30
29
|
def each(root, &)
|
|
31
30
|
return to_enum(__method__, root) unless block_given?
|
|
32
31
|
|
|
@@ -48,9 +47,7 @@ module Rigor
|
|
|
48
47
|
# block invocation MUST copy it (`Plugin::NodeContext` does). Used by the plugin engine to give
|
|
49
48
|
# `node_rule` blocks their enclosing class / method / block context (ADR-37 slice 1d).
|
|
50
49
|
#
|
|
51
|
-
# @
|
|
52
|
-
# @yieldparam ancestors [Array<Prism::Node>]
|
|
53
|
-
# @return [Enumerator] when no block is given.
|
|
50
|
+
# @return when no block is given.
|
|
54
51
|
def each_with_ancestors(root, &)
|
|
55
52
|
return to_enum(__method__, root) unless block_given?
|
|
56
53
|
|
|
@@ -91,9 +91,7 @@ module Rigor
|
|
|
91
91
|
INFO_GUARDED = %i[h5_systemic_cluster h6_genuine_bugs].freeze
|
|
92
92
|
private_constant :INFO_GUARDED
|
|
93
93
|
|
|
94
|
-
# @param diagnostics
|
|
95
|
-
# @param include_info [Boolean] let H5/H6 see `:info` diagnostics
|
|
96
|
-
# @return [Array<Hint>]
|
|
94
|
+
# @param include_info — let H5/H6 see `:info` diagnostics
|
|
97
95
|
def recognise(diagnostics, include_info: false)
|
|
98
96
|
claimed = {}.compare_by_identity
|
|
99
97
|
recognisers.filter_map do |recogniser|
|
data/lib/rigor/triage.rb
CHANGED
|
@@ -34,11 +34,9 @@ module Rigor
|
|
|
34
34
|
# `rbs.coverage.missing-gem`) survives; the count-based H5/H6 recognisers guard against info themselves so
|
|
35
35
|
# recognition trace never reads as a bug.
|
|
36
36
|
#
|
|
37
|
-
# @param
|
|
38
|
-
# @param
|
|
39
|
-
# @param
|
|
40
|
-
# @param include_info [Boolean] route info into the volume views
|
|
41
|
-
# @return [Report]
|
|
37
|
+
# @param top — hotspot-file cap
|
|
38
|
+
# @param hints — run the heuristic catalogue
|
|
39
|
+
# @param include_info — route info into the volume views
|
|
42
40
|
def analyze(diagnostics, top: 10, hints: true, include_info: false)
|
|
43
41
|
routed = include_info ? diagnostics : diagnostics.reject { |d| d.severity == :info }
|
|
44
42
|
Report.new(
|
|
@@ -26,9 +26,8 @@ module Rigor
|
|
|
26
26
|
|
|
27
27
|
attr_reader :trinary, :mode, :reasons
|
|
28
28
|
|
|
29
|
-
# @param
|
|
30
|
-
# @param
|
|
31
|
-
# @param reasons [Array<String>, String, nil] textual reasons; a
|
|
29
|
+
# @param mode — currently `:gradual` (default) or `:strict`.
|
|
30
|
+
# @param reasons — textual reasons; a
|
|
32
31
|
# single string is wrapped, `nil` becomes an empty array.
|
|
33
32
|
def initialize(trinary, mode: :gradual, reasons: nil)
|
|
34
33
|
raise ArgumentError, "trinary must be Rigor::Trinary, got #{trinary.class}" unless trinary.is_a?(Trinary)
|
|
@@ -42,18 +41,37 @@ module Rigor
|
|
|
42
41
|
|
|
43
42
|
class << self
|
|
44
43
|
def yes(mode: :gradual, reasons: nil)
|
|
45
|
-
|
|
44
|
+
literal_result(Trinary.yes, mode, reasons)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
def no(mode: :gradual, reasons: nil)
|
|
49
|
-
|
|
48
|
+
literal_result(Trinary.no, mode, reasons)
|
|
50
49
|
end
|
|
51
50
|
|
|
52
51
|
def maybe(mode: :gradual, reasons: nil)
|
|
53
|
-
|
|
52
|
+
literal_result(Trinary.maybe, mode, reasons)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# #775 — a result is an immutable value, so one instance serves every site that asks for the same
|
|
58
|
+
# verdict with the same literal reason (`"structural equality"`, `"exact name match"`, ...): the
|
|
59
|
+
# acceptance rules answer ~360k times on the lib self-check, most of them through a handful of
|
|
60
|
+
# such literals, and each answer cost the result plus its reasons array. A reason built at the
|
|
61
|
+
# site (an unfrozen interpolated String) or a reason list still builds a fresh result.
|
|
62
|
+
def literal_result(trinary, mode, reasons)
|
|
63
|
+
literal = reasons.nil? || (reasons.is_a?(String) && reasons.frozen?)
|
|
64
|
+
return new(trinary, mode: mode, reasons: reasons) unless literal
|
|
65
|
+
|
|
66
|
+
by_mode = (@literal_results[trinary] ||= {})
|
|
67
|
+
by_reason = (by_mode[mode] ||= {})
|
|
68
|
+
by_reason[reasons] ||= new(trinary, mode: mode, reasons: reasons)
|
|
54
69
|
end
|
|
55
70
|
end
|
|
56
71
|
|
|
72
|
+
# `Trinary` verdict => mode => literal reason (or nil) => the shared result.
|
|
73
|
+
@literal_results = {}
|
|
74
|
+
|
|
57
75
|
def yes?
|
|
58
76
|
trinary.yes?
|
|
59
77
|
end
|
|
@@ -7,6 +7,7 @@ require_relative "nominal"
|
|
|
7
7
|
require_relative "singleton"
|
|
8
8
|
require_relative "constant"
|
|
9
9
|
require_relative "integer_range"
|
|
10
|
+
require_relative "float_range"
|
|
10
11
|
require_relative "tuple"
|
|
11
12
|
require_relative "hash_shape"
|
|
12
13
|
require_relative "data_class"
|
|
@@ -60,7 +61,7 @@ module Rigor
|
|
|
60
61
|
# list of Rigor::Type values that carry the receiver's generic instantiation (`Array[Integer]` is
|
|
61
62
|
# `Nominal["Array", [Nominal["Integer"]]]`). Omitting the keyword produces the raw form
|
|
62
63
|
# `Nominal["Array"]`, which is structurally distinct from any applied form.
|
|
63
|
-
def nominal_of(class_name_or_object, type_args:
|
|
64
|
+
def nominal_of(class_name_or_object, type_args: Nominal::EMPTY_TYPE_ARGS)
|
|
64
65
|
Nominal.new(resolve_class_name(class_name_or_object), type_args)
|
|
65
66
|
end
|
|
66
67
|
|
|
@@ -68,8 +69,16 @@ module Rigor
|
|
|
68
69
|
Singleton.new(resolve_class_name(class_name_or_object))
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
# #775 — `nil`, `true` and `false` are interned: a Constant is an immutable value, and these three
|
|
73
|
+
# are built on every optional translation, nil check and predicate fold (~200k of the ~210k Constant
|
|
74
|
+
# constructions on the lib self-check), so one shared instance each is unobservable and free.
|
|
71
75
|
def constant_of(value)
|
|
72
|
-
|
|
76
|
+
case value
|
|
77
|
+
when nil then NIL_CONSTANT
|
|
78
|
+
when true then TRUE_CONSTANT
|
|
79
|
+
when false then FALSE_CONSTANT
|
|
80
|
+
else Constant.new(value)
|
|
81
|
+
end
|
|
73
82
|
end
|
|
74
83
|
|
|
75
84
|
# Widens every value-pinned (`Constant`) constituent of `type` to its nominal base (`Constant[1]` ->
|
|
@@ -87,9 +96,11 @@ module Rigor
|
|
|
87
96
|
# rather than flooring to `Dynamic[top]` (ADR-56).
|
|
88
97
|
widen_value_pinned(type.base)
|
|
89
98
|
when IntegerRange
|
|
90
|
-
# `
|
|
99
|
+
# `Integer[1..6]` is likewise a value-narrowed `Integer` (it erases to `Integer` in RBS); widen it
|
|
91
100
|
# so a bounded-int accumulator converges (ADR-56).
|
|
92
101
|
nominal_of("Integer")
|
|
102
|
+
when FloatRange
|
|
103
|
+
nominal_of("Float")
|
|
93
104
|
when Union
|
|
94
105
|
union(*type.members.map { |member| widen_value_pinned(member) })
|
|
95
106
|
else
|
|
@@ -131,6 +142,23 @@ module Rigor
|
|
|
131
142
|
IntegerRange.new(IntegerRange::NEG_INFINITY, IntegerRange::POS_INFINITY)
|
|
132
143
|
end
|
|
133
144
|
|
|
145
|
+
# ADR-109 WD4 — `Float[min..max]` / `Float[min...max]`. Bounds are non-NaN Floats; the infinities
|
|
146
|
+
# are ordinary bounds. Raises on an empty range (the payload builder declines before reaching
|
|
147
|
+
# here).
|
|
148
|
+
def float_range(min, max, exclude_end: false)
|
|
149
|
+
FloatRange.new(min, max, exclude_end: exclude_end)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Every Float except NaN: `Float[-Float::INFINITY..]`.
|
|
153
|
+
def non_nan_float
|
|
154
|
+
FloatRange.new(-Float::INFINITY, Float::INFINITY)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Every Float except NaN and the infinities: `Float[-Float::MAX..Float::MAX]`.
|
|
158
|
+
def finite_float
|
|
159
|
+
FloatRange.new(-Float::MAX, Float::MAX)
|
|
160
|
+
end
|
|
161
|
+
|
|
134
162
|
# Point-removal refinement carrier (ADR-3 OQ3 Option C). Use `non_empty_string` / `non_zero_int` /
|
|
135
163
|
# `non_empty_array` / `non_empty_hash` for the imported built-in shapes; raw `difference(base,
|
|
136
164
|
# removed)` for ad-hoc refinements an `RBS::Extended` annotation introduces.
|
|
@@ -382,6 +410,13 @@ module Rigor
|
|
|
382
410
|
# Normalized union. Flattens nested Unions, deduplicates structurally equal members, drops Bot, and
|
|
383
411
|
# collapses 0/1-member results.
|
|
384
412
|
def union(*types)
|
|
413
|
+
# #775 — the same carrier on both sides of a join is already its own union. With the closed-alias
|
|
414
|
+
# translation memo every `Rigor::Type::t` reader shares one 21-member Union, so a control-flow join of
|
|
415
|
+
# an untouched binding would otherwise re-flatten and re-unique 42 members to rebuild an equal object.
|
|
416
|
+
# `rigor trace` still sees the merge (the tracer records degenerate merges).
|
|
417
|
+
first = types.first
|
|
418
|
+
return first if types.size == 2 && first.equal?(types[1]) && !Inference::FlowTracer.active?
|
|
419
|
+
|
|
385
420
|
result = collapse_union(normalized_union_members(types))
|
|
386
421
|
if Inference::BudgetTrace.enabled? && result.is_a?(Union)
|
|
387
422
|
Inference::BudgetTrace.observe(Inference::BudgetTrace::UNION_ARITY, result.members.size)
|
|
@@ -790,12 +825,35 @@ module Rigor
|
|
|
790
825
|
|
|
791
826
|
return [top] if flattened.any?(Top)
|
|
792
827
|
|
|
793
|
-
unique_members(flattened)
|
|
828
|
+
absorb_float_ranges(unique_members(flattened))
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
# ADR-109 WD5 — a `FloatRange` member is absorbed by a bare `Nominal[Float]` member or by a
|
|
832
|
+
# `FloatRange` member that contains it. The truthy edge of `x > c` is a range and the falsy
|
|
833
|
+
# edge keeps `Float`, so the join after every Float guard would otherwise carry both and every
|
|
834
|
+
# later guard would narrow member by member into a growing union (`Float[...1.0] |
|
|
835
|
+
# Float[0.0...1.0]`) that names one set. `IntegerRange` keeps its pre-ADR-109 behaviour
|
|
836
|
+
# (`Integer | Integer[0..5]` stays as written) because both of its edges narrow.
|
|
837
|
+
def absorb_float_ranges(members)
|
|
838
|
+
return members unless members.any?(FloatRange)
|
|
839
|
+
|
|
840
|
+
members.reject do |member|
|
|
841
|
+
member.is_a?(FloatRange) &&
|
|
842
|
+
members.any? { |other| !other.equal?(member) && float_range_absorbed_by?(member, other) }
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def float_range_absorbed_by?(range, other)
|
|
847
|
+
case other
|
|
848
|
+
when Nominal then other.class_name == "Float" && other.type_args.empty?
|
|
849
|
+
when FloatRange then other != range && other.accepts(range).yes?
|
|
850
|
+
else false
|
|
851
|
+
end
|
|
794
852
|
end
|
|
795
853
|
|
|
796
854
|
def unique_members(types)
|
|
797
855
|
types.each_with_object([]) do |type, unique|
|
|
798
|
-
unique << type unless unique.any? { |member| member == type }
|
|
856
|
+
unique << type unless unique.any? { |member| member.equal?(type) || member == type }
|
|
799
857
|
end
|
|
800
858
|
end
|
|
801
859
|
|
|
@@ -864,6 +922,11 @@ module Rigor
|
|
|
864
922
|
|
|
865
923
|
# Eager-allocated at load time; see `untyped` method comment above.
|
|
866
924
|
@untyped = Dynamic.new(Top.instance)
|
|
925
|
+
|
|
926
|
+
NIL_CONSTANT = Constant.new(nil)
|
|
927
|
+
TRUE_CONSTANT = Constant.new(true)
|
|
928
|
+
FALSE_CONSTANT = Constant.new(false)
|
|
929
|
+
private_constant :NIL_CONSTANT, :TRUE_CONSTANT, :FALSE_CONSTANT
|
|
867
930
|
end
|
|
868
931
|
end
|
|
869
932
|
end
|
|
@@ -24,8 +24,8 @@ module Rigor
|
|
|
24
24
|
class DataClass
|
|
25
25
|
attr_reader :members, :class_name
|
|
26
26
|
|
|
27
|
-
# @param members
|
|
28
|
-
# @param class_name
|
|
27
|
+
# @param members — ordered member names.
|
|
28
|
+
# @param class_name — the bound class name, or nil for
|
|
29
29
|
# the anonymous `Data.define(...)` result.
|
|
30
30
|
def initialize(members, class_name = nil)
|
|
31
31
|
unless members.is_a?(Array) && members.all?(Symbol)
|
|
@@ -27,9 +27,9 @@ module Rigor
|
|
|
27
27
|
class DataInstance
|
|
28
28
|
attr_reader :members, :class_name
|
|
29
29
|
|
|
30
|
-
# @param members
|
|
30
|
+
# @param members — ordered member -> type
|
|
31
31
|
# map. Every declared member is present (Data instances are total).
|
|
32
|
-
# @param class_name
|
|
32
|
+
# @param class_name — the tagging class name, or nil for
|
|
33
33
|
# an instance of an anonymous `Data.define(...)` class.
|
|
34
34
|
def initialize(members, class_name = nil)
|
|
35
35
|
unless members.is_a?(Hash) && members.each_key.all?(Symbol)
|
|
@@ -44,12 +44,12 @@ module Rigor
|
|
|
44
44
|
freeze
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
# @return
|
|
47
|
+
# @return ordered member names.
|
|
48
48
|
def member_names
|
|
49
49
|
members.keys
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
# @return
|
|
52
|
+
# @return the member's value type, or nil when the
|
|
53
53
|
# name is not a declared member.
|
|
54
54
|
def member_type(name)
|
|
55
55
|
members[name]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../trinary"
|
|
4
|
+
require_relative "acceptance_router"
|
|
5
|
+
require_relative "plain_lattice"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Type
|
|
9
|
+
# A bounded float range carrier: every `Float` that the Ruby range literal it displays as would
|
|
10
|
+
# `cover?` (ADR-109 WD4). Bounds are doubles; `-Float::INFINITY` and `Float::INFINITY` are ordinary
|
|
11
|
+
# bounds, never sentinels, because Ruby orders them. The range may exclude its end (`0.0...1.0`)
|
|
12
|
+
# and never its beginning, because Ruby's literal has no exclusive begin.
|
|
13
|
+
#
|
|
14
|
+
# No bounded range contains NaN: `(a..b).cover?(Float::NAN)` is false for every `a` and `b`, so
|
|
15
|
+
# the range from `-Float::INFINITY` to `Float::INFINITY` is "every Float except NaN"
|
|
16
|
+
# (`non-nan-float`), not `Float`. The whole of `Float` (`nil..nil` in Ruby, which does cover NaN)
|
|
17
|
+
# is never a carrier; the payload builder normalises it to `Nominal[Float]`.
|
|
18
|
+
#
|
|
19
|
+
# Two spellings bound the same set when their ends are neighbouring doubles: `0.0...1.0` and
|
|
20
|
+
# `0.0..1.0.prev_float` cover the same floats. Equality, hashing and containment run on the
|
|
21
|
+
# canonical closed form (`canonical_max`); `describe` keeps the written form.
|
|
22
|
+
#
|
|
23
|
+
# Erasure to RBS is always "Float".
|
|
24
|
+
class FloatRange
|
|
25
|
+
attr_reader :min, :max
|
|
26
|
+
|
|
27
|
+
def initialize(min, max, exclude_end: false)
|
|
28
|
+
validate_bound!(min, "min")
|
|
29
|
+
validate_bound!(max, "max")
|
|
30
|
+
# `-0.0` and `0.0` are one point in the order (`-0.0 == 0.0`, `(-0.0..0.0).cover?(0.0)`); keep
|
|
31
|
+
# the one spelling so the display cannot suggest two ranges.
|
|
32
|
+
@min = min.zero? ? 0.0 : min
|
|
33
|
+
@max = max.zero? ? 0.0 : max
|
|
34
|
+
@exclude_end = exclude_end == true
|
|
35
|
+
if @min > canonical_max
|
|
36
|
+
raise ArgumentError, "FloatRange #{Range.new(@min, @max, @exclude_end).inspect} is empty"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def exclude_end?
|
|
43
|
+
@exclude_end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The greatest double the range contains. `Float::INFINITY.prev_float` is `Float::MAX`, so an
|
|
47
|
+
# exclusive infinite end reads as "finite above".
|
|
48
|
+
def canonical_max
|
|
49
|
+
@exclude_end ? max.prev_float : max
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Every Float except NaN.
|
|
53
|
+
def universal?
|
|
54
|
+
min == -Float::INFINITY && canonical_max == Float::INFINITY
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Ruby's own predicate is the definition: the carrier covers `value` exactly when the range it
|
|
58
|
+
# displays as does.
|
|
59
|
+
def covers?(value)
|
|
60
|
+
return false unless value.is_a?(Float)
|
|
61
|
+
|
|
62
|
+
Range.new(min, max, @exclude_end).cover?(value)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Keyed by the canonical closed bounds so a written `-Float::MAX...Float::INFINITY` reads as the
|
|
66
|
+
# set it is.
|
|
67
|
+
ALIAS_NAMES = Ractor.make_shareable({
|
|
68
|
+
[-Float::INFINITY, Float::INFINITY] => "non-nan-float",
|
|
69
|
+
[-Float::MAX, Float::MAX] => "finite-float"
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
def describe(_verbosity = :short)
|
|
73
|
+
ALIAS_NAMES[[min, canonical_max]] || generic_description
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The written form: an infinite bound is left out where Ruby's literal can (beginless, endless),
|
|
77
|
+
# spelled `Float::INFINITY` where it cannot (`...Float::INFINITY` is "finite above"), and the two
|
|
78
|
+
# extreme finite doubles read as the constants a Rubyist would write.
|
|
79
|
+
def generic_description
|
|
80
|
+
op = @exclude_end ? "..." : ".."
|
|
81
|
+
lo = min == -Float::INFINITY ? "" : float_literal(min)
|
|
82
|
+
hi = max == Float::INFINITY && !@exclude_end ? "" : float_literal(max)
|
|
83
|
+
"Float[#{lo}#{op}#{hi}]"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def erase_to_rbs
|
|
87
|
+
"Float"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
include Rigor::Type::PlainLattice
|
|
91
|
+
|
|
92
|
+
include Rigor::Type::AcceptanceRouter
|
|
93
|
+
|
|
94
|
+
# Hand-written rather than `value_fields`: two carriers are the same type when they cover the
|
|
95
|
+
# same doubles, whatever end they were written with.
|
|
96
|
+
def ==(other)
|
|
97
|
+
other.is_a?(FloatRange) && min == other.min && canonical_max == other.canonical_max
|
|
98
|
+
end
|
|
99
|
+
alias eql? ==
|
|
100
|
+
|
|
101
|
+
def hash
|
|
102
|
+
[self.class, min, canonical_max].hash
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def inspect
|
|
106
|
+
"#<Rigor::Type::FloatRange #{describe(:short)}>"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def float_literal(value)
|
|
112
|
+
case value
|
|
113
|
+
when Float::INFINITY then "Float::INFINITY"
|
|
114
|
+
when -Float::INFINITY then "-Float::INFINITY"
|
|
115
|
+
when Float::MAX then "Float::MAX"
|
|
116
|
+
when -Float::MAX then "-Float::MAX"
|
|
117
|
+
else value.inspect
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def validate_bound!(bound, label)
|
|
122
|
+
return if bound.is_a?(Float) && !bound.nan?
|
|
123
|
+
|
|
124
|
+
raise ArgumentError, "FloatRange #{label} must be a non-NaN Float, got #{bound.inspect}"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -34,21 +34,21 @@ module Rigor
|
|
|
34
34
|
|
|
35
35
|
attr_reader :pairs, :required_keys, :optional_keys, :read_only_keys, :extra_keys
|
|
36
36
|
|
|
37
|
-
# @param pairs
|
|
37
|
+
# @param pairs — ordered map of
|
|
38
38
|
# keys to declared types. Keys MUST be Symbol or String;
|
|
39
39
|
# values MUST be Rigor::Type instances. The hash is duped and
|
|
40
40
|
# frozen at construction; callers MUST NOT mutate the input
|
|
41
41
|
# afterwards (mutation does not affect the carrier, but the
|
|
42
42
|
# carrier is a value object).
|
|
43
|
-
# @param required_keys
|
|
43
|
+
# @param required_keys — keys that MUST
|
|
44
44
|
# be present. When omitted, every non-optional key is required.
|
|
45
45
|
# When supplied without optional_keys, every remaining known key
|
|
46
46
|
# is treated as optional.
|
|
47
|
-
# @param optional_keys
|
|
47
|
+
# @param optional_keys — keys that MAY
|
|
48
48
|
# be absent. Optional absence is not a stored nil.
|
|
49
|
-
# @param read_only_keys
|
|
49
|
+
# @param read_only_keys — entries that cannot
|
|
50
50
|
# be written through this shape view.
|
|
51
|
-
# @param extra_keys
|
|
51
|
+
# @param extra_keys — :closed rejects keys outside pairs;
|
|
52
52
|
# :open permits them.
|
|
53
53
|
def initialize(pairs = nil, **keywords)
|
|
54
54
|
pairs, policy = split_constructor_args(pairs, keywords)
|
|
@@ -8,10 +8,11 @@ require_relative "plain_lattice"
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
10
|
# A bounded integer range carrier. Each bound is either an `Integer` or one of the symbolic
|
|
11
|
-
# infinities `:neg_infinity` / `:pos_infinity`.
|
|
12
|
-
#
|
|
13
|
-
# `
|
|
14
|
-
#
|
|
11
|
+
# infinities `:neg_infinity` / `:pos_infinity`. Displayed with the Ruby range literal that
|
|
12
|
+
# `Range#cover?` would accept the same integers for (`Integer[1..10]`, `Integer[0..]`,
|
|
13
|
+
# `Integer[..-1]`; ADR-109). The named aliases `positive-int` (1..), `non-negative-int` (0..),
|
|
14
|
+
# `negative-int` (..-1), `non-positive-int` (..0) all surface through this single carrier and are
|
|
15
|
+
# recovered in `describe` for human-friendly output.
|
|
15
16
|
#
|
|
16
17
|
# Constraints on construction:
|
|
17
18
|
# - both bounds must be either `Integer` or one of the two infinity sentinels;
|
|
@@ -76,8 +77,10 @@ module Rigor
|
|
|
76
77
|
Float::INFINITY
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
# The universal range reads as the class it erases to: a range with no bound carries no fact
|
|
81
|
+
# a Rubyist would spell.
|
|
79
82
|
ALIAS_NAMES = Ractor.make_shareable({
|
|
80
|
-
[NEG_INFINITY, POS_INFINITY] => "
|
|
83
|
+
[NEG_INFINITY, POS_INFINITY] => "Integer",
|
|
81
84
|
[1, POS_INFINITY] => "positive-int",
|
|
82
85
|
[0, POS_INFINITY] => "non-negative-int",
|
|
83
86
|
[NEG_INFINITY, -1] => "negative-int",
|
|
@@ -88,11 +91,13 @@ module Rigor
|
|
|
88
91
|
ALIAS_NAMES[[min, max]] || generic_description
|
|
89
92
|
end
|
|
90
93
|
|
|
94
|
+
# Always the closed form: for integers `a...b` and `a..b-1` are the same set, and the closed
|
|
95
|
+
# spelling is the one the grammar reads back without a canonicalisation step.
|
|
91
96
|
def generic_description
|
|
92
|
-
return "
|
|
93
|
-
return "
|
|
97
|
+
return "Integer[#{min}..]" if max == POS_INFINITY
|
|
98
|
+
return "Integer[..#{max}]" if min == NEG_INFINITY
|
|
94
99
|
|
|
95
|
-
"
|
|
100
|
+
"Integer[#{min}..#{max}]"
|
|
96
101
|
end
|
|
97
102
|
|
|
98
103
|
def erase_to_rbs
|
data/lib/rigor/type/nominal.rb
CHANGED
|
@@ -25,13 +25,19 @@ module Rigor
|
|
|
25
25
|
class Nominal
|
|
26
26
|
attr_reader :class_name, :type_args
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# The shared empty argument list. The raw form `Nominal["Array"]` is by far the most constructed
|
|
29
|
+
# type (~600k on the lib self-check); a fresh `[]` default plus its defensive `dup` cost two
|
|
30
|
+
# allocations per construction for an array nobody can observe.
|
|
31
|
+
EMPTY_TYPE_ARGS = [].freeze
|
|
32
|
+
|
|
33
|
+
def initialize(class_name, type_args = EMPTY_TYPE_ARGS)
|
|
29
34
|
raise ArgumentError, "class_name must be a String, got #{class_name.class}" unless class_name.is_a?(String)
|
|
30
35
|
raise ArgumentError, "class_name must not be empty" if class_name.empty?
|
|
31
36
|
raise ArgumentError, "type_args must be an Array, got #{type_args.class}" unless type_args.is_a?(Array)
|
|
32
37
|
|
|
33
38
|
@class_name = class_name.freeze
|
|
34
|
-
|
|
39
|
+
# A frozen array is already immutable, so sharing it is unobservable; only a mutable one is copied.
|
|
40
|
+
@type_args = type_args.frozen? ? type_args : type_args.dup.freeze
|
|
35
41
|
freeze
|
|
36
42
|
end
|
|
37
43
|
|
data/lib/rigor/type/refined.rb
CHANGED
|
@@ -135,8 +135,8 @@ module Rigor
|
|
|
135
135
|
NUMERIC_LITERAL_PREFIX = /\A[+-]?\d/
|
|
136
136
|
private_constant :NUMERIC_LITERAL_PREFIX
|
|
137
137
|
|
|
138
|
-
# @param value
|
|
139
|
-
# @return
|
|
138
|
+
# @param value — typically a `Constant#value`
|
|
139
|
+
# @return true when `value` is a String that is a
|
|
140
140
|
# single, complete Ruby numeric literal. Total over
|
|
141
141
|
# arbitrary input — never raises (Prism reports malformed
|
|
142
142
|
# input through `errors`, it does not throw).
|
|
@@ -216,7 +216,7 @@ module Rigor
|
|
|
216
216
|
}.freeze
|
|
217
217
|
private_constant :COMPLEMENT_PAIRS
|
|
218
218
|
|
|
219
|
-
# @return
|
|
219
|
+
# @return the registered complement predicate
|
|
220
220
|
# id, or nil when no pair is registered for this predicate.
|
|
221
221
|
def complement_predicate_id
|
|
222
222
|
COMPLEMENT_PAIRS[predicate_id]
|
|
@@ -26,10 +26,10 @@ module Rigor
|
|
|
26
26
|
class StructClass
|
|
27
27
|
attr_reader :members, :class_name, :keyword_init
|
|
28
28
|
|
|
29
|
-
# @param members
|
|
30
|
-
# @param class_name
|
|
29
|
+
# @param members — ordered member names.
|
|
30
|
+
# @param class_name — the bound class name, or nil for
|
|
31
31
|
# the anonymous `Struct.new(...)` result.
|
|
32
|
-
# @param keyword_init
|
|
32
|
+
# @param keyword_init — the `keyword_init:` flag.
|
|
33
33
|
def initialize(members, class_name = nil, keyword_init: false)
|
|
34
34
|
unless members.is_a?(Array) && members.all?(Symbol)
|
|
35
35
|
raise ArgumentError, "members must be an Array of Symbols, got #{members.inspect}"
|
|
@@ -29,9 +29,9 @@ module Rigor
|
|
|
29
29
|
class StructInstance
|
|
30
30
|
attr_reader :members, :class_name
|
|
31
31
|
|
|
32
|
-
# @param members
|
|
32
|
+
# @param members — ordered member -> type
|
|
33
33
|
# map. Every declared member is present (Struct instances are total).
|
|
34
|
-
# @param class_name
|
|
34
|
+
# @param class_name — the tagging class name, or nil for
|
|
35
35
|
# an instance of an anonymous `Struct.new(...)` class.
|
|
36
36
|
def initialize(members, class_name = nil)
|
|
37
37
|
unless members.is_a?(Hash) && members.each_key.all?(Symbol)
|
|
@@ -46,12 +46,12 @@ module Rigor
|
|
|
46
46
|
freeze
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# @return
|
|
49
|
+
# @return ordered member names.
|
|
50
50
|
def member_names
|
|
51
51
|
members.keys
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# @return
|
|
54
|
+
# @return the member's value type, or nil when the
|
|
55
55
|
# name is not a declared member.
|
|
56
56
|
def member_type(name)
|
|
57
57
|
members[name]
|
data/lib/rigor/type.rb
CHANGED
|
@@ -17,6 +17,7 @@ require_relative "type/nominal"
|
|
|
17
17
|
require_relative "type/singleton"
|
|
18
18
|
require_relative "type/constant"
|
|
19
19
|
require_relative "type/integer_range"
|
|
20
|
+
require_relative "type/float_range"
|
|
20
21
|
require_relative "type/tuple"
|
|
21
22
|
require_relative "type/hash_shape"
|
|
22
23
|
require_relative "type/data_class"
|
|
@@ -48,7 +48,7 @@ module Rigor
|
|
|
48
48
|
# stays additive — every earlier-shape Generic remains valid.
|
|
49
49
|
def valid_arg?(arg)
|
|
50
50
|
arg.is_a?(Identifier) || arg.is_a?(Generic) || arg.is_a?(IntegerLiteral) ||
|
|
51
|
-
arg.is_a?(SymbolLiteral) || arg.is_a?(StringLiteral) ||
|
|
51
|
+
arg.is_a?(SymbolLiteral) || arg.is_a?(StringLiteral) || arg.is_a?(RangeLiteral) ||
|
|
52
52
|
arg.is_a?(IndexedAccess) || arg.is_a?(Union)
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module TypeNode
|
|
5
|
+
# Range-literal AST node: a Ruby `Range` literal in a type-arg position, spelled as Ruby spells
|
|
6
|
+
# it (`1..10`, `1...10`, `1..`, `..10`, `nil..nil`). ADR-109 makes it the bound of the numeric
|
|
7
|
+
# range refinements, `Integer[1..10]`; under any other head the resolver lifts it to a
|
|
8
|
+
# `Constant<Range>` so a plugin resolver may consume it like the other leaf literals.
|
|
9
|
+
#
|
|
10
|
+
# The value is the `Range` object itself, which already carries `begin`, `end` and
|
|
11
|
+
# `exclude_end?`; there is no second representation to keep in step. A missing endpoint is
|
|
12
|
+
# `nil`, as in Ruby.
|
|
13
|
+
class RangeLiteral < Data.define(:value)
|
|
14
|
+
def initialize(value:)
|
|
15
|
+
unless value.is_a?(Range)
|
|
16
|
+
raise ArgumentError,
|
|
17
|
+
"TypeNode::RangeLiteral value must be a Range, " \
|
|
18
|
+
"got #{value.inspect}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|