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
data/lib/rigor/environment.rb
CHANGED
|
@@ -8,10 +8,12 @@ require_relative "environment/rbs_loader"
|
|
|
8
8
|
require_relative "environment/reflection"
|
|
9
9
|
require_relative "environment/reporters"
|
|
10
10
|
require_relative "environment/hkt_registry_holder"
|
|
11
|
+
require_relative "environment/failure_slot"
|
|
11
12
|
require_relative "environment/constant_type_cache_holder"
|
|
12
13
|
require_relative "environment/missing_gem_constant_index"
|
|
13
14
|
require_relative "environment/bundle_sig_discovery"
|
|
14
15
|
require_relative "environment/lockfile_resolver"
|
|
16
|
+
require_relative "environment/installed_gem_set"
|
|
15
17
|
require_relative "environment/rbs_collection_discovery"
|
|
16
18
|
require_relative "plugin/isolation"
|
|
17
19
|
require_relative "environment/rbs_coverage_report"
|
|
@@ -47,16 +49,15 @@ module Rigor
|
|
|
47
49
|
:reporters, :name_scope,
|
|
48
50
|
:synthetic_method_index, :project_patched_methods
|
|
49
51
|
|
|
50
|
-
# @param
|
|
51
|
-
# @param rbs_loader [Rigor::Environment::RbsLoader, nil] when nil the environment is "RBS-blind"; useful
|
|
52
|
+
# @param rbs_loader — when nil the environment is "RBS-blind"; useful
|
|
52
53
|
# in tests that want to assert how the engine behaves without RBS data. The default Environment wires
|
|
53
54
|
# the shared core loader, which is itself lazy: requesting an environment instance does NOT load RBS
|
|
54
55
|
# until a method or class query actually consults the loader.
|
|
55
|
-
# @param plugin_registry
|
|
56
|
+
# @param plugin_registry — v0.1.1 Track 2 slice 7. The per-run plugin
|
|
56
57
|
# registry the inference engine consults at call sites for plugin `dynamic_return` rules. When nil
|
|
57
58
|
# (the default), no plugin-level return-type contribution participates — useful for tests, the
|
|
58
59
|
# `Environment.default` facade, and analyses that don't load plugins.
|
|
59
|
-
# @param dependency_source_index
|
|
60
|
+
# @param dependency_source_index — ADR-10 slice
|
|
60
61
|
# 2b-ii. The per-run index of opt-in gem sources the dispatcher consults BELOW RBS dispatch. When nil
|
|
61
62
|
# (the default), no dep-source contribution participates and the dispatcher tier is a no-op.
|
|
62
63
|
def initialize(class_registry: ClassRegistry.default, rbs_loader: nil, # rubocop:disable Metrics/ParameterLists
|
|
@@ -88,6 +89,10 @@ module Rigor
|
|
|
88
89
|
# --no-stats` from doing the RBS env build at all.
|
|
89
90
|
@hkt_registry_base = hkt_registry || Inference::HktRegistry::EMPTY
|
|
90
91
|
@hkt_registry_holder = HktRegistryHolder.new
|
|
92
|
+
# Issue #784 — where either stage of the HKT-registry build (the plugin overlay, #791; the RBS
|
|
93
|
+
# `type`-alias scan) records a raise, so it surfaces once for the run instead of once per file — or,
|
|
94
|
+
# for the overlay, instead of aborting the run outright (see {#hkt_registry}).
|
|
95
|
+
@hkt_scan_failure = FailureSlot.new
|
|
91
96
|
@constant_type_cache = ConstantTypeCacheHolder.new
|
|
92
97
|
# ADR-82 WD9 — `[gem_name, version]` pairs for the locked gems with no resolvable RBS. The
|
|
93
98
|
# root-constant ownership index over them is built lazily (first unresolved constant read) so runs
|
|
@@ -104,21 +109,91 @@ module Rigor
|
|
|
104
109
|
# beat plugin entries, which beat the bundled JSON_VALUE. Memoised; single-threaded use only (under the
|
|
105
110
|
# Ractor pool path each worker has its own Environment so cross-worker mutation is impossible; the LSP
|
|
106
111
|
# single-publish-at-a-time invariant serialises here).
|
|
112
|
+
#
|
|
113
|
+
# BOTH stages are guarded (#784, #791): whichever raises is recorded on {#hkt_scan_failure} with the
|
|
114
|
+
# stage that failed and the other stage still runs, so this getter never raises into its callers —
|
|
115
|
+
# neither into a file's `analyze_body` rescue nor out of the run-owned demands, which have no rescue
|
|
116
|
+
# above them at all.
|
|
107
117
|
def hkt_registry
|
|
108
118
|
@hkt_registry_holder.fetch do
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
pre_scan = pre_scan_hkt_registry
|
|
120
|
+
begin
|
|
121
|
+
Inference::HktRegistry.scan_rbs_loader(
|
|
122
|
+
@rbs_loader,
|
|
123
|
+
base: pre_scan,
|
|
124
|
+
reporter: rbs_extended_reporter
|
|
125
|
+
)
|
|
126
|
+
rescue StandardError => e
|
|
127
|
+
# Issue #784 — the seam. This build is shared and memoised, and it is first demanded from inside
|
|
128
|
+
# a file's analysis, so a raise here would otherwise land in every file's `analyze_body` rescue
|
|
129
|
+
# and turn the whole run into N identical `internal analyzer error` rows (issue #776). Record it
|
|
130
|
+
# and degrade to the pre-scan registry: analysis proceeds, HKT inference from RBS `type` aliases
|
|
131
|
+
# reads its bound (`Dynamic[top]`, ADR-20 WD2), and the run surfaces ONE
|
|
132
|
+
# `rbs.coverage.hkt-scan-failed` row. Not a silent skip — the row is `:error` and names the
|
|
133
|
+
# exception and raise site — and the same shape as `RbsLoader#record_env_build_failure`.
|
|
134
|
+
# Memoising the degraded registry is what keeps the scan from being re-attempted per file.
|
|
135
|
+
record_hkt_registry_failure(e, stage: :scan)
|
|
136
|
+
pre_scan
|
|
137
|
+
end
|
|
119
138
|
end
|
|
120
139
|
end
|
|
121
140
|
|
|
141
|
+
# Issue #791 — the OTHER build behind {#hkt_registry}, and the reason the seam covers two stages
|
|
142
|
+
# rather than one. Aggregating the loaded plugins' manifest-declared HKT entries runs plugin-authored
|
|
143
|
+
# code (`Plugin::Registry#hkt_overlay_registry` reads each manifest); before this it sat one line ABOVE
|
|
144
|
+
# the #784 rescue, so a raise there escaped {#hkt_registry} entirely. Post-#788 that is not a per-file
|
|
145
|
+
# crash storm but an uncaught abort: {HktRegistryHolder#fetch} memoises only on success, so the raise
|
|
146
|
+
# is re-attempted at every demand, and the run-owned demands (`Runner::PoolCoordinator#hkt_scan_outcome`
|
|
147
|
+
# after the file loop, `Analysis::WorkerSession#drain_reporters`) sit outside any rescue.
|
|
148
|
+
#
|
|
149
|
+
# The degradation is narrower than the scan's: only the plugin overlay is dropped, and the RBS scan
|
|
150
|
+
# still runs on top of the bundled base, so a user's own `.rbs` HKT registrations survive a plugin
|
|
151
|
+
# defect. First-write-wins on the slot means a run whose overlay AND scan both raise reports the
|
|
152
|
+
# overlay — the actionable one, since the plugin is what a user can remove.
|
|
153
|
+
#
|
|
154
|
+
# @return the registry the RBS scan is layered on top of.
|
|
155
|
+
def pre_scan_hkt_registry
|
|
156
|
+
return @hkt_registry_base unless @plugin_registry.respond_to?(:hkt_overlay_registry)
|
|
157
|
+
|
|
158
|
+
@hkt_registry_base.merge(@plugin_registry.hkt_overlay_registry)
|
|
159
|
+
rescue StandardError => e
|
|
160
|
+
record_hkt_registry_failure(e, stage: :overlay)
|
|
161
|
+
@hkt_registry_base
|
|
162
|
+
end
|
|
163
|
+
private :pre_scan_hkt_registry
|
|
164
|
+
|
|
165
|
+
# Issue #784 — the `[error_class_name, first_message_line, raw_frame_or_nil, stage]` tuple the seam in
|
|
166
|
+
# {#hkt_registry} recorded, or nil when both stages built (or were never demanded). `stage` is `:scan`
|
|
167
|
+
# for the RBS `type`-alias scan and `:overlay` for the plugin-manifest aggregation (#791); it decides
|
|
168
|
+
# the row's wording, because "the implicit HKT scan over RBS `type` aliases raised" would point a user
|
|
169
|
+
# at their own `.rbs` for a plugin's defect. The run reads the tuple only after demanding
|
|
170
|
+
# {#hkt_registry} itself — after the file loop on the sequential paths, at drain time in each pool
|
|
171
|
+
# worker (the coordinator never analyses a file under the pool, so the drain is how its snapshot learns
|
|
172
|
+
# the outcome: the #696 lesson), and from a resolved environment when the analyze set is empty — so the
|
|
173
|
+
# row never depends on which files happened to be analysed.
|
|
174
|
+
def hkt_scan_failure
|
|
175
|
+
@hkt_scan_failure.value
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def record_hkt_registry_failure(error, stage:)
|
|
179
|
+
frames = error.backtrace || []
|
|
180
|
+
frame = frames.find { |f| f.include?("/lib/rigor/") } || frames.first
|
|
181
|
+
# `Class#name` is nil for an anonymous exception class; its nearest NAMED ancestor is the stable
|
|
182
|
+
# description (`inspect` embeds an object address, which would make the row's text differ across
|
|
183
|
+
# processes). Each String is frozen individually, not just the Array: the tuple crosses the fork
|
|
184
|
+
# boundary Marshal-clean and the drain channel's stated invariant
|
|
185
|
+
# ({Analysis::WorkerSession#drain_reporters}) is that its payload is also `Ractor.shareable?`, which a
|
|
186
|
+
# shallow freeze over `chomp`'s fresh String would not satisfy.
|
|
187
|
+
named = error.class.ancestors.find { |a| a.is_a?(Class) && a.name }
|
|
188
|
+
@hkt_scan_failure.record([
|
|
189
|
+
(error.class.name || "anonymous #{named&.name || 'Exception'}").dup.freeze,
|
|
190
|
+
error.message.to_s.lines.first.to_s.chomp.freeze,
|
|
191
|
+
frame&.dup&.freeze,
|
|
192
|
+
stage
|
|
193
|
+
])
|
|
194
|
+
end
|
|
195
|
+
private :record_hkt_registry_failure
|
|
196
|
+
|
|
122
197
|
# ADR-82 WD9 — the gem name owning `root_constant_name`, when that gem is locked in the project's
|
|
123
198
|
# Gemfile.lock, ships no resolvable RBS, and its entry file declares the constant at top level. Nil for
|
|
124
199
|
# everything else — the caller then keeps the generic provenance cause. The index is built once, lazily,
|
|
@@ -178,18 +253,17 @@ module Rigor
|
|
|
178
253
|
# Builds an Environment that consults the project's local signatures and any opt-in stdlib libraries on
|
|
179
254
|
# top of RBS core.
|
|
180
255
|
#
|
|
181
|
-
# @param root
|
|
256
|
+
# @param root — project root used to auto-detect the default signature path. Defaults
|
|
182
257
|
# to the current working directory.
|
|
183
|
-
# @param libraries
|
|
258
|
+
# @param libraries — additional stdlib libraries to load on top of
|
|
184
259
|
# {DEFAULT_LIBRARIES}. The final list is the union of the two, de-duplicated while preserving order.
|
|
185
260
|
# Pass an empty array (the default) to load only the defaults.
|
|
186
|
-
# @param signature_paths
|
|
261
|
+
# @param signature_paths — explicit list of `sig/`-style directories. When
|
|
187
262
|
# `nil` (the default), the canonical project layout `<root>/sig` is used if it exists, otherwise no
|
|
188
263
|
# signature path is loaded.
|
|
189
|
-
# @param cache_store
|
|
264
|
+
# @param cache_store — persistent cache threaded into the underlying
|
|
190
265
|
# {Environment::RbsLoader} so constant lookups (and, in later v0.0.9 slices, other reflection
|
|
191
266
|
# artefacts) consult the cache. Pass `nil` (the default) to skip caching for this environment.
|
|
192
|
-
# @return [Rigor::Environment]
|
|
193
267
|
# rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
|
|
194
268
|
def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil,
|
|
195
269
|
plugin_registry: nil, dependency_source_index: nil,
|
|
@@ -259,7 +333,7 @@ module Rigor
|
|
|
259
333
|
missing_gems, overlay_paths = missing_gems_and_overlay_paths(
|
|
260
334
|
locked: locked, default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
|
|
261
335
|
rbs_collection_paths: collection_paths, plugin_registry: plugin_registry,
|
|
262
|
-
signature_paths: resolved_paths
|
|
336
|
+
signature_paths: resolved_paths, bundle_root: bundle_root
|
|
263
337
|
)
|
|
264
338
|
loader_signature_paths = resolved_paths + plugin_sig_paths + gem_sig_paths +
|
|
265
339
|
collection_paths + overlay_paths
|
|
@@ -284,6 +358,13 @@ module Rigor
|
|
|
284
358
|
# the bundle walk and `rbs collection install` all stay authoritative.
|
|
285
359
|
deferred_signature_paths: plugin_sig_paths
|
|
286
360
|
)
|
|
361
|
+
# Issue #824 / ADR-32 WD13 — one `:info` per inline member the loader stood down against the
|
|
362
|
+
# project's own `sig/`. Recorded HERE because this is where the reporter is: the run's
|
|
363
|
+
# `source_rbs_synthesis` stream already carries WD12's "parsed but not honoured" rows, drains out of
|
|
364
|
+
# pool workers, and is regenerated (never cached) on every run, so the member-level stand-down needs
|
|
365
|
+
# no plumbing of its own. Costs nothing for a project with no inline RBS: the loader's reader
|
|
366
|
+
# short-circuits on an empty `virtual_rbs`.
|
|
367
|
+
record_inline_member_standdowns(loader, source_rbs_synthesis_reporter, root)
|
|
287
368
|
# ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
|
|
288
369
|
# `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
|
|
289
370
|
# don't consult HKT (e.g. `rigor check --cache-stats --no-stats`) don't pay the eager env-build cost
|
|
@@ -335,13 +416,13 @@ module Rigor
|
|
|
335
416
|
# and consumed twice: as `[gem_name, version]` pairs for the ADR-82 WD9 constant-ownership index, and
|
|
336
417
|
# as the eligible-gem set for the ADR-72 overlay resolution. Returns `[pairs, overlay_paths]`.
|
|
337
418
|
def missing_gems_and_overlay_paths(locked:, default_libraries:, bundle_sig_paths:,
|
|
338
|
-
rbs_collection_paths:, plugin_registry:, signature_paths: []
|
|
339
|
-
|
|
419
|
+
rbs_collection_paths:, plugin_registry:, signature_paths: [],
|
|
420
|
+
bundle_root: nil)
|
|
421
|
+
if locked.empty?
|
|
422
|
+
return unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
|
|
423
|
+
end
|
|
340
424
|
|
|
341
|
-
rows =
|
|
342
|
-
locked_gems: locked, default_libraries: default_libraries,
|
|
343
|
-
bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
|
|
344
|
-
).select { |row| row.source == :missing }
|
|
425
|
+
rows = missing_rows(locked, default_libraries, bundle_sig_paths, rbs_collection_paths)
|
|
345
426
|
overlays = gem_overlay_paths(
|
|
346
427
|
missing_gem_names: rows.map(&:gem_name), plugin_registry: plugin_registry,
|
|
347
428
|
signature_paths: signature_paths
|
|
@@ -349,6 +430,27 @@ module Rigor
|
|
|
349
430
|
[rows.map { |row| [row.gem_name, row.version] }, overlays]
|
|
350
431
|
end
|
|
351
432
|
|
|
433
|
+
# Issue #530 — a project with no `Gemfile.lock` still has gems on disk, and before this it handed the
|
|
434
|
+
# ADR-82 WD9 index nothing, so every constant reaching into an RBS-less gem kept the generic cause and
|
|
435
|
+
# the whole boundary reported as `engine_gap` rather than `add_rbs`. The installed set stands in for
|
|
436
|
+
# the locked one ({InstalledGemSet} says why that stays honest), and only for the provenance index:
|
|
437
|
+
# the second element is always empty because the ADR-72 overlays change what type-checks and so remain
|
|
438
|
+
# gated on the project's own declaration.
|
|
439
|
+
def unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
|
|
440
|
+
installed = InstalledGemSet.gems(bundle_path: bundle_root)
|
|
441
|
+
return [[], []] if installed.empty?
|
|
442
|
+
|
|
443
|
+
rows = missing_rows(installed, default_libraries, bundle_sig_paths, rbs_collection_paths)
|
|
444
|
+
[rows.map { |row| [row.gem_name, row.version] }, []]
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def missing_rows(gems, default_libraries, bundle_sig_paths, rbs_collection_paths)
|
|
448
|
+
RbsCoverageReport.classify(
|
|
449
|
+
locked_gems: gems, default_libraries: default_libraries,
|
|
450
|
+
bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
|
|
451
|
+
).select { |row| row.source == :missing }
|
|
452
|
+
end
|
|
453
|
+
|
|
352
454
|
# ADR-72 — resolve the bundled RBS overlay directories to load for this project. A gem is eligible when
|
|
353
455
|
# it is classified `:missing` (see {missing_rbs_gem_rows}) and its conflicting opt-in plugin twin (if
|
|
354
456
|
# any) is NOT already reachable. Returns `[Pathname]`, deterministically ordered, or `[]` when no
|
|
@@ -463,6 +565,54 @@ module Rigor
|
|
|
463
565
|
reporter&.record(plugin_id: plugin.manifest.id, path: path, message: message.to_s, kind: kind)
|
|
464
566
|
end
|
|
465
567
|
|
|
568
|
+
# ADR-32 WD13 — turn each {Environment::RbsLoader#inline_member_standdowns} record into a WD12
|
|
569
|
+
# `:not_honoured` entry, so the run's `plugin.rbs-inline.source-rbs-annotation-not-honoured` row names
|
|
570
|
+
# BOTH sources and which one won. WD12's rule is what makes this necessary rather than optional: an
|
|
571
|
+
# annotation Rigor parses and does not honour is reported, never swallowed — and a silent per-member
|
|
572
|
+
# strip is exactly the shape that rule forbids.
|
|
573
|
+
#
|
|
574
|
+
# The row is positioned at the annotated `.rb` (line 1, as every entry on this stream is): the member's
|
|
575
|
+
# own position lives in the synthesized RBS buffer, which describes a document nobody has, and the
|
|
576
|
+
# ADR-54 environment cache drops positions anyway, so a line read off it would differ warm and cold.
|
|
577
|
+
def record_inline_member_standdowns(loader, reporter, root)
|
|
578
|
+
return if reporter.nil?
|
|
579
|
+
|
|
580
|
+
loader.inline_member_standdowns.each do |class_name, method_name, kind, signature_path, virtual_name|
|
|
581
|
+
plugin_id, path = split_virtual_source_name(virtual_name)
|
|
582
|
+
next if path.nil?
|
|
583
|
+
|
|
584
|
+
reporter.record(
|
|
585
|
+
plugin_id: plugin_id, path: path, kind: :not_honoured,
|
|
586
|
+
message: inline_member_standdown_message(class_name, method_name, kind, signature_path, root)
|
|
587
|
+
)
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Splits a virtual buffer name back into the pair {.collect_virtual_rbs} composed it from
|
|
592
|
+
# (`virtual:<plugin id>:<source path>`). Limited to three fields so a path carrying a colon survives.
|
|
593
|
+
# Returns `[nil, nil]` for a name in any other shape — a synthesizer channel this method does not own.
|
|
594
|
+
def split_virtual_source_name(virtual_name)
|
|
595
|
+
prefix, plugin_id, path = virtual_name.to_s.split(":", 3)
|
|
596
|
+
return [nil, nil] unless prefix == "virtual" && plugin_id && !plugin_id.empty? && path && !path.empty?
|
|
597
|
+
|
|
598
|
+
[plugin_id, path]
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def inline_member_standdown_message(class_name, method_name, kind, signature_path, root)
|
|
602
|
+
separator = kind == :singleton ? "." : "#"
|
|
603
|
+
"`#{class_name}#{separator}#{method_name}` is also declared in " \
|
|
604
|
+
"`#{path_relative_to(signature_path, root)}`, and an explicit `.rbs` declaration wins over an " \
|
|
605
|
+
"inline annotation for the same member, so the inline signature was dropped. Left to collide the " \
|
|
606
|
+
"two would fail the class's definition build, and every call on it — real methods and typos " \
|
|
607
|
+
"alike — would read `Dynamic[top]`. Remove one of the two declarations to make the inline " \
|
|
608
|
+
"annotation bind."
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def path_relative_to(path, root)
|
|
612
|
+
prefix = "#{root}#{File::SEPARATOR}"
|
|
613
|
+
path.to_s.start_with?(prefix) ? path.to_s.delete_prefix(prefix) : path.to_s
|
|
614
|
+
end
|
|
615
|
+
|
|
466
616
|
SYNTHESIZER_CACHE_PRODUCER_ID = "plugin.source_rbs_synthesizer"
|
|
467
617
|
private_constant :SYNTHESIZER_CACHE_PRODUCER_ID
|
|
468
618
|
|
|
@@ -576,6 +726,15 @@ module Rigor
|
|
|
576
726
|
rbs_loader.rbs_module?(name)
|
|
577
727
|
end
|
|
578
728
|
|
|
729
|
+
# Issue #915 — the module names RBS puts in `name`'s SINGLETON ancestry, the declaration-side twin of
|
|
730
|
+
# `Scope#singleton_extends_of`. `Inference::Narrowing` reads it the same one-directional way: presence
|
|
731
|
+
# withholds a `Bot`, absence asserts nothing (a class the environment does not know answers `[]`).
|
|
732
|
+
def singleton_extended_modules(name)
|
|
733
|
+
return [] unless rbs_loader
|
|
734
|
+
|
|
735
|
+
rbs_loader.singleton_extended_module_names(normalize_class_name(name))
|
|
736
|
+
end
|
|
737
|
+
|
|
579
738
|
# Compares two class/module names using analyzer-owned class data. Returns `:equal`, `:subclass`,
|
|
580
739
|
# `:superclass`, `:disjoint`, or `:unknown`. The static registry handles built-ins cheaply; the RBS
|
|
581
740
|
# loader handles project/stdlib classes without relying on host Ruby constants being loaded.
|
|
@@ -600,8 +759,11 @@ module Rigor
|
|
|
600
759
|
rbs_loader.class_known?(name)
|
|
601
760
|
end
|
|
602
761
|
|
|
762
|
+
# Allocates only when there is a prefix to strip: `String#delete_prefix` copies unconditionally, and
|
|
763
|
+
# this runs once per class-registry / hierarchy probe (~320k times on the lib self-check).
|
|
603
764
|
def normalize_class_name(name)
|
|
604
|
-
name.to_s
|
|
765
|
+
name = name.to_s
|
|
766
|
+
name.start_with?("::") ? name.delete_prefix("::") : name
|
|
605
767
|
end
|
|
606
768
|
|
|
607
769
|
# ADR-13 slice 3b — composes the per-run plugin-supplied {Rigor::TypeNode::ResolverChain} into a single
|
|
@@ -49,24 +49,24 @@ module Rigor
|
|
|
49
49
|
|
|
50
50
|
attr_reader(*SLOT_NAMES, :provenance)
|
|
51
51
|
|
|
52
|
-
# @param return_type
|
|
52
|
+
# @param return_type — normal-edge return type. Use `nil` when the contribution does not
|
|
53
53
|
# refine the return type selected from the RBS contract.
|
|
54
|
-
# @param truthy_facts
|
|
54
|
+
# @param truthy_facts — facts that hold only on the truthy control-flow edge. Edge-local: a
|
|
55
55
|
# truthy-edge fact does NOT imply its falsey-edge complement (ADR-2 § "Plugin Contribution Merging").
|
|
56
|
-
# @param falsey_facts
|
|
57
|
-
# @param post_return_facts
|
|
56
|
+
# @param falsey_facts — dual of `truthy_facts`.
|
|
57
|
+
# @param post_return_facts — facts that hold after the call returns normally on every edge —
|
|
58
58
|
# the carrier for assertion-style contributions.
|
|
59
|
-
# @param mutations
|
|
60
|
-
# @param invalidations
|
|
59
|
+
# @param mutations — receiver and argument mutation effects.
|
|
60
|
+
# @param invalidations — targeted fact invalidations beyond what mutation effects already
|
|
61
61
|
# imply.
|
|
62
|
-
# @param exceptional
|
|
63
|
-
# @param role_conformance
|
|
64
|
-
# @param effects
|
|
62
|
+
# @param exceptional — non-returning, raising, or unreachable effect.
|
|
63
|
+
# @param role_conformance — capability-role conformance facts the contribution provides.
|
|
64
|
+
# @param effects — ADR-103 WD5 — the effect labels this call edge
|
|
65
65
|
# attributes to its callee, as an upper bound. `nil` means "says nothing about effects", which is
|
|
66
66
|
# NOT the same as the empty set (which asserts the call performs none). Merged by union, the
|
|
67
67
|
# conservative direction: two sources that each name part of a call's footprint together name
|
|
68
68
|
# more of it, and neither can shrink the other's claim.
|
|
69
|
-
# @param provenance
|
|
69
|
+
# @param provenance — source-family, plugin-id, node, and cache-descriptor metadata. Defaults
|
|
70
70
|
# to `Provenance.builtin`.
|
|
71
71
|
# rubocop:disable Metrics/ParameterLists
|
|
72
72
|
def initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil,
|
|
@@ -87,7 +87,7 @@ module Rigor
|
|
|
87
87
|
freeze
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# @return
|
|
90
|
+
# @return true when every content slot is unset (nil or an empty collection). Provenance does
|
|
91
91
|
# not count toward emptiness — an empty bundle still carries source attribution.
|
|
92
92
|
def empty?
|
|
93
93
|
SLOT_NAMES.all? { |slot| slot_empty?(slot, public_send(slot)) }
|
|
@@ -116,8 +116,6 @@ module Rigor
|
|
|
116
116
|
# | exceptional | exceptional | exception | :raise |
|
|
117
117
|
# | role_conformance | normal | role | (per-role target) |
|
|
118
118
|
# | effects | normal | effects | :effects |
|
|
119
|
-
#
|
|
120
|
-
# @return [Array<Element>]
|
|
121
119
|
def to_element_list # rubocop:disable Metrics/AbcSize
|
|
122
120
|
elements = []
|
|
123
121
|
elements << element_for(:return, :normal, :return_type, return_type) unless return_type.nil?
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../type"
|
|
4
|
+
require_relative "range_constant"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Inference
|
|
@@ -27,10 +28,7 @@ module Rigor
|
|
|
27
28
|
module Acceptance
|
|
28
29
|
module_function
|
|
29
30
|
|
|
30
|
-
# @param
|
|
31
|
-
# @param other_type [Rigor::Type]
|
|
32
|
-
# @param mode [Symbol] `:gradual` (default) or `:strict`.
|
|
33
|
-
# @return [Rigor::Type::AcceptsResult]
|
|
31
|
+
# @param mode — `:gradual` (default) or `:strict`.
|
|
34
32
|
def accepts(self_type, other_type, mode: :gradual)
|
|
35
33
|
raise ArgumentError, "Acceptance mode #{mode.inspect} is not implemented yet" unless mode == :gradual
|
|
36
34
|
|
|
@@ -63,6 +61,7 @@ module Rigor
|
|
|
63
61
|
Type::Nominal => :accepts_nominal,
|
|
64
62
|
Type::Constant => :accepts_constant,
|
|
65
63
|
Type::IntegerRange => :accepts_integer_range,
|
|
64
|
+
Type::FloatRange => :accepts_float_range,
|
|
66
65
|
Type::Difference => :accepts_difference,
|
|
67
66
|
Type::Refined => :accepts_refined,
|
|
68
67
|
Type::Intersection => :accepts_intersection,
|
|
@@ -201,7 +200,8 @@ module Rigor
|
|
|
201
200
|
# {#accepts_nominal_args});
|
|
202
201
|
# - Constant[v] when v.is_a?(klass(C)). The type_args of self are ignored here because a Constant
|
|
203
202
|
# carries a concrete value, not a generic instantiation, and the analyzer has no way to refute the
|
|
204
|
-
# args from a literal alone.
|
|
203
|
+
# args from a literal alone. A Range value is the one exception — see
|
|
204
|
+
# {#accepts_range_constant_type_arg}.
|
|
205
205
|
# - Tuple[*] when self is the Array (or a supertype) family. The Tuple is projected to
|
|
206
206
|
# `Nominal[Array, [union(elements)]]` so the existing generic-arg machinery handles it.
|
|
207
207
|
# - HashShape{*} when self is the Hash (or a supertype) family, projected to
|
|
@@ -215,6 +215,7 @@ module Rigor
|
|
|
215
215
|
when Type::Singleton then accepts_nominal_from_singleton(self_type, other_type, mode)
|
|
216
216
|
when Type::StructClass, Type::DataClass then accepts_nominal_from_class_factory(self_type, other_type, mode)
|
|
217
217
|
when Type::IntegerRange then accepts_nominal_from_integer_range(self_type, other_type, mode)
|
|
218
|
+
when Type::FloatRange then accepts_nominal_from_float_range(self_type, other_type, mode)
|
|
218
219
|
else accepts_nominal_from_shape(self_type, other_type, mode)
|
|
219
220
|
end
|
|
220
221
|
end
|
|
@@ -311,6 +312,32 @@ module Rigor
|
|
|
311
312
|
end
|
|
312
313
|
end
|
|
313
314
|
|
|
315
|
+
# ADR-109 WD4 — the Float twin of the IntegerRange rule: `Nominal[Float]` and everything Float
|
|
316
|
+
# is-a accept any `FloatRange`; nothing else does.
|
|
317
|
+
FLOAT_NOMINAL_ANCESTORS = %w[Float Numeric Comparable Object BasicObject].freeze
|
|
318
|
+
private_constant :FLOAT_NOMINAL_ANCESTORS
|
|
319
|
+
|
|
320
|
+
def accepts_nominal_from_float_range(self_type, _other_type, mode)
|
|
321
|
+
unless self_type.type_args.empty?
|
|
322
|
+
return Type::AcceptsResult.no(
|
|
323
|
+
mode: mode,
|
|
324
|
+
reasons: "Nominal[#{self_type.class_name}] with type args rejects FloatRange"
|
|
325
|
+
)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
if FLOAT_NOMINAL_ANCESTORS.include?(self_type.class_name)
|
|
329
|
+
Type::AcceptsResult.yes(
|
|
330
|
+
mode: mode,
|
|
331
|
+
reasons: "FloatRange is-a #{self_type.class_name}"
|
|
332
|
+
)
|
|
333
|
+
else
|
|
334
|
+
Type::AcceptsResult.no(
|
|
335
|
+
mode: mode,
|
|
336
|
+
reasons: "Nominal[#{self_type.class_name}] rejects FloatRange"
|
|
337
|
+
)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
314
341
|
# v0.0.2 — meta-type rule. A `Singleton[T]` is the class object for `T`, so it is an instance of
|
|
315
342
|
# `Class` (when `T` is a class) and always an instance of `Module`. Without this rule a method whose
|
|
316
343
|
# parameter is typed `Class | Module` would reject every `is_a?(SomeClass)` call and similar
|
|
@@ -492,7 +519,7 @@ module Rigor
|
|
|
492
519
|
|
|
493
520
|
def accepts_nominal_from_constant(self_type, constant, mode)
|
|
494
521
|
ruby_class = resolve_class(self_type.class_name)
|
|
495
|
-
return
|
|
522
|
+
return accepts_loaded_class_from_constant(ruby_class, constant, self_type, mode) if ruby_class
|
|
496
523
|
|
|
497
524
|
# The host process may not have required the constant's declared self_type (e.g. `BigDecimal`
|
|
498
525
|
# since Ruby 3.4 is no longer a default gem). Fall back to inspecting the value's own class
|
|
@@ -527,6 +554,13 @@ module Rigor
|
|
|
527
554
|
end
|
|
528
555
|
end
|
|
529
556
|
|
|
557
|
+
def accepts_loaded_class_from_constant(ruby_class, constant, self_type, mode)
|
|
558
|
+
result = constant_is_a_result(ruby_class, constant, self_type, mode)
|
|
559
|
+
return result unless result.yes?
|
|
560
|
+
|
|
561
|
+
accepts_range_constant_type_arg(self_type, constant, mode) || result
|
|
562
|
+
end
|
|
563
|
+
|
|
530
564
|
def constant_is_a_result(ruby_class, constant, self_type, mode)
|
|
531
565
|
if constant.value.is_a?(ruby_class)
|
|
532
566
|
Type::AcceptsResult.yes(mode: mode, reasons: "Constant value is_a?(#{self_type.class_name})")
|
|
@@ -538,10 +572,53 @@ module Rigor
|
|
|
538
572
|
end
|
|
539
573
|
end
|
|
540
574
|
|
|
575
|
+
# Issue #833 — the single exception to "the type_args of self are ignored for a Constant".
|
|
576
|
+
#
|
|
577
|
+
# A `Constant<Range>` is the one literal that does pin a generic instantiation: the engine
|
|
578
|
+
# builds it only when both endpoints are static, so `Range[T]` CAN be refuted from the literal.
|
|
579
|
+
# Without this, `Nominal[Range, [Integer]]` accepted `0.0...1.0` and `OverloadSelector` pass 1
|
|
580
|
+
# pinned whichever `Range[…]` arm came first in declaration order — for `Kernel#rand` that is
|
|
581
|
+
# `(::Range[Integer]) -> Integer?`, ahead of every Float arm.
|
|
582
|
+
#
|
|
583
|
+
# Returns nil — "no opinion, keep the class-level answer" — outside the envelope: a raw `Range`
|
|
584
|
+
# or any other arity of type_args, and a range the shared reader cannot read endpoints from.
|
|
585
|
+
# A missing endpoint contributes nothing, because a beginless or endless literal inhabits
|
|
586
|
+
# `Range[T]` for every T (`Range[Integer?]` is the common core spelling of the slicing param).
|
|
587
|
+
#
|
|
588
|
+
# Known coarse spot, deliberately not special-cased: a mixed-endpoint literal (`1.0..2`) is
|
|
589
|
+
# rejected by `Range[Integer]` and by `Range[Float]` alike, so overload selection falls through
|
|
590
|
+
# to its gradual pass and then to the first-overload fallback exactly as it did before.
|
|
591
|
+
def accepts_range_constant_type_arg(self_type, constant, mode)
|
|
592
|
+
return nil unless self_type.type_args.size == 1
|
|
593
|
+
|
|
594
|
+
endpoints = RangeConstant.literal_endpoints(constant)
|
|
595
|
+
return nil if endpoints.nil?
|
|
596
|
+
|
|
597
|
+
element = self_type.type_args.first
|
|
598
|
+
combine_range_endpoint_results(
|
|
599
|
+
endpoints.map { |endpoint| accepts(element, Type::Combinator.constant_of(endpoint), mode: mode) },
|
|
600
|
+
element,
|
|
601
|
+
mode
|
|
602
|
+
)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def combine_range_endpoint_results(per_endpoint, element, mode)
|
|
606
|
+
if per_endpoint.any?(&:no?)
|
|
607
|
+
Type::AcceptsResult.no(mode: mode, reasons: "a Range endpoint is rejected by #{element.describe}")
|
|
608
|
+
elsif per_endpoint.any?(&:maybe?)
|
|
609
|
+
Type::AcceptsResult.maybe(
|
|
610
|
+
mode: mode,
|
|
611
|
+
reasons: "a Range endpoint could not be proven accepted by #{element.describe}"
|
|
612
|
+
)
|
|
613
|
+
else
|
|
614
|
+
Type::AcceptsResult.yes(mode: mode, reasons: "every Range endpoint accepted by #{element.describe}")
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
541
618
|
# IntegerRange[a..b] accepts:
|
|
542
619
|
# - Constant[n] where n is an Integer covered by [a..b];
|
|
543
620
|
# - IntegerRange[c..d] where [c..d] ⊆ [a..b];
|
|
544
|
-
# - Nominal[Integer] only when self is the universal range (`
|
|
621
|
+
# - Nominal[Integer] only when self is the universal range (`Integer[min..max]`), since otherwise an
|
|
545
622
|
# arbitrary Integer could fall outside the bound.
|
|
546
623
|
# Anything else is rejected.
|
|
547
624
|
def accepts_integer_range(self_type, other_type, mode)
|
|
@@ -616,6 +693,65 @@ module Rigor
|
|
|
616
693
|
end
|
|
617
694
|
end
|
|
618
695
|
|
|
696
|
+
# ADR-109 WD4 — `FloatRange` accepts:
|
|
697
|
+
# - Constant[f] where f is a Float the range covers (never NaN: `cover?` compares);
|
|
698
|
+
# - FloatRange[c..d] whose canonical closed bounds sit inside self's.
|
|
699
|
+
# `Nominal[Float]` is never accepted, not even by the universal range: an arbitrary Float may be
|
|
700
|
+
# NaN, and the universal range is exactly "every Float except NaN".
|
|
701
|
+
def accepts_float_range(self_type, other_type, mode)
|
|
702
|
+
case other_type
|
|
703
|
+
when Type::Constant
|
|
704
|
+
accepts_float_range_from_constant(self_type, other_type, mode)
|
|
705
|
+
when Type::FloatRange
|
|
706
|
+
accepts_float_range_from_float_range(self_type, other_type, mode)
|
|
707
|
+
when Type::Nominal
|
|
708
|
+
Type::AcceptsResult.no(
|
|
709
|
+
mode: mode,
|
|
710
|
+
reasons: "#{self_type.describe} rejects Nominal[#{other_type.class_name}] (could be NaN or fall outside)"
|
|
711
|
+
)
|
|
712
|
+
else
|
|
713
|
+
Type::AcceptsResult.no(
|
|
714
|
+
mode: mode,
|
|
715
|
+
reasons: "FloatRange rejects #{other_type.class}"
|
|
716
|
+
)
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def accepts_float_range_from_constant(self_type, constant, mode)
|
|
721
|
+
unless constant.value.is_a?(Float)
|
|
722
|
+
return Type::AcceptsResult.no(
|
|
723
|
+
mode: mode,
|
|
724
|
+
reasons: "FloatRange rejects non-Float Constant"
|
|
725
|
+
)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
if self_type.covers?(constant.value)
|
|
729
|
+
Type::AcceptsResult.yes(
|
|
730
|
+
mode: mode,
|
|
731
|
+
reasons: "Constant[#{constant.value}] is in #{self_type.describe}"
|
|
732
|
+
)
|
|
733
|
+
else
|
|
734
|
+
Type::AcceptsResult.no(
|
|
735
|
+
mode: mode,
|
|
736
|
+
reasons: "Constant[#{constant.value}] outside #{self_type.describe}"
|
|
737
|
+
)
|
|
738
|
+
end
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def accepts_float_range_from_float_range(self_type, other_range, mode)
|
|
742
|
+
if self_type.min <= other_range.min && other_range.canonical_max <= self_type.canonical_max
|
|
743
|
+
Type::AcceptsResult.yes(
|
|
744
|
+
mode: mode,
|
|
745
|
+
reasons: "#{other_range.describe} ⊆ #{self_type.describe}"
|
|
746
|
+
)
|
|
747
|
+
else
|
|
748
|
+
Type::AcceptsResult.no(
|
|
749
|
+
mode: mode,
|
|
750
|
+
reasons: "#{other_range.describe} not contained in #{self_type.describe}"
|
|
751
|
+
)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
619
755
|
# `Difference[base, removed]` accepts another type X when the base accepts X *and* X's value set is
|
|
620
756
|
# provably disjoint from `removed`. The disjointness test is the subtle part — it is NOT the same as
|
|
621
757
|
# `removed.accepts(X)`, because `Nominal[String]` includes `""` even though `Constant[""]` does not
|
|
@@ -936,7 +1072,7 @@ module Rigor
|
|
|
936
1072
|
def resolve_class(name)
|
|
937
1073
|
return nil unless name.is_a?(::String)
|
|
938
1074
|
|
|
939
|
-
parts = name
|
|
1075
|
+
parts = class_name_parts(name)
|
|
940
1076
|
return nil if parts.empty?
|
|
941
1077
|
|
|
942
1078
|
mod = ::Object
|
|
@@ -950,7 +1086,16 @@ module Rigor
|
|
|
950
1086
|
rescue ::StandardError, ::ScriptError, ::SystemExit
|
|
951
1087
|
nil
|
|
952
1088
|
end
|
|
1089
|
+
|
|
1090
|
+
# #775 — the split of a class name is a pure function of the string, and the subtype check asks it
|
|
1091
|
+
# twice per Nominal-vs-Nominal comparison (~370k times on the lib self-check) for the same few
|
|
1092
|
+
# hundred names. Only the split is memoised; the constant walk above still runs every time, so a
|
|
1093
|
+
# class loaded (or a spec constant removed) after the first ask is answered by the live tree.
|
|
1094
|
+
def class_name_parts(name)
|
|
1095
|
+
@class_name_parts[name] ||= name.delete_prefix("::").split("::").freeze
|
|
1096
|
+
end
|
|
953
1097
|
end
|
|
1098
|
+
@class_name_parts = {}
|
|
954
1099
|
end
|
|
955
1100
|
end
|
|
956
1101
|
end
|
|
@@ -34,15 +34,14 @@ module Rigor
|
|
|
34
34
|
#
|
|
35
35
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
36
36
|
class BlockParameterBinder
|
|
37
|
-
# @param expected_param_types
|
|
37
|
+
# @param expected_param_types — positional block parameter types in order. Indices
|
|
38
38
|
# the binder cannot fill from this array (because the array is shorter than the parameter list, or
|
|
39
39
|
# because the slot is a kind we do not pull from the array) default to `Dynamic[Top]`.
|
|
40
40
|
def initialize(expected_param_types: [])
|
|
41
41
|
@expected_param_types = expected_param_types
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# @
|
|
45
|
-
# @return [Hash{Symbol => Rigor::Type}] ordered map from parameter name to bound type. Anonymous
|
|
44
|
+
# @return ordered map from parameter name to bound type. Anonymous
|
|
46
45
|
# parameters are skipped; MultiTargetNode destructuring slots delegate to {MultiTargetBinder} and
|
|
47
46
|
# contribute every named local in declaration order. Numbered-parameter forms (`_1`, `_2`, ...) bind
|
|
48
47
|
# `:_1`, `:_2`, ... up to the maximum the block body refers to.
|