rigortype 0.4.6 → 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 +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- 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 +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -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/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- 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 +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -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 +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- 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 +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- 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 +77 -71
- 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 +24 -31
- 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 +31 -12
- 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 +108 -102
- 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 +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 +12 -8
- 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 +84 -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 +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 +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 +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- 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 +480 -67
- 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 +41 -10
- 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 +88 -77
- 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 +39 -7
- 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 +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- 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/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- 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 +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -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 +3 -3
- 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 +1 -1
- 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 +9 -7
- 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 +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -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 +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- 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_yielder.rb +11 -17
- 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 +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- 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.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- 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 +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- 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 +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/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- 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 +9 -13
- data/lib/rigor/plugin/registry.rb +3 -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 +44 -5
- 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 +15 -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 +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- 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 +371 -58
- 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 +3 -5
- 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/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -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 +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 +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- 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 +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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -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-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 +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 +1 -1
- 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 +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/sig/rigor/analysis/baseline.rbs +0 -3
- 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/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- 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/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- 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 +36 -3
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class Environment
|
|
5
|
-
# Frozen
|
|
5
|
+
# Frozen read-only RBS query facade.
|
|
6
6
|
# [ADR-15](../../../docs/adr/15-ractor-concurrency.md) Phase 2b extracts the read-only surface of
|
|
7
7
|
# {RbsLoader} into this carrier so future Ractor-isolated workers can share one Reflection across the
|
|
8
8
|
# pool while keeping the per-Ractor mutable accelerator state (per-process memo Hashes) where it belongs.
|
|
9
9
|
#
|
|
10
10
|
# Backing tables (all frozen at construction):
|
|
11
11
|
#
|
|
12
|
-
# - `known_class_names` —
|
|
13
|
-
#
|
|
14
|
-
# - `instance_definitions` — `
|
|
12
|
+
# - `known_class_names` — every class / module / alias name in the loaded RBS environment.
|
|
13
|
+
# Top-level prefixed (`"::Hash"`); the query methods accept either form.
|
|
14
|
+
# - `instance_definitions` — `RBS::Definition` per class, keyed on `RBS::TypeName#to_s` (top-level
|
|
15
15
|
# prefixed).
|
|
16
16
|
# - `singleton_definitions` — same shape, singleton side.
|
|
17
|
-
# - `type_param_names` —
|
|
18
|
-
# - `constant_types` —
|
|
19
|
-
# - `ancestor_names` —
|
|
17
|
+
# - `type_param_names` — declared type parameters per class.
|
|
18
|
+
# - `constant_types` — translated constant declarations.
|
|
19
|
+
# - `ancestor_names` — normalised ancestor chains per class.
|
|
20
20
|
#
|
|
21
21
|
# Each `Reflection` instance is `frozen?` at construction — every cached table is frozen, `self` is
|
|
22
22
|
# frozen. **NOT** `Ractor.shareable?`: the `instance_definitions` / `singleton_definitions` tables hold
|
|
@@ -296,17 +296,14 @@ module Rigor
|
|
|
296
296
|
name.start_with?(TOKEN_PREFIX) ? name.delete_prefix(TOKEN_PREFIX) : nil
|
|
297
297
|
end
|
|
298
298
|
|
|
299
|
-
# The vendored directory basenames a library list activates.
|
|
300
299
|
def active_dirs(library_names)
|
|
301
300
|
library_names.filter_map { |name| VENDORED_DIRS[feature_of_token(name)] }
|
|
302
301
|
end
|
|
303
302
|
|
|
304
|
-
# Whether `dir_basename` is one of the gated directories.
|
|
305
303
|
def gated_dir?(dir_basename)
|
|
306
304
|
VENDORED_DIRS.value?(dir_basename)
|
|
307
305
|
end
|
|
308
306
|
|
|
309
|
-
# The feature a gated directory loads for.
|
|
310
307
|
def feature_for_dir(dir_basename)
|
|
311
308
|
VENDORED_DIRS.key(dir_basename)
|
|
312
309
|
end
|
|
@@ -28,7 +28,7 @@ module Rigor
|
|
|
28
28
|
#
|
|
29
29
|
# The directories are resolved at load time, on the main Ractor: `RbConfig::CONFIG` and `Gem.default_dir`
|
|
30
30
|
# read non-shareable constants and raise `Ractor::IsolationError` inside a pool worker. The scan itself runs
|
|
31
|
-
# lazily (about 0.1 s), memoised per
|
|
31
|
+
# lazily (about 0.1 s), memoised per process, and only for a class about to be reported.
|
|
32
32
|
module RubyLibraryRoots
|
|
33
33
|
STDLIB_ROOT = begin
|
|
34
34
|
::RBS::Repository::DEFAULT_STDLIB_ROOT.to_s.dup.freeze
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbs"
|
|
4
|
+
require_relative "../rooted_glob"
|
|
5
|
+
require_relative "gated_signature_plan"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
class Environment
|
|
9
|
+
# Issue #2470 — a `data/vendored_gem_sigs/<gem>/` member another signature source also declares stands down,
|
|
10
|
+
# so the other source's declaration binds instead of the two failing the class's definition build.
|
|
11
|
+
#
|
|
12
|
+
# The vendored directories load on `RBS::EnvironmentLoader`'s batch path, ahead of the project's
|
|
13
|
+
# `signature_paths:` (its own `sig/`, an `rbs collection install`, a gem's `sig/`). A file there that reopens
|
|
14
|
+
# a vendored class and declares one of its methods again — the activerecord 8.1 collection's `patch.rbs`
|
|
15
|
+
# writes `def async_exec` on `PG::Connection`, where the vendored `pg.rbs` has `alias async_exec exec` — made
|
|
16
|
+
# `RBS::DefinitionBuilder` raise `DuplicatedMethodDefinitionError` for the class, which Rigor keeps known, so
|
|
17
|
+
# every call into it read `Dynamic[top]`.
|
|
18
|
+
#
|
|
19
|
+
# {.build} is the #1713 member rule ({GatedSignaturePlan}) applied to the ungated directories, planned from
|
|
20
|
+
# the signature files themselves rather than from a trial build: the vendored copy is Rigor's guess at a gem's
|
|
21
|
+
# surface, and the project chose the other source, so the other source wins (`RbsLoader.build_env_for`'s
|
|
22
|
+
# stated precedence). Only a directory with a member to leave out is planned; the rest stay on the batch path.
|
|
23
|
+
# An overload continuation (`def m: ... | ...`) declares nothing of its own and keeps the vendored base.
|
|
24
|
+
#
|
|
25
|
+
# The plan is a pure function of the vendored files and the signature files, both of which the env-cache key
|
|
26
|
+
# digests, so a cold and a warm run build the same environment.
|
|
27
|
+
module VendoredSignaturePlan
|
|
28
|
+
# `dirs` — the vendored directories (Pathnames) to keep off the batch path; `sources` — their files as
|
|
29
|
+
# `[buffer, directives, decls]`, the taken members left out, to add right after the batch; `takers` — the
|
|
30
|
+
# signature files a member stood down to. A member stands down only to a file that stays in the env: the
|
|
31
|
+
# caller re-plans without each taker the build quarantined (`RbsLoader.build_env_attempt`), or the method
|
|
32
|
+
# would have no declaration at all.
|
|
33
|
+
Plan = Data.define(:dirs, :sources, :takers)
|
|
34
|
+
EMPTY = Plan.new(dirs: [].freeze, sources: [].freeze, takers: Set.new.freeze)
|
|
35
|
+
|
|
36
|
+
# A `class` / `module` header, after any `%a{...}` / `%a(...)` / `%a[...]` annotations on the same line.
|
|
37
|
+
ANNOTATED_HEADER = /^\s*(?:%a(?:\{[^}]*\}|\([^)]*\)|\[[^\]]*\])\s*)*(?:class|module)\s+(?:::)?/
|
|
38
|
+
|
|
39
|
+
module_function
|
|
40
|
+
|
|
41
|
+
# @param roots — the ungated vendored directories this build loads.
|
|
42
|
+
# @param signature_files — the absolute paths of the signature files that outrank them.
|
|
43
|
+
# @param excluded — signature files to plan without: takers a previous build of this env quarantined.
|
|
44
|
+
def build(roots, signature_files, excluded = Set.new)
|
|
45
|
+
files = signature_files.reject { |file| excluded.include?(file) }
|
|
46
|
+
return EMPTY if roots.empty? || files.empty?
|
|
47
|
+
|
|
48
|
+
parsed = roots.to_h { |root| [root, parsed_sources(root)] }
|
|
49
|
+
taken = taken_keys(files, header_pattern(parsed.values.flatten(1)))
|
|
50
|
+
return EMPTY if taken.empty?
|
|
51
|
+
|
|
52
|
+
takers = Set.new
|
|
53
|
+
planned = parsed.filter_map do |root, sources|
|
|
54
|
+
kept = plan_sources(sources, taken, takers)
|
|
55
|
+
[root, kept] if kept
|
|
56
|
+
end
|
|
57
|
+
return EMPTY if planned.empty?
|
|
58
|
+
|
|
59
|
+
Plan.new(dirs: planned.map(&:first), sources: planned.flat_map(&:last), takers: takers.freeze)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A vendored directory's files, parsed, under the Pathname buffer names the loader's batch path gives them,
|
|
63
|
+
# in the set `RBS::EnvironmentLoader#add(path:)` reads: every `.rbs` under it.
|
|
64
|
+
def parsed_sources(root)
|
|
65
|
+
RootedGlob.glob(root, "**/*.rbs").sort.filter_map { |file| RbsLoader.parse_signature_file(Pathname(file)) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# `sources` with the taken members left out, or nil when nothing is taken from any of them.
|
|
69
|
+
def plan_sources(sources, taken, takers)
|
|
70
|
+
kept = sources.map do |buffer, directives, decls|
|
|
71
|
+
[buffer, directives, plan_decls(decls, nil, taken, takers)]
|
|
72
|
+
end
|
|
73
|
+
kept unless kept.zip(sources).all? { |(_, _, after), (_, _, before)| unchanged?(after, before) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# A header naming one of the vendored top-level types, which every declaration of a vendored type (nested or
|
|
77
|
+
# `A::B`-qualified) starts from. A pre-filter only: the matches are parsed.
|
|
78
|
+
def header_pattern(parsed)
|
|
79
|
+
heads = parsed.flat_map do |_buffer, _directives, decls|
|
|
80
|
+
decls.filter_map { |decl| GatedSignaturePlan.type_declaration?(decl) && decl.name.to_s.delete_prefix("::") }
|
|
81
|
+
end
|
|
82
|
+
segments = heads.map { |name| Regexp.escape(name.split("::").first) }.uniq
|
|
83
|
+
return nil if segments.empty?
|
|
84
|
+
|
|
85
|
+
/#{ANNOTATED_HEADER}(?:#{segments.join('|')})\b/
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# `{type name => {[method, kind] => Set[file]}}` over the signature files that declare a vendored top-level
|
|
89
|
+
# type.
|
|
90
|
+
def taken_keys(signature_files, pattern)
|
|
91
|
+
taken = Hash.new { |hash, name| hash[name] = Hash.new { |keys, key| keys[key] = Set.new } }
|
|
92
|
+
return taken if pattern.nil?
|
|
93
|
+
|
|
94
|
+
signature_files.sort.each do |file|
|
|
95
|
+
text = RbsLoader.read_signature_text(file)
|
|
96
|
+
next if text.nil? || !text.match?(pattern)
|
|
97
|
+
|
|
98
|
+
decls = RbsLoader.parse_signature_source(file, text)
|
|
99
|
+
collect_keys(decls, nil, taken, file) if decls
|
|
100
|
+
end
|
|
101
|
+
taken
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def collect_keys(decls, namespace, taken, file)
|
|
105
|
+
decls.each do |decl|
|
|
106
|
+
next unless GatedSignaturePlan.type_declaration?(decl)
|
|
107
|
+
|
|
108
|
+
name = GatedSignaturePlan.qualify(namespace, decl.name)
|
|
109
|
+
decl.members.each do |member|
|
|
110
|
+
RbsLoader.member_method_keys(member).each { |key| taken[name][key] << file }
|
|
111
|
+
end
|
|
112
|
+
collect_keys(decl.members, name, taken, file)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# `decls` with every member `taken` declares for its type left out, nested types planned the same way, and
|
|
117
|
+
# the files each left-out member stood down to added to `takers`. A declaration nothing is taken from is
|
|
118
|
+
# returned as itself, which is how {.build} tells a directory unchanged.
|
|
119
|
+
def plan_decls(decls, namespace, taken, takers)
|
|
120
|
+
decls.map do |decl|
|
|
121
|
+
next decl unless GatedSignaturePlan.type_declaration?(decl)
|
|
122
|
+
|
|
123
|
+
plan_type(decl, GatedSignaturePlan.qualify(namespace, decl.name), taken, takers)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def unchanged?(after, before)
|
|
128
|
+
after.size == before.size && after.zip(before).all? { |a, b| a.equal?(b) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def plan_type(decl, name, taken, takers)
|
|
132
|
+
keys = taken.fetch(name, nil)
|
|
133
|
+
members = decl.members.filter_map do |member|
|
|
134
|
+
if GatedSignaturePlan.type_declaration?(member)
|
|
135
|
+
plan_type(member, GatedSignaturePlan.qualify(name, member.name), taken, takers)
|
|
136
|
+
elsif keys
|
|
137
|
+
plan_member(member, keys, takers)
|
|
138
|
+
else
|
|
139
|
+
member
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
return decl if unchanged?(members, decl.members)
|
|
143
|
+
|
|
144
|
+
GatedSignaturePlan.rebuild(decl, members: members)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def plan_member(member, keys, takers)
|
|
148
|
+
RbsLoader.member_method_keys(member).each { |key| takers.merge(keys[key]) if keys.key?(key) }
|
|
149
|
+
GatedSignaturePlan.untaken(member, keys)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
data/lib/rigor/environment.rb
CHANGED
|
@@ -16,6 +16,7 @@ require_relative "environment/bundle_sig_discovery"
|
|
|
16
16
|
require_relative "environment/lockfile_resolver"
|
|
17
17
|
require_relative "environment/installed_gem_set"
|
|
18
18
|
require_relative "environment/rbs_collection_discovery"
|
|
19
|
+
require_relative "environment/project_path_gems"
|
|
19
20
|
require_relative "environment/source_rbs_synthesis"
|
|
20
21
|
require_relative "plugin/isolation"
|
|
21
22
|
require_relative "cache/engine_source"
|
|
@@ -39,7 +40,7 @@ module Rigor
|
|
|
39
40
|
DEFAULT_PROJECT_SIG_DIR = "sig"
|
|
40
41
|
private_constant :DEFAULT_PROJECT_SIG_DIR
|
|
41
42
|
|
|
42
|
-
# `DEFAULT_LIBRARIES` is defined in the light `environment/default_libraries.rb` (required
|
|
43
|
+
# `DEFAULT_LIBRARIES` is defined in the light `environment/default_libraries.rb` (required above) so the
|
|
43
44
|
# ADR-87 WD4 boot-slimming probe can read it without loading the inference engine.
|
|
44
45
|
|
|
45
46
|
# ADR-72 — a Gemfile.lock gem name mapped to the opt-in plugin id that ships the SAME core-ext RBS. When
|
|
@@ -146,7 +147,7 @@ module Rigor
|
|
|
146
147
|
# code (`Plugin::Registry#hkt_overlay_registry` reads each manifest); before this it sat one line ABOVE
|
|
147
148
|
# the #784 rescue, so a raise there escaped {#hkt_registry} entirely. Post-#788 that is not a per-file
|
|
148
149
|
# crash storm but an uncaught abort: {HktRegistryHolder#fetch} memoises only on success, so the raise
|
|
149
|
-
# is re-attempted at every demand, and the run-owned demands (`Runner::
|
|
150
|
+
# is re-attempted at every demand, and the run-owned demands (`Runner::RunLevelFacts::Collect.hkt_scan_failure`
|
|
150
151
|
# after the file loop, `Analysis::WorkerSession#drain_reporters`) sit outside any rescue.
|
|
151
152
|
#
|
|
152
153
|
# The degradation is narrower than the scan's: only the plugin overlay is dropped, and the RBS scan
|
|
@@ -265,8 +266,10 @@ module Rigor
|
|
|
265
266
|
# `nil` (the default), the canonical project layout `<root>/sig` is used if it exists, otherwise no
|
|
266
267
|
# signature path is loaded.
|
|
267
268
|
# @param cache_store — persistent cache threaded into the underlying
|
|
268
|
-
# {Environment::RbsLoader} so constant lookups
|
|
269
|
-
#
|
|
269
|
+
# {Environment::RbsLoader} so constant lookups consult the cache. Pass `nil` (the default) to skip
|
|
270
|
+
# caching for this environment.
|
|
271
|
+
# @param core_root — issue #2517 — a core signature directory loaded in place of the `rbs` gem's `core/`
|
|
272
|
+
# ({Configuration#core_root}); nil (the default) loads the gem's core.
|
|
270
273
|
# rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
|
|
271
274
|
def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil, # rubocop:disable Metrics/AbcSize
|
|
272
275
|
plugin_registry: nil, dependency_source_index: nil,
|
|
@@ -276,7 +279,8 @@ module Rigor
|
|
|
276
279
|
bundler_lockfile: nil,
|
|
277
280
|
rbs_collection_lockfile: nil, rbs_collection_auto_detect: false,
|
|
278
281
|
synthetic_method_index: nil, project_patched_methods: nil,
|
|
279
|
-
source_files: [], locked_gems: nil, required_features: nil
|
|
282
|
+
source_files: [], locked_gems: nil, required_features: nil, core_root: nil,
|
|
283
|
+
paths: nil, exclude_patterns: nil, exclude_base: nil)
|
|
280
284
|
resolved_paths = project_signature_roots(signature_paths, root: root)
|
|
281
285
|
# O4 MVP — append per-gem `sig/` directories discovered under the target project's bundler install
|
|
282
286
|
# root. Empty array when neither an explicit path nor auto-detection finds a bundle. Order: user
|
|
@@ -318,7 +322,11 @@ module Rigor
|
|
|
318
322
|
# Issue #1700 — plus the gated vendored directories this run's source activates ({#feature_tokens}).
|
|
319
323
|
merged_libraries = (DEFAULT_LIBRARIES + libraries.map(&:to_s) +
|
|
320
324
|
feature_tokens(required_features, source_files, libraries)).uniq
|
|
321
|
-
|
|
325
|
+
# Issue #2441 — plus the project's own path gems ({ProjectPathGems}), whose collection copy would
|
|
326
|
+
# double-declare the project's `sig/`.
|
|
327
|
+
project_gems = ProjectPathGems.names(locked, paths: paths, root: root, exclude_patterns: exclude_patterns,
|
|
328
|
+
exclude_base: exclude_base || root)
|
|
329
|
+
skip_gem_names = collection_skip_gem_names(merged_libraries) + project_gems.to_a
|
|
322
330
|
collection_paths = RbsCollectionDiscovery.discover(
|
|
323
331
|
lockfile_path: rbs_collection_lockfile,
|
|
324
332
|
project_root: root,
|
|
@@ -338,8 +346,10 @@ module Rigor
|
|
|
338
346
|
# opt-in plugin twin is REACHABLE — loaded as a plugin, or wired in by `signature_paths:` (#672) —
|
|
339
347
|
# so the two never both declare the methods. The paths ride in `loader_signature_paths`, so the env
|
|
340
348
|
# cache descriptor digests them for free.
|
|
349
|
+
# Issue #2441 — the project's own path gems are neither missing RBS nor overlay-eligible: the project's
|
|
350
|
+
# `sig/` declares them, and an overlay (`data/gem_overlay/activesupport`) would collide with it.
|
|
341
351
|
missing_gems, overlay_paths = missing_gems_and_overlay_paths(
|
|
342
|
-
locked: locked, default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
|
|
352
|
+
locked: locked.except(*project_gems), default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
|
|
343
353
|
rbs_collection_paths: collection_paths, plugin_registry: plugin_registry,
|
|
344
354
|
signature_paths: resolved_paths, bundle_root: bundle_root
|
|
345
355
|
)
|
|
@@ -366,7 +376,8 @@ module Rigor
|
|
|
366
376
|
# the bundle walk and `rbs collection install` all stay authoritative.
|
|
367
377
|
deferred_signature_paths: plugin_sig_paths,
|
|
368
378
|
project_signature_paths: resolved_paths,
|
|
369
|
-
required_features: required_features || RequiredFeatures.scan(source_files, sig_paths: resolved_paths)
|
|
379
|
+
required_features: required_features || RequiredFeatures.scan(source_files, sig_paths: resolved_paths),
|
|
380
|
+
core_root: core_root
|
|
370
381
|
)
|
|
371
382
|
# Issues #824 / #1075 — the rows ADR-112 WD5's consistency rule reports for members the project's
|
|
372
383
|
# own `sig/` and an inline source both declare: an error per contradiction (or per refinement that
|
|
@@ -376,6 +387,10 @@ module Rigor
|
|
|
376
387
|
# on every run, so the member-level outcome needs no plumbing of its own. Costs nothing for a project
|
|
377
388
|
# with no inline RBS: the loader's reader short-circuits on an empty `virtual_rbs`.
|
|
378
389
|
record_member_consistency(loader, source_rbs_synthesis_reporter, root)
|
|
390
|
+
# Issue #2517 — a selected core is built here, not lazily. The env is first demanded from inside a file's
|
|
391
|
+
# analysis, under rescues that turn any raise into one `internal analyzer error` row per file; building it
|
|
392
|
+
# now raises the {CoreRootError} once, before analysis, where the CLI reports it as a configuration error.
|
|
393
|
+
loader.verify_core_root!
|
|
379
394
|
# ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
|
|
380
395
|
# `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
|
|
381
396
|
# don't consult HKT (e.g. `rigor check --cache-stats --no-stats`) don't pay the eager env-build cost
|
|
@@ -492,7 +507,7 @@ module Rigor
|
|
|
492
507
|
end
|
|
493
508
|
|
|
494
509
|
# ADR-72 — resolve the bundled RBS overlay directories to load for this project. A gem is eligible when
|
|
495
|
-
# it is classified `:missing` (see {
|
|
510
|
+
# it is classified `:missing` (see {missing_rows}) and its conflicting opt-in plugin twin (if
|
|
496
511
|
# any) is NOT already reachable. Returns `[Pathname]`, deterministically ordered, or `[]` when no
|
|
497
512
|
# eligible gem.
|
|
498
513
|
#
|
|
@@ -644,6 +659,8 @@ module Rigor
|
|
|
644
659
|
def record_member_undecided(reporter, record, root)
|
|
645
660
|
plugin_id, path = split_virtual_source_name(record.virtual_name)
|
|
646
661
|
return if path.nil?
|
|
662
|
+
return record_variable_undecided(reporter, record, root, plugin_id, path) if
|
|
663
|
+
MemberConsistency.variable_kind?(record.kind)
|
|
647
664
|
|
|
648
665
|
reporter.record(
|
|
649
666
|
plugin_id: plugin_id, path: path, kind: :not_honoured,
|
|
@@ -656,8 +673,22 @@ module Rigor
|
|
|
656
673
|
)
|
|
657
674
|
end
|
|
658
675
|
|
|
676
|
+
# Issue #2440 — an instance variable declared by both sides with different types. `sig/` binds
|
|
677
|
+
# (the coordinator's ruling on #2440, ADR-5): the row is `:info`, never `rbs.contradicting-signature`.
|
|
678
|
+
def record_variable_undecided(reporter, record, root, plugin_id, path)
|
|
679
|
+
reporter.record(
|
|
680
|
+
plugin_id: plugin_id, path: path, kind: :not_honoured,
|
|
681
|
+
message: "`#{member_label(record)}` is also declared in " \
|
|
682
|
+
"`#{path_relative_to(record.signature_path, root)}` with a different type " \
|
|
683
|
+
"(#{record.detail}), so the `.rbs` declaration binds and the inline one was dropped. " \
|
|
684
|
+
"RBS rejects two declarations of one instance variable, so only one can stay; " \
|
|
685
|
+
"regenerate the committed `.rbs` (`rbs-inline --output`) or align the two types."
|
|
686
|
+
)
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# `Demo#m` / `Demo.m` for a method, `Demo#@x` / `Demo.@x` for an instance / class instance variable.
|
|
659
690
|
def member_label(record)
|
|
660
|
-
separator = record.kind
|
|
691
|
+
separator = %i[singleton class_instance_variable].include?(record.kind) ? "." : "#"
|
|
661
692
|
"#{record.class_name}#{separator}#{record.method_name}"
|
|
662
693
|
end
|
|
663
694
|
|
|
@@ -26,13 +26,13 @@ module Rigor
|
|
|
26
26
|
#
|
|
27
27
|
# ## Composition rules (ADR-2)
|
|
28
28
|
#
|
|
29
|
-
# - `:return_type` — Intersect via `Type::Combinator.intersection`; collapse to bot
|
|
30
|
-
# `:return_type_collapse
|
|
29
|
+
# - `:return_type` — Intersect via `Type::Combinator.intersection`; collapse to bot records a
|
|
30
|
+
# `:return_type_collapse` conflict.
|
|
31
31
|
# - `:truthy_fact` / `:falsey_fact` / `:post_return_fact` — Edge-local; accumulate while deduping by
|
|
32
32
|
# payload equality.
|
|
33
33
|
# - `:mutation` / `:invalidation` / `:role` — Union; dedupe by equality.
|
|
34
|
-
# - `:exception` — Single-valued. Two non-`nil` non-equal exceptional effects
|
|
35
|
-
# `:exceptional_disagreement
|
|
34
|
+
# - `:exception` — Single-valued. Two non-`nil` non-equal exceptional effects record an
|
|
35
|
+
# `:exceptional_disagreement` conflict.
|
|
36
36
|
#
|
|
37
37
|
# ## Cross-tier contradictions
|
|
38
38
|
#
|
|
@@ -75,41 +75,66 @@ module Rigor
|
|
|
75
75
|
}.freeze
|
|
76
76
|
private_constant :TYPE_HANDLERS
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
private_constant :
|
|
80
|
-
|
|
81
|
-
# Whether a `maybe` for `param` against `arg` rests on a
|
|
82
|
-
# `Hash` with a gradual arm ({#gradual_hash?}) or an open `HashShape
|
|
83
|
-
# of
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return false unless
|
|
90
|
-
|
|
91
|
-
|
|
78
|
+
UNREAD_SHAPE_REFUSED_KEY = :__rigor_acceptance_unread_shape_refused__
|
|
79
|
+
private_constant :UNREAD_SHAPE_REFUSED_KEY
|
|
80
|
+
|
|
81
|
+
# Whether a `maybe` for `param` against `arg` rests on a shape that could not read its argument: a record
|
|
82
|
+
# against a `Hash` with a gradual arm ({#gradual_hash?}) or an open `HashShape`, or a tuple against an
|
|
83
|
+
# `Array` of unknown arity ({#array_carrier?}). The shape cannot prove the key set or the length, and its
|
|
84
|
+
# `maybe` climbs through whatever holds it. `OverloadSelector`'s strict pass reads that `maybe` as no
|
|
85
|
+
# evidence for the overload, so this re-asks with each such shape answering `no` and reports whether the
|
|
86
|
+
# pair then fails. A `maybe` from anywhere else (a subclass only the Ruby source declares, a splat under a
|
|
87
|
+
# parameter that is no shape) survives the re-ask and still counts.
|
|
88
|
+
def maybe_rests_on_unread_shape?(param, arg)
|
|
89
|
+
return false unless rests_on_unread_hash?(param, arg) || rests_on_unread_array?(param, arg)
|
|
90
|
+
|
|
91
|
+
refusing_unread(:all) { accepts(param, arg, mode: :gradual).no? }
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
# Whether any of `types` holds an `Array` a tuple cannot read ({#array_carrier?}), so a match may rest on
|
|
95
|
+
# the tuple's `maybe`. `OverloadSelector`'s gradual pass asks before re-matching under
|
|
96
|
+
# {#refusing_unread_arrays}.
|
|
97
|
+
def unread_array_in?(types)
|
|
98
|
+
types.any? { |type| contains_type?(type) { |inner| array_carrier?(inner) } }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Runs the block with each tuple answering `no`, not `maybe`, for an `Array` of unknown arity. A record's
|
|
102
|
+
# `maybe` for an unread hash is untouched.
|
|
103
|
+
def refusing_unread_arrays(&) = refusing_unread(:array, &)
|
|
104
|
+
|
|
94
105
|
# rubocop:disable-next Metrics/ClassLength
|
|
95
106
|
class << self
|
|
96
107
|
private
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
def rests_on_unread_hash?(param, arg)
|
|
110
|
+
contains_type?(param) { |type| type.is_a?(Type::HashShape) } &&
|
|
111
|
+
contains_type?(arg) { |type| unread_hash?(type) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def rests_on_unread_array?(param, arg)
|
|
115
|
+
contains_type?(param) { |type| type.is_a?(Type::Tuple) } &&
|
|
116
|
+
contains_type?(arg) { |type| array_carrier?(type) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The flag lives on the thread, not on a parameter, because the shape may sit any number of `accepts`
|
|
99
120
|
# calls below the one asked (a tuple element, a `Hash` value, a union member), and those calls go
|
|
100
|
-
# through each carrier's `Type#accepts`. It is set only for the duration of the block.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Thread.current[
|
|
121
|
+
# through each carrier's `Type#accepts`. It is set only for the duration of the block. `kind` is `:all`
|
|
122
|
+
# (records and tuples) or `:array` (tuples only).
|
|
123
|
+
def refusing_unread(kind)
|
|
124
|
+
previous = Thread.current[UNREAD_SHAPE_REFUSED_KEY]
|
|
125
|
+
Thread.current[UNREAD_SHAPE_REFUSED_KEY] = kind
|
|
104
126
|
yield
|
|
105
127
|
ensure
|
|
106
|
-
Thread.current[
|
|
128
|
+
Thread.current[UNREAD_SHAPE_REFUSED_KEY] = previous
|
|
107
129
|
end
|
|
108
130
|
|
|
109
|
-
# The `maybe` a
|
|
110
|
-
# {#
|
|
111
|
-
def
|
|
112
|
-
|
|
131
|
+
# The `maybe` a shape of `kind` (`:hash` or `:array`) answers for an argument it cannot read, or `no`
|
|
132
|
+
# inside {#refusing_unread} for that kind.
|
|
133
|
+
def unread_shape_maybe(mode, reason, kind)
|
|
134
|
+
refused = Thread.current[UNREAD_SHAPE_REFUSED_KEY]
|
|
135
|
+
if refused == :all || refused == kind
|
|
136
|
+
return Type::AcceptsResult.no(mode: mode, reasons: "#{reason} (refused as evidence)")
|
|
137
|
+
end
|
|
113
138
|
|
|
114
139
|
Type::AcceptsResult.maybe(mode: mode, reasons: reason)
|
|
115
140
|
end
|
|
@@ -469,9 +494,10 @@ module Rigor
|
|
|
469
494
|
end
|
|
470
495
|
|
|
471
496
|
# Returns `other_type` rewritten so its type_args have the same arity as `self_type.type_args`, or
|
|
472
|
-
# `nil` if no projection is known. Today
|
|
473
|
-
# general RBS-driven implementation that consults
|
|
474
|
-
# subclass / module-include relations is the principled
|
|
497
|
+
# `nil` if no projection is known. Today the Hash → Enumerable and ArithmeticSequence → Enumerator
|
|
498
|
+
# projections are hand-rolled; a general RBS-driven implementation that consults
|
|
499
|
+
# `definition.ancestors[i].args` for arbitrary subclass / module-include relations is the principled
|
|
500
|
+
# follow-up.
|
|
475
501
|
def project_to_target_arity(self_type, other_type)
|
|
476
502
|
return nil if self_type.type_args.size == other_type.type_args.size
|
|
477
503
|
return nil if self_type.type_args.empty? || other_type.type_args.empty?
|
|
@@ -881,8 +907,8 @@ module Rigor
|
|
|
881
907
|
# `Refined[base, predicate]` accepts another type X when the base accepts the *base* of X *and* X is
|
|
882
908
|
# provably contained in the predicate's value set. The base check is delegated to
|
|
883
909
|
# `accepts(self.base, X.base)` so handlers like `accepts_nominal` see Nominal-vs-Nominal and return
|
|
884
|
-
# their normal answer (
|
|
885
|
-
#
|
|
910
|
+
# their normal answer (`accepts_nominal` projects a `Refined` / `Difference` other onto its base,
|
|
911
|
+
# which is what makes the comparison meaningful).
|
|
886
912
|
#
|
|
887
913
|
# Provability rules in gradual mode (the conservative analogue of `accepts_difference`):
|
|
888
914
|
#
|
|
@@ -1003,9 +1029,12 @@ module Rigor
|
|
|
1003
1029
|
|
|
1004
1030
|
# Tuple[A1..An] accepts:
|
|
1005
1031
|
# - Tuple[B1..Bn] when arities match and each Ai accepts Bi (covariant per-position).
|
|
1006
|
-
# -
|
|
1032
|
+
# - An `Array[E]` (see {#array_element_verdict}): maybe, or no on an element contradiction.
|
|
1033
|
+
# - Anything else: no.
|
|
1007
1034
|
def accepts_tuple(self_type, other_type, mode)
|
|
1008
1035
|
unless other_type.is_a?(Type::Tuple)
|
|
1036
|
+
return array_element_verdict(self_type, other_type, mode) if array_carrier?(other_type)
|
|
1037
|
+
|
|
1009
1038
|
return Type::AcceptsResult.no(
|
|
1010
1039
|
mode: mode,
|
|
1011
1040
|
reasons: "Tuple does not accept #{other_type.class}"
|
|
@@ -1025,6 +1054,38 @@ module Rigor
|
|
|
1025
1054
|
combine_arg_results(per_element, mode)
|
|
1026
1055
|
end
|
|
1027
1056
|
|
|
1057
|
+
# An `Array` nominal, directly or as the base of a `Difference` (`non-empty-array[E]`). Its arity is
|
|
1058
|
+
# unknown, so a tuple cannot reject it on length: `URI.encode_www_form` takes
|
|
1059
|
+
# `Enumerable[[_ToS, _ToS]]` and is handed the `Array[Array[String]]` that `URI.decode_www_form`
|
|
1060
|
+
# answers. An `Array` subclass is not read, as {#gradual_hash?} does not read a `Hash` subclass.
|
|
1061
|
+
def array_carrier?(type)
|
|
1062
|
+
nominal = type.is_a?(Type::Difference) ? type.base : type
|
|
1063
|
+
nominal.is_a?(Type::Nominal) && nominal.class_name == "Array"
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
# `maybe`, unless the element type rules the tuple out at any length: some position accepts no member
|
|
1067
|
+
# of `E` (`[Integer, Integer]` never takes an `Array[String]`). A raw `Array` stays `maybe`, and so does
|
|
1068
|
+
# an element with a gradual arm, since no position rejects a `Dynamic` member. The accepted cost (ADR-5)
|
|
1069
|
+
# is that a wrong-length `Array[E]` passed or returned where a tuple is declared goes unreported.
|
|
1070
|
+
def array_element_verdict(self_type, other_type, mode)
|
|
1071
|
+
nominal = other_type.is_a?(Type::Difference) ? other_type.base : other_type
|
|
1072
|
+
element = nominal.type_args.first
|
|
1073
|
+
unless element.nil?
|
|
1074
|
+
members = ContentJoin.union_members(element)
|
|
1075
|
+
refused = self_type.elements.find do |formal|
|
|
1076
|
+
members.all? { |member| accepts(formal, member, mode: mode).no? }
|
|
1077
|
+
end
|
|
1078
|
+
if refused
|
|
1079
|
+
return Type::AcceptsResult.no(
|
|
1080
|
+
mode: mode,
|
|
1081
|
+
reasons: "tuple member #{refused.describe(:short)} rejects #{element.describe(:short)}"
|
|
1082
|
+
)
|
|
1083
|
+
end
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
unread_shape_maybe(mode, "tuple arity unknown for #{other_type.describe(:short)}", :array)
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1028
1089
|
# HashShape{k1: T1, ...} accepts another HashShape when every required key of self is required on
|
|
1029
1090
|
# the other side and Ti accepts Ui (depth covariant). Optional keys may be absent on the other side;
|
|
1030
1091
|
# when present, their values are checked. A closed self rejects known extra keys, and answers at most
|
|
@@ -1056,7 +1117,7 @@ module Rigor
|
|
|
1056
1117
|
result = combine_arg_results(per_entry, mode)
|
|
1057
1118
|
return result unless result.yes? && self_type.closed? && other_type.open?
|
|
1058
1119
|
|
|
1059
|
-
|
|
1120
|
+
unread_shape_maybe(mode, "HashShape closed target cannot check open source", :hash)
|
|
1060
1121
|
end
|
|
1061
1122
|
|
|
1062
1123
|
# A `Hash` nominal — directly or as the base of a `Difference` (`non-empty-hash[K, V]`) — that is raw or
|
|
@@ -1085,7 +1146,7 @@ module Rigor
|
|
|
1085
1146
|
refused = refused_value_keys(self_type, value_type, mode)
|
|
1086
1147
|
return hash_shape_no(mode, "Hash value type refused for required keys: #{refused.inspect}") if refused.any?
|
|
1087
1148
|
|
|
1088
|
-
|
|
1149
|
+
unread_shape_maybe(mode, "HashShape cannot check #{other_type.describe(:short)}", :hash)
|
|
1089
1150
|
end
|
|
1090
1151
|
|
|
1091
1152
|
def unholdable_keys(self_type, key_type, mode)
|
|
@@ -211,9 +211,8 @@ module Rigor
|
|
|
211
211
|
cursor
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
-
# `|*rest|` binds an Array of the leftover positional arguments
|
|
215
|
-
#
|
|
216
|
-
# `Array[Dynamic[Top]]`. Element-type precision for rest parameters is deferred (demand-gated).
|
|
214
|
+
# `|*rest|` binds an Array of the leftover positional arguments: `Array[Dynamic[Top]]`, unless an
|
|
215
|
+
# auto-splat answered a rest type ({BlockAutoSplat}). The expected types are per-position, not per-rest.
|
|
217
216
|
def bind_rest(params_node, bindings)
|
|
218
217
|
rest = params_node.rest
|
|
219
218
|
return unless rest.respond_to?(:name) && rest&.name
|