rigortype 0.4.6 → 0.4.8
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 +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -46,6 +46,27 @@ module Rigor
|
|
|
46
46
|
@untyped
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# ADR-130 WD2 — the parity predicate. Every site that keys a leniency on `untyped` asks this instead of testing
|
|
50
|
+
# a `Dynamic`'s facet for `Top` or comparing with {untyped}, so a value whose facet parity erases
|
|
51
|
+
# ({erased_facet?}) reads as `untyped` everywhere but member dispatch. `spec/rigor/type/untyped_parity_spec.rb`
|
|
52
|
+
# flags the raw spellings it knows outside this module. True for `Dynamic[top]` and for a plugin-interface
|
|
53
|
+
# value, `Dynamic[Rigor::Plugin::_Scope]`.
|
|
54
|
+
def untyped?(type)
|
|
55
|
+
return false unless type.is_a?(Dynamic)
|
|
56
|
+
|
|
57
|
+
facet = type.static_facet
|
|
58
|
+
facet.is_a?(Top) || erased_facet?(facet)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# ADR-130 WD1 — true for a `Dynamic` facet that parity reads as `top`: the `Rigor::Plugin` interface carrier
|
|
62
|
+
# ({Interface}). {untyped?} answers true for it, and {merge_erased_members} merges it with `Dynamic[top]`.
|
|
63
|
+
# `spec/rigor/type/combinator_facet_erasure_spec.rb` also drives the merge through a stub facet. An engine
|
|
64
|
+
# hook, not public API.
|
|
65
|
+
def erased_facet?(facet)
|
|
66
|
+
facet.is_a?(Interface)
|
|
67
|
+
end
|
|
68
|
+
private_class_method :erased_facet?
|
|
69
|
+
|
|
49
70
|
# Wraps the static facet in a Dynamic[T] carrier. Idempotent on the static facet so
|
|
50
71
|
# Dynamic[Dynamic[T]] collapses to Dynamic[T] per the value-lattice algebra.
|
|
51
72
|
def dynamic(static_facet)
|
|
@@ -245,8 +266,8 @@ module Rigor
|
|
|
245
266
|
end
|
|
246
267
|
|
|
247
268
|
# `literal-string` — a `String` that is statically known to come from a source-code literal (or a
|
|
248
|
-
# composition of literals).
|
|
249
|
-
#
|
|
269
|
+
# composition of literals). Interpolation `"#{...}"` and `+` / `*` composition keep
|
|
270
|
+
# the flow (see {literal_string_compatible?}). Every `Constant<String>` is implicitly
|
|
250
271
|
# literal-string-compatible; the carrier exists for cases where the concrete value is unknown but
|
|
251
272
|
# literal-ness has been established (an RBS::Extended `return: literal-string` annotation, or
|
|
252
273
|
# interpolation over literal-bearing parts).
|
|
@@ -433,8 +454,7 @@ module Rigor
|
|
|
433
454
|
# - `Type::Tuple[A, B, C]` → `Constant<0> | Constant<1> | Constant<2>`.
|
|
434
455
|
# - `Type::Nominal["Hash", [K, V]]` → `K` (untyped if absent).
|
|
435
456
|
# - `Type::Nominal["Array", [E]]` → `non-negative-int`.
|
|
436
|
-
# - `Type::Constant`
|
|
437
|
-
# per-element keys.
|
|
457
|
+
# - `Type::Constant` wrapping a Range with an Integer begin — `non-negative-int`.
|
|
438
458
|
#
|
|
439
459
|
# Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
|
|
440
460
|
# `Intersection`) project to `top` so the type function always returns a value — callers may narrow
|
|
@@ -456,8 +476,7 @@ module Rigor
|
|
|
456
476
|
# - `Type::Tuple[A, B, C]` → `A | B | C`.
|
|
457
477
|
# - `Type::Nominal["Hash", [K, V]]` → `V` (untyped if absent).
|
|
458
478
|
# - `Type::Nominal["Array", [E]]` → `E` (untyped if absent).
|
|
459
|
-
# - `Type::Constant`
|
|
460
|
-
# element.
|
|
479
|
+
# - `Type::Constant` wrapping an integer Range — the `IntegerRange` it spans.
|
|
461
480
|
def value_of(type)
|
|
462
481
|
case type
|
|
463
482
|
when HashShape then hash_shape_values(type)
|
|
@@ -528,8 +547,7 @@ module Rigor
|
|
|
528
547
|
# Phase A handles `Type::HashShape` (literal-key K). Phase B (slice 5) extends to `Type::Tuple`
|
|
529
548
|
# (integer-index K) — `pick_of[Tuple[A, B, C], 0 | 2]` evaluates to `Tuple[A, C]`. Non-shape inputs
|
|
530
549
|
# (`Type::Nominal`, etc.) return `type` unchanged ("lossy degradation"; the
|
|
531
|
-
# `dynamic.shape.lossy-projection` diagnostic that flags the boundary
|
|
532
|
-
# diagnostic threading arrives).
|
|
550
|
+
# `dynamic.shape.lossy-projection` diagnostic that flags the boundary).
|
|
533
551
|
def pick_of(type, keys)
|
|
534
552
|
case type
|
|
535
553
|
when HashShape then hash_shape_pick(type, keys)
|
|
@@ -597,8 +615,7 @@ module Rigor
|
|
|
597
615
|
# site where the projection was authored.
|
|
598
616
|
#
|
|
599
617
|
# `HashShape` and `Tuple` carry shape-level information the projections honour; every other carrier
|
|
600
|
-
# is lossy.
|
|
601
|
-
# predicate stands alone in slice 5 for unit-test coverage and future composition.
|
|
618
|
+
# is lossy.
|
|
602
619
|
def shape_projection_lossy?(type)
|
|
603
620
|
!type.is_a?(HashShape) && !type.is_a?(Tuple)
|
|
604
621
|
end
|
|
@@ -792,9 +809,9 @@ module Rigor
|
|
|
792
809
|
top
|
|
793
810
|
end
|
|
794
811
|
|
|
795
|
-
# `Type::Constant` only carries scalar literals (
|
|
796
|
-
#
|
|
797
|
-
#
|
|
812
|
+
# `Type::Constant` only carries scalar literals (`Constant::SCALAR_CLASSES`); Array and Hash literals
|
|
813
|
+
# become Tuple / HashShape carriers earlier in the typer. Range is the only scalar with meaningful
|
|
814
|
+
# key/value projections.
|
|
798
815
|
def constant_keys(value)
|
|
799
816
|
return non_negative_int if value.is_a?(Range) && value.begin.is_a?(Integer)
|
|
800
817
|
|
|
@@ -825,7 +842,104 @@ module Rigor
|
|
|
825
842
|
|
|
826
843
|
return [top] if flattened.any?(Top)
|
|
827
844
|
|
|
828
|
-
absorb_contained_members(unique_members(flattened))
|
|
845
|
+
absorb_contained_members(merge_erased_members(unique_members(flattened)))
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
# ADR-130 WD1 — a facet parity erases ({erased_facet?}) must not give a join a shape master's dedup would
|
|
849
|
+
# not: `Dynamic[_A] | Dynamic[top]` is `Dynamic[top]`, and so are the same members anywhere inside a carrier
|
|
850
|
+
# that holds a type ({TYPE_CHILDREN}). Members equal after {erase_facets} merge into the erased form, at the
|
|
851
|
+
# first one's position; a member no other matches keeps its facet, and {absorb_contained_members} compares
|
|
852
|
+
# the erased forms too. The facet scan runs on every join and allocates nothing (no enumerator, no array),
|
|
853
|
+
# so the merge costs only where a facet is present, which is nowhere until ADR-130 slice 1b.
|
|
854
|
+
def merge_erased_members(members)
|
|
855
|
+
return members if members.size < 2
|
|
856
|
+
return members unless members.any? { |member| facet_bearing?(member) }
|
|
857
|
+
|
|
858
|
+
erased = members.map { |member| erase_facets(member) }
|
|
859
|
+
counts = Hash.new(0)
|
|
860
|
+
erased.each { |type| counts[type] += 1 }
|
|
861
|
+
merged = {}
|
|
862
|
+
result = []
|
|
863
|
+
members.each_with_index do |member, index|
|
|
864
|
+
type = erased[index]
|
|
865
|
+
if counts[type] == 1
|
|
866
|
+
result << member
|
|
867
|
+
elsif !merged.key?(type)
|
|
868
|
+
merged[type] = true
|
|
869
|
+
result << type
|
|
870
|
+
end
|
|
871
|
+
end
|
|
872
|
+
result
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
def facet_bearing?(type)
|
|
876
|
+
return erased_facet?(type.static_facet) || facet_bearing?(type.static_facet) if type.is_a?(Dynamic)
|
|
877
|
+
|
|
878
|
+
readers = TYPE_CHILDREN[type.class]
|
|
879
|
+
return false if readers.nil?
|
|
880
|
+
|
|
881
|
+
readers.each { |reader| return true if child_facet_bearing?(type.public_send(reader)) }
|
|
882
|
+
false
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def child_facet_bearing?(child)
|
|
886
|
+
case child
|
|
887
|
+
when Array then child.any? { |type| facet_bearing?(type) }
|
|
888
|
+
when Hash
|
|
889
|
+
child.each_value { |type| return true if facet_bearing?(type) }
|
|
890
|
+
false
|
|
891
|
+
else facet_bearing?(child)
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
# `type` with every facet {erased_facet?} accepts rewritten to `top`, through every carrier
|
|
896
|
+
# {TYPE_CHILDREN} lists. The identity, by reference, on a type that bears none. A facet-bearing `Dynamic`
|
|
897
|
+
# is never `Dynamic[top]`, so {untyped?} answers the hook for it here.
|
|
898
|
+
def erase_facets(type)
|
|
899
|
+
return type unless facet_bearing?(type)
|
|
900
|
+
|
|
901
|
+
case type
|
|
902
|
+
when Dynamic then untyped?(type) ? untyped : dynamic(erase_facets(type.static_facet))
|
|
903
|
+
when Union then union(*erase_each(type.members))
|
|
904
|
+
when Intersection then intersection(*erase_each(type.members))
|
|
905
|
+
when Tuple then tuple_of(*erase_each(type.elements))
|
|
906
|
+
when Nominal then nominal_of(type.class_name, type_args: erase_each(type.type_args))
|
|
907
|
+
when HashShape then erase_shape_facets(type)
|
|
908
|
+
else erase_wrapper_facets(type)
|
|
909
|
+
end
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def erase_wrapper_facets(type)
|
|
913
|
+
case type
|
|
914
|
+
when Difference then Difference.new(erase_facets(type.base), erase_facets(type.removed))
|
|
915
|
+
when Refined then Refined.new(erase_facets(type.base), type.predicate_id)
|
|
916
|
+
when Maybe then Maybe.new(erase_facets(type.value_type))
|
|
917
|
+
when Result then Result.new(erase_facets(type.ok_type), erase_facets(type.err_type))
|
|
918
|
+
else erase_bearer_facets(type)
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def erase_bearer_facets(type)
|
|
923
|
+
case type
|
|
924
|
+
when App then App.new(type.uri, erase_each(type.args), bound: erase_facets(type.bound))
|
|
925
|
+
when BoundMethod
|
|
926
|
+
BoundMethod.new(receiver_type: erase_facets(type.receiver_type), method_name: type.method_name)
|
|
927
|
+
when DataInstance then DataInstance.new(erase_values(type.members), type.class_name)
|
|
928
|
+
when StructInstance then StructInstance.new(erase_values(type.members), type.class_name)
|
|
929
|
+
# The facet itself, erased apart from its `Dynamic` (which erases whole, to `untyped`).
|
|
930
|
+
when Interface then Interface.new(type.name, erase_each(type.type_args))
|
|
931
|
+
else type
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def erase_each(types) = types.map { |type| erase_facets(type) }
|
|
936
|
+
|
|
937
|
+
def erase_values(pairs) = pairs.transform_values { |type| erase_facets(type) }
|
|
938
|
+
|
|
939
|
+
def erase_shape_facets(shape)
|
|
940
|
+
HashShape.new(erase_values(shape.pairs),
|
|
941
|
+
required_keys: shape.required_keys, optional_keys: shape.optional_keys,
|
|
942
|
+
read_only_keys: shape.read_only_keys, extra_keys: shape.extra_keys)
|
|
829
943
|
end
|
|
830
944
|
|
|
831
945
|
# Drops every member another member already contains, so the join after a guard names one set
|
|
@@ -839,15 +953,30 @@ module Rigor
|
|
|
839
953
|
# The `!absorbed_by?(other, member)` re-check costs one predicate call per pair that absorbed
|
|
840
954
|
# at all, and it is what makes "both arms dropped" unrepresentable should a future clause make
|
|
841
955
|
# the relation symmetric on a pair of distinct members.
|
|
956
|
+
#
|
|
957
|
+
# ADR-130 WD1 — the relation reads each member's erased form ({erase_facets}), so a facet does not keep an
|
|
958
|
+
# arm master absorbs: `[Dynamic[_A]] | [Dynamic[top] | Integer]` drops the first arm as master's
|
|
959
|
+
# `[Dynamic[top]] | [Dynamic[top] | Integer]` does. Without a facet the readings are the members themselves.
|
|
842
960
|
def absorb_contained_members(members)
|
|
843
961
|
return members unless members.any? { |m| absorbable_member?(m) }
|
|
844
962
|
|
|
845
963
|
members = join_sequence_elements(members)
|
|
846
|
-
members.
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
964
|
+
readings = members.any? { |member| facet_bearing?(member) } ? members.map { |m| erase_facets(m) } : members
|
|
965
|
+
# `each_index`, not `each_with_index`: the Enumerable form allocates an iterator object on every join.
|
|
966
|
+
kept = []
|
|
967
|
+
members.each_index do |index|
|
|
968
|
+
member = members[index]
|
|
969
|
+
kept << member unless absorbable_member?(member) && absorbed_reading?(readings, index)
|
|
970
|
+
end
|
|
971
|
+
kept
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
# `any?` rather than an early `return` from an `each` block: a non-local return allocates on every absorbed
|
|
975
|
+
# member. Readings are distinct objects (the members were made unique, and an erased form is a new object).
|
|
976
|
+
def absorbed_reading?(readings, index)
|
|
977
|
+
reading = readings[index]
|
|
978
|
+
readings.any? do |wider|
|
|
979
|
+
!wider.equal?(reading) && absorbed_by?(reading, wider) && !absorbed_by?(wider, reading)
|
|
851
980
|
end
|
|
852
981
|
end
|
|
853
982
|
|
|
@@ -1044,10 +1173,18 @@ module Rigor
|
|
|
1044
1173
|
# need not render alike (`Constant[1..2]` and `Constant[1..2.0]`), so equality is not a safe key. A
|
|
1045
1174
|
# non-main Ractor may not read the module's ivar, so a pool worker renders the key every time, as every
|
|
1046
1175
|
# run did before.
|
|
1176
|
+
#
|
|
1177
|
+
# ADR-130 WD1 — a member bearing a facet sorts by its erased form's rendering, so a facet does not move it:
|
|
1178
|
+
# `Dynamic[_A] | Dynamic[String]` keeps master's `Dynamic[String] | Dynamic[top]` order. The facet scan runs
|
|
1179
|
+
# only when a key is rendered, and a member without a facet renders as it always did.
|
|
1047
1180
|
def sort_key(member)
|
|
1048
|
-
return member
|
|
1181
|
+
return render_sort_key(member) unless Ractor.main?
|
|
1049
1182
|
|
|
1050
|
-
@sort_keys[member] ||= member
|
|
1183
|
+
@sort_keys[member] ||= render_sort_key(member)
|
|
1184
|
+
end
|
|
1185
|
+
|
|
1186
|
+
def render_sort_key(member)
|
|
1187
|
+
(facet_bearing?(member) ? erase_facets(member) : member).describe(:short)
|
|
1051
1188
|
end
|
|
1052
1189
|
end
|
|
1053
1190
|
|
|
@@ -1056,6 +1193,21 @@ module Rigor
|
|
|
1056
1193
|
# The main Ractor's {sort_key} memo, allocated here for the same reason.
|
|
1057
1194
|
@sort_keys = ObjectSpace::WeakMap.new
|
|
1058
1195
|
|
|
1196
|
+
# ADR-130 WD1 — the readers of every type a carrier holds, which facet erasure walks. A leaf lists none.
|
|
1197
|
+
# `spec/rigor/type/combinator_facet_erasure_spec.rb` fails on a `Rigor::Type` carrier missing here, and on a
|
|
1198
|
+
# listed carrier whose erasure leaves a facet in any of its type slots.
|
|
1199
|
+
TYPE_CHILDREN = Ractor.make_shareable(
|
|
1200
|
+
{
|
|
1201
|
+
Top => [], Bot => [], Constant => [], IntegerRange => [], FloatRange => [], Singleton => [],
|
|
1202
|
+
DataClass => [], StructClass => [],
|
|
1203
|
+
Dynamic => %i[static_facet], Nominal => %i[type_args], Tuple => %i[elements], HashShape => %i[pairs],
|
|
1204
|
+
Union => %i[members], Intersection => %i[members], Difference => %i[base removed], Refined => %i[base],
|
|
1205
|
+
Maybe => %i[value_type], Result => %i[ok_type err_type], App => %i[args bound],
|
|
1206
|
+
BoundMethod => %i[receiver_type], DataInstance => %i[members], StructInstance => %i[members],
|
|
1207
|
+
Interface => %i[type_args]
|
|
1208
|
+
}
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1059
1211
|
NIL_CONSTANT = Constant.new(nil)
|
|
1060
1212
|
TRUE_CONSTANT = Constant.new(true)
|
|
1061
1213
|
FALSE_CONSTANT = Constant.new(false)
|
data/lib/rigor/type/constant.rb
CHANGED
|
@@ -8,9 +8,8 @@ require_relative "plain_lattice"
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Type
|
|
10
10
|
# A literal carrier under ADR-3 OQ1 Option C (Hybrid). Wraps a Ruby literal value of one of the
|
|
11
|
-
# supported immutable-ish classes. Compound literal shapes (Tuple, HashShape
|
|
12
|
-
#
|
|
13
|
-
# tuple slicing.
|
|
11
|
+
# supported immutable-ish classes. Compound literal shapes (Tuple, HashShape) have their own classes;
|
|
12
|
+
# Range is carried only when both static endpoints are known enough for tuple slicing.
|
|
14
13
|
#
|
|
15
14
|
# See docs/adr/4-type-inference-engine.md for the tentative answer to the open question and
|
|
16
15
|
# docs/type-specification/rigor-extensions.md for the refinement neighbourhood this carrier lives in.
|
|
@@ -74,12 +73,12 @@ module Rigor
|
|
|
74
73
|
|
|
75
74
|
# RBS supports `Literal` types for booleans, nil, integer literals (positive and negative), symbol
|
|
76
75
|
# literals, and string literals. Erasing to these preserves the carrier's precision at the RBS
|
|
77
|
-
# boundary —
|
|
76
|
+
# boundary — the constant 64 round-trips as `64`, not as `Integer` — and
|
|
78
77
|
# `RbsTypeTranslator#translate_literal` already maps the parsed RBS Literal back to `Constant`.
|
|
79
78
|
# Scalar carriers without RBS Literal support (Float, Range, Rational, Complex, Regexp, Pathname)
|
|
80
79
|
# keep their pre-existing widen-to-class-name behaviour because RBS rejects their literal spellings
|
|
81
80
|
# as syntax errors. `Range` is generic (`Range[out E]`), and a bare `Range` fails `rbs validate`, so
|
|
82
|
-
# it carries the class of its endpoints, or `untyped` when both are nil (#1891). `Set
|
|
81
|
+
# it carries the class of its endpoints, or `untyped` when both are nil (#1891). `Set` is
|
|
83
82
|
# generic too, and a set is mutable: the constant's elements are only what it held when it was built, and
|
|
84
83
|
# `x << "a"` adds a member they do not name. It erases to `Set[untyped]`, as an Array or Hash literal's
|
|
85
84
|
# element type is widened rather than pinned (#2177).
|
|
@@ -28,14 +28,14 @@ module Rigor
|
|
|
28
28
|
ALLOWED_KEY_CLASSES = [Symbol, String, Integer, Float, TrueClass, FalseClass, NilClass].freeze
|
|
29
29
|
EXTRA_KEY_POLICIES = %i[open closed].freeze
|
|
30
30
|
POLICY_KEYWORDS = %i[required_keys optional_keys read_only_keys extra_keys].freeze
|
|
31
|
-
# A Symbol whose text matches renders as a bare RBS record key (`lang:`) in {#erase_to_rbs};
|
|
32
|
-
# anything else is a quoted Symbol with a fat arrow (`:"data-contrast" =>`). See {#
|
|
31
|
+
# A Symbol whose text matches renders as a bare RBS record key (`lang:`) in {#describe} and {#erase_to_rbs};
|
|
32
|
+
# anything else is a quoted Symbol with a fat arrow (`:"data-contrast" =>`). See {#key_prefix}.
|
|
33
33
|
BARE_RECORD_KEY = /\A[A-Za-z_][A-Za-z0-9_]*[?!]?\z/
|
|
34
34
|
|
|
35
35
|
attr_reader :pairs, :required_keys, :optional_keys, :read_only_keys, :extra_keys
|
|
36
36
|
|
|
37
37
|
# @param pairs — ordered map of
|
|
38
|
-
# keys to declared types. Keys MUST be
|
|
38
|
+
# keys to declared types. Keys MUST be one of ALLOWED_KEY_CLASSES;
|
|
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
|
|
@@ -74,7 +74,7 @@ module Rigor
|
|
|
74
74
|
return hash_erasure unless closed?
|
|
75
75
|
return hash_erasure if pairs.each_key.any? { |k| !k.is_a?(Symbol) || !record_key_spellable?(k) }
|
|
76
76
|
|
|
77
|
-
rendered = pairs.map { |k, v| "#{
|
|
77
|
+
rendered = pairs.map { |k, v| "#{key_prefix(k)} #{v.erase_to_rbs}" }
|
|
78
78
|
"{ #{rendered.join(', ')} }"
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -193,46 +193,27 @@ module Rigor
|
|
|
193
193
|
def render_entry(key, value, verbosity)
|
|
194
194
|
prefix = []
|
|
195
195
|
prefix << "readonly" if read_only_key?(key)
|
|
196
|
-
|
|
197
|
-
prefix << "#{rendered_key}#{key_separator(key)}"
|
|
196
|
+
prefix << key_prefix(key)
|
|
198
197
|
"#{prefix.join(' ')} #{value.describe(verbosity)}"
|
|
199
198
|
end
|
|
200
199
|
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# Symbol / String keys keep the compact colon form (`a: 1`, `"k": 2`); every other scalar key uses
|
|
214
|
-
# the hashrocket (`1 => 2`) — the form the user would have to write in source.
|
|
215
|
-
def key_separator(key)
|
|
216
|
-
case key
|
|
217
|
-
when Symbol, String then ":"
|
|
218
|
-
else " =>"
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# An RBS record entry's key + separator, for the {#erase_to_rbs} path (which only ever sees
|
|
223
|
-
# Symbol keys — string-keyed shapes degrade to `Hash[...]` before reaching here). A Symbol whose
|
|
224
|
-
# text is a plain identifier is a bare key with a colon (`lang:`, Ruby keywords like `class`
|
|
225
|
-
# included). Any other Symbol is a quoted Symbol literal with a fat arrow (`:"data-contrast" =>`):
|
|
226
|
-
# RBS rejects a bare non-identifier (`data-contrast:`) and a quoted key with a colon
|
|
227
|
-
# (`"data-contrast":`), and reads `"data-contrast" =>` as the String key, so the written record
|
|
228
|
-
# declared a key the body never has (#2176). Optional keys carry a leading `?`.
|
|
229
|
-
def erase_key_prefix(key)
|
|
200
|
+
# A key and its separator as RBS spells them, with a leading `?` for an optional key. `describe`
|
|
201
|
+
# (the diagnostic display) and {#erase_to_rbs} (which only ever sees Symbol keys — string-keyed
|
|
202
|
+
# shapes degrade to `Hash[...]` before reaching here) share it, so a key reads the same in a
|
|
203
|
+
# message as in the signature it erases to. A Symbol whose text is a plain identifier is a bare
|
|
204
|
+
# key with a colon (`lang:`, Ruby keywords like `class` included). Any other Symbol is a quoted
|
|
205
|
+
# Symbol literal with a fat arrow (`:"data-contrast" =>`): RBS rejects a bare non-identifier
|
|
206
|
+
# (`data-contrast:`) and a quoted key with a colon (`"data-contrast":`), and reads
|
|
207
|
+
# `"data-contrast" =>` as the String key, so the written record declared a key the body never has
|
|
208
|
+
# (#2176). Every other key, a String included, is its `inspect` literal with a fat arrow
|
|
209
|
+
# (`"a-b" =>`, `1 =>`, `nil =>`): the compact `"a-b":` would read as a Symbol key in Ruby, so a
|
|
210
|
+
# String-vs-Symbol key mismatch in a diagnostic was unreadable (#2235).
|
|
211
|
+
def key_prefix(key)
|
|
230
212
|
optional = optional_key?(key) ? "?" : ""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
end
|
|
213
|
+
return "#{optional}#{key.inspect} =>" unless key.is_a?(Symbol)
|
|
214
|
+
return "#{optional}#{key}:" if BARE_RECORD_KEY.match?(key.to_s)
|
|
215
|
+
|
|
216
|
+
"#{optional}:#{key.to_s.inspect} =>"
|
|
236
217
|
end
|
|
237
218
|
|
|
238
219
|
# `String#inspect` escapes `#{`, `#$` and `#@` as `\#`, which RBS's parser does not read back as
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
6
|
+
require_relative "plain_lattice"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Type
|
|
10
|
+
# ADR-130 WD1 — an RBS interface under `Rigor::Plugin` (`_Scope`, `_Environment`, ...), as the facet of the
|
|
11
|
+
# `Dynamic` that carries a plugin-interface value: `Dynamic[Rigor::Plugin::_Scope]`. It appears only there.
|
|
12
|
+
# `Combinator.untyped?` reads such a `Dynamic` as `untyped`, and union normalisation erases it to `Dynamic[top]`
|
|
13
|
+
# wherever a join would otherwise keep an arm master does not, so the facet changes one behaviour: a call of a
|
|
14
|
+
# member the interface declares resolves through that member's RBS (`RbsDispatch`, ADR-130 WD3).
|
|
15
|
+
#
|
|
16
|
+
# `name` is spelled as `Nominal#class_name` is (`Rigor::Plugin::_Scope`), and `type_args` are the interface's
|
|
17
|
+
# type arguments, translated in the caller's context. Every other RBS interface still translates to
|
|
18
|
+
# `Dynamic[top]` (`RbsTypeTranslator`, ADR-130 WD4).
|
|
19
|
+
class Interface
|
|
20
|
+
attr_reader :name, :type_args
|
|
21
|
+
|
|
22
|
+
EMPTY_TYPE_ARGS = [].freeze
|
|
23
|
+
private_constant :EMPTY_TYPE_ARGS
|
|
24
|
+
|
|
25
|
+
def initialize(name, type_args = EMPTY_TYPE_ARGS)
|
|
26
|
+
raise ArgumentError, "name must be a String, got #{name.class}" unless name.is_a?(String)
|
|
27
|
+
|
|
28
|
+
@name = name.frozen? ? name : name.dup.freeze
|
|
29
|
+
@type_args = type_args.frozen? ? type_args : type_args.dup.freeze
|
|
30
|
+
freeze
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def describe(verbosity = :short)
|
|
34
|
+
return name if type_args.empty?
|
|
35
|
+
|
|
36
|
+
"#{name}[#{type_args.map { |arg| arg.describe(verbosity) }.join(', ')}]"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A facet never reaches erasure on its own (`Dynamic#erase_to_rbs` is `untyped`); spelled as RBS for a
|
|
40
|
+
# caller that erases the carrier directly.
|
|
41
|
+
def erase_to_rbs
|
|
42
|
+
return name if type_args.empty?
|
|
43
|
+
|
|
44
|
+
"#{name}[#{type_args.map(&:erase_to_rbs).join(', ')}]"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
include Rigor::Type::PlainLattice
|
|
48
|
+
|
|
49
|
+
include Rigor::Type::AcceptanceRouter
|
|
50
|
+
|
|
51
|
+
include Rigor::ValueSemantics
|
|
52
|
+
|
|
53
|
+
value_fields :name, :type_args
|
|
54
|
+
|
|
55
|
+
def inspect
|
|
56
|
+
"#<Rigor::Type::Interface #{describe(:short)}>"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/rigor/type/union.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Rigor
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
# ADR-1 § "
|
|
52
|
+
# ADR-1 § "Erasure Must Be Conservative" + the value-lattice rule `untyped | T = untyped` (every `T`
|
|
53
53
|
# is gradually consistent with `untyped`). When any union member erases to `"untyped"`, the whole
|
|
54
54
|
# union erases to `"untyped"` — the RBS surface has no carrier for "Dynamic-origin alongside a
|
|
55
55
|
# static facet", and the gradual-consistency contract guarantees the substitution is sound at every
|
data/lib/rigor/type.rb
CHANGED
|
@@ -28,6 +28,7 @@ require_relative "type/union"
|
|
|
28
28
|
require_relative "type/difference"
|
|
29
29
|
require_relative "type/refined"
|
|
30
30
|
require_relative "type/intersection"
|
|
31
|
+
require_relative "type/interface"
|
|
31
32
|
require_relative "type/app"
|
|
32
33
|
require_relative "type/result"
|
|
33
34
|
require_relative "type/maybe"
|
|
@@ -22,8 +22,7 @@ module Rigor
|
|
|
22
22
|
# Freeze the String field so the resulting Data object is `Ractor.shareable?` regardless of
|
|
23
23
|
# whether the caller passed a `# frozen_string_literal: true` constant or a dynamically built
|
|
24
24
|
# String. The same discipline applies to every other TypeNode value object — they live in the
|
|
25
|
-
# parser's hot path and are the natural carriers to flow through future Ractor boundaries
|
|
26
|
-
# (see CURRENT_WORK Open Items #8).
|
|
25
|
+
# parser's hot path and are the natural carriers to flow through future Ractor boundaries.
|
|
27
26
|
super(name: name.frozen? ? name : name.dup.freeze)
|
|
28
27
|
end
|
|
29
28
|
end
|
|
@@ -16,7 +16,7 @@ module Rigor
|
|
|
16
16
|
# resolver in the chain plus the built-in registry through the
|
|
17
17
|
# same entry point.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
19
|
+
# Built by `Environment#build_name_scope` from
|
|
20
20
|
# `Plugin::Registry#type_node_resolvers`. The chain is
|
|
21
21
|
# immutable and re-entrant; the parser may consult it many
|
|
22
22
|
# times for the same node.
|
|
@@ -7,8 +7,7 @@ module Rigor
|
|
|
7
7
|
# `Pick[T, "name"]`, `pick_of[Shape, "a" | "b"]`, and downstream
|
|
8
8
|
# plugin resolvers that accept literal key selectors.
|
|
9
9
|
#
|
|
10
|
-
# ADR-13 follow-up
|
|
11
|
-
# #2): the RBS::Extended grammar previously could not tokenise
|
|
10
|
+
# ADR-13 follow-up: the RBS::Extended grammar previously could not tokenise
|
|
12
11
|
# `"name"` inside a type-arg position. The resolver translates
|
|
13
12
|
# this node to a `Type::Constant` carrying the string value.
|
|
14
13
|
# Slice 1 supports double-quoted strings without escape
|
|
@@ -7,8 +7,7 @@ module Rigor
|
|
|
7
7
|
# `Pick[T, :name]`, `pick_of[Shape, :a | :b]`, and downstream
|
|
8
8
|
# plugin resolvers that accept literal key selectors.
|
|
9
9
|
#
|
|
10
|
-
# ADR-13 follow-up
|
|
11
|
-
# #2): the RBS::Extended grammar previously could not tokenise
|
|
10
|
+
# ADR-13 follow-up: the RBS::Extended grammar previously could not tokenise
|
|
12
11
|
# `:name` inside a type-arg position; this addition closes the
|
|
13
12
|
# gap so `ImportedRefinements.parse` produces a uniform AST.
|
|
14
13
|
# The resolver translates this node to a `Type::Constant`
|
|
@@ -5,8 +5,7 @@ module Rigor
|
|
|
5
5
|
# Union AST node. Carries the nodes of a `T1 | T2 | …` union
|
|
6
6
|
# expression authored at type-arg position.
|
|
7
7
|
#
|
|
8
|
-
# ADR-13 follow-up
|
|
9
|
-
# #2): together with {SymbolLiteral} / {StringLiteral} this
|
|
8
|
+
# ADR-13 follow-up: together with {SymbolLiteral} / {StringLiteral} this
|
|
10
9
|
# closes the parser-side gap that prevented `Pick[T, :a | :b]`
|
|
11
10
|
# / `Pick[T, "a" | "b"]` from tokenising. The resolver pass
|
|
12
11
|
# recurses through each node and folds them via
|
|
@@ -16,10 +15,8 @@ module Rigor
|
|
|
16
15
|
# shadowing `Data#members`, which is the introspection method
|
|
17
16
|
# every `Data.define` mix-in inherits.
|
|
18
17
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# `Union` nodes are permitted but the parser does not
|
|
22
|
-
# synthesise them by itself.
|
|
18
|
+
# The parser builds a flat list (left-associative); the constructor
|
|
19
|
+
# rejects a nested `Union` node.
|
|
23
20
|
class Union < Data.define(:nodes)
|
|
24
21
|
def initialize(nodes:)
|
|
25
22
|
unless nodes.is_a?(Array) && nodes.size >= 2 && nodes.all? { |m| valid_member?(m) }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module TypeOfMemo
|
|
5
|
+
# G1's stage-4 form (ADR-129 WD5), mixed into {TypeOfMemo}'s singleton: on a would-be hit with
|
|
6
|
+
# `TypeOfMemo.enable!(serve: true)`, the memo serves the entry and checks it against a recomputation. For specs
|
|
7
|
+
# and audit tools; the CLI's `RIGOR_TYPE_OF_SHADOW=1` never serves.
|
|
8
|
+
module Serving
|
|
9
|
+
EMPTY = [].freeze
|
|
10
|
+
private_constant :EMPTY
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
# G1's stage-4 form (ADR-129 WD5) on one would-be hit:
|
|
15
|
+
# 1. recompute on the same scope objects with the memo suspended, in a capture and a priors window, and record
|
|
16
|
+
# the final state of every entry either side touches; a recomputation that touched a Resource is a mismatch
|
|
17
|
+
# and its answer stands;
|
|
18
|
+
# 2. undo the recomputation's Table writes and drop its journal records, which the served entry replaces;
|
|
19
|
+
# 3. serve the entry: validate its reads, merge them into the enclosing captures, replay its writes, and compare
|
|
20
|
+
# the touched entries and the type with the recorded ones. A recomputation that raises where an entry exists
|
|
21
|
+
# is a mismatch too.
|
|
22
|
+
def serve_hit(state, table, phase, scope, node, entry, &)
|
|
23
|
+
stored, reads, writes = entry.is_a?(Array) ? entry : [entry, EMPTY, EMPTY]
|
|
24
|
+
count(phase, :checked)
|
|
25
|
+
mark = state.journal_size
|
|
26
|
+
fresh, priors, refused = recompute(state, table, phase, scope, node, stored, &)
|
|
27
|
+
if refused
|
|
28
|
+
judge(state, phase, scope, node, stored, fresh, writes, priors, refused)
|
|
29
|
+
return fresh
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
serve(state, phase, scope, node, stored, fresh, [reads, writes, priors, mark])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Steps 2 and 3 of {#serve_hit}. An error in them (a replay into a store the entry outlived) is a mismatch,
|
|
36
|
+
# counted rather than raised into the typing path's rescues. The recomputation's type is answered; the tables may
|
|
37
|
+
# hold a partial replay by then, which the counted mismatch already fails.
|
|
38
|
+
def serve(state, phase, scope, node, stored, fresh, slices)
|
|
39
|
+
reads, writes, priors, mark = slices
|
|
40
|
+
live = touched(writes, priors).map { |slot, key| [slot, key, state.entry(slot, key)] }
|
|
41
|
+
state.undo(priors)
|
|
42
|
+
state.truncate_journal(mark)
|
|
43
|
+
kinds = []
|
|
44
|
+
kinds << :revalidate if state.stale_read(reads)
|
|
45
|
+
state.merge_reads(reads)
|
|
46
|
+
state.replay(writes)
|
|
47
|
+
served(state, phase, scope, node, kinds, stored, fresh, live)
|
|
48
|
+
stored
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
report(phase, scope, node, [:serve_error, "#{e.class}: #{e.message[0, 120]}"], stored, fresh, [])
|
|
51
|
+
fresh
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def recompute(state, table, phase, scope, node, stored)
|
|
55
|
+
state.open_capture
|
|
56
|
+
state.open_priors
|
|
57
|
+
table.suspended += 1
|
|
58
|
+
returned = false
|
|
59
|
+
begin
|
|
60
|
+
fresh = yield
|
|
61
|
+
returned = true
|
|
62
|
+
ensure
|
|
63
|
+
table.suspended -= 1
|
|
64
|
+
priors = state.close_priors
|
|
65
|
+
refused, = state.close_capture
|
|
66
|
+
report(phase, scope, node, [:raised], stored, nil, []) unless returned
|
|
67
|
+
end
|
|
68
|
+
[fresh, priors, refused]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Every entry the served writes or the recomputation's writes touch, each once.
|
|
72
|
+
def touched(writes, priors)
|
|
73
|
+
seen = TypingState::KeyIndex.new
|
|
74
|
+
keys = []
|
|
75
|
+
[writes, priors].each do |slice|
|
|
76
|
+
slice.each_slice(3) do |slot, key, _|
|
|
77
|
+
next if seen.key?(slot, key)
|
|
78
|
+
|
|
79
|
+
seen.store(slot, key, true)
|
|
80
|
+
keys << [slot, key]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
keys
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def served(state, phase, scope, node, kinds, stored, fresh, live) # rubocop:disable Metrics/ParameterLists
|
|
87
|
+
kinds << :type unless same?(fresh, stored)
|
|
88
|
+
diffs = live.filter_map do |slot, key, want|
|
|
89
|
+
now = state.entry(slot, key)
|
|
90
|
+
[slot, key, want, now] unless same?(want, now)
|
|
91
|
+
end
|
|
92
|
+
kinds << :table unless diffs.empty?
|
|
93
|
+
report(phase, scope, node, kinds, stored, fresh, diffs) unless kinds.empty?
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
report(phase, scope, node, [:judge_error, "#{e.class}: #{e.message[0, 120]}"], stored, fresh, [])
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|