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,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require_relative "../source/node_walker"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
# Which of a def's parameters its own recursion rebinds (issue #3115). The bot-collapse retry of
|
|
9
|
+
# `ExpressionTyper#fixpoint_user_method_return` types the body once for every call the recursion makes, and a
|
|
10
|
+
# recursive call binds a parameter to whatever it passes: `tog(n: n - 1, f: !f)` gives `f` a value the
|
|
11
|
+
# outer call never held. A parameter is KEPT only when every call to the def's own name inside the body
|
|
12
|
+
# passes it the same parameter read unchanged (`cfg(n - 1, mode)` keeps `mode`), and the body never
|
|
13
|
+
# assigns it; every other parameter is rebound, and the retry binds it `Dynamic[top]`.
|
|
14
|
+
#
|
|
15
|
+
# The test is syntactic and reads only the def. A call counts by its method name alone, whatever its
|
|
16
|
+
# receiver (`child.depth(n + 1)` re-enters the same recursion guard), so a same-named method of another
|
|
17
|
+
# class is read as a recursive call too, which only rebinds more. The caller supplies the def's other
|
|
18
|
+
# names (`alias`), since the def does not carry them. A call to ANOTHER method that recurses back (mutual
|
|
19
|
+
# recursion) is not followed: its parameters are judged by the def's own calls alone.
|
|
20
|
+
#
|
|
21
|
+
# Two shapes the test cannot read rebind more instead. A body that calls a method by a computed name
|
|
22
|
+
# ({REFLECTIVE_CALLS}) may recurse with any arguments, so every parameter is rebound ({ALL}). A name a
|
|
23
|
+
# block or lambda in the body declares (a parameter, a block-local, `it`) is rebound, because a read of
|
|
24
|
+
# it inside that block is the block's variable, not the parameter.
|
|
25
|
+
module RecursionRebinding
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
LOCAL_WRITE_NODES = Set[
|
|
29
|
+
Prism::LocalVariableWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode,
|
|
30
|
+
Prism::LocalVariableAndWriteNode, Prism::LocalVariableTargetNode
|
|
31
|
+
].freeze
|
|
32
|
+
private_constant :LOCAL_WRITE_NODES
|
|
33
|
+
|
|
34
|
+
# The calls that reach a method by a name the scan cannot read.
|
|
35
|
+
REFLECTIVE_CALLS = Set[:send, :__send__, :public_send, :method, :instance_method, :define_method].freeze
|
|
36
|
+
private_constant :REFLECTIVE_CALLS
|
|
37
|
+
|
|
38
|
+
# {.rebound_parameters}'s answer when every parameter is rebound.
|
|
39
|
+
ALL = :all
|
|
40
|
+
|
|
41
|
+
# The names the body's recursive calls, its blocks or its own assignments rebind, as a Set, or {ALL}. The
|
|
42
|
+
# Set may hold a name that is no parameter (any assigned local); the caller looks up parameters only.
|
|
43
|
+
# `names` is every name the def answers to: its own and its aliases.
|
|
44
|
+
def rebound_parameters(def_node, names = Set[def_node.name])
|
|
45
|
+
params = def_node.parameters
|
|
46
|
+
return Set.new unless params.is_a?(Prism::ParametersNode)
|
|
47
|
+
|
|
48
|
+
rebound = Set.new
|
|
49
|
+
catch(ALL) { collect(def_node.body, names, params, rebound) } == ALL ? ALL : rebound
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Every method name the body calls, for the caller to ask which of them are this def's aliases.
|
|
53
|
+
def called_names(node, names = Set.new)
|
|
54
|
+
return names if node.nil?
|
|
55
|
+
|
|
56
|
+
names << node.name if node.is_a?(Prism::CallNode)
|
|
57
|
+
node.rigor_each_child { |child| called_names(child, names) }
|
|
58
|
+
names
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def collect(node, names, params, rebound)
|
|
62
|
+
return if node.nil?
|
|
63
|
+
|
|
64
|
+
case node
|
|
65
|
+
when *LOCAL_WRITE_NODES then rebound << node.name
|
|
66
|
+
when Prism::BlockNode, Prism::LambdaNode then note_block(node, rebound)
|
|
67
|
+
when Prism::CallNode
|
|
68
|
+
throw ALL, ALL if REFLECTIVE_CALLS.include?(node.name)
|
|
69
|
+
|
|
70
|
+
note_call(node, params, rebound) if names.include?(node.name)
|
|
71
|
+
end
|
|
72
|
+
node.rigor_each_child { |child| collect(child, names, params, rebound) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# A block's `locals` are its parameters, its block-locals and the locals it first assigns; `it` is not
|
|
76
|
+
# among them.
|
|
77
|
+
def note_block(block, rebound)
|
|
78
|
+
rebound.merge(block.locals)
|
|
79
|
+
rebound << :it if block.parameters.is_a?(Prism::ItParametersNode)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def note_call(call, params, rebound)
|
|
83
|
+
args = call.arguments&.arguments || []
|
|
84
|
+
keywords = args.last if args.last.is_a?(Prism::KeywordHashNode)
|
|
85
|
+
positional = keywords ? args[0...-1] : args
|
|
86
|
+
forwarding = args.any?(Prism::ForwardingArgumentsNode)
|
|
87
|
+
note_positionals(params, positional, rebound)
|
|
88
|
+
note_keywords(params, keywords, forwarding, rebound)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Positionals correspond by index up to the first `*splat` (or `...`), which may reach every slot from
|
|
92
|
+
# its own on. The rest parameter is kept only by `*rest` in its own place as the last positional.
|
|
93
|
+
def note_positionals(params, positional, rebound)
|
|
94
|
+
slots = params.requireds + params.optionals
|
|
95
|
+
cut = positional.index { |arg| arg.is_a?(Prism::SplatNode) || arg.is_a?(Prism::ForwardingArgumentsNode) }
|
|
96
|
+
cut ||= positional.size
|
|
97
|
+
slots.each_with_index do |param, index|
|
|
98
|
+
next if param.is_a?(Prism::MultiTargetNode)
|
|
99
|
+
|
|
100
|
+
rebound << param.name unless index < cut && reads?(positional[index], param.name)
|
|
101
|
+
end
|
|
102
|
+
note_rest(params.rest, positional[slots.size..] || [], cut >= slots.size, rebound)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def note_rest(rest, tail, aligned, rebound)
|
|
106
|
+
return unless rest.is_a?(Prism::RestParameterNode) && rest.name
|
|
107
|
+
|
|
108
|
+
splat = tail.first
|
|
109
|
+
kept = aligned && tail.size == 1 && splat.is_a?(Prism::SplatNode) && reads?(splat.expression, rest.name)
|
|
110
|
+
rebound << rest.name unless kept
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Keywords correspond by name. A keyword the call omits falls back to its default, so it is rebound. A
|
|
114
|
+
# `**splat` other than the def's own `**rest`, a non-Symbol key or `...` may carry any keyword, so each of
|
|
115
|
+
# them rebinds every keyword. `**rest` is kept by `**rest` alone, with no key beside it that it would collect.
|
|
116
|
+
def note_keywords(params, keywords, forwarding, rebound)
|
|
117
|
+
names = params.keywords.map { |param| param.name.to_s.delete_suffix(":").to_sym }
|
|
118
|
+
rest_name = keyword_rest_name(params)
|
|
119
|
+
passed, splats, opaque = keyword_entries(keywords)
|
|
120
|
+
opaque ||= forwarding || splats.any? { |value| !reads?(value, rest_name) }
|
|
121
|
+
return rebound.merge(names, [rest_name].compact) if opaque
|
|
122
|
+
|
|
123
|
+
names.each { |name| rebound << name unless reads?(passed[name], name) }
|
|
124
|
+
rebound << rest_name if rest_name && !(splats.size == 1 && (passed.keys - names).empty?)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def keyword_rest_name(params)
|
|
128
|
+
rest = params.keyword_rest
|
|
129
|
+
rest.name if rest.is_a?(Prism::KeywordRestParameterNode)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The call's keyword hash as `[{ name => value node }, [**splat value nodes], opaque]`, `opaque` when
|
|
133
|
+
# a key is not a plain Symbol.
|
|
134
|
+
def keyword_entries(keywords)
|
|
135
|
+
passed = {}
|
|
136
|
+
splats = []
|
|
137
|
+
opaque = false
|
|
138
|
+
(keywords&.elements || []).each do |element|
|
|
139
|
+
if element.is_a?(Prism::AssocSplatNode)
|
|
140
|
+
splats << element.value
|
|
141
|
+
elsif element.key.is_a?(Prism::SymbolNode) && element.key.unescaped
|
|
142
|
+
passed[element.key.unescaped.to_sym] = element.value
|
|
143
|
+
else
|
|
144
|
+
opaque = true
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
[passed, splats, opaque]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Whether `node` is a bare read of the local `name` (`n`, or the `n:` shorthand's implicit read).
|
|
151
|
+
def reads?(node, name)
|
|
152
|
+
node = node.value if node.is_a?(Prism::ImplicitNode)
|
|
153
|
+
!name.nil? && node.is_a?(Prism::LocalVariableReadNode) && node.name == name
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../typing_state"
|
|
4
|
+
require_relative "../../memo_audit"
|
|
5
|
+
|
|
3
6
|
module Rigor
|
|
4
7
|
module Inference
|
|
5
8
|
module RefinedDispatch
|
|
@@ -16,9 +19,16 @@ module Rigor
|
|
|
16
19
|
# the name itself, and no constant write of it. Any other shape answers false, as does a class a
|
|
17
20
|
# `K = Class.new(...)` write names.
|
|
18
21
|
module ConstructedClass
|
|
19
|
-
|
|
22
|
+
# ADR-129 WD1 — a run-scoped Memo keyed on the run-generation token and the environment, whose tables every
|
|
23
|
+
# answer reads and which are fixed for a run.
|
|
24
|
+
MEMO = TypingState.memo(
|
|
25
|
+
:constructed_class,
|
|
26
|
+
run_scoped: true,
|
|
27
|
+
audit: ["constructed class verdict", "constructed class segments", "constructed class names"],
|
|
28
|
+
reason: "verdicts keyed on the run-generation token and environment identities"
|
|
29
|
+
)
|
|
20
30
|
EMPTY = [].freeze
|
|
21
|
-
private_constant :
|
|
31
|
+
private_constant :MEMO, :EMPTY
|
|
22
32
|
|
|
23
33
|
module_function
|
|
24
34
|
|
|
@@ -34,9 +44,19 @@ module Rigor
|
|
|
34
44
|
return false if named.empty?
|
|
35
45
|
|
|
36
46
|
by_class = (table[:verdicts][class_name] ||= {})
|
|
37
|
-
|
|
47
|
+
if by_class.key?(named)
|
|
48
|
+
return MemoAudit.check("constructed class verdict", by_class[named], class_name, named) do
|
|
49
|
+
apart_verdict(scope, named, class_name, memo(scope))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
by_class[named] = MemoAudit.key_audit("constructed class verdict") do
|
|
54
|
+
apart_verdict(scope, named, class_name, table)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
38
57
|
|
|
39
|
-
|
|
58
|
+
def apart_verdict(scope, named, class_name, table)
|
|
59
|
+
named.all? { |constant| core_class_apart?(scope, constant, class_name.to_s, table) }
|
|
40
60
|
end
|
|
41
61
|
|
|
42
62
|
def core_class_apart?(scope, constant, class_name, table)
|
|
@@ -66,7 +86,18 @@ module Rigor
|
|
|
66
86
|
|
|
67
87
|
# The last segment of every class or module the project declares, reopenings included.
|
|
68
88
|
def declared_segments(scope, table)
|
|
69
|
-
table[:segments]
|
|
89
|
+
segments = table[:segments]
|
|
90
|
+
if segments.nil?
|
|
91
|
+
return table[:segments] = MemoAudit.key_audit("constructed class segments") do
|
|
92
|
+
compute_declared_segments(scope)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
MemoAudit.check("constructed class segments", segments, :segments) { compute_declared_segments(scope) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def compute_declared_segments(scope)
|
|
100
|
+
declared_names(scope).each_key.to_set { |key| key.to_s.split("::").last }.freeze
|
|
70
101
|
end
|
|
71
102
|
|
|
72
103
|
# The constants `name`'s superclass expression names, or an empty Array; memoised per envelope bucket,
|
|
@@ -76,15 +107,18 @@ module Rigor
|
|
|
76
107
|
return EMPTY if bucket.nil?
|
|
77
108
|
|
|
78
109
|
names = table[:names]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
if names.key?(bucket)
|
|
111
|
+
return MemoAudit.check("constructed class names", names[bucket], name) { bucket_constructed_names(bucket) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
names[bucket] = MemoAudit.key_audit("constructed class names") { bucket_constructed_names(bucket) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def bucket_constructed_names(bucket)
|
|
118
|
+
return EMPTY if bucket.key?(Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK)
|
|
119
|
+
|
|
120
|
+
kind = Scope::DiscoveryIndex::ENVELOPE_CONSTRUCTED_KIND
|
|
121
|
+
bucket.each_key.filter_map { |key| key[1] if key.is_a?(Array) && key[0] == kind }.freeze
|
|
88
122
|
end
|
|
89
123
|
|
|
90
124
|
# The run-wide table of declared classes: the seeded `discovered_class_sources`, or a single-file run's
|
|
@@ -98,15 +132,21 @@ module Rigor
|
|
|
98
132
|
# `discovered_def_nodes`, as `ExpressionTyper` falls back) and the environment: every table read here is
|
|
99
133
|
# fixed for a run, so each answer is computed once a run, and a new run replaces the slot.
|
|
100
134
|
def memo(scope)
|
|
101
|
-
|
|
135
|
+
return new_table if MemoAudit.enabled? && MemoAudit.suspended?
|
|
136
|
+
|
|
137
|
+
state = TypingState.current
|
|
138
|
+
slot = state[MEMO]
|
|
102
139
|
generation = scope.run_generation || scope.discovered_def_nodes
|
|
103
140
|
environment = scope.environment
|
|
104
141
|
unless slot && slot[0].equal?(generation) && slot[1].equal?(environment)
|
|
105
|
-
slot = [generation, environment,
|
|
106
|
-
|
|
142
|
+
slot = [generation, environment, new_table]
|
|
143
|
+
state[MEMO] = slot
|
|
107
144
|
end
|
|
108
145
|
slot[2]
|
|
109
146
|
end
|
|
147
|
+
|
|
148
|
+
# Empty tables: a new slot's, and a throwaway set while an audit recomputation bypasses the memo.
|
|
149
|
+
def new_table = { names: {}.compare_by_identity, verdicts: {} }
|
|
110
150
|
end
|
|
111
151
|
end
|
|
112
152
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../builtins/cruby_definers"
|
|
4
4
|
require_relative "constructed_class"
|
|
5
|
+
require_relative "../../typing_state"
|
|
6
|
+
require_relative "../../memo_audit"
|
|
5
7
|
|
|
6
8
|
module Rigor
|
|
7
9
|
module Inference
|
|
@@ -306,30 +308,54 @@ module Rigor
|
|
|
306
308
|
|
|
307
309
|
def unknown_superclass_classes(scope, class_name)
|
|
308
310
|
Analysis::DependencyRecorder.read_name(:subclass_of, "*") if Analysis::DependencyRecorder.active?
|
|
309
|
-
|
|
310
|
-
all = table[:unknown] ||= begin
|
|
311
|
-
mark = Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK
|
|
312
|
-
computed = scope.discovered_superclasses.filter_map { |child, parent| child if parent.nil? }
|
|
313
|
-
meta = scope.discovered_parameter_envelopes.filter_map { |name, bucket| name if bucket.key?(mark) }
|
|
314
|
-
(computed + meta).uniq.freeze
|
|
315
|
-
end
|
|
311
|
+
all = all_unknown_superclass_classes(scope)
|
|
316
312
|
name = class_name.to_s
|
|
317
313
|
all.include?(name) ? all - [name] : all
|
|
318
314
|
end
|
|
319
315
|
|
|
320
|
-
|
|
321
|
-
|
|
316
|
+
def all_unknown_superclass_classes(scope)
|
|
317
|
+
table = subclass_memo(scope)
|
|
318
|
+
stored = table[:unknown]
|
|
319
|
+
if stored.nil?
|
|
320
|
+
return table[:unknown] = MemoAudit.key_audit("own method unknown superclasses") do
|
|
321
|
+
compute_unknown_superclass_classes(scope)
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
MemoAudit.check("own method unknown superclasses", stored, :unknown) do
|
|
326
|
+
compute_unknown_superclass_classes(scope)
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def compute_unknown_superclass_classes(scope)
|
|
331
|
+
mark = Scope::DiscoveryIndex::ENVELOPE_META_SUPERCLASS_MARK
|
|
332
|
+
computed = scope.discovered_superclasses.filter_map { |child, parent| child if parent.nil? }
|
|
333
|
+
meta = scope.discovered_parameter_envelopes.filter_map { |name, bucket| name if bucket.key?(mark) }
|
|
334
|
+
(computed + meta).uniq.freeze
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# ADR-129 WD1 — a run-scoped Memo keyed on the identities of the discovery index and environment, the only
|
|
338
|
+
# inputs of the buckets it holds.
|
|
339
|
+
SUBCLASS_MEMO = TypingState.memo(
|
|
340
|
+
:own_method_subclasses,
|
|
341
|
+
run_scoped: true, audit: ["own method unknown superclasses", "own method children"],
|
|
342
|
+
reason: "subclass buckets keyed on the discovery index and environment identities"
|
|
343
|
+
)
|
|
344
|
+
private_constant :SUBCLASS_MEMO
|
|
322
345
|
|
|
323
346
|
# One thread-local slot keyed on the scope's discovery index (a frozen `Data`) and environment, the only
|
|
324
347
|
# inputs `children_by_parent` and the unknown-superclass list read; neither files a dependency edge
|
|
325
348
|
# (`each_subclass` files its own `subclass_of:` edges on every call), so a hit drops none.
|
|
326
349
|
def subclass_memo(scope)
|
|
327
|
-
|
|
350
|
+
return {} if MemoAudit.enabled? && MemoAudit.suspended?
|
|
351
|
+
|
|
352
|
+
state = TypingState.current
|
|
353
|
+
slot = state[SUBCLASS_MEMO]
|
|
328
354
|
discovery = scope.discovery
|
|
329
355
|
environment = scope.environment
|
|
330
356
|
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
|
|
331
357
|
slot = [discovery, environment, {}]
|
|
332
|
-
|
|
358
|
+
state[SUBCLASS_MEMO] = slot
|
|
333
359
|
end
|
|
334
360
|
slot[2]
|
|
335
361
|
end
|
|
@@ -341,11 +367,20 @@ module Rigor
|
|
|
341
367
|
end
|
|
342
368
|
|
|
343
369
|
def children_by_parent(scope)
|
|
344
|
-
subclass_memo(scope)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
370
|
+
table = subclass_memo(scope)
|
|
371
|
+
stored = table[:children]
|
|
372
|
+
if stored.nil?
|
|
373
|
+
return table[:children] = MemoAudit.key_audit("own method children") { compute_children_by_parent(scope) }
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
MemoAudit.check("own method children", stored, :children) { compute_children_by_parent(scope) }
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def compute_children_by_parent(scope)
|
|
380
|
+
scope.discovered_superclasses.each_with_object({}) do |(child, raw_parent), children|
|
|
381
|
+
parent = scope.ancestor_name_candidates(child, raw_parent).find { |name| known_class?(scope, name) }
|
|
382
|
+
(children[parent] ||= []) << child if parent
|
|
383
|
+
end.each_value(&:freeze).freeze
|
|
349
384
|
end
|
|
350
385
|
|
|
351
386
|
def known_class?(scope, name)
|
|
@@ -126,6 +126,19 @@ module Rigor
|
|
|
126
126
|
out
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
# ADR-121 WD7 — a names-wildcard row counts as a refinement of every name. True when a row `list` puts in
|
|
130
|
+
# effect names `method_name` (or the wildcard) for ANY refined class — which class the row refines is not
|
|
131
|
+
# decided here — or `list` carries {InEffectRefinements::UNKNOWN} and any row names it. {ExpressionTyper}
|
|
132
|
+
# asks it for a `&:name` block, whose receiver is not known; {SelfPreservingBlock} for the yielding method
|
|
133
|
+
# (issue #2909).
|
|
134
|
+
def refined_anywhere?(scope, method_name, list)
|
|
135
|
+
refinements = scope.discovered_refinements
|
|
136
|
+
return true if list.include?(InEffectRefinements::UNKNOWN) &&
|
|
137
|
+
refinements.any? { |_refined, methods| methods.key?(method_name) || methods.key?(wildcard) }
|
|
138
|
+
|
|
139
|
+
!targets(refinements, method_name, list).nil? || !targets(refinements, wildcard, list).nil?
|
|
140
|
+
end
|
|
141
|
+
|
|
129
142
|
# The refinement table records every name a `refine` argument's spelling can denote (`refine String` inside
|
|
130
143
|
# `module M` records `M::String` and `String`), which only withholds a check. To type, a key must be the class
|
|
131
144
|
# Ruby's lexical lookup resolves: one the project or RBS knows, with no longer spelling of the same last segment
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "../type"
|
|
6
|
+
require_relative "../typing_state"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module Inference
|
|
@@ -26,8 +27,13 @@ module Rigor
|
|
|
26
27
|
IVAR_NAME = /\A@[A-Za-z_]\w*\z/
|
|
27
28
|
private_constant :IVAR_NAME
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
# ADR-129 WD1 — a Frame: the depth is installed for a region and restored on the way out.
|
|
31
|
+
REPEATING = TypingState.frame(
|
|
32
|
+
:reflective_ivar_write_repeating,
|
|
33
|
+
reason: "the repeat depth of `for` bodies and loop predicates, installed per region",
|
|
34
|
+
witness: "spec/fixtures/typing_state_witnesses/reflective_ivar_write_repeating.rb"
|
|
35
|
+
)
|
|
36
|
+
private_constant :REPEATING
|
|
31
37
|
|
|
32
38
|
module_function
|
|
33
39
|
|
|
@@ -69,18 +75,20 @@ module Rigor
|
|
|
69
75
|
# rebind fixpoint, and a predicate runs before every iteration but is walked once, so a literal-name write there
|
|
70
76
|
# must not replace the binding with one pass's value ({.apply}). A `while` / `until` body does not enter it: its
|
|
71
77
|
# fixpoint widens the ivar (issue #1930).
|
|
72
|
-
def in_repeating_body
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
def in_repeating_body(&)
|
|
79
|
+
state = TypingState.current
|
|
80
|
+
state.with(REPEATING, (state[REPEATING] || 0) + 1, &)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Runs the block at repeat depth zero, restoring the depth on exit. A user-method body re-typed for its return
|
|
84
|
+
# starts from its own scope and reads its own write, so a caller's `for` body does not repeat it
|
|
85
|
+
# ({ExpressionTyper.with_call_site_frame}).
|
|
86
|
+
def outside_repeating_body(&)
|
|
87
|
+
TypingState.current.with(REPEATING, 0, &)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
def repeating_body?
|
|
83
|
-
(
|
|
91
|
+
(TypingState.current[REPEATING] || 0).positive?
|
|
84
92
|
end
|
|
85
93
|
|
|
86
94
|
# `scope` after `node` runs. A literal name binds that ivar to the value's type (yielded), as `@n = v` does.
|