rigortype 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- 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 +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- 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 +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- 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 +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- 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/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- 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 +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- 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/incremental_snapshot.rb +60 -18
- 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 +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -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 +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- 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 +27 -21
- 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 +18 -17
- 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 +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- 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 +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- 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 +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- 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 +1 -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 +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- 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 +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- 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 +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -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/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_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -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 +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -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 +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- 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/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- 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 +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- 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 +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- 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/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 +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.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/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -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 +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- 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 +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -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 +28 -9
- 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 +98 -4
- 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 +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- 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 +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- 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 +2 -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 +96 -2
- 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 +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- 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 +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- 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/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- 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 +217 -37
- 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 +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- 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/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 +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- 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 +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- 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 +7 -12
- 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 +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -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 +7 -6
- 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 +4 -5
- 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-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 +2 -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 +1 -3
- 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 +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- 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 +5 -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 +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- 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/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
|
@@ -245,8 +245,8 @@ module Rigor
|
|
|
245
245
|
end
|
|
246
246
|
|
|
247
247
|
# `literal-string` — a `String` that is statically known to come from a source-code literal (or a
|
|
248
|
-
# composition of literals).
|
|
249
|
-
#
|
|
248
|
+
# composition of literals). Interpolation `"#{...}"` and `+` / `*` composition keep
|
|
249
|
+
# the flow (see {literal_string_compatible?}). Every `Constant<String>` is implicitly
|
|
250
250
|
# literal-string-compatible; the carrier exists for cases where the concrete value is unknown but
|
|
251
251
|
# literal-ness has been established (an RBS::Extended `return: literal-string` annotation, or
|
|
252
252
|
# interpolation over literal-bearing parts).
|
|
@@ -433,8 +433,7 @@ module Rigor
|
|
|
433
433
|
# - `Type::Tuple[A, B, C]` → `Constant<0> | Constant<1> | Constant<2>`.
|
|
434
434
|
# - `Type::Nominal["Hash", [K, V]]` → `K` (untyped if absent).
|
|
435
435
|
# - `Type::Nominal["Array", [E]]` → `non-negative-int`.
|
|
436
|
-
# - `Type::Constant`
|
|
437
|
-
# per-element keys.
|
|
436
|
+
# - `Type::Constant` wrapping a Range with an Integer begin — `non-negative-int`.
|
|
438
437
|
#
|
|
439
438
|
# Other inputs (`Top`, `Dynamic`, untyped Nominals, `Union`, `Refined`, `Difference`,
|
|
440
439
|
# `Intersection`) project to `top` so the type function always returns a value — callers may narrow
|
|
@@ -456,8 +455,7 @@ module Rigor
|
|
|
456
455
|
# - `Type::Tuple[A, B, C]` → `A | B | C`.
|
|
457
456
|
# - `Type::Nominal["Hash", [K, V]]` → `V` (untyped if absent).
|
|
458
457
|
# - `Type::Nominal["Array", [E]]` → `E` (untyped if absent).
|
|
459
|
-
# - `Type::Constant`
|
|
460
|
-
# element.
|
|
458
|
+
# - `Type::Constant` wrapping an integer Range — the `IntegerRange` it spans.
|
|
461
459
|
def value_of(type)
|
|
462
460
|
case type
|
|
463
461
|
when HashShape then hash_shape_values(type)
|
|
@@ -528,8 +526,7 @@ module Rigor
|
|
|
528
526
|
# Phase A handles `Type::HashShape` (literal-key K). Phase B (slice 5) extends to `Type::Tuple`
|
|
529
527
|
# (integer-index K) — `pick_of[Tuple[A, B, C], 0 | 2]` evaluates to `Tuple[A, C]`. Non-shape inputs
|
|
530
528
|
# (`Type::Nominal`, etc.) return `type` unchanged ("lossy degradation"; the
|
|
531
|
-
# `dynamic.shape.lossy-projection` diagnostic that flags the boundary
|
|
532
|
-
# diagnostic threading arrives).
|
|
529
|
+
# `dynamic.shape.lossy-projection` diagnostic that flags the boundary).
|
|
533
530
|
def pick_of(type, keys)
|
|
534
531
|
case type
|
|
535
532
|
when HashShape then hash_shape_pick(type, keys)
|
|
@@ -597,8 +594,7 @@ module Rigor
|
|
|
597
594
|
# site where the projection was authored.
|
|
598
595
|
#
|
|
599
596
|
# `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.
|
|
597
|
+
# is lossy.
|
|
602
598
|
def shape_projection_lossy?(type)
|
|
603
599
|
!type.is_a?(HashShape) && !type.is_a?(Tuple)
|
|
604
600
|
end
|
|
@@ -792,9 +788,9 @@ module Rigor
|
|
|
792
788
|
top
|
|
793
789
|
end
|
|
794
790
|
|
|
795
|
-
# `Type::Constant` only carries scalar literals (
|
|
796
|
-
#
|
|
797
|
-
#
|
|
791
|
+
# `Type::Constant` only carries scalar literals (`Constant::SCALAR_CLASSES`); Array and Hash literals
|
|
792
|
+
# become Tuple / HashShape carriers earlier in the typer. Range is the only scalar with meaningful
|
|
793
|
+
# key/value projections.
|
|
798
794
|
def constant_keys(value)
|
|
799
795
|
return non_negative_int if value.is_a?(Range) && value.begin.is_a?(Integer)
|
|
800
796
|
|
|
@@ -1040,9 +1036,10 @@ module Rigor
|
|
|
1040
1036
|
# No type carrier has a method that changes it once built (a built type is frozen; one `Marshal.load`
|
|
1041
1037
|
# restores is not, but nothing mutates it either), so a memoised key cannot go stale. The memo is weak
|
|
1042
1038
|
# in both key and value: it keeps neither the type nor the string alive, and an entry whose string
|
|
1043
|
-
# nothing else holds is dropped at the next GC. It is keyed by identity on purpose:
|
|
1044
|
-
# `Constant[
|
|
1045
|
-
# module's ivar, so a pool worker renders the key every time, as every
|
|
1039
|
+
# nothing else holds is dropped at the next GC. It is keyed by identity on purpose: two equal members
|
|
1040
|
+
# need not render alike (`Constant[1..2]` and `Constant[1..2.0]`), so equality is not a safe key. A
|
|
1041
|
+
# non-main Ractor may not read the module's ivar, so a pool worker renders the key every time, as every
|
|
1042
|
+
# run did before.
|
|
1046
1043
|
def sort_key(member)
|
|
1047
1044
|
return member.describe(:short) unless Ractor.main?
|
|
1048
1045
|
|
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,15 @@ 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).
|
|
81
|
+
# it carries the class of its endpoints, or `untyped` when both are nil (#1891). `Set` is
|
|
82
|
+
# generic too, and a set is mutable: the constant's elements are only what it held when it was built, and
|
|
83
|
+
# `x << "a"` adds a member they do not name. It erases to `Set[untyped]`, as an Array or Hash literal's
|
|
84
|
+
# element type is widened rather than pinned (#2177).
|
|
83
85
|
def erase_to_rbs
|
|
84
86
|
case value
|
|
85
87
|
when true then "true"
|
|
@@ -88,6 +90,7 @@ module Rigor
|
|
|
88
90
|
when Integer then value.to_s
|
|
89
91
|
when Symbol, String then value.inspect
|
|
90
92
|
when Range then "Range[#{range_element_rbs}]"
|
|
93
|
+
when ::Set then "Set[untyped]"
|
|
91
94
|
else value.class.name
|
|
92
95
|
end
|
|
93
96
|
end
|
|
@@ -96,13 +99,51 @@ module Rigor
|
|
|
96
99
|
|
|
97
100
|
include Rigor::Type::AcceptanceRouter
|
|
98
101
|
|
|
102
|
+
# Two carriers are equal when they pin the same value, which for a Float is stricter than Ruby's `==`
|
|
103
|
+
# (#1513): `0.0` and `-0.0` are distinct values (`1.0 / x` tells them apart), so `0.0 | -0.0` keeps both
|
|
104
|
+
# members, and a NaN equals another NaN, so `==` stays reflexive and a union never collects one NaN member
|
|
105
|
+
# per occurrence. A Complex compares its parts the same way.
|
|
99
106
|
def ==(other)
|
|
100
|
-
other.is_a?(Constant) && value.class == other.value.class && value
|
|
107
|
+
other.is_a?(Constant) && value.class == other.value.class && Constant.same_value?(value, other.value)
|
|
101
108
|
end
|
|
102
109
|
alias eql? ==
|
|
103
110
|
|
|
104
111
|
def hash
|
|
105
|
-
[Constant, value.class, value].hash
|
|
112
|
+
[Constant, value.class, Constant.value_key(value)].hash
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# True when `left` and `right` (of one class) are the same value in the sense of {#==}.
|
|
116
|
+
def self.same_value?(left, right)
|
|
117
|
+
case left
|
|
118
|
+
when Float then same_float?(left, right)
|
|
119
|
+
when Complex then same_float?(left.real, right.real) && same_float?(left.imaginary, right.imaginary)
|
|
120
|
+
else left == right
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.same_float?(left, right)
|
|
125
|
+
return left == right unless left.is_a?(Float) && right.is_a?(Float)
|
|
126
|
+
return right.nan? if left.nan?
|
|
127
|
+
|
|
128
|
+
left.eql?(right) && (!left.zero? || (1.0 / left).positive? == (1.0 / right).positive?)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# A key whose `eql?` / `hash` agree with {.same_value?}: `uniq { |v| Constant.value_key(v) }` keeps
|
|
132
|
+
# `0.0` and `-0.0` apart and folds every NaN into one. Any other value is its own key, so plain `uniq`
|
|
133
|
+
# semantics are unchanged for it.
|
|
134
|
+
def self.value_key(value)
|
|
135
|
+
case value
|
|
136
|
+
when Float then float_key(value)
|
|
137
|
+
when Complex then [Complex, float_key(value.real), float_key(value.imaginary)]
|
|
138
|
+
else value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.float_key(value)
|
|
143
|
+
return value unless value.is_a?(Float)
|
|
144
|
+
return :nan if value.nan?
|
|
145
|
+
|
|
146
|
+
value.zero? ? [Float, (1.0 / value)] : value
|
|
106
147
|
end
|
|
107
148
|
|
|
108
149
|
def inspect
|
|
@@ -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
|
|
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
|
|
@@ -72,9 +72,9 @@ module Rigor
|
|
|
72
72
|
def erase_to_rbs
|
|
73
73
|
return "{}" if pairs.empty? && closed?
|
|
74
74
|
return hash_erasure unless closed?
|
|
75
|
-
return hash_erasure if pairs.each_key.any? { |k| !k.is_a?(Symbol) }
|
|
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,47 +193,33 @@ 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
|
-
|
|
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)
|
|
212
|
+
optional = optional_key?(key) ? "?" : ""
|
|
213
|
+
return "#{optional}#{key.inspect} =>" unless key.is_a?(Symbol)
|
|
214
|
+
return "#{optional}#{key}:" if BARE_RECORD_KEY.match?(key.to_s)
|
|
212
215
|
|
|
213
|
-
|
|
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
|
|
216
|
+
"#{optional}:#{key.to_s.inspect} =>"
|
|
220
217
|
end
|
|
221
218
|
|
|
222
|
-
#
|
|
223
|
-
# Symbol
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
# non-identifier (`data-contrast:`) and a quoted key with a colon (`"data-contrast":`). Getting
|
|
227
|
-
# this wrong made sig-gen emit an unparseable Mastodon `html_attributes` shape that crashed the
|
|
228
|
-
# whole RBS env build. Valid identifier keys (incl. Ruby keywords like `class`) keep the colon
|
|
229
|
-
# form, so only genuinely non-identifier keys change. Optional keys carry a leading `?`.
|
|
230
|
-
def erase_key_prefix(key)
|
|
231
|
-
optional = optional_key?(key) ? "?" : ""
|
|
232
|
-
if BARE_RECORD_KEY.match?(key.to_s)
|
|
233
|
-
"#{optional}#{key}:"
|
|
234
|
-
else
|
|
235
|
-
"#{optional}#{key.to_s.inspect} =>"
|
|
236
|
-
end
|
|
219
|
+
# `String#inspect` escapes `#{`, `#$` and `#@` as `\#`, which RBS's parser does not read back as
|
|
220
|
+
# `#`. A key spelled that way would name a different Symbol, so the shape erases to `Hash[...]`.
|
|
221
|
+
def record_key_spellable?(key)
|
|
222
|
+
!key.to_s.inspect.include?("\\#")
|
|
237
223
|
end
|
|
238
224
|
|
|
239
225
|
def hash_erasure
|
|
@@ -63,10 +63,11 @@ module Rigor
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Erases to the tagging class nominal (conservative: the structural members are not RBS-expressible
|
|
66
|
-
# as a class instance). The anonymous case erases to the `Struct` supertype
|
|
66
|
+
# as a class instance). The anonymous case erases to the `Struct` supertype with an `untyped`
|
|
67
|
+
# element: `Struct[Elem]` is generic, and a bare `Struct` fails `rbs validate` (#2177).
|
|
67
68
|
def erase_to_rbs
|
|
68
69
|
name = class_name
|
|
69
|
-
return "Struct" if name.nil?
|
|
70
|
+
return "Struct[untyped]" if name.nil?
|
|
70
71
|
|
|
71
72
|
name
|
|
72
73
|
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
|
|
@@ -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) }
|
|
@@ -46,16 +46,7 @@ module Rigor
|
|
|
46
46
|
key = (["self.class"] + fields.map(&:to_s)).join(", ")
|
|
47
47
|
|
|
48
48
|
# Codegen (not per-call reflection) so the generated `==` / `hash` read fields
|
|
49
|
-
# directly and run at hand-written speed on the inference hot path.
|
|
50
|
-
# `value_fields :class_name, :type_args` the emitted source is:
|
|
51
|
-
#
|
|
52
|
-
# def ==(other)
|
|
53
|
-
# other.is_a?(self.class) && class_name == other.class_name && type_args == other.type_args
|
|
54
|
-
# end
|
|
55
|
-
# alias_method :eql?, :==
|
|
56
|
-
# def hash
|
|
57
|
-
# [self.class, class_name, type_args].hash
|
|
58
|
-
# end
|
|
49
|
+
# directly and run at hand-written speed on the inference hot path.
|
|
59
50
|
class_eval(<<~RUBY, __FILE__, __LINE__ + 1) # rubocop:disable Style/DocumentDynamicEvalDefinition
|
|
60
51
|
def ==(other)
|
|
61
52
|
other.is_a?(self.class)#{guard}
|
data/lib/rigor/version.rb
CHANGED
|
@@ -167,8 +167,8 @@ module Rigor
|
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
# Walks the channel body recursively (so `stream_from` / `stream_for` calls inside `subscribed`
|
|
170
|
-
# / helper methods are picked up). Returns
|
|
171
|
-
#
|
|
170
|
+
# / helper methods are picked up). Returns the literal stream names and whether any dynamic
|
|
171
|
+
# registration was seen, as a pair.
|
|
172
172
|
def collect_stream_registrations(node, names: [], dynamic: false)
|
|
173
173
|
return [names, dynamic] if node.nil?
|
|
174
174
|
|
|
@@ -11,8 +11,8 @@ module Rigor
|
|
|
11
11
|
# `subscription.perform("action_name", data)`.
|
|
12
12
|
# - `stream_names` — the set of literal-string stream names registered via `stream_from "name"`
|
|
13
13
|
# calls inside the channel body. Dynamic registrations (`stream_from interpolated_string`) are
|
|
14
|
-
# recorded separately as `dynamic_streams: true
|
|
15
|
-
# warnings
|
|
14
|
+
# recorded separately as `dynamic_streams: true`; when any channel has one, the analyzer suppresses
|
|
15
|
+
# unknown-stream warnings for the whole run.
|
|
16
16
|
class ChannelIndex
|
|
17
17
|
Entry = Data.define(:class_name, :file_path, :action_methods, :stream_names, :dynamic_streams) do
|
|
18
18
|
def includes_action?(name)
|
|
@@ -59,7 +59,6 @@ module Rigor
|
|
|
59
59
|
@by_name.keys
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
# All literal stream names registered across every discovered channel.
|
|
63
62
|
def all_stream_names
|
|
64
63
|
@entries.flat_map { |e| e.stream_names.to_a }.to_set
|
|
65
64
|
end
|
|
@@ -12,7 +12,7 @@ module Rigor
|
|
|
12
12
|
# rigor-actioncable — validates ActionCable `<Channel>.broadcast_to(...)` and
|
|
13
13
|
# `ActionCable.server.broadcast(stream_name, ...)` call sites against the discovered channel index.
|
|
14
14
|
#
|
|
15
|
-
# Tier 3F of the [Rails plugins roadmap](
|
|
15
|
+
# Tier 3F of the [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
16
16
|
# Statically discovers channel classes by walking `channel_search_paths` and parsing each file with
|
|
17
17
|
# Prism — no `actioncable` runtime dependency.
|
|
18
18
|
#
|