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
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../type"
|
|
4
|
+
require_relative "external_ancestor_resolution"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
# Issue #1234 — does the PROJECT define the method a receiver answers a name with? The captured-binding
|
|
9
|
+
# pass reads a catalogued iterator name on an unclassified receiver as repetition
|
|
10
|
+
# ({ClosureEscapeAnalyzer.repeats_by_name?}), and that reading is wrong for a method the project wrote
|
|
11
|
+
# under the same name: `class Vault; def select(key) = yield(key.to_s); end` runs its block once. The
|
|
12
|
+
# ancestry step of {ClosureEscapeAnalyzer.classify} asks the same question, of the same call, to rule the
|
|
13
|
+
# project out as the method's owner.
|
|
14
|
+
#
|
|
15
|
+
# "Defines" is a `def`, `define_method` or `attr_*` anywhere in a project class's ancestry, on the
|
|
16
|
+
# instance side or on the singleton side for a class-object receiver, or a signature whose declaring
|
|
17
|
+
# owner is a project class or module.
|
|
18
|
+
module ProjectMethodOwnership
|
|
19
|
+
NO_TARGETS = [].freeze
|
|
20
|
+
private_constant :NO_TARGETS
|
|
21
|
+
|
|
22
|
+
MEMO_KEY = :__rigor_project_method_ownership_memo__
|
|
23
|
+
private_constant :MEMO_KEY
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# The `[class_name, kind]` pairs the receiver's method is looked up on — `[]` for a receiver Rigor
|
|
28
|
+
# cannot see at all — or nil when the carrier names a class this cannot resolve, or is one it does not
|
|
29
|
+
# recognise, so a caller that cannot tell whether the project owns the method can decline. The audit,
|
|
30
|
+
# carrier by carrier:
|
|
31
|
+
#
|
|
32
|
+
# - `Dynamic`, `Top` — unseen: `[]`.
|
|
33
|
+
# - `Nominal`, `StructInstance`, `DataInstance` — their `class_name`, instance side.
|
|
34
|
+
# - `Singleton`, `StructClass`, `DataClass` — their `class_name`, singleton side.
|
|
35
|
+
# - `Tuple` → `Array`, `HashShape` → `Hash`, `IntegerRange` → `Integer`, `FloatRange` → `Float`,
|
|
36
|
+
# `Constant` → its value's class, `BoundMethod` → `Method`: instance side of that core class, which a
|
|
37
|
+
# project reopening may still define the method on.
|
|
38
|
+
# - `Union`, `Intersection` — every member's targets; a member it cannot resolve resolves the whole.
|
|
39
|
+
# - `Refined`, `Difference` — their `base`. `App` — its erasure `bound`.
|
|
40
|
+
# - A struct / data carrier with no `class_name` (an anonymous `Struct.new` value), `Bot`, `Maybe`,
|
|
41
|
+
# `Result`, and any carrier added later — nil.
|
|
42
|
+
def targets(receiver_type)
|
|
43
|
+
case receiver_type
|
|
44
|
+
when Type::Dynamic, Type::Top then NO_TARGETS
|
|
45
|
+
when Type::Nominal, Type::StructInstance, Type::DataInstance
|
|
46
|
+
class_target(receiver_type.class_name, :instance)
|
|
47
|
+
when Type::Singleton, Type::StructClass, Type::DataClass
|
|
48
|
+
class_target(receiver_type.class_name, :singleton)
|
|
49
|
+
when Type::Union, Type::Intersection then member_targets(receiver_type.members)
|
|
50
|
+
when Type::Refined, Type::Difference then targets(receiver_type.base)
|
|
51
|
+
when Type::App then targets(receiver_type.bound)
|
|
52
|
+
else core_targets(receiver_type)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Whether the project's source, or a signature whose declaring owner is a project class or module,
|
|
57
|
+
# defines `method_sym` on `class_name` or an ancestor the project declares. Memoised ({.memo}): the
|
|
58
|
+
# ancestry step and the name reading ask it of the same call, and every block call on the class asks
|
|
59
|
+
# it again.
|
|
60
|
+
def defines?(class_name, method_sym, kind, scope)
|
|
61
|
+
return false if scope.nil?
|
|
62
|
+
|
|
63
|
+
by_method = (memo(scope)[kind][class_name] ||= {})
|
|
64
|
+
return by_method[method_sym] if by_method.key?(method_sym)
|
|
65
|
+
|
|
66
|
+
by_method[method_sym] = compute_defines?(class_name, method_sym, kind, scope)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# One slot per thread, keyed on the identity of the scope's frozen discovery index and its environment —
|
|
70
|
+
# the only inputs {.defines?} and the ancestry step read — and replaced, not accumulated, when either
|
|
71
|
+
# changes: the shape of `ExpressionTyper#class_graph_buckets`, whose rationale applies unchanged. A
|
|
72
|
+
# `Scope` merges each file's discovery with the project pre-pass, so the slot turns over per file, and
|
|
73
|
+
# an ADR-46 dependency the first lookup recorded belongs to the file every later hit serves. Pool
|
|
74
|
+
# workers are separate processes, and a Ractor has its own `Thread.current`. The `:ancestry` bucket
|
|
75
|
+
# belongs to {ClosureEscapeAnalyzer}'s ancestry step.
|
|
76
|
+
def memo(scope)
|
|
77
|
+
discovery = scope.discovery
|
|
78
|
+
environment = scope.environment
|
|
79
|
+
slot = Thread.current[MEMO_KEY]
|
|
80
|
+
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment)
|
|
81
|
+
slot = [discovery, environment, { instance: {}, singleton: {}, ancestry: {} }]
|
|
82
|
+
Thread.current[MEMO_KEY] = slot
|
|
83
|
+
end
|
|
84
|
+
slot[2]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class << self
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def class_target(class_name, kind)
|
|
91
|
+
class_name.nil? ? nil : [[class_name.to_s, kind]]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def member_targets(members)
|
|
95
|
+
members.each_with_object([]) do |member, out|
|
|
96
|
+
resolved = targets(member)
|
|
97
|
+
return nil if resolved.nil?
|
|
98
|
+
|
|
99
|
+
out.concat(resolved)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def core_targets(receiver_type)
|
|
104
|
+
case receiver_type
|
|
105
|
+
when Type::Tuple then class_target("Array", :instance)
|
|
106
|
+
when Type::HashShape then class_target("Hash", :instance)
|
|
107
|
+
when Type::IntegerRange then class_target("Integer", :instance)
|
|
108
|
+
when Type::FloatRange then class_target("Float", :instance)
|
|
109
|
+
when Type::Constant then class_target(receiver_type.value.class.name, :instance)
|
|
110
|
+
when Type::BoundMethod then class_target("Method", :instance)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def compute_defines?(class_name, method_sym, kind, scope)
|
|
115
|
+
return true if source_defines?(class_name, method_sym, kind, scope)
|
|
116
|
+
|
|
117
|
+
# `ExternalAncestorResolution.method_definition` owns the rescue for a malformed signature.
|
|
118
|
+
definition = ExternalAncestorResolution.method_definition(class_name, method_sym, kind, scope: scope)
|
|
119
|
+
owner = definition.respond_to?(:defined_in) ? definition.defined_in : nil
|
|
120
|
+
!owner.nil? && scope.known_user_class?(owner.to_s.delete_prefix("::"))
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def source_defines?(class_name, method_sym, kind, scope)
|
|
124
|
+
return true if scope.discovered_method_through_ancestors?(class_name, method_sym, kind)
|
|
125
|
+
|
|
126
|
+
found, = if kind == :singleton
|
|
127
|
+
scope.singleton_def_through_ancestors(class_name, method_sym)
|
|
128
|
+
else
|
|
129
|
+
scope.user_def_through_ancestors(class_name, method_sym)
|
|
130
|
+
end
|
|
131
|
+
!found.nil?
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "global_write_census"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
module Inference
|
|
5
7
|
# ADR-17 § "Inference contract" — project-wide patched-method registry populated by the pre-eval
|
|
@@ -26,20 +28,23 @@ module Rigor
|
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
attr_reader :by_key
|
|
31
|
+
attr_reader :by_key, :write_census
|
|
30
32
|
|
|
31
|
-
# @param entries
|
|
33
|
+
# @param entries — flat list of declarations observed during the pre-pass.
|
|
32
34
|
# First-write-wins on `(class_name, method_name, kind)` duplicates so the
|
|
33
35
|
# `pre-eval.duplicate-declaration` diagnostic emission stays decoupled from registry behaviour.
|
|
34
|
-
|
|
36
|
+
# @param write_census — issue #1367: the `pre_eval:` files' {GlobalWriteCensus}, which the `global.*` write
|
|
37
|
+
# rules join with the project's, since a patch file is loaded ahead of the code it patches.
|
|
38
|
+
def initialize(entries: [], write_census: GlobalWriteCensus::EMPTY)
|
|
35
39
|
@by_key = entries.each_with_object({}) do |entry, acc|
|
|
36
40
|
key = [entry.class_name, entry.method_name, entry.kind]
|
|
37
41
|
acc[key] ||= entry
|
|
38
42
|
end.freeze
|
|
43
|
+
@write_census = write_census.frozen? ? write_census : write_census.dup.freeze
|
|
39
44
|
freeze
|
|
40
45
|
end
|
|
41
46
|
|
|
42
|
-
# @return
|
|
47
|
+
# @return the recorded entry for the given `(class_name, method_name, kind)` triple,
|
|
43
48
|
# or `nil` when no pre-eval file declared it.
|
|
44
49
|
def lookup(class_name:, method_name:, kind:)
|
|
45
50
|
@by_key[[class_name, method_name, kind]]
|
|
@@ -31,20 +31,21 @@ module Rigor
|
|
|
31
31
|
|
|
32
32
|
module_function
|
|
33
33
|
|
|
34
|
-
# @param paths
|
|
34
|
+
# @param paths — absolute paths to the pre-eval files. The runner has already
|
|
35
35
|
# validated that each path exists (slice-1 `pre-eval.file-not-found` `:error` covers missing
|
|
36
36
|
# entries); the scanner does NOT re-check existence.
|
|
37
|
-
# @param buffer
|
|
37
|
+
# @param buffer — editor-mode buffer binding. When set, the
|
|
38
38
|
# scanner reads the buffer's physical bytes if a pre-eval entry matches the logical path, so
|
|
39
39
|
# users editing a monkey-patch file see the in-flight version in their analysis.
|
|
40
|
-
# @return
|
|
40
|
+
# @return the populated registry plus any per-file warnings.
|
|
41
41
|
def scan(paths, buffer: nil)
|
|
42
42
|
entries = []
|
|
43
43
|
diagnostics = []
|
|
44
|
-
|
|
44
|
+
census = Set.new
|
|
45
|
+
paths.each { |path| scan_file(path, entries, diagnostics, buffer, census) }
|
|
45
46
|
diagnostics.concat(duplicate_declaration_diagnostics(entries))
|
|
46
47
|
Result.new(
|
|
47
|
-
registry: ProjectPatchedMethods.new(entries: entries),
|
|
48
|
+
registry: ProjectPatchedMethods.new(entries: entries, write_census: census.freeze),
|
|
48
49
|
diagnostics: diagnostics
|
|
49
50
|
)
|
|
50
51
|
end
|
|
@@ -77,7 +78,7 @@ module Rigor
|
|
|
77
78
|
end
|
|
78
79
|
private_class_method :duplicate_declaration_diagnostics
|
|
79
80
|
|
|
80
|
-
def scan_file(path, entries, diagnostics, buffer = nil)
|
|
81
|
+
def scan_file(path, entries, diagnostics, buffer = nil, census = Set.new)
|
|
81
82
|
physical = buffer ? buffer.resolve(path) : path
|
|
82
83
|
parse_result =
|
|
83
84
|
if physical == path
|
|
@@ -91,6 +92,9 @@ module Rigor
|
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
walk_node(parse_result.value, [], false, path, entries)
|
|
95
|
+
# Issue #1367 — the file's `global.*` write facts: a patch file may alias a special or give a class the
|
|
96
|
+
# method a setter asks for, as any project file may.
|
|
97
|
+
census.merge(GlobalWriteCensus.scan(parse_result.value))
|
|
94
98
|
rescue StandardError => e
|
|
95
99
|
diagnostics << build_diagnostic(
|
|
96
100
|
path: path, line: 1, column: 1,
|
|
@@ -37,8 +37,7 @@ module Rigor
|
|
|
37
37
|
@scope = scope || Scope.empty
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# @param root
|
|
41
|
-
# @return [FileResult]
|
|
40
|
+
# @param root — the parsed AST
|
|
42
41
|
def scan(root)
|
|
43
42
|
index = ScopeIndexer.index(root, default_scope: @scope)
|
|
44
43
|
protected_count = 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
# Reads the endpoints out of a `Constant<Range>`.
|
|
8
|
+
#
|
|
9
|
+
# A `Constant` normally carries a value and no generic instantiation, which is why acceptance
|
|
10
|
+
# ignores a nominal's `type_args` against one. A Range value is the exception: the engine only
|
|
11
|
+
# builds `Constant<Range>` when both endpoints are static (`ExpressionTyper#type_of_range`), so
|
|
12
|
+
# the element the range would yield IS known, and a `Range[T]` parameter can be judged against it.
|
|
13
|
+
#
|
|
14
|
+
# Two consumers share the reading so they cannot drift: `Acceptance` refutes a `Range[T]`
|
|
15
|
+
# parameter whose `T` rejects an endpoint (issue #833), and `RbsDispatch` binds a method-level
|
|
16
|
+
# `Range[A]` parameter's `A` from the endpoints (issue #834).
|
|
17
|
+
module RangeConstant
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# The non-`nil` endpoints of the range `type` pins, or nil when there is nothing to read from.
|
|
21
|
+
#
|
|
22
|
+
# nil — meaning "no opinion, keep the answer you already had" — covers a carrier that is not a
|
|
23
|
+
# `Constant<Range>`, a range with no endpoint at all (`nil..nil` constrains nothing), and a
|
|
24
|
+
# range whose endpoints are not literals. The literal set is the one
|
|
25
|
+
# `ExpressionTyper#static_range_endpoint` pins; a value outside it reached the carrier by some
|
|
26
|
+
# other route and the caller has no lifting for it.
|
|
27
|
+
def literal_endpoints(type)
|
|
28
|
+
return nil unless type.is_a?(Type::Constant)
|
|
29
|
+
|
|
30
|
+
value = type.value
|
|
31
|
+
return nil unless value.is_a?(::Range)
|
|
32
|
+
|
|
33
|
+
endpoints = [value.begin, value.end].compact
|
|
34
|
+
return nil if endpoints.empty?
|
|
35
|
+
return nil unless endpoints.all? { |endpoint| literal_endpoint?(endpoint) }
|
|
36
|
+
|
|
37
|
+
endpoints
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The endpoints lifted to their own classes and unioned — the element type the range would
|
|
41
|
+
# yield. Lifting rather than pinning the values mirrors
|
|
42
|
+
# `ExpressionTyper#nominal_range_for_endpoints`: `1..9` yields Integers, not the two values 1
|
|
43
|
+
# and 9, so binding a `Range[A]` parameter's `A` to `Integer` states what the runtime can
|
|
44
|
+
# honour. Nil when {#literal_endpoints} has nothing to read.
|
|
45
|
+
def element_type(type)
|
|
46
|
+
endpoints = literal_endpoints(type)
|
|
47
|
+
return nil if endpoints.nil?
|
|
48
|
+
|
|
49
|
+
Type::Combinator.union(*endpoints.map { |endpoint| Type::Combinator.nominal_of(endpoint.class.name) })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def literal_endpoint?(value)
|
|
53
|
+
value.is_a?(::Integer) || value.is_a?(::Float) || value.is_a?(::String)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -100,17 +100,31 @@ module Rigor
|
|
|
100
100
|
ALIAS_EXPANSION_LIMIT = 8
|
|
101
101
|
private_constant :ALIAS_EXPANSION_LIMIT
|
|
102
102
|
|
|
103
|
+
# #775 — `TypeName#relative!` allocates a TypeName and a Namespace and `#to_s` joins the path: four
|
|
104
|
+
# objects per class-instance translation, ~380k of them on the lib self-check, for the same few
|
|
105
|
+
# hundred names. rbs compares a TypeName by namespace + name, so a value-keyed memo answers every
|
|
106
|
+
# repeat with one frozen String.
|
|
107
|
+
@relative_names = {}
|
|
108
|
+
|
|
109
|
+
# #775 — the translated form of a CLOSED alias expansion, keyed by the expansion. Closed means no
|
|
110
|
+
# `self`, `instance` or type variable anywhere inside, so the caller's context cannot change the
|
|
111
|
+
# answer; at expansion depth 0 the budget cannot either. rbs compares types by value, so the memo
|
|
112
|
+
# is per distinct expansion, not per loader (a re-parsed identical alias hits), and the weak keys
|
|
113
|
+
# let an entry go with the loader whose memo held the expansion. `Rigor::Type::t` on the self-check
|
|
114
|
+
# is a 21-member union that was re-translated ~13k times, each time re-normalising the union from
|
|
115
|
+
# scratch; every repeat now shares one Union.
|
|
116
|
+
@closed_alias_translations = ObjectSpace::WeakKeyMap.new
|
|
117
|
+
|
|
103
118
|
class << self
|
|
104
|
-
# @param
|
|
105
|
-
# @param
|
|
106
|
-
# @param instance_type [Rigor::Type, nil] substitute for `Bases::Instance`. Defaults to `nil`,
|
|
119
|
+
# @param self_type — substitute for `Bases::Self`.
|
|
120
|
+
# @param instance_type — substitute for `Bases::Instance`. Defaults to `nil`,
|
|
107
121
|
# which degrades to Dynamic[Top].
|
|
108
|
-
# @param type_vars
|
|
122
|
+
# @param type_vars — substitution map for `Bases::Variable`. Keys
|
|
109
123
|
# are the RBS variable names (e.g., `:Elem`); values are Rigor types that replace the
|
|
110
124
|
# variable. Variables that are not bound in the map degrade to Dynamic[Top].
|
|
111
|
-
# @param alias_expander
|
|
112
|
-
# in practice the environment's `RbsLoader`. When nil, aliases degrade to
|
|
113
|
-
#
|
|
125
|
+
# @param alias_expander — anything answering `expand_type_alias`, which resolves `RBS::Types::Alias`
|
|
126
|
+
# one level out — in practice the environment's `RbsLoader`. When nil, aliases degrade to
|
|
127
|
+
# Dynamic[Top].
|
|
114
128
|
def translate(rbs_type, self_type: nil, instance_type: nil, type_vars: EMPTY_TYPE_VARS,
|
|
115
129
|
alias_expander: nil)
|
|
116
130
|
translate_in(rbs_type, Context.new(self_type, instance_type, type_vars, alias_expander, 0))
|
|
@@ -180,11 +194,17 @@ module Rigor
|
|
|
180
194
|
# `Nominal["Array", [Nominal["Integer"]]]`. Variables inside the args participate in
|
|
181
195
|
# substitution through the same `type_vars:` map.
|
|
182
196
|
def translate_class_instance(rbs_type, context)
|
|
183
|
-
name = rbs_type.name
|
|
197
|
+
name = relative_name(rbs_type.name)
|
|
198
|
+
return Type::Combinator.nominal_of(name) if rbs_type.args.empty?
|
|
199
|
+
|
|
184
200
|
translated_args = rbs_type.args.map { |arg| translate_in(arg, context) }
|
|
185
201
|
Type::Combinator.nominal_of(name, type_args: translated_args)
|
|
186
202
|
end
|
|
187
203
|
|
|
204
|
+
def relative_name(type_name)
|
|
205
|
+
@relative_names[type_name] ||= type_name.relative!.to_s.freeze
|
|
206
|
+
end
|
|
207
|
+
|
|
188
208
|
# Preserves tuple precision through the boundary. Each positional element type is translated
|
|
189
209
|
# recursively under the caller's substitution context, and the resulting list is wrapped in a
|
|
190
210
|
# `Rigor::Type::Tuple`.
|
|
@@ -218,8 +238,7 @@ module Rigor
|
|
|
218
238
|
# `singleton(Foo)` is the type of the constant `Foo` itself (the class object). With the
|
|
219
239
|
# dedicated Singleton type, we map directly to `Singleton[Foo]`.
|
|
220
240
|
def translate_class_singleton(rbs_type, _context)
|
|
221
|
-
|
|
222
|
-
Type::Combinator.singleton_of(name)
|
|
241
|
+
Type::Combinator.singleton_of(relative_name(rbs_type.name))
|
|
223
242
|
end
|
|
224
243
|
|
|
225
244
|
# #529 — sees through a type alias instead of reading `untyped`. `expand_type_alias` resolves
|
|
@@ -232,8 +251,27 @@ module Rigor
|
|
|
232
251
|
|
|
233
252
|
expanded = expander.expand_type_alias(rbs_type)
|
|
234
253
|
return Type::Combinator.untyped if expanded.nil?
|
|
254
|
+
# A nested expansion is translated under its parent's budget, and `rigor trace` records every
|
|
255
|
+
# union merge as it happens, so neither consults the memo.
|
|
256
|
+
return translate_in(expanded, context.deeper) unless context.alias_depth.zero? && !FlowTracer.active?
|
|
257
|
+
|
|
258
|
+
cached = @closed_alias_translations[expanded]
|
|
259
|
+
return cached if cached
|
|
260
|
+
|
|
261
|
+
translated = translate_in(expanded, context.deeper)
|
|
262
|
+
@closed_alias_translations[expanded] = translated if closed_type?(expanded)
|
|
263
|
+
translated
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# True when nothing in `rbs_type`'s tree reads the translation context: no `self`, no `instance`,
|
|
267
|
+
# no type variable. Every rbs type answers `each_type` (the leaves through `EmptyEachType`).
|
|
268
|
+
def closed_type?(rbs_type)
|
|
269
|
+
case rbs_type
|
|
270
|
+
when RBS::Types::Bases::Self, RBS::Types::Bases::Instance, RBS::Types::Variable then return false
|
|
271
|
+
end
|
|
235
272
|
|
|
236
|
-
|
|
273
|
+
rbs_type.each_type { |inner| return false unless closed_type?(inner) }
|
|
274
|
+
true
|
|
237
275
|
end
|
|
238
276
|
|
|
239
277
|
# #529 — `A & B` reads as its first member that carries static evidence. Every value of the
|
|
@@ -24,21 +24,53 @@ module Rigor
|
|
|
24
24
|
# object no binding can be attributed to and yields `[]`, which is what every receiver form
|
|
25
25
|
# outside the single-read case already contributed. The walk is depth-capped so a pathological
|
|
26
26
|
# nest cannot make receiver classification unbounded.
|
|
27
|
+
#
|
|
28
|
+
# The `it` parameter (Ruby 3.4) is a local like any other, but Prism reads it through
|
|
29
|
+
# `Prism::ItLocalVariableReadNode`, which carries no `name`; {.read_name} answers `:it` for it, the
|
|
30
|
+
# binding `BlockParameterBinder` installs. A consumer names a read through {.read_name}, never `#name`.
|
|
31
|
+
#
|
|
32
|
+
# A local or instance-variable write evaluates to the variable it writes, so `(buf ||= []) << x`
|
|
33
|
+
# mutates `buf` and yields a read of it ({.read_of}). Issue #1223 made that binding visible: the
|
|
34
|
+
# statement evaluator threads a write in a receiver, and without the alias `buf` kept the `[]`
|
|
35
|
+
# the `||=` stored.
|
|
27
36
|
module ReceiverAlias
|
|
28
37
|
# Deep enough for any hand-written selection; a nest beyond it degrades to "names no binding".
|
|
29
38
|
WALK_DEPTH_CAP = 6
|
|
30
39
|
|
|
40
|
+
NON_ALIASED_READS = [Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode].freeze
|
|
41
|
+
private_constant :NON_ALIASED_READS
|
|
42
|
+
|
|
43
|
+
LOCAL_WRITE_NODES = Set[
|
|
44
|
+
Prism::LocalVariableWriteNode, Prism::LocalVariableOrWriteNode,
|
|
45
|
+
Prism::LocalVariableAndWriteNode, Prism::LocalVariableOperatorWriteNode
|
|
46
|
+
].freeze
|
|
47
|
+
INSTANCE_WRITE_NODES = Set[
|
|
48
|
+
Prism::InstanceVariableWriteNode, Prism::InstanceVariableOrWriteNode,
|
|
49
|
+
Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode
|
|
50
|
+
].freeze
|
|
51
|
+
CLASS_VARIABLE_WRITE_NODES = Set[
|
|
52
|
+
Prism::ClassVariableWriteNode, Prism::ClassVariableOrWriteNode,
|
|
53
|
+
Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode
|
|
54
|
+
].freeze
|
|
55
|
+
GLOBAL_WRITE_NODES = Set[
|
|
56
|
+
Prism::GlobalVariableWriteNode, Prism::GlobalVariableOrWriteNode,
|
|
57
|
+
Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode
|
|
58
|
+
].freeze
|
|
59
|
+
private_constant :LOCAL_WRITE_NODES, :INSTANCE_WRITE_NODES, :CLASS_VARIABLE_WRITE_NODES, :GLOBAL_WRITE_NODES
|
|
60
|
+
|
|
31
61
|
module_function
|
|
32
62
|
|
|
33
|
-
# @param node
|
|
34
|
-
# @param depth
|
|
35
|
-
# @return
|
|
63
|
+
# @param node — the receiver expression.
|
|
64
|
+
# @param depth — recursion depth, internal.
|
|
65
|
+
# @return every variable
|
|
36
66
|
# read the expression can evaluate to; empty when it can evaluate to none.
|
|
37
67
|
def candidates(node, depth = 0)
|
|
38
68
|
return [] if node.nil? || depth > WALK_DEPTH_CAP
|
|
69
|
+
# Tested before the `case`: a `when *SET` arm would copy the set into an Array on every call.
|
|
70
|
+
return [read_of(node)] if variable_write?(node)
|
|
39
71
|
|
|
40
72
|
case node
|
|
41
|
-
when Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode then [node]
|
|
73
|
+
when Prism::LocalVariableReadNode, Prism::ItLocalVariableReadNode, Prism::InstanceVariableReadNode then [node]
|
|
42
74
|
when Prism::ParenthesesNode then candidates(node.body, depth + 1)
|
|
43
75
|
when Prism::StatementsNode then candidates(node.body.last, depth + 1)
|
|
44
76
|
when Prism::ElseNode then candidates(node.statements, depth + 1)
|
|
@@ -52,6 +84,63 @@ module Rigor
|
|
|
52
84
|
def branches(first, second, depth)
|
|
53
85
|
candidates(first, depth + 1) + candidates(second, depth + 1)
|
|
54
86
|
end
|
|
87
|
+
|
|
88
|
+
# The variable reads an in-place mutation of `receiver` changes: {.candidates}' locals and instance
|
|
89
|
+
# variables, or the class variable or global the receiver reads or writes directly, parenthesised or not
|
|
90
|
+
# (`(@@c ||= []) << x` mutates `@@c`, as `(buf ||= []) << x` mutates `buf`). A class variable or global counts
|
|
91
|
+
# only as the receiver itself, not through a branch that selects it. This is the
|
|
92
|
+
# one answer the straight-line widening ({MutationWidening.widen_receiver_aliases}), the block-return
|
|
93
|
+
# threading gate that predicts it (`ExpressionTyper#prefix_statement_jump_free?`) and the per-element fold's
|
|
94
|
+
# content-mutation scan ({CapturedLocals.content_mutations}) all read, so "the scan says the body changed
|
|
95
|
+
# it" and "evaluating the body changes it" cannot disagree about which kinds of variable count.
|
|
96
|
+
def mutated_reads(receiver)
|
|
97
|
+
direct = receiver
|
|
98
|
+
direct = direct.body.body.last while direct.is_a?(Prism::ParenthesesNode) && direct.body.is_a?(Prism::StatementsNode)
|
|
99
|
+
return [direct] if NON_ALIASED_READS.include?(direct.class)
|
|
100
|
+
return [read_of(direct)] if non_aliased_write?(direct)
|
|
101
|
+
|
|
102
|
+
candidates(receiver)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# True when `node` is a class-variable or global write, which {.mutated_reads} answers only as the receiver
|
|
106
|
+
# itself, as it answers a class-variable or global read.
|
|
107
|
+
def non_aliased_write?(node)
|
|
108
|
+
CLASS_VARIABLE_WRITE_NODES.include?(node.class) || GLOBAL_WRITE_NODES.include?(node.class)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# The binding a read from {.candidates} or {.mutated_reads} names.
|
|
112
|
+
def read_name(read) = read.is_a?(Prism::ItLocalVariableReadNode) ? :it : read.name
|
|
113
|
+
|
|
114
|
+
# True when `read` is a local the innermost block around it owns: one Prism resolves at `depth == 0`, or an
|
|
115
|
+
# `it` read, which is always that block's own parameter and never a capture of an enclosing scope.
|
|
116
|
+
def block_local?(read)
|
|
117
|
+
read.is_a?(Prism::ItLocalVariableReadNode) || (read.is_a?(Prism::LocalVariableReadNode) && read.depth.zero?)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# True when `node` is a local or instance-variable write, whose value is the variable it leaves.
|
|
121
|
+
def variable_write?(node)
|
|
122
|
+
LOCAL_WRITE_NODES.include?(node.class) || INSTANCE_WRITE_NODES.include?(node.class)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# A read of the variable `write` writes, at the write's name, for the analysers that key a receiver on a
|
|
126
|
+
# read node — a local, instance-variable, class-variable or global write. `Prism` 1.x constructs every node
|
|
127
|
+
# from `source, node_id, location, flags` and its fields.
|
|
128
|
+
def read_of(write)
|
|
129
|
+
source = write.send(:source)
|
|
130
|
+
if LOCAL_WRITE_NODES.include?(write.class)
|
|
131
|
+
Prism::LocalVariableReadNode.new(source, write.node_id, write.name_loc, 0, write.name, write.depth)
|
|
132
|
+
else
|
|
133
|
+
named_read_class(write.class).new(source, write.node_id, write.name_loc, 0, write.name)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# The read node class for an instance-variable, class-variable or global write class.
|
|
138
|
+
def named_read_class(write_class)
|
|
139
|
+
if INSTANCE_WRITE_NODES.include?(write_class) then Prism::InstanceVariableReadNode
|
|
140
|
+
elsif CLASS_VARIABLE_WRITE_NODES.include?(write_class) then Prism::ClassVariableReadNode
|
|
141
|
+
else Prism::GlobalVariableReadNode
|
|
142
|
+
end
|
|
143
|
+
end
|
|
55
144
|
end
|
|
56
145
|
end
|
|
57
146
|
end
|