rigortype 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -0,0 +1,381 @@
|
|
|
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
|
+
# - an `include`, `extend` or `prepend` of a module this file declares but that stood down whole, or that another
|
|
27
|
+
# source declares as a class, stands down too (#2974), where keeping it would have raised `NoMixinFoundError` or
|
|
28
|
+
# `MixinClassError` for the class that mixes it in (`Integer`, for the overlay's `ActiveSupport::NumericWithFormat`);
|
|
29
|
+
# - an instance, class-instance or class variable another source declares on the type stands down (#2978), where
|
|
30
|
+
# two declarations of it would have raised `InstanceVariableDuplicationError` or its twins for the class.
|
|
31
|
+
#
|
|
32
|
+
# A declaration with nothing taken comes back as the same object, so a file with no collision loads as it
|
|
33
|
+
# stands. {.standdown_rows} reads what stood down back off the built environment, so a warm run off the env
|
|
34
|
+
# cache reports what the cold run decided.
|
|
35
|
+
module DeferredSignaturePlan
|
|
36
|
+
module_function
|
|
37
|
+
|
|
38
|
+
# @param own — the buffer names that are the project's own sources: its `signature_paths:` files (not the
|
|
39
|
+
# collection or gem directories also on that list) and its inline RBS.
|
|
40
|
+
def plan(decls, env, own, namespace = nil)
|
|
41
|
+
mixins = MixinGate.new(env, decls)
|
|
42
|
+
planned = decls.filter_map { |decl| plan_declaration(decl, env, own, namespace, mixins) }
|
|
43
|
+
same?(planned, decls) ? decls : planned
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def plan_declaration(decl, env, own, namespace, mixins)
|
|
47
|
+
name = GatedSignaturePlan.qualify(namespace, declared_name(decl))
|
|
48
|
+
if GatedSignaturePlan.type_declaration?(decl)
|
|
49
|
+
return nil if kind_clash?(env, decl, name)
|
|
50
|
+
|
|
51
|
+
return plan_type(decl, env, own, name, mixins)
|
|
52
|
+
end
|
|
53
|
+
declared_elsewhere(env, decl, name) ? nil : decl
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def plan_type(decl, env, own, name, mixins)
|
|
57
|
+
others = GatedSignaturePlan.other_declarations(env, name)
|
|
58
|
+
taken = GatedSignaturePlan.declared_keys(others)
|
|
59
|
+
variables = variable_keys(others)
|
|
60
|
+
external = others.reject { |other| own.include?(buffer_of(other)) }
|
|
61
|
+
defined = GatedSignaturePlan.declared_keys(external) | definition_keys(external)
|
|
62
|
+
members = decl.members.filter_map do |member|
|
|
63
|
+
next plan_declaration(member, env, own, name, mixins) if declaration?(member)
|
|
64
|
+
next nil if mixins.drop?(member, name) || variables.include?(variable_key(member))
|
|
65
|
+
next member if taken.empty?
|
|
66
|
+
|
|
67
|
+
composed(member, defined) || GatedSignaturePlan.untaken(member, taken)
|
|
68
|
+
end
|
|
69
|
+
drop = superclass_given?(decl, others)
|
|
70
|
+
return decl if same?(members, decl.members) && !drop
|
|
71
|
+
|
|
72
|
+
GatedSignaturePlan.rebuild(decl, members: members, drop_super_class: drop)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Issue #2978 — an instance, class-instance or class variable another source declares on the type stands down:
|
|
76
|
+
# RBS raises `InstanceVariableDuplicationError` (or its class-instance and class-variable twins) for two
|
|
77
|
+
# declarations of one, and the class reads `Dynamic[top]`. Keyed by name and kind, not type: the other source's
|
|
78
|
+
# declaration binds either way.
|
|
79
|
+
VARIABLE_MEMBERS = {
|
|
80
|
+
::RBS::AST::Members::InstanceVariable => :instance_variable,
|
|
81
|
+
::RBS::AST::Members::ClassInstanceVariable => :class_instance_variable,
|
|
82
|
+
::RBS::AST::Members::ClassVariable => :class_variable
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
# `[name, kind]` for a variable member, else nil.
|
|
86
|
+
def variable_key(member)
|
|
87
|
+
kind = VARIABLE_MEMBERS[member.class]
|
|
88
|
+
kind && [member.name, kind]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The variable keys `decls` declare, an attribute's implied instance variable included (`attr_reader x: String`
|
|
92
|
+
# declares `@x` unless written `attr_reader x(): String`).
|
|
93
|
+
def variable_keys(decls)
|
|
94
|
+
decls.each_with_object(Set.new) do |decl, keys|
|
|
95
|
+
decl.members.each do |member|
|
|
96
|
+
key = variable_key(member) || attribute_variable_key(member)
|
|
97
|
+
keys << key if key
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def attribute_variable_key(member)
|
|
103
|
+
return nil unless member.is_a?(::RBS::AST::Members::Attribute) && member.ivar_name != false
|
|
104
|
+
|
|
105
|
+
[member.ivar_name || :"@#{member.name}",
|
|
106
|
+
member.kind == :singleton ? :class_instance_variable : :instance_variable]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Issue #2439 — true when `decl` is a class with a superclass clause and another source (`others`) already
|
|
110
|
+
# gives the class one. Two different clauses raise `RBS::SuperclassMismatchError` and the class and every
|
|
111
|
+
# descendant read `Dynamic[top]`, so the plugin's header stands down and reopens the class without one. The
|
|
112
|
+
# two names are not compared: they are not resolved yet, and the other source's superclass binds either way.
|
|
113
|
+
def superclass_given?(decl, others)
|
|
114
|
+
super_class?(decl) && others.any? { |other| super_class?(other) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# `member` as an overload continuation, or nil when it cannot be one: only a plain one-sided `def` can, and
|
|
118
|
+
# only onto a method — a `def`, an attribute or an alias, all of which RBS continues — that a source other
|
|
119
|
+
# than the project's own declares.
|
|
120
|
+
def composed(member, defined)
|
|
121
|
+
return nil unless member.is_a?(::RBS::AST::Members::MethodDefinition) && !member.overloading?
|
|
122
|
+
return nil if member.kind == :singleton_instance
|
|
123
|
+
|
|
124
|
+
key = [member.name, member.kind]
|
|
125
|
+
return nil unless defined.include?(key)
|
|
126
|
+
|
|
127
|
+
member.update(overloading: true)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The `[name, kind]` keys a plain or continued `def` in `decls` declares — what a continuation can extend.
|
|
131
|
+
def definition_keys(decls)
|
|
132
|
+
decls.each_with_object(Set.new) do |decl, keys|
|
|
133
|
+
decl.members.each do |member|
|
|
134
|
+
next unless member.is_a?(::RBS::AST::Members::MethodDefinition)
|
|
135
|
+
|
|
136
|
+
sides = member.kind == :singleton_instance ? %i[instance singleton] : [member.kind]
|
|
137
|
+
sides.each { |side| keys << [member.name, side] }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def kind_clash?(env, decl, name)
|
|
143
|
+
type_name = ::RBS::TypeName.parse("::#{name}")
|
|
144
|
+
return true if env.class_alias_decls.key?(type_name)
|
|
145
|
+
|
|
146
|
+
first = GatedSignaturePlan.other_declarations(env, name).first
|
|
147
|
+
!first.nil? && !first.instance_of?(decl.class)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The buffer of the declaration another source already makes under `decl`'s name, or nil.
|
|
151
|
+
def declared_elsewhere(env, decl, name)
|
|
152
|
+
entry = declaration_entry(env, decl, name)
|
|
153
|
+
entry && (RbsLoader.declaration_buffer_name(entry.decl) || "")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def declaration_entry(env, decl, name)
|
|
157
|
+
type_name = -> { ::RBS::TypeName.parse("::#{name}") }
|
|
158
|
+
case decl
|
|
159
|
+
when ::RBS::AST::Declarations::Constant then env.constant_decls[type_name.call]
|
|
160
|
+
when ::RBS::AST::Declarations::Global then env.global_decls[decl.name]
|
|
161
|
+
when ::RBS::AST::Declarations::TypeAlias then env.type_alias_decls[type_name.call]
|
|
162
|
+
when ::RBS::AST::Declarations::Interface then env.interface_decls[type_name.call]
|
|
163
|
+
when ::RBS::AST::Declarations::ClassAlias, ::RBS::AST::Declarations::ModuleAlias
|
|
164
|
+
env.class_alias_decls[type_name.call] || env.class_decls[type_name.call]
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# A class or module alias names itself as `new_name`; every other declaration as `name`.
|
|
169
|
+
def declared_name(decl)
|
|
170
|
+
decl.respond_to?(:new_name) ? decl.new_name : decl.name
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def declaration?(member)
|
|
174
|
+
member.is_a?(::RBS::AST::Declarations::Base)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def same?(planned, original)
|
|
178
|
+
planned.size == original.size && planned.zip(original).all? { |a, b| a.equal?(b) }
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def buffer_of(decl)
|
|
182
|
+
RbsLoader.declaration_buffer_name(decl)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Issue #2974 — which of a deferred file's mixins name a module that will not be there as a module: one the file
|
|
186
|
+
# itself declares, inside a type that stood down whole on a kind clash (the project's `class ActiveSupport` takes
|
|
187
|
+
# the overlay's `module ActiveSupport` and the `NumericWithFormat` in it), or one another source declares as a
|
|
188
|
+
# class. A mixin of a module the file does not declare, which some other source may supply, is left alone.
|
|
189
|
+
class MixinGate
|
|
190
|
+
MIXINS = [::RBS::AST::Members::Include, ::RBS::AST::Members::Extend, ::RBS::AST::Members::Prepend].freeze
|
|
191
|
+
|
|
192
|
+
def initialize(env, decls)
|
|
193
|
+
@env = env
|
|
194
|
+
@declared = Set.new
|
|
195
|
+
@dropped = Set.new
|
|
196
|
+
decls.each { |decl| collect(decl, nil, false) }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# `namespace` is the absolute name, without the leading `::`, of the type `member` is written in.
|
|
200
|
+
def drop?(member, namespace)
|
|
201
|
+
return false unless MIXINS.any? { |klass| member.is_a?(klass) } && member.name.class?
|
|
202
|
+
|
|
203
|
+
target = resolve(member.name, namespace)
|
|
204
|
+
return false if target.nil?
|
|
205
|
+
|
|
206
|
+
first = GatedSignaturePlan.other_declarations(@env, target).first
|
|
207
|
+
return first.is_a?(::RBS::AST::Declarations::Class) unless first.nil?
|
|
208
|
+
|
|
209
|
+
@dropped.include?(target)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def collect(decl, namespace, inside_dropped)
|
|
215
|
+
return unless GatedSignaturePlan.type_declaration?(decl)
|
|
216
|
+
|
|
217
|
+
name = GatedSignaturePlan.qualify(namespace, decl.name)
|
|
218
|
+
dropped = inside_dropped || DeferredSignaturePlan.kind_clash?(@env, decl, name)
|
|
219
|
+
@declared << name
|
|
220
|
+
@dropped << name if dropped
|
|
221
|
+
decl.members.each { |member| collect(member, name, dropped) }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# The first lexical candidate for `type_name` written inside `namespace` that this file or the env declares,
|
|
225
|
+
# as RBS resolves a relative name outward from the innermost scope.
|
|
226
|
+
def resolve(type_name, namespace)
|
|
227
|
+
candidates(type_name, namespace).find do |name|
|
|
228
|
+
@declared.include?(name) || !GatedSignaturePlan.other_declarations(@env, name).empty?
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def candidates(type_name, namespace)
|
|
233
|
+
text = type_name.to_s
|
|
234
|
+
return [text.delete_prefix("::")] if type_name.absolute?
|
|
235
|
+
|
|
236
|
+
scopes = namespace.to_s.split("::")
|
|
237
|
+
scopes.size.downto(0).map { |depth| [*scopes.first(depth), text].join("::") }
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# One {.standdown_rows} walk over a deferred file: the built env, the file's buffer name, the rows so far, and
|
|
242
|
+
# the env's type-name resolver, built on first use (only a dropped superclass clause needs it).
|
|
243
|
+
class Walk
|
|
244
|
+
attr_reader :env, :file, :rows
|
|
245
|
+
|
|
246
|
+
def initialize(env, file)
|
|
247
|
+
@env = env
|
|
248
|
+
@file = file
|
|
249
|
+
@rows = []
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# rbs 4.x builds the resolver with `TypeNameResolver.build(env)`; rbs 3.x's constructor takes the env, which
|
|
253
|
+
# the 4.x signature Rigor reads (`new(all_names, aliases)`) does not describe.
|
|
254
|
+
def resolver
|
|
255
|
+
@resolver ||= begin
|
|
256
|
+
klass = ::RBS::Resolver::TypeNameResolver
|
|
257
|
+
if klass.respond_to?(:build)
|
|
258
|
+
klass.build(env)
|
|
259
|
+
else
|
|
260
|
+
klass.new(env) # rigor:disable call.wrong-arity
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# What {.plan} left out of the deferred `file` (its parsed `decls`) in the built `env`, as `[declaration,
|
|
267
|
+
# cause_file]` rows — `Object#acts_like?`, `Time.at`, `FFI::Pointer::NULL`, `Foo < Bar`, or a type name — with
|
|
268
|
+
# the buffer of the other source's declaration, or nil when it cannot be named. A composed method is not a row:
|
|
269
|
+
# the plugin's overloads still bind.
|
|
270
|
+
def standdown_rows(env, file, decls)
|
|
271
|
+
walk = Walk.new(env, file)
|
|
272
|
+
decls.each { |decl| declaration_rows(walk, decl, nil) }
|
|
273
|
+
walk.rows.uniq
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# `context` is RBS's resolver context for where `decl` is written: nil at the top level, else `[outer,
|
|
277
|
+
# absolute name of the enclosing declaration]`, as `RBS::Environment#append_context` builds it.
|
|
278
|
+
def declaration_rows(walk, decl, context)
|
|
279
|
+
name = GatedSignaturePlan.qualify(namespace_of(context), declared_name(decl))
|
|
280
|
+
return type_rows(walk, decl, name, context) if GatedSignaturePlan.type_declaration?(decl)
|
|
281
|
+
|
|
282
|
+
cause = displaced_by(walk.env, decl, name, walk.file)
|
|
283
|
+
walk.rows << [decl.is_a?(::RBS::AST::Declarations::Global) ? decl.name.to_s : name, cause] if cause
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def namespace_of(context)
|
|
287
|
+
context && context.last.to_s.delete_prefix("::")
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# A non-nil buffer name (possibly empty) when another source's declaration of `decl` is the one loaded.
|
|
291
|
+
def displaced_by(env, decl, name, file)
|
|
292
|
+
entry = declaration_entry(env, decl, name)
|
|
293
|
+
return nil if entry.nil?
|
|
294
|
+
|
|
295
|
+
buffer = RbsLoader.declaration_buffer_name(entry.decl)
|
|
296
|
+
buffer == file ? nil : buffer
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def type_rows(walk, decl, name, context)
|
|
300
|
+
loaded, others = loaded_and_others(walk, name)
|
|
301
|
+
if loaded.empty?
|
|
302
|
+
cause = others.empty? ? alias_cause(walk.env, name) : buffer_of(others.first)
|
|
303
|
+
walk.rows << [name, cause] if cause
|
|
304
|
+
return
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
walk.rows.concat(superclass_rows(walk, decl, name, context, others))
|
|
308
|
+
walk.rows.concat(member_rows(decl, name, loaded, others))
|
|
309
|
+
inner = [context, ::RBS::TypeName.parse("::#{name}")]
|
|
310
|
+
decl.members.each { |member| declaration_rows(walk, member, inner) if declaration?(member) }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# The declarations of `name` the walked file loaded, and the ones other sources made (synthesized stubs left out).
|
|
314
|
+
def loaded_and_others(walk, name)
|
|
315
|
+
entry = walk.env.class_decls[::RBS::TypeName.parse("::#{name}")]
|
|
316
|
+
declarations = entry.nil? ? [] : RbsLoader.entry_declarations(entry)
|
|
317
|
+
loaded = declarations.select { |other| buffer_of(other) == walk.file }
|
|
318
|
+
[loaded, declarations.reject { |other| loaded.include?(other) || GatedSignaturePlan.synthetic?(other) }]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# The buffer of a class or module alias declaring `name` (empty when it cannot be named), or nil.
|
|
322
|
+
def alias_cause(env, name)
|
|
323
|
+
entry = env.class_alias_decls[::RBS::TypeName.parse("::#{name}")]
|
|
324
|
+
entry && (RbsLoader.declaration_buffer_name(entry.decl) || "")
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# The plugin's superclass clause as a row (`ActiveRecord::StatementInvalid < ActiveRecordError`) when the plan
|
|
328
|
+
# dropped it for another source's ({.superclass_given?}) and that source's names a different class or type
|
|
329
|
+
# arguments. The plan drops an identical clause too, but nothing is lost, so no row.
|
|
330
|
+
def superclass_rows(walk, decl, name, context, others)
|
|
331
|
+
return [] unless superclass_given?(decl, others)
|
|
332
|
+
|
|
333
|
+
cause = others.find { |other| super_class?(other) }
|
|
334
|
+
return [] if same_super_class?(walk, decl.super_class, context, cause.super_class)
|
|
335
|
+
|
|
336
|
+
written = decl.super_class
|
|
337
|
+
args = written.args.empty? ? "" : "[#{written.args.join(', ')}]"
|
|
338
|
+
[["#{name} < #{written.name}#{args}", buffer_of(cause)]]
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Whether the plugin's clause `written`, resolved as RBS resolves a superclass (in the context the class is
|
|
342
|
+
# written in, not its own), names the class and type arguments of `other`, the built env's already-absolute
|
|
343
|
+
# clause, class aliases normalised on both sides. An unresolvable name differs.
|
|
344
|
+
def same_super_class?(walk, written, context, other)
|
|
345
|
+
name = absolute_name(walk, written.name, context)
|
|
346
|
+
return false if name.nil? || normalized(walk.env, name) != normalized(walk.env, other.name)
|
|
347
|
+
|
|
348
|
+
written.args.map { |arg| normalized_type(walk, arg, context) } ==
|
|
349
|
+
other.args.map { |arg| normalized_type(walk, arg, nil) }
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def absolute_name(walk, name, context)
|
|
353
|
+
name.absolute? ? name : walk.resolver.resolve(name, context: context)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def normalized(env, name)
|
|
357
|
+
name.class? ? env.normalize_module_name(name) : name
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def normalized_type(walk, type, context)
|
|
361
|
+
return type.to_s unless type.respond_to?(:map_type_name)
|
|
362
|
+
|
|
363
|
+
type.map_type_name { |name, _, _| normalized(walk.env, absolute_name(walk, name, context) || name) }.to_s
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def super_class?(decl)
|
|
367
|
+
decl.is_a?(::RBS::AST::Declarations::Class) && !decl.super_class.nil?
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def member_rows(decl, name, loaded, others)
|
|
371
|
+
present = GatedSignaturePlan.declared_keys(loaded) | definition_keys(loaded)
|
|
372
|
+
decl.members.flat_map do |member|
|
|
373
|
+
RbsLoader.member_method_keys(member).reject { |key| present.include?(key) }.filter_map do |key|
|
|
374
|
+
cause = others.find { |other| other.members.any? { |m| RbsLoader.member_method_keys(m).include?(key) } }
|
|
375
|
+
cause && [GatedSignaturePlan.member_label(name, key), buffer_of(cause)]
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../typing_state"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
class Environment
|
|
5
7
|
# ADR-20 slice 2e — mutable single-slot memoization container for the per-Environment HKT registry. Held
|
|
@@ -10,6 +12,10 @@ module Rigor
|
|
|
10
12
|
# prevent cross-thread races. If a future caller introduces a multi-threaded reader path against a
|
|
11
13
|
# shared Environment, the synchronisation belongs at that caller's seam, not here.
|
|
12
14
|
class HktRegistryHolder
|
|
15
|
+
TypingState.memo_field(self, :@value, :@loaded,
|
|
16
|
+
reason: "the one value its owner's block builds from the environment's fixed inputs, " \
|
|
17
|
+
"kept once built")
|
|
18
|
+
|
|
13
19
|
def initialize
|
|
14
20
|
@loaded = false
|
|
15
21
|
@value = nil
|
|
@@ -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
|