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
|
@@ -10,7 +10,10 @@ require_relative "mutation_rejoin"
|
|
|
10
10
|
require_relative "deep_erasure"
|
|
11
11
|
require_relative "receiver_alias"
|
|
12
12
|
require_relative "refinement_mutation"
|
|
13
|
+
# `RemoverNarrowing` is loaded with the engine (`scope.rb`), not here: the engine-free value layer requires this file
|
|
14
|
+
# and must not pull in the remover narrowing and its holder scan (`spec/rigor/cli/effects_cache_probe_spec.rb`).
|
|
13
15
|
require_relative "rewrite_mutation"
|
|
16
|
+
require_relative "set_mutation"
|
|
14
17
|
require_relative "string_mutation"
|
|
15
18
|
|
|
16
19
|
module Rigor
|
|
@@ -19,8 +22,7 @@ module Rigor
|
|
|
19
22
|
# AND whose method is a known in-place mutator.
|
|
20
23
|
#
|
|
21
24
|
# Closes the **G1 / G2** flow-folding gaps documented at
|
|
22
|
-
# `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md
|
|
23
|
-
# [`docs/CURRENT_WORK.md`](../../../docs/CURRENT_WORK.md) § "Flow-folding". The user-visible
|
|
25
|
+
# `docs/notes/20260521-mastodon-cluster4-flow-folding-triage.md`. The user-visible
|
|
24
26
|
# symptom they shared was a spurious `flow.always-truthy-condition` on a `arr.size == N` /
|
|
25
27
|
# `arr.empty?` / `@arr.empty?` check that follows a loop body or sibling method that mutates
|
|
26
28
|
# `arr` / `@arr` in place.
|
|
@@ -76,6 +78,7 @@ module Rigor
|
|
|
76
78
|
# ARE also handled** (ivars live in the method-body scope, not the block-local scope).
|
|
77
79
|
#
|
|
78
80
|
# The remaining three items above are demand-gated; see ADR-56.
|
|
81
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
79
82
|
module MutationWidening
|
|
80
83
|
# Array mutators that change either the size or the element set of a literal-shape carrier
|
|
81
84
|
# (Tuple). Receiver-mutating methods only — non-mutating siblings (`map` vs `map!`,
|
|
@@ -107,7 +110,8 @@ module Rigor
|
|
|
107
110
|
# Every method name {#widen_for_mutator} responds to — the one set a body scan asks "could an
|
|
108
111
|
# in-place call on this name change its binding?" against (issue #587: the block-return
|
|
109
112
|
# threading gate). Derived from the tables rather than spelled out, so the scan and the
|
|
110
|
-
# widening it predicts cannot drift apart.
|
|
113
|
+
# widening it predicts cannot drift apart. The Set-only names ({SetMutation::MUTATORS}) stay out: the scans read
|
|
114
|
+
# a name without its receiver, and `merge` on a Hash returns a new Hash (issue #2294).
|
|
111
115
|
SHAPE_MUTATORS = (ARRAY_MUTATORS | HASH_MUTATORS | HashLookupMutation::MUTATORS | StringMutation::MUTATORS).freeze
|
|
112
116
|
|
|
113
117
|
# Methods that return the receiver (or a shallow copy) and cannot mutate it. They must not
|
|
@@ -134,10 +138,61 @@ module Rigor
|
|
|
134
138
|
# pre-join behaviour exactly.
|
|
135
139
|
NO_ARG_TYPES = [].freeze
|
|
136
140
|
|
|
137
|
-
|
|
141
|
+
# `rewritten_element` is the element a typed element rewriter stores ({RewriteMutation.typed}), or `nil`.
|
|
142
|
+
def widen_after_call(call_node:, current_scope:, arg_types: NO_ARG_TYPES, rewritten_element: nil)
|
|
138
143
|
return current_scope if pure_self_returner?(call_node.name)
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
if RemoverNarrowing.candidate?(call_node)
|
|
146
|
+
narrowed = narrow_remover_receiver(call_node, current_scope, arg_types)
|
|
147
|
+
return narrowed if narrowed
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
widen_receiver_aliases(call_node.receiver, call_node.name, current_scope, arg_types:, rewritten_element:,
|
|
151
|
+
removes_nil: removes_nil?(call_node))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Issue #2608 — a remover that narrows its receiver by what it removes ({RemoverNarrowing}): the receiver's
|
|
155
|
+
# binding widens as any mutator's does, then loses what the call provably took out. This answers before the
|
|
156
|
+
# #2555 remover floor, so a synthesised carrier a store grew keeps its precise reading (`parts << maybe_nil;
|
|
157
|
+
# parts.compact!` reads `Array[String]`). `nil` when the call narrows nothing; the caller then takes the
|
|
158
|
+
# ordinary path, the floor included.
|
|
159
|
+
def narrow_remover_receiver(call_node, scope, arg_types)
|
|
160
|
+
read = call_node.receiver
|
|
161
|
+
getter, builder = ALIAS_ACCESSORS[read.class]
|
|
162
|
+
name = ReceiverAlias.read_name(read)
|
|
163
|
+
current = scope.public_send(getter, name)
|
|
164
|
+
return nil if current.nil? || RemoverHolderScan.unsafe_receiver?(scope, name)
|
|
165
|
+
|
|
166
|
+
narrowed = RemoverNarrowing.narrow_shape(current, call_node)
|
|
167
|
+
if narrowed.nil?
|
|
168
|
+
synthesised = synthesised_binding?(scope, getter, name)
|
|
169
|
+
base = widen_for_mutator(current, call_node.name, arg_types:, synthesised:)
|
|
170
|
+
narrowed = RemoverNarrowing.narrow(base || current, call_node, scope)
|
|
171
|
+
end
|
|
172
|
+
return nil if narrowed.nil?
|
|
173
|
+
return scope if narrowed == current
|
|
174
|
+
|
|
175
|
+
MutationRejoin.rebind(scope, builder, name, narrowed, pre_state: current, kind: getter)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Issue #2555 — the removers that may take a `nil` out, which is every one but a `delete` / `delete?` given a
|
|
179
|
+
# non-nil literal: false positives outrank, so a positional remover (`pop`, `shift`, `slice!`), `subtract`
|
|
180
|
+
# (whose argument may hold `nil`) and a `delete` of an unknown value count. `Hash#delete` removes by key, so
|
|
181
|
+
# whatever value the key held may go ({.floor_grown_carrier} reads the receiver for it).
|
|
182
|
+
NIL_REMOVERS = %i[compact! reject! select! filter! delete_if keep_if clear pop shift slice!
|
|
183
|
+
subtract].to_set.freeze
|
|
184
|
+
NON_NIL_LITERALS = [
|
|
185
|
+
Prism::IntegerNode, Prism::FloatNode, Prism::RationalNode, Prism::ImaginaryNode, Prism::StringNode,
|
|
186
|
+
Prism::SymbolNode, Prism::TrueNode, Prism::FalseNode
|
|
187
|
+
].freeze
|
|
188
|
+
private_constant :NON_NIL_LITERALS
|
|
189
|
+
|
|
190
|
+
def removes_nil?(call_node)
|
|
191
|
+
return true if NIL_REMOVERS.include?(call_node.name)
|
|
192
|
+
return false unless %i[delete delete?].include?(call_node.name)
|
|
193
|
+
|
|
194
|
+
argument = call_node.arguments&.arguments&.first
|
|
195
|
+
NON_NIL_LITERALS.none? { |klass| argument.is_a?(klass) }
|
|
141
196
|
end
|
|
142
197
|
|
|
143
198
|
# True when `receiver` names at least one variable whose CURRENT binding is a literal-shape
|
|
@@ -148,10 +203,19 @@ module Rigor
|
|
|
148
203
|
|
|
149
204
|
ReceiverAlias.mutated_reads(receiver).any? do |read|
|
|
150
205
|
getter, = ALIAS_ACCESSORS[read.class]
|
|
151
|
-
|
|
206
|
+
next false if getter.nil?
|
|
207
|
+
|
|
208
|
+
name = ReceiverAlias.read_name(read)
|
|
209
|
+
shape_carrier?(scope.public_send(getter, name), synthesised: synthesised_binding?(scope, getter, name))
|
|
152
210
|
end
|
|
153
211
|
end
|
|
154
212
|
|
|
213
|
+
# Issue #2555 L3 — whether the binding `getter` / `name` reads carries the `:synthesised` construction fact
|
|
214
|
+
# ({Scope#construction_fact?}). Only a local can: an instance, class or global variable never carries one.
|
|
215
|
+
def synthesised_binding?(scope, getter, name)
|
|
216
|
+
getter == :local && scope.construction_fact?(name)
|
|
217
|
+
end
|
|
218
|
+
|
|
155
219
|
# A union carrying a shape member is joinable too (issue #645): {#widen_union} widens that
|
|
156
220
|
# member through the same join, so the arguments the caller would skip are the evidence it
|
|
157
221
|
# needs.
|
|
@@ -160,26 +224,34 @@ module Rigor
|
|
|
160
224
|
# An empty-witness `Difference` counts too (issue #936): the `non-empty-array[T]` arm joins the
|
|
161
225
|
# mutator's added element now, so its arguments are evidence this seam consumes rather than
|
|
162
226
|
# arguments nothing will read.
|
|
163
|
-
|
|
227
|
+
# A precise `Hash` counts too when its binding carries the `:synthesised` construction fact (issue #2555): the
|
|
228
|
+
# re-join grows it from these arguments.
|
|
229
|
+
# An intersection counts when a member does: `non-empty-lowercase-string`'s witness half reads `tr!`'s
|
|
230
|
+
# replacement ({#widen_string_intersection}).
|
|
231
|
+
def shape_carrier?(type, synthesised: false)
|
|
164
232
|
case type
|
|
165
233
|
when Type::Tuple, Type::HashShape then true
|
|
166
|
-
when Type::Constant then StringMutation.constant?(type)
|
|
234
|
+
when Type::Constant then StringMutation.constant?(type) || SetMutation.constant?(type)
|
|
167
235
|
# A re-openable nominal counts since issue #580: skipping it made the one-way door
|
|
168
236
|
# self-fulfilling, since the arm never saw the arg types it grows from.
|
|
169
|
-
|
|
237
|
+
# A precise `Array[E]` / `Set[E]` counts since issue #2361, which re-grows it from these arguments.
|
|
238
|
+
when Type::Nominal
|
|
239
|
+
MutationRejoin.regrowable_carrier?(type) || MutationRejoin.precise_array?(type) ||
|
|
240
|
+
SetMutation.regrowable?(type) || (synthesised && MutationRejoin.synthesised_carrier?(type))
|
|
170
241
|
when Type::Difference then type.removes_empty_witness?
|
|
171
|
-
when Type::Union then type.members.any? { |m| shape_carrier?(m) }
|
|
242
|
+
when Type::Union, Type::Intersection then type.members.any? { |m| shape_carrier?(m, synthesised:) }
|
|
172
243
|
else false
|
|
173
244
|
end
|
|
174
245
|
end
|
|
175
246
|
|
|
176
247
|
# Widens every variable `receiver` can evaluate to ({ReceiverAlias.mutated_reads}), against `method_name`'s
|
|
177
248
|
# mutator table.
|
|
178
|
-
def widen_receiver_aliases(receiver, method_name, current_scope, arg_types: NO_ARG_TYPES
|
|
249
|
+
def widen_receiver_aliases(receiver, method_name, current_scope, arg_types: NO_ARG_TYPES, rewritten_element: nil,
|
|
250
|
+
removes_nil: false)
|
|
179
251
|
return current_scope if receiver.nil?
|
|
180
252
|
|
|
181
253
|
ReceiverAlias.mutated_reads(receiver).reduce(current_scope) do |acc, read|
|
|
182
|
-
widen_alias_read(method_name, read, acc, arg_types:
|
|
254
|
+
widen_alias_read(method_name, read, acc, arg_types:, rewritten_element:, removes_nil:)
|
|
183
255
|
end
|
|
184
256
|
end
|
|
185
257
|
|
|
@@ -196,9 +268,8 @@ module Rigor
|
|
|
196
268
|
# always safe — it can only LOSE precision — so blindly propagating is sound regardless of
|
|
197
269
|
# whether the block actually runs.
|
|
198
270
|
#
|
|
199
|
-
# Recurses into nested
|
|
200
|
-
# g(y)`, `arr.push(x) if cond`) are all caught.
|
|
201
|
-
# `Prism::BlockNode`s — each block is processed by its own `eval_call`.
|
|
271
|
+
# Recurses into every nested node, blocks included, so chained / nested forms (`arr << f(x); arr <<
|
|
272
|
+
# g(y)`, `arr.push(x) if cond`) are all caught.
|
|
202
273
|
def widen_after_block(call_node:, outer_scope:)
|
|
203
274
|
block = call_node.block
|
|
204
275
|
return DeepErasure.widen_symbol_proc(call_node, outer_scope) if DeepErasure.symbol_proc_mutation?(block)
|
|
@@ -235,6 +306,13 @@ module Rigor
|
|
|
235
306
|
receiver = call_node.receiver
|
|
236
307
|
return scope if receiver.nil?
|
|
237
308
|
|
|
309
|
+
# Issue #2335 — a block parameter, an element read or any other receiver that may be an outer local's object.
|
|
310
|
+
if AliasMutationFloor.candidate?(call_node)
|
|
311
|
+
scope = AliasMutationFloor.floor_after_call(call_node, scope) { captured_element_root(receiver) }
|
|
312
|
+
end
|
|
313
|
+
widened = widen_outer_element_read(call_node, scope)
|
|
314
|
+
return widened if widened
|
|
315
|
+
|
|
238
316
|
ReceiverAlias.candidates(receiver).reduce(scope) do |acc, read|
|
|
239
317
|
# A block-local read ({ReceiverAlias.block_local?}: a `depth == 0` local, or `it`) is not a
|
|
240
318
|
# capture of the outer scope, so widening its name against the OUTER scope would hit an
|
|
@@ -245,10 +323,35 @@ module Rigor
|
|
|
245
323
|
# after it re-adds the appended values' types onto the widened seed, so the seed's own value
|
|
246
324
|
# pinning is still justified (`out = [0]; arr.each { out << x }` never rewrites slot 0). The
|
|
247
325
|
# straight-line paths have no such join, which is why their default is `:widen` (issue #560).
|
|
248
|
-
widen_alias_read(call_node.name, read, acc, values: :keep)
|
|
326
|
+
widen_alias_read(call_node.name, read, acc, values: :keep, unread_arguments: !call_node.arguments.nil?)
|
|
249
327
|
end
|
|
250
328
|
end
|
|
251
329
|
|
|
330
|
+
# The outer root of an element read in a block body, whose element {#widen_outer_element_read} widens. (A captured
|
|
331
|
+
# local the receiver names takes the capture path's widening and the block content join after it, which a floor
|
|
332
|
+
# here would not change.)
|
|
333
|
+
def captured_element_root(receiver)
|
|
334
|
+
root = ElementReadWidening.element_read_path(receiver)&.first
|
|
335
|
+
root && !ReceiverAlias.block_local?(root) ? [root.name] : nil
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Issue #2287 — a mutator on an element read rooted at a captured local (`[0].each { |i| a[i] << 2 }`,
|
|
339
|
+
# `loop { a[0] << 2; break }`) mutates an element the outer binding still carries, and names no variable
|
|
340
|
+
# {ReceiverAlias.candidates} could widen. The element at that path widens in the outer scope as the
|
|
341
|
+
# straight-line {ElementReadWidening.widen_element_read} widens it; a block-local root is skipped as a
|
|
342
|
+
# block-local read is. Answers the widened scope, or `nil` when the receiver is no such read or nothing widened.
|
|
343
|
+
def widen_outer_element_read(call_node, scope)
|
|
344
|
+
path = ElementReadWidening.element_read_path(call_node.receiver)
|
|
345
|
+
return nil if path.nil?
|
|
346
|
+
|
|
347
|
+
read, steps = path
|
|
348
|
+
return nil if ReceiverAlias.block_local?(read)
|
|
349
|
+
|
|
350
|
+
current = scope.local(read.name)
|
|
351
|
+
widened = current && ElementReadWidening.widen_through_path(current, steps, call_node.name, NO_ARG_TYPES)
|
|
352
|
+
widened && scope.with_mutated_local(read.name, widened)
|
|
353
|
+
end
|
|
354
|
+
|
|
252
355
|
# The arms per kind of binding differ only in which pair of `Scope` accessors they use, so they are read out
|
|
253
356
|
# of one table rather than written once per kind: a carrier the widening responds to must not be widened on
|
|
254
357
|
# one kind of binding and left on another. `$g << x; $g == "k"` folded always-truthy on a `$g` Ruby holds
|
|
@@ -262,16 +365,37 @@ module Rigor
|
|
|
262
365
|
Prism::GlobalVariableReadNode => %i[global with_global]
|
|
263
366
|
})
|
|
264
367
|
|
|
265
|
-
|
|
368
|
+
# `unread_arguments` says the call passes arguments this seam did not type (the block-capture path): a Set,
|
|
369
|
+
# which has no block content join, takes one unknown store for it ({SetMutation.widen}).
|
|
370
|
+
def widen_alias_read(method_name, read, scope, values: :widen, arg_types: NO_ARG_TYPES, unread_arguments: false, # rubocop:disable Metrics/ParameterLists
|
|
371
|
+
rewritten_element: nil, removes_nil: false)
|
|
266
372
|
getter, builder = ALIAS_ACCESSORS[read.class]
|
|
267
373
|
return scope if getter.nil?
|
|
268
374
|
|
|
269
375
|
name = ReceiverAlias.read_name(read)
|
|
270
376
|
current = scope.public_send(getter, name)
|
|
271
|
-
|
|
272
|
-
|
|
377
|
+
synthesised = synthesised_binding?(scope, getter, name)
|
|
378
|
+
if synthesised && MutationRejoin.floors_grown_carrier?(scope, name, method_name)
|
|
379
|
+
return floor_grown_carrier(scope, builder, name, current, method_name, getter, removes_nil)
|
|
380
|
+
end
|
|
273
381
|
|
|
274
|
-
|
|
382
|
+
widened = widen_for_mutator(current, method_name, values:, arg_types:, unread_arguments:, rewritten_element:,
|
|
383
|
+
synthesised:)
|
|
384
|
+
scope = MutationRejoin.rebind(scope, builder, name, widened, pre_state: current, kind: getter) if widened
|
|
385
|
+
return scope unless synthesised && MutationRejoin.content_adder?(method_name)
|
|
386
|
+
|
|
387
|
+
MutationRejoin.grew_unfloored?(current, widened) ? scope.with_construction_fact(name, :grown) : scope
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Issue #2555 — a remover or filter on a synthesised carrier a store has grown. Nothing narrows the carrier back,
|
|
391
|
+
# so the stored arm would outlive the values the call removes (`parts << nil; parts.compact!`); the carrier takes
|
|
392
|
+
# the `Dynamic[top]` floor it would have carried without the fact. The binding keeps its facts: the floored
|
|
393
|
+
# carrier is gradual, so the synthesised re-join no longer applies to it, and a later remover floors it again.
|
|
394
|
+
def floor_grown_carrier(scope, builder, name, current, method_name, getter, removes_nil)
|
|
395
|
+
drop_nil = removes_nil || (method_name == :delete && MutationRejoin.hash_carrier?(current))
|
|
396
|
+
floored = MutationRejoin.floor_parameters(current, drop_nil:)
|
|
397
|
+
floored = widen_for_mutator(floored, method_name) || floored
|
|
398
|
+
MutationRejoin.rebind(scope, builder, name, floored, pre_state: current, kind: getter)
|
|
275
399
|
end
|
|
276
400
|
|
|
277
401
|
# Mutators that can land a NEW value in an EXISTING slot, falsifying that slot's value pinning
|
|
@@ -285,15 +409,32 @@ module Rigor
|
|
|
285
409
|
# Returns the widened type for a binding whose receiver is about to be mutated by
|
|
286
410
|
# `method_name`, or `nil` when no widening applies (binding is not a carrier the mutation
|
|
287
411
|
# stands for, OR the method is not a mutator for that shape, OR the carrier is a PRECISE
|
|
288
|
-
# nominal
|
|
289
|
-
|
|
412
|
+
# nominal the call stores nothing into — see {MutationRejoin.widen_precise_array}).
|
|
413
|
+
# A `rewritten_element` answers first for the carriers {RewriteMutation.typed} types (issue #2427).
|
|
414
|
+
# `synthesised` says the binding carries the `:synthesised` construction fact (issue #2555,
|
|
415
|
+
# {Scope#construction_fact?}): a precise nominal or Set it holds re-grows without the `Dynamic[top]` floor
|
|
416
|
+
# ({MutationRejoin.widen_synthesised}, {SetMutation.widen}).
|
|
417
|
+
def widen_for_mutator(type, method_name, values: :widen, arg_types: NO_ARG_TYPES, unread_arguments: false,
|
|
418
|
+
rewritten_element: nil, synthesised: false)
|
|
419
|
+
typed = rewritten_element && type && RewriteMutation.typed(type, method_name, rewritten_element)
|
|
420
|
+
return typed == type ? nil : typed if typed
|
|
421
|
+
|
|
422
|
+
widen_carrier(type, method_name, values:, arg_types:, unread_arguments:, rewritten_element:, synthesised:)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# {#widen_for_mutator} for a carrier no typed element rewrite answered.
|
|
426
|
+
def widen_carrier(type, method_name, values:, arg_types:, unread_arguments:, rewritten_element:,
|
|
427
|
+
synthesised: false)
|
|
290
428
|
values = :keep unless VALUE_REWRITING_MUTATORS.include?(method_name)
|
|
291
429
|
values = RewriteMutation.values_mode(type, method_name, values)
|
|
292
430
|
|
|
293
431
|
return nil if type.nil?
|
|
432
|
+
if SetMutation.set?(type)
|
|
433
|
+
return SetMutation.widen(type, method_name, arg_types, unread_arguments:, synthesised:)
|
|
434
|
+
end
|
|
294
435
|
|
|
295
436
|
case type
|
|
296
|
-
when Type::Nominal then MutationRejoin.widen_nominal(type, method_name, values:, arg_types:)
|
|
437
|
+
when Type::Nominal then MutationRejoin.widen_nominal(type, method_name, values:, arg_types:, synthesised:)
|
|
297
438
|
when Type::Tuple
|
|
298
439
|
return nil unless ARRAY_MUTATORS.include?(method_name)
|
|
299
440
|
|
|
@@ -304,10 +445,9 @@ module Rigor
|
|
|
304
445
|
join_added_pairs(widen_hash_shape(type, values: values), method_name, arg_types,
|
|
305
446
|
ContentJoin.hash_shape_key_values(type))
|
|
306
447
|
when Type::Constant then StringMutation.widen_constant(type, method_name)
|
|
307
|
-
when Type::Difference
|
|
308
|
-
widen_difference(type, method_name, arg_types: arg_types)
|
|
448
|
+
when Type::Difference, Type::Refined, Type::Intersection then widen_refinement(type, method_name, arg_types)
|
|
309
449
|
when Type::Union
|
|
310
|
-
widen_union(type, method_name, values
|
|
450
|
+
widen_union(type, method_name, values:, arg_types:, unread_arguments:, rewritten_element:, synthesised:)
|
|
311
451
|
end
|
|
312
452
|
end
|
|
313
453
|
|
|
@@ -325,12 +465,14 @@ module Rigor
|
|
|
325
465
|
#
|
|
326
466
|
# `nil` when no member widened — a union with no carrier member, or one whose carriers are
|
|
327
467
|
# already nominal, is untouched exactly as the scalar path leaves them.
|
|
328
|
-
def widen_union(union, method_name, values: :widen, arg_types: NO_ARG_TYPES
|
|
468
|
+
def widen_union(union, method_name, values: :widen, arg_types: NO_ARG_TYPES, unread_arguments: false,
|
|
469
|
+
rewritten_element: nil, synthesised: false)
|
|
329
470
|
widened_any = false
|
|
330
471
|
members = union.members.map do |member|
|
|
331
472
|
next member unless mutation_carrier?(member, method_name)
|
|
332
473
|
|
|
333
|
-
widened = widen_for_mutator(member, method_name, values
|
|
474
|
+
widened = widen_for_mutator(member, method_name, values:, arg_types:, unread_arguments:, rewritten_element:,
|
|
475
|
+
synthesised:)
|
|
334
476
|
next member if widened.nil?
|
|
335
477
|
|
|
336
478
|
widened_any = true
|
|
@@ -348,7 +490,9 @@ module Rigor
|
|
|
348
490
|
(ARRAY_MUTATORS.include?(method_name) && ContentJoin.array_residue(member).empty?) ||
|
|
349
491
|
((HASH_MUTATORS.include?(method_name) || HashLookupMutation::MUTATORS.include?(method_name)) &&
|
|
350
492
|
ContentJoin.hash_residue(member).empty?) ||
|
|
351
|
-
(StringMutation::MUTATORS.include?(method_name) &&
|
|
493
|
+
(StringMutation::MUTATORS.include?(method_name) &&
|
|
494
|
+
(StringMutation.constant?(member) || StringMutation.refined?(member))) ||
|
|
495
|
+
(SetMutation::MUTATORS.include?(method_name) && SetMutation.carrier?(member))
|
|
352
496
|
end
|
|
353
497
|
|
|
354
498
|
# Joins the element evidence the mutator's own ARGUMENTS introduce into the already-widened
|
|
@@ -530,6 +674,32 @@ module Rigor
|
|
|
530
674
|
end
|
|
531
675
|
end
|
|
532
676
|
|
|
677
|
+
# An intersection of String refinements carrying a predicate one (`non-empty-lowercase-string`) widens member
|
|
678
|
+
# by member: the `Refined` member to `String` ({StringMutation.widen_refined}), a `non-empty-string` member on
|
|
679
|
+
# {#widen_difference}'s witness terms. A member that widened to the bare `String` is implied by the others and
|
|
680
|
+
# drops out, so `s.upcase!` reads `non-empty-string` rather than `non-empty-string & String`. `nil` when the
|
|
681
|
+
# intersection carries no String predicate refinement, or nothing widened.
|
|
682
|
+
def widen_string_intersection(intersection, method_name, arg_types)
|
|
683
|
+
return nil unless intersection.members.any? { |m| StringMutation.refined?(m) }
|
|
684
|
+
|
|
685
|
+
members = intersection.members.map { |m| widen_refinement(m, method_name, arg_types) || m }
|
|
686
|
+
base = Type::Combinator.nominal_of("String")
|
|
687
|
+
kept = members.reject { |m| m == base }
|
|
688
|
+
widened = kept.empty? ? base : Type::Combinator.intersection(*kept)
|
|
689
|
+
widened == intersection ? nil : widened
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
# The refinement carriers {#widen_carrier} hands on: the empty witness ({#widen_difference}), a String predicate
|
|
693
|
+
# ({StringMutation.widen_refined}) and an intersection of them ({#widen_string_intersection}). `nil` when nothing
|
|
694
|
+
# widened.
|
|
695
|
+
def widen_refinement(type, method_name, arg_types)
|
|
696
|
+
case type
|
|
697
|
+
when Type::Difference then widen_difference(type, method_name, arg_types:)
|
|
698
|
+
when Type::Refined then StringMutation.widen_refined(type, method_name)
|
|
699
|
+
when Type::Intersection then widen_string_intersection(type, method_name, arg_types)
|
|
700
|
+
end
|
|
701
|
+
end
|
|
702
|
+
|
|
533
703
|
# `Tuple[A, B, C]` → `Nominal[Array, [union(A, B, C)]]`; under `values: :widen`, each element's
|
|
534
704
|
# VALUE pinning widens to its class nominal (`1 | 2` → `Integer`): a slot-rewriting mutator
|
|
535
705
|
# falsifies values along with the shape — `t = [1, 2]; t[0] += 5` holds `6` at slot 0, so a
|