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
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbs"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
class Environment
|
|
7
|
+
# Issue #2422 — what a bundled plugin's deferred signature file loads as, against the environment every other
|
|
8
|
+
# signature source has already built. The #610 arity check stands a file down whole first
|
|
9
|
+
# (`RbsLoader.generic_arity_conflict`); {.plan} decides the rest declaration by declaration and member by
|
|
10
|
+
# member, with {GatedSignaturePlan}'s #1713 helpers:
|
|
11
|
+
#
|
|
12
|
+
# - a method another source declares (a `def`, an attribute or an alias), where that source is NOT the project's own
|
|
13
|
+
# (an `rbs collection install`, a gem's `sig/`, a vendored or another plugin's file), is *composed*: the plugin's
|
|
14
|
+
# `def` becomes an overload continuation, `def m: <plugin overloads> | ...`, so RBS puts the plugin's overloads
|
|
15
|
+
# first and keeps the other source's as the fallback. The plugin's signatures are often written wider on purpose
|
|
16
|
+
# (`Object#html_safe?: () -> bool` where the collection says `-> false`), and standing them down would let the
|
|
17
|
+
# narrower one fold correct code into dead branches;
|
|
18
|
+
# - any other method, attribute or alias another source declares stands down: the project's own `sig/` or
|
|
19
|
+
# inline RBS binds by #610's rule, and an attribute, an alias or one side of a `def self?.m` cannot be
|
|
20
|
+
# continued ({GatedSignaturePlan.untaken} narrows an `attr_accessor` or `self?` to its untaken half);
|
|
21
|
+
# - a constant, global, type alias, interface or class alias another source already declares, and a type
|
|
22
|
+
# whose kind (class or module) contradicts another source's, stands down on its own, where adding it would
|
|
23
|
+
# have raised `RBS::DuplicatedDeclarationError` and dropped the plugin's whole file;
|
|
24
|
+
# - a class header's superclass clause another source already gives the class one stands down (#2439), where two
|
|
25
|
+
# different clauses would have raised `RBS::SuperclassMismatchError`; the plugin's declaration reopens the class.
|
|
26
|
+
#
|
|
27
|
+
# A declaration with nothing taken comes back as the same object, so a file with no collision loads as it
|
|
28
|
+
# stands. {.standdown_rows} reads what stood down back off the built environment, so a warm run off the env
|
|
29
|
+
# cache reports what the cold run decided.
|
|
30
|
+
module DeferredSignaturePlan
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
# @param own — the buffer names that are the project's own sources: its `signature_paths:` files (not the
|
|
34
|
+
# collection or gem directories also on that list) and its inline RBS.
|
|
35
|
+
def plan(decls, env, own, namespace = nil)
|
|
36
|
+
planned = decls.filter_map { |decl| plan_declaration(decl, env, own, namespace) }
|
|
37
|
+
same?(planned, decls) ? decls : planned
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def plan_declaration(decl, env, own, namespace)
|
|
41
|
+
name = GatedSignaturePlan.qualify(namespace, declared_name(decl))
|
|
42
|
+
if GatedSignaturePlan.type_declaration?(decl)
|
|
43
|
+
return nil if kind_clash?(env, decl, name)
|
|
44
|
+
|
|
45
|
+
return plan_type(decl, env, own, name)
|
|
46
|
+
end
|
|
47
|
+
declared_elsewhere(env, decl, name) ? nil : decl
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def plan_type(decl, env, own, name)
|
|
51
|
+
others = GatedSignaturePlan.other_declarations(env, name)
|
|
52
|
+
taken = GatedSignaturePlan.declared_keys(others)
|
|
53
|
+
external = others.reject { |other| own.include?(buffer_of(other)) }
|
|
54
|
+
defined = GatedSignaturePlan.declared_keys(external) | definition_keys(external)
|
|
55
|
+
members = decl.members.filter_map do |member|
|
|
56
|
+
next plan_declaration(member, env, own, name) if declaration?(member)
|
|
57
|
+
next member if taken.empty?
|
|
58
|
+
|
|
59
|
+
composed(member, defined) || GatedSignaturePlan.untaken(member, taken)
|
|
60
|
+
end
|
|
61
|
+
drop = superclass_given?(decl, others)
|
|
62
|
+
return decl if same?(members, decl.members) && !drop
|
|
63
|
+
|
|
64
|
+
GatedSignaturePlan.rebuild(decl, members: members, drop_super_class: drop)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Issue #2439 — true when `decl` is a class with a superclass clause and another source (`others`) already
|
|
68
|
+
# gives the class one. Two different clauses raise `RBS::SuperclassMismatchError` and the class and every
|
|
69
|
+
# descendant read `Dynamic[top]`, so the plugin's header stands down and reopens the class without one. The
|
|
70
|
+
# two names are not compared: they are not resolved yet, and the other source's superclass binds either way.
|
|
71
|
+
def superclass_given?(decl, others)
|
|
72
|
+
super_class?(decl) && others.any? { |other| super_class?(other) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# `member` as an overload continuation, or nil when it cannot be one: only a plain one-sided `def` can, and
|
|
76
|
+
# only onto a method — a `def`, an attribute or an alias, all of which RBS continues — that a source other
|
|
77
|
+
# than the project's own declares.
|
|
78
|
+
def composed(member, defined)
|
|
79
|
+
return nil unless member.is_a?(::RBS::AST::Members::MethodDefinition) && !member.overloading?
|
|
80
|
+
return nil if member.kind == :singleton_instance
|
|
81
|
+
|
|
82
|
+
key = [member.name, member.kind]
|
|
83
|
+
return nil unless defined.include?(key)
|
|
84
|
+
|
|
85
|
+
member.update(overloading: true)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The `[name, kind]` keys a plain or continued `def` in `decls` declares — what a continuation can extend.
|
|
89
|
+
def definition_keys(decls)
|
|
90
|
+
decls.each_with_object(Set.new) do |decl, keys|
|
|
91
|
+
decl.members.each do |member|
|
|
92
|
+
next unless member.is_a?(::RBS::AST::Members::MethodDefinition)
|
|
93
|
+
|
|
94
|
+
sides = member.kind == :singleton_instance ? %i[instance singleton] : [member.kind]
|
|
95
|
+
sides.each { |side| keys << [member.name, side] }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def kind_clash?(env, decl, name)
|
|
101
|
+
type_name = ::RBS::TypeName.parse("::#{name}")
|
|
102
|
+
return true if env.class_alias_decls.key?(type_name)
|
|
103
|
+
|
|
104
|
+
first = GatedSignaturePlan.other_declarations(env, name).first
|
|
105
|
+
!first.nil? && !first.instance_of?(decl.class)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The buffer of the declaration another source already makes under `decl`'s name, or nil.
|
|
109
|
+
def declared_elsewhere(env, decl, name)
|
|
110
|
+
entry = declaration_entry(env, decl, name)
|
|
111
|
+
entry && (RbsLoader.declaration_buffer_name(entry.decl) || "")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def declaration_entry(env, decl, name)
|
|
115
|
+
type_name = -> { ::RBS::TypeName.parse("::#{name}") }
|
|
116
|
+
case decl
|
|
117
|
+
when ::RBS::AST::Declarations::Constant then env.constant_decls[type_name.call]
|
|
118
|
+
when ::RBS::AST::Declarations::Global then env.global_decls[decl.name]
|
|
119
|
+
when ::RBS::AST::Declarations::TypeAlias then env.type_alias_decls[type_name.call]
|
|
120
|
+
when ::RBS::AST::Declarations::Interface then env.interface_decls[type_name.call]
|
|
121
|
+
when ::RBS::AST::Declarations::ClassAlias, ::RBS::AST::Declarations::ModuleAlias
|
|
122
|
+
env.class_alias_decls[type_name.call] || env.class_decls[type_name.call]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A class or module alias names itself as `new_name`; every other declaration as `name`.
|
|
127
|
+
def declared_name(decl)
|
|
128
|
+
decl.respond_to?(:new_name) ? decl.new_name : decl.name
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def declaration?(member)
|
|
132
|
+
member.is_a?(::RBS::AST::Declarations::Base)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def same?(planned, original)
|
|
136
|
+
planned.size == original.size && planned.zip(original).all? { |a, b| a.equal?(b) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def buffer_of(decl)
|
|
140
|
+
RbsLoader.declaration_buffer_name(decl)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# One {.standdown_rows} walk over a deferred file: the built env, the file's buffer name, the rows so far, and
|
|
144
|
+
# the env's type-name resolver, built on first use (only a dropped superclass clause needs it).
|
|
145
|
+
class Walk
|
|
146
|
+
attr_reader :env, :file, :rows
|
|
147
|
+
|
|
148
|
+
def initialize(env, file)
|
|
149
|
+
@env = env
|
|
150
|
+
@file = file
|
|
151
|
+
@rows = []
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# rbs 4.x builds the resolver with `TypeNameResolver.build(env)`; rbs 3.x's constructor takes the env, which
|
|
155
|
+
# the 4.x signature Rigor reads (`new(all_names, aliases)`) does not describe.
|
|
156
|
+
def resolver
|
|
157
|
+
@resolver ||= begin
|
|
158
|
+
klass = ::RBS::Resolver::TypeNameResolver
|
|
159
|
+
if klass.respond_to?(:build)
|
|
160
|
+
klass.build(env)
|
|
161
|
+
else
|
|
162
|
+
klass.new(env) # rigor:disable call.wrong-arity
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# What {.plan} left out of the deferred `file` (its parsed `decls`) in the built `env`, as `[declaration,
|
|
169
|
+
# cause_file]` rows — `Object#acts_like?`, `Time.at`, `FFI::Pointer::NULL`, `Foo < Bar`, or a type name — with
|
|
170
|
+
# the buffer of the other source's declaration, or nil when it cannot be named. A composed method is not a row:
|
|
171
|
+
# the plugin's overloads still bind.
|
|
172
|
+
def standdown_rows(env, file, decls)
|
|
173
|
+
walk = Walk.new(env, file)
|
|
174
|
+
decls.each { |decl| declaration_rows(walk, decl, nil) }
|
|
175
|
+
walk.rows.uniq
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# `context` is RBS's resolver context for where `decl` is written: nil at the top level, else `[outer,
|
|
179
|
+
# absolute name of the enclosing declaration]`, as `RBS::Environment#append_context` builds it.
|
|
180
|
+
def declaration_rows(walk, decl, context)
|
|
181
|
+
name = GatedSignaturePlan.qualify(namespace_of(context), declared_name(decl))
|
|
182
|
+
return type_rows(walk, decl, name, context) if GatedSignaturePlan.type_declaration?(decl)
|
|
183
|
+
|
|
184
|
+
cause = displaced_by(walk.env, decl, name, walk.file)
|
|
185
|
+
walk.rows << [decl.is_a?(::RBS::AST::Declarations::Global) ? decl.name.to_s : name, cause] if cause
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def namespace_of(context)
|
|
189
|
+
context && context.last.to_s.delete_prefix("::")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# A non-nil buffer name (possibly empty) when another source's declaration of `decl` is the one loaded.
|
|
193
|
+
def displaced_by(env, decl, name, file)
|
|
194
|
+
entry = declaration_entry(env, decl, name)
|
|
195
|
+
return nil if entry.nil?
|
|
196
|
+
|
|
197
|
+
buffer = RbsLoader.declaration_buffer_name(entry.decl)
|
|
198
|
+
buffer == file ? nil : buffer
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def type_rows(walk, decl, name, context)
|
|
202
|
+
loaded, others = loaded_and_others(walk, name)
|
|
203
|
+
if loaded.empty?
|
|
204
|
+
cause = others.empty? ? alias_cause(walk.env, name) : buffer_of(others.first)
|
|
205
|
+
walk.rows << [name, cause] if cause
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
walk.rows.concat(superclass_rows(walk, decl, name, context, others))
|
|
210
|
+
walk.rows.concat(member_rows(decl, name, loaded, others))
|
|
211
|
+
inner = [context, ::RBS::TypeName.parse("::#{name}")]
|
|
212
|
+
decl.members.each { |member| declaration_rows(walk, member, inner) if declaration?(member) }
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# The declarations of `name` the walked file loaded, and the ones other sources made (synthesized stubs left out).
|
|
216
|
+
def loaded_and_others(walk, name)
|
|
217
|
+
entry = walk.env.class_decls[::RBS::TypeName.parse("::#{name}")]
|
|
218
|
+
declarations = entry.nil? ? [] : RbsLoader.entry_declarations(entry)
|
|
219
|
+
loaded = declarations.select { |other| buffer_of(other) == walk.file }
|
|
220
|
+
[loaded, declarations.reject { |other| loaded.include?(other) || GatedSignaturePlan.synthetic?(other) }]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# The buffer of a class or module alias declaring `name` (empty when it cannot be named), or nil.
|
|
224
|
+
def alias_cause(env, name)
|
|
225
|
+
entry = env.class_alias_decls[::RBS::TypeName.parse("::#{name}")]
|
|
226
|
+
entry && (RbsLoader.declaration_buffer_name(entry.decl) || "")
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# The plugin's superclass clause as a row (`ActiveRecord::StatementInvalid < ActiveRecordError`) when the plan
|
|
230
|
+
# dropped it for another source's ({.superclass_given?}) and that source's names a different class or type
|
|
231
|
+
# arguments. The plan drops an identical clause too, but nothing is lost, so no row.
|
|
232
|
+
def superclass_rows(walk, decl, name, context, others)
|
|
233
|
+
return [] unless superclass_given?(decl, others)
|
|
234
|
+
|
|
235
|
+
cause = others.find { |other| super_class?(other) }
|
|
236
|
+
return [] if same_super_class?(walk, decl.super_class, context, cause.super_class)
|
|
237
|
+
|
|
238
|
+
written = decl.super_class
|
|
239
|
+
args = written.args.empty? ? "" : "[#{written.args.join(', ')}]"
|
|
240
|
+
[["#{name} < #{written.name}#{args}", buffer_of(cause)]]
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Whether the plugin's clause `written`, resolved as RBS resolves a superclass (in the context the class is
|
|
244
|
+
# written in, not its own), names the class and type arguments of `other`, the built env's already-absolute
|
|
245
|
+
# clause, class aliases normalised on both sides. An unresolvable name differs.
|
|
246
|
+
def same_super_class?(walk, written, context, other)
|
|
247
|
+
name = absolute_name(walk, written.name, context)
|
|
248
|
+
return false if name.nil? || normalized(walk.env, name) != normalized(walk.env, other.name)
|
|
249
|
+
|
|
250
|
+
written.args.map { |arg| normalized_type(walk, arg, context) } ==
|
|
251
|
+
other.args.map { |arg| normalized_type(walk, arg, nil) }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def absolute_name(walk, name, context)
|
|
255
|
+
name.absolute? ? name : walk.resolver.resolve(name, context: context)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def normalized(env, name)
|
|
259
|
+
name.class? ? env.normalize_module_name(name) : name
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def normalized_type(walk, type, context)
|
|
263
|
+
return type.to_s unless type.respond_to?(:map_type_name)
|
|
264
|
+
|
|
265
|
+
type.map_type_name { |name, _, _| normalized(walk.env, absolute_name(walk, name, context) || name) }.to_s
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def super_class?(decl)
|
|
269
|
+
decl.is_a?(::RBS::AST::Declarations::Class) && !decl.super_class.nil?
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def member_rows(decl, name, loaded, others)
|
|
273
|
+
present = GatedSignaturePlan.declared_keys(loaded) | definition_keys(loaded)
|
|
274
|
+
decl.members.flat_map do |member|
|
|
275
|
+
RbsLoader.member_method_keys(member).reject { |key| present.include?(key) }.filter_map do |key|
|
|
276
|
+
cause = others.find { |other| other.members.any? { |m| RbsLoader.member_method_keys(m).include?(key) } }
|
|
277
|
+
cause && [GatedSignaturePlan.member_label(name, key), buffer_of(cause)]
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
module GatedSignatureGuard
|
|
14
14
|
module_function
|
|
15
15
|
|
|
16
|
-
# @param dir — the vendored directory
|
|
16
|
+
# @param dir — the vendored directory.
|
|
17
17
|
# @return the absolute-less names (`"Prime::PseudoPrimeGenerator"`) of every class and module it declares.
|
|
18
18
|
def type_names(dir)
|
|
19
19
|
RootedGlob.glob(dir, "*.rbs").flat_map do |file|
|
|
@@ -213,10 +213,13 @@ module Rigor
|
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
# `decl` with `members` in place of its own. A class keeps its superclass unless `drop_super_class` (#2439: a
|
|
217
|
+
# plugin's class header another source already gives a superclass reopens the class without one).
|
|
218
|
+
def rebuild(decl, members:, drop_super_class: false)
|
|
217
219
|
if decl.is_a?(::RBS::AST::Declarations::Class)
|
|
218
220
|
::RBS::AST::Declarations::Class.new(
|
|
219
|
-
name: decl.name, type_params: decl.type_params, super_class: decl.super_class,
|
|
221
|
+
name: decl.name, type_params: decl.type_params, super_class: drop_super_class ? nil : decl.super_class,
|
|
222
|
+
members: members,
|
|
220
223
|
annotations: decl.annotations, location: decl.location, comment: decl.comment
|
|
221
224
|
)
|
|
222
225
|
else
|
|
@@ -7,7 +7,7 @@ module Rigor
|
|
|
7
7
|
# Gemfile.lock parser for target-project bundler awareness (O4 Layer 3, implemented).
|
|
8
8
|
#
|
|
9
9
|
# Parses a target project's `Gemfile.lock` via Bundler's `LockfileParser` and exposes the locked gem set
|
|
10
|
-
# as a frozen
|
|
10
|
+
# as a frozen map from gem name to `LockedGem`. Used by
|
|
11
11
|
# {Rigor::Environment::BundleSigDiscovery} as a filter so the discovered `sig/` directories under the
|
|
12
12
|
# bundler install root are limited to gems the project actually declares (and at the version it declared
|
|
13
13
|
# them).
|
|
@@ -38,8 +38,12 @@ module Rigor
|
|
|
38
38
|
# variant for the readers that want a single version (coverage report, doctor): the `ruby` one when the
|
|
39
39
|
# lockfile has it, otherwise the last parsed. Defaults to the single `[version, platform]` pair, and the
|
|
40
40
|
# pair must be one of `variants`.
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
#
|
|
42
|
+
# `path_source` is the absolute directory a lockfile `PATH` stanza resolved this gem from (its `remote:`
|
|
43
|
+
# expanded against the lockfile's directory), and `nil` for every other source, `GIT` included. Issue
|
|
44
|
+
# #2441 reads it to tell the project's own path gems apart from the rest of the bundle. Defaults `nil`.
|
|
45
|
+
LockedGem = Data.define(:name, :version, :platform, :git_source, :variants, :path_source) do
|
|
46
|
+
def initialize(name:, version:, platform:, git_source: false, variants: nil, path_source: nil)
|
|
43
47
|
version = -version.to_s
|
|
44
48
|
platform = -platform.to_s
|
|
45
49
|
variants = (variants || [[version, platform]]).map { |v, p| [-v.to_s, -p.to_s].freeze }.uniq.freeze
|
|
@@ -52,7 +56,8 @@ module Rigor
|
|
|
52
56
|
version: version,
|
|
53
57
|
platform: platform,
|
|
54
58
|
git_source: git_source ? true : false,
|
|
55
|
-
variants: variants
|
|
59
|
+
variants: variants,
|
|
60
|
+
path_source: path_source && -path_source.to_s
|
|
56
61
|
)
|
|
57
62
|
end
|
|
58
63
|
end
|
|
@@ -115,9 +120,9 @@ module Rigor
|
|
|
115
120
|
def self.do_parse_dependency_names(path)
|
|
116
121
|
parser = Bundler::LockfileParser.new(File.read(path.to_s))
|
|
117
122
|
# `#dependencies` is the parsed `DEPENDENCIES` section keyed by gem name, so the version constraint
|
|
118
|
-
# and the pinned-source `!` suffix are already stripped for us.
|
|
119
|
-
#
|
|
120
|
-
parser.dependencies.keys.to_set { |name| -name.to_s }.freeze
|
|
123
|
+
# and the pinned-source `!` suffix are already stripped for us. Rigor's vendored bundler
|
|
124
|
+
# signature declares it as a Hash keyed by gem name.
|
|
125
|
+
parser.dependencies.keys.to_set { |name| -name.to_s }.freeze
|
|
121
126
|
rescue ::Ractor::IsolationError => e
|
|
122
127
|
warn_isolated(path, e)
|
|
123
128
|
EMPTY_NAMES
|
|
@@ -171,10 +176,9 @@ module Rigor
|
|
|
171
176
|
parser = Bundler::LockfileParser.new(body)
|
|
172
177
|
locked = parser.specs.each_with_object({}) do |spec, h|
|
|
173
178
|
# `Bundler::LazySpecification` carries name, version, platform. Platform is `Gem::Platform` or the
|
|
174
|
-
# symbol `:ruby`; both stringify cleanly.
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
platform = spec.platform.to_s # rigor:disable undefined-method
|
|
179
|
+
# symbol `:ruby`; both stringify cleanly. Rigor's vendored bundler signature declares
|
|
180
|
+
# `LazySpecification#platform`, so the call needs no suppression.
|
|
181
|
+
platform = spec.platform.to_s
|
|
178
182
|
# `#source` is the `Bundler::Source::*` this spec resolved from — `Git` for a `GIT` lockfile
|
|
179
183
|
# stanza, `Rubygems` for a `GEM` one, `Path`/`Gemspec` for `path:`. Only `Git` sources land under
|
|
180
184
|
# {BundleSigDiscovery}'s `bundler/gems/` walk.
|
|
@@ -182,7 +186,8 @@ module Rigor
|
|
|
182
186
|
# instead of letting the last one overwrite the rest (#1844).
|
|
183
187
|
entry = LockedGem.new(
|
|
184
188
|
name: spec.name, version: spec.version.to_s, platform: platform,
|
|
185
|
-
git_source: spec.source.is_a?(Bundler::Source::Git)
|
|
189
|
+
git_source: spec.source.is_a?(Bundler::Source::Git),
|
|
190
|
+
path_source: path_source_dir(spec.source, path)
|
|
186
191
|
)
|
|
187
192
|
h[entry.name] = (prior = h[entry.name]) ? merge_variant(prior, entry) : entry
|
|
188
193
|
end
|
|
@@ -196,6 +201,17 @@ module Rigor
|
|
|
196
201
|
end
|
|
197
202
|
private_class_method :do_parse
|
|
198
203
|
|
|
204
|
+
# The absolute directory a `PATH` stanza names, resolved against the lockfile's own directory — never
|
|
205
|
+
# against `Bundler.root`, which is Rigor's process and not the analysed project. `Bundler::Source::Git`
|
|
206
|
+
# subclasses `Path`, so a `GIT` stanza is excluded by name.
|
|
207
|
+
def self.path_source_dir(source, lockfile_path)
|
|
208
|
+
return nil unless source.is_a?(Bundler::Source::Path)
|
|
209
|
+
return nil if source.is_a?(Bundler::Source::Git)
|
|
210
|
+
|
|
211
|
+
File.expand_path(source.path.to_s, File.dirname(File.expand_path(lockfile_path.to_s)))
|
|
212
|
+
end
|
|
213
|
+
private_class_method :path_source_dir
|
|
214
|
+
|
|
199
215
|
# Folds a further platform variant of an already-parsed name into it. The representative keeps a `ruby`
|
|
200
216
|
# variant once seen; otherwise the last variant wins, as it did before variants were kept.
|
|
201
217
|
def self.merge_variant(prior, entry)
|
|
@@ -14,7 +14,7 @@ module Rigor
|
|
|
14
14
|
# declared types. See {MemberConsistency} for the rule; this class holds the type work.
|
|
15
15
|
#
|
|
16
16
|
# A contradiction is reported only where it is PROVEN: no value, and no call, satisfies both sides.
|
|
17
|
-
# Everything short of a proof is undecided.
|
|
17
|
+
# Everything short of a proof is undecided. Five places carry that burden:
|
|
18
18
|
#
|
|
19
19
|
# - {#disjoint?} proves two types share no value, from the RBS hierarchy of Ruby core and stdlib
|
|
20
20
|
# classes ({RbsProof}) only: two such classes neither of which is an RBS ancestor of the other, or a
|
|
@@ -103,6 +103,7 @@ module Rigor
|
|
|
103
103
|
# `:inline` (the inline side is more precise), `:contradiction` or `:undecided`; `detail` names the
|
|
104
104
|
# deciding position for the two reported outcomes and is nil otherwise.
|
|
105
105
|
def compare(signature_member, inline_member, method_name)
|
|
106
|
+
return compare_variables(signature_member, inline_member) if variable?(signature_member)
|
|
106
107
|
return EQUAL if same_declaration?(signature_member, inline_member)
|
|
107
108
|
return SIGNATURE_MORE_PRECISE if inferred_signature?(inline_member)
|
|
108
109
|
|
|
@@ -174,6 +175,22 @@ module Rigor
|
|
|
174
175
|
member_annotation_strings(member).include?(INFERRED_SIGNATURE_DIRECTIVE)
|
|
175
176
|
end
|
|
176
177
|
|
|
178
|
+
def variable?(member)
|
|
179
|
+
member.is_a?(::RBS::AST::Members::InstanceVariable) ||
|
|
180
|
+
member.is_a?(::RBS::AST::Members::ClassInstanceVariable)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Issue #2440 — an instance variable (or class instance variable) declared on both sides. Two
|
|
184
|
+
# declarations of one type are equal and merge silently. Any other pair is undecided, never ranked
|
|
185
|
+
# and never a contradiction: `sig/` binds and the inline declaration is reported at `:info`. A
|
|
186
|
+
# variable has no call to satisfy, so "more precise" has no reader to favour, and the committed
|
|
187
|
+
# `.rbs` (typically `rbs-inline --output`'s own) is the declaration the project's other tools read.
|
|
188
|
+
def compare_variables(signature_member, inline_member)
|
|
189
|
+
return EQUAL if signature_member.type == inline_member.type
|
|
190
|
+
|
|
191
|
+
[:undecided, "the type is `#{signature_member.type}` in `sig/` and `#{inline_member.type}` inline"]
|
|
192
|
+
end
|
|
193
|
+
|
|
177
194
|
def compare_aliases(signature_member, inline_member)
|
|
178
195
|
both = signature_member.is_a?(::RBS::AST::Members::Alias) && inline_member.is_a?(::RBS::AST::Members::Alias)
|
|
179
196
|
return EQUAL if both && signature_member.old_name == inline_member.old_name
|
|
@@ -59,6 +59,9 @@ module Rigor
|
|
|
59
59
|
NO_RECORDS = [].freeze
|
|
60
60
|
private_constant :NO_RECORDS
|
|
61
61
|
|
|
62
|
+
# The key kinds {.member_keys} gives a variable member.
|
|
63
|
+
VARIABLE_KINDS = %i[instance_variable class_instance_variable].freeze
|
|
64
|
+
|
|
62
65
|
# What a disjointness proof may lean on (ADR-112 WD5, #1075): the RBS class hierarchy the analysis
|
|
63
66
|
# itself uses, restricted to classes Ruby core and the stdlib declare. Never Ruby constants loaded in
|
|
64
67
|
# the analyzer process — those depend on which libraries happen to be required, and rbs and Ruby
|
|
@@ -115,8 +118,8 @@ module Rigor
|
|
|
115
118
|
class << self
|
|
116
119
|
# @param signature_members — `{[class, method, kind] => [signature_path, member, visibility]}`, the
|
|
117
120
|
# first project `.rbs` member declaring each key, with its effective visibility.
|
|
118
|
-
# @param inline_members — `[[virtual_name, class_name, member, visibility], ...]`, every member
|
|
119
|
-
# inline sources declare.
|
|
121
|
+
# @param inline_members — `[[virtual_name, class_name, member, visibility, nesting], ...]`, every member
|
|
122
|
+
# the inline sources declare, with the lexical nesting {.member_keys} keys a mixin by.
|
|
120
123
|
# @param shadowable — the first segment of every class / module name the project's inputs declare
|
|
121
124
|
# ({Comparator#initialize}).
|
|
122
125
|
# @param proof — an {RbsProof} for the reported rows, or nil for the environment build.
|
|
@@ -131,9 +134,9 @@ module Rigor
|
|
|
131
134
|
|
|
132
135
|
state = { comparator: Comparator.new(shadowable, proof: proof), records: [], inline: Set.new,
|
|
133
136
|
signature: Hash.new { |hash, file| hash[file] = Set.new }, checked: {}.compare_by_identity }
|
|
134
|
-
inline_members.each do |virtual_name, class_name, member, visibility|
|
|
137
|
+
inline_members.each do |virtual_name, class_name, member, visibility, nesting|
|
|
135
138
|
state[:records].concat(refinement_records(state, class_name, member, nil, virtual_name))
|
|
136
|
-
resolve_member(state, signature_members, [virtual_name, class_name, member, visibility])
|
|
139
|
+
resolve_member(state, signature_members, [virtual_name, class_name, member, visibility, nesting])
|
|
137
140
|
end
|
|
138
141
|
Resolution.new(
|
|
139
142
|
records: state[:records].uniq.sort_by { |r| record_sort_key(r) }.freeze,
|
|
@@ -159,11 +162,57 @@ module Rigor
|
|
|
159
162
|
end
|
|
160
163
|
end
|
|
161
164
|
|
|
165
|
+
# Issue #2440 — every key one RBS member contributes to a definition build: its method keys
|
|
166
|
+
# ({.member_method_keys}) and, for an instance variable (`@x: T`) or a class instance variable
|
|
167
|
+
# (`self.@x: T`), `[:@x, :instance_variable]` / `[:@x, :class_instance_variable]`. rbs 4.2 raises
|
|
168
|
+
# `InstanceVariableDuplicationError` / `ClassInstanceVariableDuplicationError` for two of those in one
|
|
169
|
+
# class even when the types agree, so a `sig/` and an inline declaration of one has to be reconciled
|
|
170
|
+
# like a method. A method name never starts with `@`, so the two key spaces cannot meet. A class
|
|
171
|
+
# variable (`@@x`) contributes nothing: rbs does not reject a duplicate. Neither does an attribute's
|
|
172
|
+
# implied instance variable, which rbs lets an explicit `@x` declaration override.
|
|
173
|
+
#
|
|
174
|
+
# A mixin (`include M`, `extend M`, `prepend M`) is keyed by the lexical nesting of the declaration
|
|
175
|
+
# it sits in and the module and type arguments exactly as written, `[:"A/C|M[String]", :include]`:
|
|
176
|
+
# rbs 4.2 counts every instance variable `M` declares once per declaration of the class that mixes it
|
|
177
|
+
# in, so the same `include M` in `sig/` and inline raises `InstanceVariableDuplicationError ... in ::M`
|
|
178
|
+
# for the class. A relative name resolves through the nesting it is written in, so `include M` inside
|
|
179
|
+
# `module A; class C` (`A::M` when that exists) and inside `class A::C` (`::M`) are different mixins,
|
|
180
|
+
# and so are `M` and `::M`; only an identical spelling in an identical nesting is the same member and
|
|
181
|
+
# stands down. `rbs-inline --output` mirrors the Ruby file's nesting, so its committed output still
|
|
182
|
+
# pairs with the inline buffer. A mixin with different arguments keys differently and is left alone.
|
|
183
|
+
# Two members sharing a mixin key declare no method types to compare, so they read as equal.
|
|
184
|
+
#
|
|
185
|
+
# `nesting` is the declaration names as written, outermost first, joined by `/`
|
|
186
|
+
# ({RbsLoader.each_declared_member}); nil keys a mixin by its spelling alone.
|
|
187
|
+
#
|
|
188
|
+
# Kept apart from {.member_method_keys} on purpose: the plugin-signature plans key collisions by
|
|
189
|
+
# method only, and this rule is the inline-vs-`sig/` one.
|
|
190
|
+
def member_keys(member, nesting = nil)
|
|
191
|
+
case member
|
|
192
|
+
when ::RBS::AST::Members::InstanceVariable then [[member.name, :instance_variable]]
|
|
193
|
+
when ::RBS::AST::Members::ClassInstanceVariable then [[member.name, :class_instance_variable]]
|
|
194
|
+
when ::RBS::AST::Members::Include then [[mixin_key(member, nesting), :include]]
|
|
195
|
+
when ::RBS::AST::Members::Extend then [[mixin_key(member, nesting), :extend]]
|
|
196
|
+
when ::RBS::AST::Members::Prepend then [[mixin_key(member, nesting), :prepend]]
|
|
197
|
+
else member_method_keys(member)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Whether a key `kind` names a variable rather than a method.
|
|
202
|
+
def variable_kind?(kind)
|
|
203
|
+
VARIABLE_KINDS.include?(kind)
|
|
204
|
+
end
|
|
205
|
+
|
|
162
206
|
private
|
|
163
207
|
|
|
208
|
+
def mixin_key(member, nesting)
|
|
209
|
+
args = member.args.empty? ? "" : "[#{member.args.join(', ')}]"
|
|
210
|
+
:"#{nesting}|#{member.name}#{args}"
|
|
211
|
+
end
|
|
212
|
+
|
|
164
213
|
def resolve_member(state, signature_members, inline)
|
|
165
|
-
_virtual_name, class_name, member, = inline
|
|
166
|
-
overlapping =
|
|
214
|
+
_virtual_name, class_name, member, _visibility, nesting = inline
|
|
215
|
+
overlapping = member_keys(member, nesting).filter_map do |method_name, kind|
|
|
167
216
|
key = [class_name, method_name, kind]
|
|
168
217
|
owner = signature_members[key]
|
|
169
218
|
owner && [key, *owner]
|
|
@@ -235,7 +284,7 @@ module Rigor
|
|
|
235
284
|
end
|
|
236
285
|
|
|
237
286
|
def full_keys(class_name, member)
|
|
238
|
-
|
|
287
|
+
member_keys(member).map { |method_name, kind| [class_name, method_name, kind] }
|
|
239
288
|
end
|
|
240
289
|
|
|
241
290
|
# The `.rbs` line is read only for an outcome that reports it: asking an `RBS::Buffer` for a line
|
|
@@ -107,8 +107,9 @@ module Rigor
|
|
|
107
107
|
# difference is worth stating because the two look like the same problem. There, a stale
|
|
108
108
|
# `bundler/gems/` directory left behind by a gem that moved to a released version made the loader
|
|
109
109
|
# load the WRONG SIGNATURES, collapsing the RBS environment. Here the index's whole output is a gem
|
|
110
|
-
# NAME, so reading a stale checkout of the same gem yields the same answer; and the
|
|
111
|
-
# is consulted FIRST in {.build}, so a gem
|
|
110
|
+
# NAME, so reading a stale checkout of the same gem yields the same answer; and the RubyGems-layout
|
|
111
|
+
# bundle directory is consulted FIRST in {.build}, so a gem installed from RubyGems at its locked
|
|
112
|
+
# version never reaches this map.
|
|
112
113
|
def git_gem_dirs(bundle_path)
|
|
113
114
|
return {} if bundle_path.nil?
|
|
114
115
|
|