rigortype 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -20,6 +20,7 @@ module Rigor
|
|
|
20
20
|
class MethodCatalog
|
|
21
21
|
FOLDABLE_PURITIES = Set["leaf", "trivial", "leaf_when_numeric"].freeze
|
|
22
22
|
EMPTY_CATALOG = { "classes" => {} }.freeze
|
|
23
|
+
Ractor.make_shareable(EMPTY_CATALOG)
|
|
23
24
|
|
|
24
25
|
# Selectors that are classified `:leaf` by the C-body analysis (they read no global mutable state
|
|
25
26
|
# in the C sense) but whose result is NOT reproducible across Ruby processes, so they must never
|
|
@@ -37,7 +38,7 @@ module Rigor
|
|
|
37
38
|
|
|
38
39
|
# Shared root for the offline-generated catalogues. Resolving it here keeps the repo-relative
|
|
39
40
|
# `../../../../` hop in one place instead of copying it into every per-topic loader.
|
|
40
|
-
DATA_ROOT = File.expand_path("../../../../data/builtins/ruby_core", __dir__)
|
|
41
|
+
DATA_ROOT = File.expand_path("../../../../data/builtins/ruby_core", __dir__).freeze
|
|
41
42
|
private_constant :DATA_ROOT
|
|
42
43
|
|
|
43
44
|
# Build a catalog for a named topic, resolving its YAML path under {DATA_ROOT}. Equivalent to
|
|
@@ -18,7 +18,7 @@ module Rigor
|
|
|
18
18
|
:replace, :initialize, :initialize_copy, :clear, :<<, :concat, :insert,
|
|
19
19
|
:prepend, :force_encoding, :encode, :scrub, :unicode_normalize, :"[]=",
|
|
20
20
|
:upto, :each_byte, :each_char, :each_codepoint,
|
|
21
|
-
:each_grapheme_cluster, :each_line, :bytesplice,
|
|
21
|
+
:each_grapheme_cluster, :each_line, :bytesplice, :append_as_bytes,
|
|
22
22
|
# `crypt` is not a mutator but is blocked from folding for the same "do not bake a non-pure
|
|
23
23
|
# result into a Constant" reason: `rb_str_crypt` delegates to the platform `crypt(3)`, whose
|
|
24
24
|
# output (algorithm and digest) varies by libc / OS, so `"x".crypt("ab")` is not deterministic
|
|
@@ -4,6 +4,12 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../source/node_walker"
|
|
6
6
|
require_relative "block_parameter_binder"
|
|
7
|
+
require_relative "element_read_widening"
|
|
8
|
+
require_relative "index_write_widening"
|
|
9
|
+
require_relative "mutation_widening"
|
|
10
|
+
require_relative "optimistic_origin"
|
|
11
|
+
require_relative "receiver_alias"
|
|
12
|
+
require_relative "unknown_store_widening"
|
|
7
13
|
|
|
8
14
|
module Rigor
|
|
9
15
|
module Inference
|
|
@@ -16,11 +22,21 @@ module Rigor
|
|
|
16
22
|
# A write counts across every local-write form — plain `=` (`LocalVariableWriteNode`), the operator /
|
|
17
23
|
# `||=` / `&&=` compound forms, and a multi-assign target (`x, y = …` → `LocalVariableTargetNode` under
|
|
18
24
|
# a `MultiWriteNode`) — at ANY depth: a block is a closure, so a write inside a nested block binds the
|
|
19
|
-
# same outer variable. Block-introduced names (parameters, numbered parameters, `;`-locals)
|
|
20
|
-
# not bound in the outer scope
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
# same outer variable. Block-introduced names (parameters, numbered parameters, `;`-locals), names
|
|
26
|
+
# not bound in the outer scope, a write a nested block's own parameter or block-local shadows, and a
|
|
27
|
+
# write inside a nested `def` or class body ({.outer_local?}) are excluded; a write to any of them is
|
|
28
|
+
# not a captured rebind of an outer variable.
|
|
29
|
+
#
|
|
30
|
+
# All three also ask for the instance variables the body rebinds (`ivars: true`). The per-element fold asks
|
|
31
|
+
# for every other binding that outlives an iteration as well (`non_locals: true`): the class variables and
|
|
32
|
+
# globals the body rebinds, and the instance variable behind an attribute setter it calls on `self`. Names
|
|
33
|
+
# keep their sigil, so a map over every kind never collides, and {.bound_type} / {.bind} reach each name
|
|
34
|
+
# through its own kind of binding.
|
|
35
|
+
#
|
|
36
|
+
# {.content_mutations} is the sibling set on the same terms: the captured outer locals the body mutates
|
|
37
|
+
# IN PLACE rather than rebinds, which the rebind set cannot see and the per-element fold needs as well.
|
|
38
|
+
module CapturedLocals # rubocop:disable Metrics/ModuleLength
|
|
39
|
+
LOCAL_WRITE_NODES = Set[
|
|
24
40
|
Prism::LocalVariableWriteNode,
|
|
25
41
|
Prism::LocalVariableOperatorWriteNode,
|
|
26
42
|
Prism::LocalVariableOrWriteNode,
|
|
@@ -28,25 +44,380 @@ module Rigor
|
|
|
28
44
|
Prism::LocalVariableTargetNode
|
|
29
45
|
].freeze
|
|
30
46
|
|
|
47
|
+
# The write forms of the bindings other than locals that outlive an iteration: instance variables, class
|
|
48
|
+
# variables and globals.
|
|
49
|
+
NON_LOCAL_WRITE_NODES = Set[
|
|
50
|
+
Prism::InstanceVariableWriteNode,
|
|
51
|
+
Prism::InstanceVariableOperatorWriteNode,
|
|
52
|
+
Prism::InstanceVariableOrWriteNode,
|
|
53
|
+
Prism::InstanceVariableAndWriteNode,
|
|
54
|
+
Prism::InstanceVariableTargetNode,
|
|
55
|
+
Prism::ClassVariableWriteNode,
|
|
56
|
+
Prism::ClassVariableOperatorWriteNode,
|
|
57
|
+
Prism::ClassVariableOrWriteNode,
|
|
58
|
+
Prism::ClassVariableAndWriteNode,
|
|
59
|
+
Prism::ClassVariableTargetNode,
|
|
60
|
+
Prism::GlobalVariableWriteNode,
|
|
61
|
+
Prism::GlobalVariableOperatorWriteNode,
|
|
62
|
+
Prism::GlobalVariableOrWriteNode,
|
|
63
|
+
Prism::GlobalVariableAndWriteNode,
|
|
64
|
+
Prism::GlobalVariableTargetNode
|
|
65
|
+
].freeze
|
|
66
|
+
|
|
67
|
+
# The call forms that invoke an attribute setter: `self.w = v`, the compound `self.w += v` / `||=` / `&&=`,
|
|
68
|
+
# and a multi-assign target `self.w, x = …`.
|
|
69
|
+
SETTER_CALL_NODES = Set[
|
|
70
|
+
Prism::CallNode, Prism::CallOperatorWriteNode, Prism::CallOrWriteNode, Prism::CallAndWriteNode,
|
|
71
|
+
Prism::CallTargetNode
|
|
72
|
+
].freeze
|
|
73
|
+
private_constant :SETTER_CALL_NODES
|
|
74
|
+
|
|
75
|
+
# An attribute writer's name: an identifier followed by `=`. `==`, `!=`, `<=`, `>=`, `===` and `[]=` are
|
|
76
|
+
# not setters.
|
|
77
|
+
SETTER_NAME = /\A[A-Za-z_]\w*=\z/
|
|
78
|
+
private_constant :SETTER_NAME
|
|
79
|
+
|
|
80
|
+
# The binding the per-element fold lays under a block's parameters: each name's type across iterations, and
|
|
81
|
+
# the optimistic nil-freeness mark an iteration's own rebind gave it, which {.bind} adds to the one the
|
|
82
|
+
# call site already holds — as `Scope#join` unions the marks of the scopes it joins. `repeated` is the
|
|
83
|
+
# `RepeatedOrWrites::Marks` of the index `||=` sites whose slot an earlier run may have filled, marked on the
|
|
84
|
+
# same scope: those of the per-element fold's `position`, or every site when no position is given.
|
|
85
|
+
Bindings = Data.define(:types, :marks, :repeated) do
|
|
86
|
+
def names = types.keys
|
|
87
|
+
|
|
88
|
+
def lay(scope, position: nil)
|
|
89
|
+
bound = types.reduce(scope) do |acc, (name, type)|
|
|
90
|
+
CapturedLocals.bind(acc, name, type, optimistic: marks[name])
|
|
91
|
+
end
|
|
92
|
+
bound.with_repeated_or_writes(repeated.at(position))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The empty answers, shared: every block-bearing call's return pass asks both questions, and nearly every
|
|
97
|
+
# body answers nothing to either.
|
|
98
|
+
NO_NAMES = [].freeze
|
|
99
|
+
NO_SITES = {}.freeze
|
|
100
|
+
private_constant :NO_NAMES, :NO_SITES
|
|
101
|
+
|
|
31
102
|
module_function
|
|
32
103
|
|
|
33
|
-
# @param
|
|
34
|
-
# @param
|
|
35
|
-
#
|
|
36
|
-
|
|
104
|
+
# @param base_scope — the call-site scope the block closes over.
|
|
105
|
+
# @param ivars — also collect the instance variables the body rebinds. An ivar is not captured — the
|
|
106
|
+
# block shares the caller's `self` — but it outlives an iteration, and the call, exactly as a captured
|
|
107
|
+
# local does. It counts on the same terms as a local (every write form, any depth, bound in
|
|
108
|
+
# `base_scope`), except that one still on its class-wide binding does not: ADR-58's declaration seed is
|
|
109
|
+
# the union of every write in the class, this body's included, so nothing the body stores can move it,
|
|
110
|
+
# and rebinding it would only drop the declaration mark that keeps its nil from being diagnostic fuel.
|
|
111
|
+
# A nested block that rebinds `self` (`o.instance_eval`) writes another object's ivar, and a nested
|
|
112
|
+
# `def` runs only when called; both still count, because an `instance_eval` without a receiver, or a
|
|
113
|
+
# call to that `def` inside the body, does write this one.
|
|
114
|
+
# @param non_locals — the per-element fold's superset of `ivars`: the class variables and globals the body
|
|
115
|
+
# rebinds count too, on the same terms, and so does an attribute setter called on `self` (`self.w = v`)
|
|
116
|
+
# as a rebind of the instance variable it is named after, `@w`: the `attr_writer` / `attr_accessor`
|
|
117
|
+
# convention stores there, and no write node shows it. A hand-written setter storing elsewhere is not
|
|
118
|
+
# seen.
|
|
119
|
+
# @return the captured names the body writes, each once, in first-write order.
|
|
120
|
+
def writes(block_node, base_scope, ivars: false, non_locals: false)
|
|
37
121
|
body = block_node.body
|
|
38
|
-
return
|
|
122
|
+
return NO_NAMES if body.nil?
|
|
123
|
+
|
|
124
|
+
introduced = nil
|
|
125
|
+
names = nil
|
|
126
|
+
outliving = non_locals || ivars
|
|
127
|
+
Source::NodeWalker.each_with_ancestors(body) do |descendant, ancestors|
|
|
128
|
+
name =
|
|
129
|
+
if LOCAL_WRITE_NODES.include?(descendant.class)
|
|
130
|
+
captured_local_write(descendant, ancestors, base_scope) { introduced ||= introduced_locals(block_node) }
|
|
131
|
+
elsif outliving
|
|
132
|
+
rebindable_outliving_write(descendant, base_scope, non_locals)
|
|
133
|
+
end
|
|
134
|
+
(names ||= []) << name if name
|
|
135
|
+
end
|
|
136
|
+
names ? names.uniq : NO_NAMES
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def rebindable_outliving_write(node, base_scope, non_locals)
|
|
140
|
+
name = outliving_write_name(node, non_locals)
|
|
141
|
+
name if name && rebindable_non_local?(base_scope, name)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# The outer local a local-write node rebinds, or nil when the call site does not bind it, the write resolves
|
|
145
|
+
# inside a nested block or `def` ({.outer_local?}), or the block introduces it (the block yields the
|
|
146
|
+
# introduced set, computed only when needed).
|
|
147
|
+
def captured_local_write(node, ancestors, base_scope)
|
|
148
|
+
name = node.name
|
|
149
|
+
return nil unless base_scope.locals.key?(name) && outer_local?(node, ancestors)
|
|
150
|
+
|
|
151
|
+
yield.include?(name) ? nil : name
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The non-local `node` rebinds under `non_locals:` — or, without it, the instance variable only.
|
|
155
|
+
def outliving_write_name(node, non_locals)
|
|
156
|
+
name = non_local_write_name(node)
|
|
157
|
+
non_locals || (name && ivar_name?(name) && NON_LOCAL_WRITE_NODES.include?(node.class)) ? name : nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The instance, class or global variable `node` rebinds, or nil when it rebinds none of them.
|
|
161
|
+
def non_local_write_name(node)
|
|
162
|
+
return node.name if NON_LOCAL_WRITE_NODES.include?(node.class)
|
|
163
|
+
|
|
164
|
+
setter_ivar_name(node)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# `@w` for an attribute setter called on `self` (`self.w = …` and its compound and target forms), else nil.
|
|
168
|
+
def setter_ivar_name(node)
|
|
169
|
+
return nil unless SETTER_CALL_NODES.include?(node.class) && node.receiver.is_a?(Prism::SelfNode)
|
|
170
|
+
|
|
171
|
+
setter = node.respond_to?(:write_name) ? node.write_name : node.name
|
|
172
|
+
return nil unless SETTER_NAME.match?(setter)
|
|
173
|
+
|
|
174
|
+
:"@#{setter.to_s.delete_suffix('=')}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def rebindable_non_local?(scope, name)
|
|
178
|
+
variable_kind(name) == :ivar ? rebindable_ivar?(scope, name) : !bound_type(scope, name).nil?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def rebindable_ivar?(scope, name)
|
|
182
|
+
!scope.ivar(name).nil? && !scope.declaration_sourced?(:ivar, name)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# The binding `scope` holds for a name from {.writes}.
|
|
186
|
+
def bound_type(scope, name)
|
|
187
|
+
case variable_kind(name)
|
|
188
|
+
when :ivar then scope.ivar(name)
|
|
189
|
+
when :cvar then scope.cvar(name)
|
|
190
|
+
when :global then scope.global(name)
|
|
191
|
+
else scope.local(name)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# `scope` with a name from {.writes} bound to `type`, keeping the name's optimistic nil-freeness mark
|
|
196
|
+
# (issue #286). `Scope#with_local` / `#with_ivar` drop it as a fresh write should, but here `type`
|
|
197
|
+
# stands for the binding across iterations, and a value that was nil-free only optimistically still is:
|
|
198
|
+
# without the mark `x.nil?` folds to `false` where the runtime answers `true`. `optimistic` is a mark the
|
|
199
|
+
# binding carries besides the one `scope` already holds — one an iteration's own rebind made. Class
|
|
200
|
+
# variables and globals carry no mark. {OptimisticOrigin.carry_local_mark} owns the re-marking, and with
|
|
201
|
+
# it the miss answer the mark records (issue #1302).
|
|
202
|
+
def bind(scope, name, type, optimistic: nil)
|
|
203
|
+
case variable_kind(name)
|
|
204
|
+
when :ivar then OptimisticOrigin.carry_ivar_mark(scope, scope.with_ivar(name, type), name, optimistic)
|
|
205
|
+
when :cvar then scope.with_cvar(name, type)
|
|
206
|
+
when :global then scope.with_global(name, type)
|
|
207
|
+
else OptimisticOrigin.carry_local_mark(scope, scope.with_local(name, type), name, optimistic)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# The optimistic nil-freeness mark `scope` holds for a name from {.writes}, or nil.
|
|
212
|
+
def optimistic_mark(scope, name)
|
|
213
|
+
case variable_kind(name)
|
|
214
|
+
when :ivar then scope.optimistic_ivar(name)
|
|
215
|
+
when :local then scope.optimistic_local(name)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def ivar_name?(name) = variable_kind(name) == :ivar
|
|
220
|
+
|
|
221
|
+
# Ruby spells a global with a leading `$`, a class variable with `@@`, an instance variable with a single
|
|
222
|
+
# `@`, and a local with none of them.
|
|
223
|
+
def variable_kind(name)
|
|
224
|
+
if name.start_with?("$") then :global
|
|
225
|
+
elsif name.start_with?("@@") then :cvar
|
|
226
|
+
elsif name.start_with?("@") then :ivar
|
|
227
|
+
else :local
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# The nodes that change a receiver's CONTENT without rebinding it: a call to a name the straight-line
|
|
232
|
+
# widening responds to ({MutationWidening::SHAPE_MUTATORS}), and the index writes that store through
|
|
233
|
+
# `[]=` without being a `[]=` call ({IndexWriteWidening::CONTENT_WRITE_NODE_CLASSES}, the one list the
|
|
234
|
+
# block-return threading gate and the captured-local write-back read too).
|
|
235
|
+
INDEX_STORE_NODES = IndexWriteWidening::CONTENT_WRITE_NODE_CLASSES
|
|
236
|
+
private_constant :INDEX_STORE_NODES
|
|
237
|
+
|
|
238
|
+
# The captured outer locals the body mutates in place, each mapped to its mutation sites (the nodes
|
|
239
|
+
# above) in source order. A site counts through every variable its receiver can evaluate to
|
|
240
|
+
# ({ReceiverAlias.candidates}), at any nesting depth as long as a local's read does not resolve inside a nested
|
|
241
|
+
# block ({.outer_local?}), and a local is excluded on exactly the terms {.writes} excludes it.
|
|
242
|
+
#
|
|
243
|
+
# Under `non_locals: true` the instance variables, class variables and globals the body mutates in place
|
|
244
|
+
# count too, on the terms {.writes} takes a rebound one ({.rebindable_non_local?}). Since the block-return
|
|
245
|
+
# threading gate threads an index write, `@cache[:first] ||= e; @cache[:first] == 2` would otherwise type
|
|
246
|
+
# every position from the empty entry hash, store THAT position's `e`, and fold `find` to `2` where Ruby,
|
|
247
|
+
# keeping the first iteration's `1`, answers `nil`; `$seen << x; n == 1` after `n = $seen.size` folded
|
|
248
|
+
# `find` to `nil` the same way. A class variable or global counts only as the receiver itself (parentheses
|
|
249
|
+
# aside), not through a branch that selects it.
|
|
250
|
+
#
|
|
251
|
+
# A local also counts through the two routes straight-line code widens it by without reading it as the
|
|
252
|
+
# receiver. A mutator on an element read rooted at it (`a[0] << e`, the local {ElementReadWidening}
|
|
253
|
+
# widens) is a site of that local. A self-call whose callee content-mutates the parameter a local is passed
|
|
254
|
+
# to (`add_to(a, e)`, the local `StatementEvaluator#content_mutated_arguments` reports) is a
|
|
255
|
+
# {UnknownStoreWidening::CalleeStore} site of it. Both were invisible here, so every position of the fold
|
|
256
|
+
# read the local at its entry contents: `a = [[1]]; [1, 2].map { |e| v = a[0].size; a[0] << e; v }`
|
|
257
|
+
# folded to `[1, 1]` where Ruby answers `[1, 2]`.
|
|
258
|
+
#
|
|
259
|
+
# @param base_scope — the call-site scope the block closes over.
|
|
260
|
+
# @param non_locals — also collect the instance variables, class variables and globals the body mutates
|
|
261
|
+
# in place.
|
|
262
|
+
# @return `{ name => [site, ...] }`, empty for the overwhelmingly common body that mutates nothing
|
|
263
|
+
# captured.
|
|
264
|
+
def content_mutations(block_node, base_scope, non_locals: false)
|
|
265
|
+
body = block_node.body
|
|
266
|
+
return NO_SITES if body.nil?
|
|
267
|
+
|
|
268
|
+
introduced = nil
|
|
269
|
+
body_mutation_sites(body, base_scope, non_locals) { introduced ||= introduced_locals(block_node) }
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Issue #1412 — false when `node` certainly holds nothing {.writes} (with `ivars: true`) or
|
|
273
|
+
# {.content_mutations} could collect against `base_scope`: no write to a local it binds, no instance, class or
|
|
274
|
+
# global variable write, no mutator or index store, and no self-call passing a local. It over-approximates
|
|
275
|
+
# (a write a block parameter shadows still answers true) and allocates nothing, so a caller can skip both
|
|
276
|
+
# scans, and whatever it would compute only to use their answers, for the common body that touches no capture.
|
|
277
|
+
def may_touch_capture?(node, base_scope)
|
|
278
|
+
return true if capture_touch?(node, base_scope)
|
|
279
|
+
|
|
280
|
+
node.rigor_each_child { |child| return true if may_touch_capture?(child, base_scope) }
|
|
281
|
+
false
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def capture_touch?(node, base_scope)
|
|
285
|
+
klass = node.class
|
|
286
|
+
return base_scope.locals.key?(node.name) if LOCAL_WRITE_NODES.include?(klass)
|
|
287
|
+
return true if NON_LOCAL_WRITE_NODES.include?(klass) || INDEX_STORE_NODES.include?(klass)
|
|
288
|
+
return false unless klass == Prism::CallNode
|
|
289
|
+
|
|
290
|
+
receiver = node.receiver
|
|
291
|
+
return true if receiver && MutationWidening::SHAPE_MUTATORS.include?(node.name)
|
|
292
|
+
|
|
293
|
+
callee_call?(node)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Issue #1412 — {.content_mutations} for a `while` / `until` / `for` body: the locals bound in `base_scope`
|
|
297
|
+
# (the scope the loop body first enters from) that the body mutates in place. A loop body opens no scope and
|
|
298
|
+
# introduces no name, so every such local counts on the same depth terms a block's capture does — a read
|
|
299
|
+
# that resolves inside a nested block or lambda, or inside a nested `def` or class body, is not the loop's.
|
|
300
|
+
#
|
|
301
|
+
# @return `{ name => [site, ...] }`, empty for the common body that mutates nothing.
|
|
302
|
+
def loop_content_mutations(statements, base_scope)
|
|
303
|
+
return NO_SITES if statements.nil?
|
|
304
|
+
|
|
305
|
+
body_mutation_sites(statements, base_scope, false) { NO_INTRODUCED }
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
NO_INTRODUCED = Set.new.freeze
|
|
309
|
+
private_constant :NO_INTRODUCED
|
|
310
|
+
|
|
311
|
+
# The mutation sites under `body` whose reads name a variable {.captured_target?} accepts, keyed by name.
|
|
312
|
+
# The block yields the names the enclosing construct introduces, asked only when a local site is found.
|
|
313
|
+
def body_mutation_sites(body, base_scope, non_locals, &)
|
|
314
|
+
sites = nil
|
|
315
|
+
evaluator = nil
|
|
316
|
+
Source::NodeWalker.each_with_ancestors(body) do |descendant, ancestors|
|
|
317
|
+
# A callee is resolved in the call-site scope, as the straight-line callee floor resolves it.
|
|
318
|
+
site = mutation_site(descendant) { evaluator ||= StatementEvaluator.new(scope: base_scope) }
|
|
319
|
+
next if site.nil?
|
|
320
|
+
|
|
321
|
+
site_reads(site).each do |read|
|
|
322
|
+
next unless captured_target?(read, ancestors, base_scope, non_locals, &)
|
|
323
|
+
|
|
324
|
+
((sites ||= {})[read.name] ||= []) << site
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
sites || NO_SITES
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# The mutation site `node` is — the node itself, or a {UnknownStoreWidening::CalleeStore} for a self-call
|
|
331
|
+
# whose callee content-mutates a local it is passed — or nil. A self-call is resolved by the
|
|
332
|
+
# `StatementEvaluator` the block yields, which the caller builds only when one is needed. A mutator name
|
|
333
|
+
# called on `self` (`self.store(h, k)`, `self.push(a, x)`) names no variable as its receiver, so it is asked
|
|
334
|
+
# as a self-call, as the straight-line callee floor asks it.
|
|
335
|
+
def mutation_site(node)
|
|
336
|
+
receiver = mutated_receiver(node)
|
|
337
|
+
return node if receiver && !receiver.is_a?(Prism::SelfNode)
|
|
338
|
+
return nil unless callee_call?(node)
|
|
339
|
+
|
|
340
|
+
arguments = yield.content_mutated_arguments(node)
|
|
341
|
+
arguments.empty? ? nil : UnknownStoreWidening::CalleeStore.new(node, arguments)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# The variable reads a mutation site reaches: a callee store's arguments, the local an element read is
|
|
345
|
+
# rooted at, or else {ReceiverAlias.mutated_reads} of the receiver.
|
|
346
|
+
def site_reads(site)
|
|
347
|
+
return site.arguments if site.is_a?(UnknownStoreWidening::CalleeStore)
|
|
348
|
+
|
|
349
|
+
receiver = mutated_receiver(site)
|
|
350
|
+
path = ElementReadWidening.element_read_path(receiver)
|
|
351
|
+
path ? [path.first] : ReceiverAlias.mutated_reads(receiver)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# A call to a method on `self` (implicit or explicit) that passes a local as an argument — the only kind
|
|
355
|
+
# the callee floor can report a site for, so the only kind worth resolving.
|
|
356
|
+
def callee_call?(node)
|
|
357
|
+
return false unless node.is_a?(Prism::CallNode)
|
|
358
|
+
return false unless node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)
|
|
359
|
+
|
|
360
|
+
arguments = node.arguments&.arguments
|
|
361
|
+
!arguments.nil? && arguments.any?(Prism::LocalVariableReadNode)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# True when `read` names a variable {.content_mutations} collects: a {.content_target?} which, when it is a
|
|
365
|
+
# local, does not resolve inside a nested block and is not one the block introduces (the block yields that set,
|
|
366
|
+
# computed only when needed). An `it` read is never one: it is the parameter of the innermost block around
|
|
367
|
+
# it, so the body's own `it` is introduced and a nested block's `it` is that block's.
|
|
368
|
+
def captured_target?(read, ancestors, base_scope, non_locals)
|
|
369
|
+
return false if read.is_a?(Prism::ItLocalVariableReadNode)
|
|
370
|
+
return false unless content_target?(read, base_scope, non_locals)
|
|
371
|
+
return true unless read.is_a?(Prism::LocalVariableReadNode)
|
|
372
|
+
|
|
373
|
+
outer_local?(read, ancestors) && !yield.include?(read.name)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# A local bound at the call site (the block's own names are excluded by the caller), or — under
|
|
377
|
+
# `non_locals:` — an instance variable, class variable or global {.rebindable_non_local?} accepts.
|
|
378
|
+
def content_target?(read, base_scope, non_locals)
|
|
379
|
+
return base_scope.locals.key?(read.name) if read.is_a?(Prism::LocalVariableReadNode)
|
|
380
|
+
|
|
381
|
+
non_locals && rebindable_non_local?(base_scope, read.name)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# False when a local read or write resolves inside a scope nested between the body and `node`. Prism resolves a
|
|
385
|
+
# name in a nested block's own scope first, so `[[9]].each { |a| a << x }` mutates, and `[[9]].each { |a| a =
|
|
386
|
+
# x }` rebinds, that block's parameter, not the outer local that happens to share its name: its `depth` stops
|
|
387
|
+
# short of the body's own scope. A `def`, `class`, `module` or `class << self` body opens a scope of its own
|
|
388
|
+
# that sees no outer local at all, so `def helper; z = 5; end` writes the method's `z` — but only its body,
|
|
389
|
+
# and a `def`'s parameters, are that scope: a `def (o = x).m` receiver, a `class Foo < (s = x)` superclass or
|
|
390
|
+
# a `class << (t = x)` target runs in the enclosing one and is left to the depth test. {.writes} and
|
|
391
|
+
# {.content_mutations} both ask it, so the two sets cannot disagree about which `a` a site names. A name
|
|
392
|
+
# resolving in the body's own scope is left to the block-introduced exclusion: Prism puts an outer local there
|
|
393
|
+
# only when the parse never saw it declared, which a synthetic call-site scope can still bind.
|
|
394
|
+
def outer_local?(node, ancestors)
|
|
395
|
+
nesting = 0
|
|
396
|
+
ancestors.each_with_index do |ancestor, index|
|
|
397
|
+
return false if hard_scope_entered?(ancestor, ancestors[index + 1] || node)
|
|
398
|
+
|
|
399
|
+
nesting += 1 if NESTED_SCOPE_NODES.include?(ancestor.class)
|
|
400
|
+
end
|
|
401
|
+
node.depth >= nesting
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# True when `child`, the next node on the path, is inside the scope `ancestor` opens.
|
|
405
|
+
def hard_scope_entered?(ancestor, child)
|
|
406
|
+
case ancestor
|
|
407
|
+
when Prism::DefNode then child.equal?(ancestor.body) || child.equal?(ancestor.parameters)
|
|
408
|
+
when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode then child.equal?(ancestor.body)
|
|
409
|
+
else false
|
|
410
|
+
end
|
|
411
|
+
end
|
|
39
412
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Source::NodeWalker.each(body) do |descendant|
|
|
43
|
-
next unless LOCAL_WRITE_NODES.any? { |klass| descendant.is_a?(klass) }
|
|
44
|
-
next if introduced.include?(descendant.name)
|
|
45
|
-
next unless base_scope.locals.key?(descendant.name)
|
|
413
|
+
NESTED_SCOPE_NODES = Set[Prism::BlockNode, Prism::LambdaNode].freeze
|
|
414
|
+
private_constant :NESTED_SCOPE_NODES
|
|
46
415
|
|
|
47
|
-
|
|
416
|
+
def mutated_receiver(node)
|
|
417
|
+
case node
|
|
418
|
+
when Prism::CallNode then node.receiver if MutationWidening::SHAPE_MUTATORS.include?(node.name)
|
|
419
|
+
when *INDEX_STORE_NODES then node.receiver
|
|
48
420
|
end
|
|
49
|
-
outer_writes.uniq
|
|
50
421
|
end
|
|
51
422
|
|
|
52
423
|
# Names the block itself introduces: parameters (numbered parameters included, via
|