rigortype 0.3.8 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- 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 +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- 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 +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -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 +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- 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/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- 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/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- 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 +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -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 +2 -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 +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- 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 +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- 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 +79 -11
- 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 +77 -32
- 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 +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- 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 +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -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 +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -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 +10 -6
- 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 +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -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/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- 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 +51 -4
- 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 +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- 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 +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- 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/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -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/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 +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- 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/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- 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 +4 -5
- 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 +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- 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 +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -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/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 +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- 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 +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- 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/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- 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 +31 -12
- 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 +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- 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 +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- 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 +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- 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/observation_collector.rb +17 -15
- 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 +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -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/writer.rb +23 -6
- 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 +2 -5
- 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 +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -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/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.rb +1 -0
- 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 +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -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.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -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 +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- 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 +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- 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 +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -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 +1 -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.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- 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 +0 -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 +1 -0
- 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 +1 -0
- 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 +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -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 +1 -0
- 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.rb +128 -3
- 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 +1 -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 +1 -0
- 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 +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -14,9 +14,11 @@ require_relative "body_fixpoint"
|
|
|
14
14
|
require_relative "captured_locals"
|
|
15
15
|
require_relative "dynamic_origin"
|
|
16
16
|
require_relative "../analysis/check_rules/inferred_param_guard"
|
|
17
|
+
require_relative "../analysis/check_rules/published_constant_guard"
|
|
17
18
|
require_relative "struct_fold_safety"
|
|
18
19
|
require_relative "closure_escape_analyzer"
|
|
19
20
|
require_relative "content_join"
|
|
21
|
+
require_relative "element_read_widening"
|
|
20
22
|
require_relative "indexed_narrowing"
|
|
21
23
|
require_relative "index_write_widening"
|
|
22
24
|
require_relative "method_dispatcher"
|
|
@@ -107,7 +109,9 @@ module Rigor
|
|
|
107
109
|
Prism::SingletonClassNode => :eval_singleton_class,
|
|
108
110
|
Prism::CallNode => :eval_call,
|
|
109
111
|
Prism::BlockNode => :eval_block,
|
|
112
|
+
Prism::LambdaNode => :eval_lambda,
|
|
110
113
|
Prism::ReturnNode => :eval_return,
|
|
114
|
+
Prism::NextNode => :eval_next,
|
|
111
115
|
Prism::BreakNode => :eval_break,
|
|
112
116
|
Prism::MatchWriteNode => :eval_match_write
|
|
113
117
|
}.freeze
|
|
@@ -121,6 +125,18 @@ module Rigor
|
|
|
121
125
|
RETURN_SINK_KEY = :rigor_return_sink
|
|
122
126
|
private_constant :RETURN_SINK_KEY
|
|
123
127
|
|
|
128
|
+
# Thread-local sink (an Array of `[NextNode, Type]`) collecting the value each `next` carries out of the block it
|
|
129
|
+
# leaves, so `ExpressionTyper#type_block_body` can join those arms into the block's value type. Issue #841: the
|
|
130
|
+
# block-return pass modelled only the fall-through tail, so `ops.all? { |o| next false unless o; true }` read as
|
|
131
|
+
# `Constant[true]` and folded the call to always-truthy — the same defect `RETURN_SINK_KEY` fixes one level up for
|
|
132
|
+
# a method's early `return`. The flow value of a `next` stays `Bot`.
|
|
133
|
+
#
|
|
134
|
+
# The sink also collects `next`s from nested blocks / loops / defs evaluated under the same installation (they do
|
|
135
|
+
# not install their own), so the consumer filters by node identity against a statically computed
|
|
136
|
+
# directly-targeting set, exactly as the break sink does. nil means "not collecting".
|
|
137
|
+
NEXT_SINK_KEY = :rigor_next_sink
|
|
138
|
+
private_constant :NEXT_SINK_KEY
|
|
139
|
+
|
|
124
140
|
# Thread-local sink (an Array of `[BreakNode, Scope]`) collecting the scope at each `break` reached while
|
|
125
141
|
# evaluating a loop body, so `eval_loop` / `eval_for` can join a `break`-path binding (`flag = true; break`) into
|
|
126
142
|
# the loop continuation that the fall-through would otherwise drop. Stacks like the return sink: a nested loop
|
|
@@ -131,6 +147,19 @@ module Rigor
|
|
|
131
147
|
BREAK_SINK_KEY = :rigor_break_sink
|
|
132
148
|
private_constant :BREAK_SINK_KEY
|
|
133
149
|
|
|
150
|
+
# Thread-local sink (an Array of `[BreakNode, Type]`) collecting the value each `break` carries out of the
|
|
151
|
+
# construct it leaves. Issue #853: `break value` terminates the yielding CALL and is that call's value, so —
|
|
152
|
+
# unlike `next`, which {NEXT_SINK_KEY} joins into the block — these arms are consumed one level up, by
|
|
153
|
+
# `ExpressionTyper#call_dispatch_type_for`. Without them `ops.all? { |o| break false unless o; true }` folded to
|
|
154
|
+
# `Constant[true]` and warned about a program that really can answer false.
|
|
155
|
+
#
|
|
156
|
+
# Separate from {BREAK_SINK_KEY}, which records SCOPES for the loop-continuation join: the two consumers want
|
|
157
|
+
# different things from the same node, and a loop body evaluated under a call's collection must keep feeding its
|
|
158
|
+
# own sink. Both are filtered by node identity against a statically computed directly-targeting set, so a `break`
|
|
159
|
+
# that belongs to an inner loop or block never reaches the wrong consumer. nil means "not collecting".
|
|
160
|
+
BREAK_VALUE_SINK_KEY = :rigor_break_value_sink
|
|
161
|
+
private_constant :BREAK_VALUE_SINK_KEY
|
|
162
|
+
|
|
134
163
|
# Lexical class frame: the `name:` field is the qualified class name as it would render in Ruby (e.g.,
|
|
135
164
|
# `"Foo::Bar"`); the `singleton:` field is `true` for `class << self` frames so nested defs resolve to
|
|
136
165
|
# singleton-method RBS lookups.
|
|
@@ -145,20 +174,18 @@ module Rigor
|
|
|
145
174
|
EMPTY_NESTING = [].freeze
|
|
146
175
|
private_constant :EMPTY_NESTING
|
|
147
176
|
|
|
148
|
-
# @param scope
|
|
149
|
-
# @param tracer [Rigor::Inference::FallbackTracer, nil]
|
|
150
|
-
# @param on_enter [#call, nil] optional `(node, scope) ->` callable
|
|
177
|
+
# @param on_enter — optional `(node, scope) ->` callable
|
|
151
178
|
# invoked once at the start of every {#evaluate} call (the node
|
|
152
179
|
# itself, *before* its handler runs). Threaded through every
|
|
153
180
|
# recursive `sub_eval` so the tooling that builds a per-node
|
|
154
181
|
# scope index (`Rigor::Inference::ScopeIndexer`) can record the
|
|
155
182
|
# entry scope for every Prism node the evaluator visits without
|
|
156
183
|
# the StatementEvaluator carrying any additional state itself.
|
|
157
|
-
# @param class_context
|
|
184
|
+
# @param class_context — lexical class scope used
|
|
158
185
|
# by {#eval_def} to look up the method's RBS signature. Each
|
|
159
186
|
# `ClassNode`/`ModuleNode` entry pushes a frame; `SingletonClassNode`
|
|
160
187
|
# over `self` flips the innermost frame to singleton mode.
|
|
161
|
-
# @param converged_loop_recording
|
|
188
|
+
# @param converged_loop_recording — when true (and an
|
|
162
189
|
# `on_enter` recorder is installed), {#eval_loop} re-evaluates a
|
|
163
190
|
# fixpoint-tracked loop body ONE extra time from the CONVERGED
|
|
164
191
|
# bindings so the last-visit-wins per-node scope index reflects
|
|
@@ -193,11 +220,40 @@ module Rigor
|
|
|
193
220
|
[result, sink]
|
|
194
221
|
end
|
|
195
222
|
|
|
223
|
+
# Runs `block` with a fresh `next` sink installed, then yields the collected `[NextNode, Type]` pairs to the
|
|
224
|
+
# caller. Stacks like the return sink, and for the same reason: a block body evaluated inside another block body's
|
|
225
|
+
# evaluation must not spill its arms into the outer collection. Used by `ExpressionTyper#type_block_body` to join
|
|
226
|
+
# the `next` arms into the block's value type.
|
|
227
|
+
def self.with_next_sink
|
|
228
|
+
previous = Thread.current[NEXT_SINK_KEY]
|
|
229
|
+
sink = []
|
|
230
|
+
Thread.current[NEXT_SINK_KEY] = sink
|
|
231
|
+
begin
|
|
232
|
+
result = yield
|
|
233
|
+
ensure
|
|
234
|
+
Thread.current[NEXT_SINK_KEY] = previous
|
|
235
|
+
end
|
|
236
|
+
[result, sink]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Runs `block` with a fresh `break`-value sink installed, then yields the collected `[BreakNode, Type]` pairs to
|
|
240
|
+
# the caller. Stacks like the other sinks: a block body evaluated inside another block body's evaluation must not
|
|
241
|
+
# spill its arms into the outer collection. Used by `ExpressionTyper#call_dispatch_type_for` to union the `break`
|
|
242
|
+
# arms into the yielding call's type (issue #853).
|
|
243
|
+
def self.with_break_value_sink
|
|
244
|
+
previous = Thread.current[BREAK_VALUE_SINK_KEY]
|
|
245
|
+
sink = []
|
|
246
|
+
Thread.current[BREAK_VALUE_SINK_KEY] = sink
|
|
247
|
+
begin
|
|
248
|
+
result = yield
|
|
249
|
+
ensure
|
|
250
|
+
Thread.current[BREAK_VALUE_SINK_KEY] = previous
|
|
251
|
+
end
|
|
252
|
+
[result, sink]
|
|
253
|
+
end
|
|
254
|
+
|
|
196
255
|
# Evaluate `node` under the receiver scope. Returns `[type, scope']` where `type` is the value the node produces
|
|
197
256
|
# and `scope'` is the scope observable after the node has run. The receiver scope is never mutated.
|
|
198
|
-
#
|
|
199
|
-
# @param node [Prism::Node]
|
|
200
|
-
# @return [Array(Rigor::Type, Rigor::Scope)]
|
|
201
257
|
def evaluate(node)
|
|
202
258
|
@on_enter&.call(node, @scope)
|
|
203
259
|
|
|
@@ -246,14 +302,27 @@ module Rigor
|
|
|
246
302
|
# to a follow-up; for now they degrade to "type the rhs, do not rebind" via the default branch in {#evaluate}.
|
|
247
303
|
def eval_local_write(node)
|
|
248
304
|
rhs_type, post_rhs = sub_eval(node.value, scope)
|
|
305
|
+
bound = bind_local_write(node, rhs_type, post_rhs)
|
|
306
|
+
# Issue #667 — `m = AppConfig::MODE` makes `m` a copy of a constant the reader's file does not
|
|
307
|
+
# declare, so `m == :production` folding to `true` is the project's configuration and not a logic
|
|
308
|
+
# error the author can see. Stamped LAST, across every binding path above: an RHS can be both an
|
|
309
|
+
# ADR-58 ivar copy and a published-constant copy (`@mode = AppConfig::MODE` in the ctor, `m = @mode`
|
|
310
|
+
# here), and the two marks answer unrelated questions.
|
|
311
|
+
return [rhs_type, bound] unless published_constant_copy?(node.value, rhs_type, post_rhs)
|
|
312
|
+
|
|
313
|
+
[rhs_type, bound.with_published_constant_mark(:local, node.name)]
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# The binding half of {#eval_local_write}: the three mutually exclusive provenance paths a local write
|
|
317
|
+
# can take, unchanged from when they were inline.
|
|
318
|
+
def bind_local_write(node, rhs_type, post_rhs)
|
|
249
319
|
# ADR-58 WD1 — `r = @right` where `@right`'s optionality is purely declaration-sourced makes `r`
|
|
250
320
|
# declaration-sourced too (the survey's exact rotation/traversal shape `r = @right; r.key`). The mark is
|
|
251
321
|
# computed on the RHS *value*'s provenance — a pure ivar read of a currently declaration-sourced ivar — so it
|
|
252
322
|
# survives the local copy. Any other RHS (a call result, a method-local-nil-bearing value) leaves the local
|
|
253
323
|
# flow-live and the diagnostic fires as before.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
end
|
|
324
|
+
return post_rhs.with_declaration_sourced_local(node.name, rhs_type) if
|
|
325
|
+
declaration_sourced_ivar_read?(node.value, post_rhs)
|
|
257
326
|
|
|
258
327
|
bound = post_rhs.with_local(node.name, rhs_type)
|
|
259
328
|
# ADR-67 WD6b — a local whose RHS is (transitively) rooted at an inferred parameter inherits the
|
|
@@ -261,14 +330,24 @@ module Rigor
|
|
|
261
330
|
# same lower-bound reason (`vindex = codepoints[i] - x; vindex < y`). The mark is STICKY across
|
|
262
331
|
# narrowing/joins (see `Scope#without_inferred_param_mark`), so a genuine rewrite from a non-param RHS
|
|
263
332
|
# must clear it here. No-op unless the `parameter_inference:` gate seeded a parameter this RHS reaches.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
end
|
|
333
|
+
return bound.with_inferred_param_mark(node.name) if
|
|
334
|
+
Analysis::CheckRules::InferredParamGuard.rooted?(node.value, scope)
|
|
267
335
|
|
|
268
336
|
bound = bound.without_inferred_param_mark(node.name)
|
|
269
337
|
bound = bound.with_local_origin(node.name, rhs_origin(node.value, post_rhs, rhs_type))
|
|
270
|
-
bound
|
|
271
|
-
|
|
338
|
+
bound.with_optimistic_local(node.name, optimistic_rhs_origin(node.value, post_rhs))
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Issue #667 — true when this write copies a value whose constancy rests on a foreign published
|
|
342
|
+
# constant. The `Type::Constant` pre-gate is what keeps the question cheap and is not merely an
|
|
343
|
+
# optimisation: the mark exists to withhold `flow.always-truthy-condition`, which only ever fires on a
|
|
344
|
+
# predicate that folded, so a non-constant RHS has nothing to withhold. It also keeps the guard's one
|
|
345
|
+
# interprocedural hop off every `x = foo(y)` in a project that publishes anything at all — the same
|
|
346
|
+
# gate {Analysis::CheckRules::AlwaysTruthyConditionCollector} puts in front of it.
|
|
347
|
+
def published_constant_copy?(value_node, rhs_type, scope_after_rhs)
|
|
348
|
+
return false unless rhs_type.is_a?(Type::Constant)
|
|
349
|
+
|
|
350
|
+
Analysis::CheckRules::PublishedConstantGuard.rooted?(value_node, scope_after_rhs)
|
|
272
351
|
end
|
|
273
352
|
|
|
274
353
|
# Issue #286 — the optimistic-nil-free counterpart of {#rhs_origin}, differing in two ways. It does not
|
|
@@ -314,6 +393,11 @@ module Rigor
|
|
|
314
393
|
bound = post_rhs.with_ivar(node.name, rhs_type)
|
|
315
394
|
bound = bound.with_ivar_origin(node.name, rhs_origin(node.value, post_rhs, rhs_type))
|
|
316
395
|
bound = bound.with_optimistic_ivar(node.name, optimistic_rhs_origin(node.value, post_rhs))
|
|
396
|
+
# Issue #667 — the ivar twin of the local stamp. This is the SAME-method half; the cross-method one
|
|
397
|
+
# (`@mode = AppConfig::MODE` in `initialize`, read in a sibling) rides the class-ivar census and is
|
|
398
|
+
# stamped by {#seed_instance_ivars}.
|
|
399
|
+
bound = bound.with_published_constant_mark(:ivar, node.name) if
|
|
400
|
+
published_constant_copy?(node.value, rhs_type, post_rhs)
|
|
317
401
|
[rhs_type, bound]
|
|
318
402
|
end
|
|
319
403
|
|
|
@@ -351,24 +435,38 @@ module Rigor
|
|
|
351
435
|
# The class context a meta-new rvalue block is entered under, or nil when the rvalue is not that shape. The
|
|
352
436
|
# KEY must be the one `ScopeIndexer` filed the body's defs and member layout under, so the two passes agree —
|
|
353
437
|
# which is why the decision is delegated to the ScopeIndexer's own recognition rather than re-spelled here. A
|
|
354
|
-
#
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
#
|
|
358
|
-
#
|
|
359
|
-
# entered under that.
|
|
438
|
+
# constant write whose rvalue `ScopeIndexer.meta_new_block_body` recognises is keyed by the constant's
|
|
439
|
+
# qualified name, one frame appended to the lexical context exactly as a `class Const` keyword body would be
|
|
440
|
+
# — the path spelling included since [#703](https://github.com/rigortype/rigor/issues/703). A shape the
|
|
441
|
+
# ScopeIndexer's stricter argument check rejects (`Const = Struct.new(*names) do … end`) is registered under
|
|
442
|
+
# the call site's anonymous name and is entered under that.
|
|
360
443
|
def meta_new_constant_body_context(node)
|
|
361
444
|
call_node = node.value
|
|
362
445
|
return nil unless call_node.is_a?(Prism::CallNode) && call_node.block.is_a?(Prism::BlockNode)
|
|
363
446
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
end
|
|
447
|
+
constant = meta_new_constant_context(node)
|
|
448
|
+
return constant if constant
|
|
367
449
|
|
|
368
450
|
anonymous = AnonymousMetaClass.name_for(call_node, scope.source_path)
|
|
369
451
|
anonymous && [ClassFrame.new(name: anonymous, singleton: false)]
|
|
370
452
|
end
|
|
371
453
|
|
|
454
|
+
# The frame stack for a constant-keyed meta-new body, or nil when nothing keys it by a constant. A ROOTED
|
|
455
|
+
# path write re-anchors at the top level the way a rooted `class ::Rooted::Bar` header does
|
|
456
|
+
# ({#eval_class_or_module}), so the stack resets to that frame alone rather than gaining one under the
|
|
457
|
+
# enclosure — `current_class_path` joins the stack, and `ScopeIndexer`'s own prefix resets there too.
|
|
458
|
+
def meta_new_constant_context(node)
|
|
459
|
+
return nil unless ScopeIndexer.meta_new_block_body(node)
|
|
460
|
+
|
|
461
|
+
case node
|
|
462
|
+
when Prism::ConstantWriteNode
|
|
463
|
+
@class_context + [ClassFrame.new(name: node.name.to_s, singleton: false)]
|
|
464
|
+
when Prism::ConstantPathWriteNode
|
|
465
|
+
frame = ClassFrame.new(name: Source::ConstantPath.qualified_name(node.target), singleton: false)
|
|
466
|
+
Source::ConstantPath.rooted?(node.target) ? [frame] : @class_context + [frame]
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
372
470
|
# Slice 7 phase 3 — compound writes (||=, &&=, +=/-=/...) for every variable kind. Each handler:
|
|
373
471
|
# 1. Reads the current type from the appropriate scope
|
|
374
472
|
# binding map (or `Dynamic[Top]` when unbound).
|
|
@@ -550,10 +648,23 @@ module Rigor
|
|
|
550
648
|
# join will not consume is pure overhead, and `<<` on a String buffer is one of the commonest calls
|
|
551
649
|
# there is. The content-adder table skips a non-adding mutator (`pop`, `sort!`); `joinable_receiver?`
|
|
552
650
|
# skips a receiver whose current binding is not a literal-shape carrier.
|
|
651
|
+
# Both mutator-receiver widenings, against one typing of the call's arguments: the bindings the
|
|
652
|
+
# receiver NAMES ({MutationWidening.widen_after_call}) and, when it names none because the
|
|
653
|
+
# receiver is an element read into a local, that element's pin inside its container
|
|
654
|
+
# ({ElementReadWidening.widen_element_read}, issue #643).
|
|
655
|
+
def widen_mutated_receivers(call_node, current_scope)
|
|
656
|
+
arg_types = mutator_arg_types(call_node, current_scope)
|
|
657
|
+
widened = MutationWidening.widen_after_call(call_node: call_node, current_scope: current_scope,
|
|
658
|
+
arg_types: arg_types)
|
|
659
|
+
ElementReadWidening.widen_element_read(call_node: call_node, current_scope: widened, arg_types: arg_types)
|
|
660
|
+
end
|
|
661
|
+
|
|
553
662
|
def mutator_arg_types(call_node, current_scope)
|
|
554
663
|
return MutationWidening::NO_ARG_TYPES unless ContentJoin::CONTENT_ADDERS.include?(call_node.name)
|
|
555
|
-
|
|
556
|
-
|
|
664
|
+
unless MutationWidening.joinable_receiver?(call_node.receiver, current_scope) ||
|
|
665
|
+
ElementReadWidening.joinable_element_read?(call_node.receiver, current_scope)
|
|
666
|
+
return MutationWidening::NO_ARG_TYPES
|
|
667
|
+
end
|
|
557
668
|
|
|
558
669
|
content_arg_types(call_node, scope)
|
|
559
670
|
end
|
|
@@ -1557,8 +1668,7 @@ module Rigor
|
|
|
1557
1668
|
# Flow-folding G1 / G2 — widen a local- or instance-variable binding when the call is an in-place mutator on it
|
|
1558
1669
|
# (e.g. `arms << x`, `@tags << hashtag`). Stops a literal-shape carrier (`Tuple` / `HashShape`) from outliving
|
|
1559
1670
|
# its justification when the value is mutated. Always-safe (loses precision, never invents facts).
|
|
1560
|
-
post_scope =
|
|
1561
|
-
arg_types: mutator_arg_types(node, post_scope))
|
|
1671
|
+
post_scope = widen_mutated_receivers(node, post_scope)
|
|
1562
1672
|
# ADR-48 slice 4 — Struct member-setter re-typing. After `s.x = v` on a fold-safe StructInstance local, rebind
|
|
1563
1673
|
# `s` to a StructInstance with member `:x` replaced by the assigned type, so a later `s.x` folds to `v` and a
|
|
1564
1674
|
# sibling `s.y` stays precise. `call_type` is the setter's own result (the assigned value type). Sound only for
|
|
@@ -2071,8 +2181,7 @@ module Rigor
|
|
|
2071
2181
|
classification = classify_closure_escape(node)
|
|
2072
2182
|
return post_scope if classification == :non_escaping
|
|
2073
2183
|
|
|
2074
|
-
post_scope =
|
|
2075
|
-
post_scope = widen_escaping_content_captures(node.block, post_scope)
|
|
2184
|
+
post_scope = escaping_closure_captures(node.block, post_scope)
|
|
2076
2185
|
post_scope.with_fact(
|
|
2077
2186
|
Analysis::FactStore::Fact.new(
|
|
2078
2187
|
bucket: :dynamic_origin,
|
|
@@ -2562,15 +2671,22 @@ module Rigor
|
|
|
2562
2671
|
].freeze
|
|
2563
2672
|
private_constant :INDEX_WRITE_NODES
|
|
2564
2673
|
|
|
2674
|
+
# The shared "not a content mutation" answer. This predicate runs on every node of every block, loop and
|
|
2675
|
+
# method body it censuses (~950k calls on the lib self-check) and almost always declines, so a fresh
|
|
2676
|
+
# `[nil, nil]` per decline was one of the largest allocation sites in the evaluator.
|
|
2677
|
+
NO_CONTENT_MUTATION = [nil, nil].freeze
|
|
2678
|
+
private_constant :NO_CONTENT_MUTATION
|
|
2679
|
+
|
|
2565
2680
|
# `[receiver_name, node]` when `node` is a content mutation whose receiver is a local variable satisfying `accept`
|
|
2566
|
-
# (depth predicate), else `[nil, nil]`. Covers `[]=`-style CallNode mutators and the index-write node
|
|
2681
|
+
# (depth predicate), else the frozen `[nil, nil]`. Covers `[]=`-style CallNode mutators and the index-write node
|
|
2682
|
+
# forms.
|
|
2567
2683
|
def content_mutation_target(node)
|
|
2568
2684
|
is_call_mutator = node.is_a?(Prism::CallNode) && ContentJoin::CONTENT_ADDERS.include?(node.name)
|
|
2569
|
-
return
|
|
2685
|
+
return NO_CONTENT_MUTATION unless is_call_mutator || index_write?(node)
|
|
2570
2686
|
|
|
2571
2687
|
receiver = node.receiver
|
|
2572
|
-
return
|
|
2573
|
-
return
|
|
2688
|
+
return NO_CONTENT_MUTATION unless receiver.is_a?(Prism::LocalVariableReadNode)
|
|
2689
|
+
return NO_CONTENT_MUTATION unless yield(receiver)
|
|
2574
2690
|
|
|
2575
2691
|
[receiver.name, node]
|
|
2576
2692
|
end
|
|
@@ -2675,6 +2791,32 @@ module Rigor
|
|
|
2675
2791
|
sub_eval(node.body, scope)
|
|
2676
2792
|
end
|
|
2677
2793
|
|
|
2794
|
+
# Issue #878 — `->() { }` and `lambda { }` build the same object, so they MUST type the same. The `lambda`
|
|
2795
|
+
# spelling is an ordinary call carrying a `Prism::BlockNode` and reaches {#eval_call}; the `->` spelling is a
|
|
2796
|
+
# node of its own, and without an entry here it was only ever typed as an expression (`Proc`) with its body left
|
|
2797
|
+
# un-evaluated. The body's reads and literals still reported — the rule walker falls back to the enclosing
|
|
2798
|
+
# method's scope — but a write made INSIDE the body joined no scope, so every later read in the body kept the
|
|
2799
|
+
# pre-write type.
|
|
2800
|
+
#
|
|
2801
|
+
# The body is run under the same entry scope `lambda { }` gets ({#build_block_entry_scope}, which binds the
|
|
2802
|
+
# parameters and enters `self` opaque), and the continuation gets the same escaping-closure treatment: a lambda
|
|
2803
|
+
# literal is a value that outlives the expression, exactly like the Proc `lambda` returns, so the outer locals it
|
|
2804
|
+
# can rebind lose their narrowing rather than being written back through ADR-56's non-escaping fixpoint. Both
|
|
2805
|
+
# halves are widenings — the two spellings now agree in both directions instead of `->` being the precise one.
|
|
2806
|
+
def eval_lambda(node)
|
|
2807
|
+
lambda_type = scope.type_of(node, tracer: tracer)
|
|
2808
|
+
sub_eval(node.body, build_block_entry_scope(nil, node)) unless node.body.nil?
|
|
2809
|
+
|
|
2810
|
+
[lambda_type, escaping_closure_captures(node, scope)]
|
|
2811
|
+
end
|
|
2812
|
+
|
|
2813
|
+
# The continuation effects of an escaping closure body: the outer locals it can rebind drop their narrowing, and
|
|
2814
|
+
# the ones it content-mutates are floored. Shared by the two spellings — a block on an escaping call
|
|
2815
|
+
# ({#record_closure_escape_if_any}) and a lambda literal ({#eval_lambda}) — so they cannot drift.
|
|
2816
|
+
def escaping_closure_captures(closure_node, post_scope)
|
|
2817
|
+
widen_escaping_content_captures(closure_node, drop_captured_narrowing(closure_node, post_scope))
|
|
2818
|
+
end
|
|
2819
|
+
|
|
2678
2820
|
# Builds the entry scope for a block body. The block sees the outer scope's locals (Ruby's lexical scoping rule)
|
|
2679
2821
|
# and adds bindings for every named block parameter on top. Parameter types come from the receiving method's RBS
|
|
2680
2822
|
# signature when one is available; the rest default to `Dynamic[Top]`.
|
|
@@ -2703,7 +2845,11 @@ module Rigor
|
|
|
2703
2845
|
params_root.locals.map(&:name)
|
|
2704
2846
|
end
|
|
2705
2847
|
|
|
2848
|
+
# A lambda literal has no receiving method, so it passes no call node and every parameter defaults to
|
|
2849
|
+
# `Dynamic[Top]` — the same answer `lambda { |y| }` gets, where the implicit-self receiver yields no signature.
|
|
2706
2850
|
def expected_block_param_types_for(call_node)
|
|
2851
|
+
return [] if call_node.nil?
|
|
2852
|
+
|
|
2707
2853
|
receiver_type = call_node.receiver ? scope.type_of(call_node.receiver, tracer: tracer) : nil
|
|
2708
2854
|
return [] if receiver_type.nil?
|
|
2709
2855
|
|
|
@@ -2846,7 +2992,15 @@ module Rigor
|
|
|
2846
2992
|
# flow-live, so `seed_declaration_sourced_ivar` marks each seeded ivar: `possible-nil-receiver` then declines to
|
|
2847
2993
|
# fire on the cross-method invariant unless a method-local write or narrowing makes the nil flow-live (which
|
|
2848
2994
|
# drops the mark).
|
|
2849
|
-
|
|
2995
|
+
#
|
|
2996
|
+
# Issue #667 — the same seed carries the published-constant mark for the ivars the class-ivar census
|
|
2997
|
+
# found assigned straight from a foreign published constant, so `@mode == :production` in a sibling
|
|
2998
|
+
# method withholds `flow.always-truthy-condition` the way the direct read of the constant does.
|
|
2999
|
+
marked = scope.published_constant_ivars_for(path)
|
|
3000
|
+
seeded.reduce(body_scope) do |acc, (name, type)|
|
|
3001
|
+
stamped = acc.seed_declaration_sourced_ivar(name, type)
|
|
3002
|
+
marked.include?(name) ? stamped.with_published_constant_mark(:ivar, name) : stamped
|
|
3003
|
+
end
|
|
2850
3004
|
end
|
|
2851
3005
|
|
|
2852
3006
|
# Cvars are visible from BOTH instance and singleton method bodies of the enclosing class, so this seed is
|
|
@@ -3013,7 +3167,22 @@ module Rigor
|
|
|
3013
3167
|
# inferred.
|
|
3014
3168
|
def eval_return(node)
|
|
3015
3169
|
sink = Thread.current[RETURN_SINK_KEY]
|
|
3016
|
-
|
|
3170
|
+
sink << jump_value_type(node) if sink
|
|
3171
|
+
[Type::Combinator.bot, scope]
|
|
3172
|
+
end
|
|
3173
|
+
|
|
3174
|
+
# `next value` ends the current block invocation and makes `value` that invocation's result. Structurally the same
|
|
3175
|
+
# story as {#eval_return} one level down: the control-transfer value is `Bot`, and the escaping value is recorded
|
|
3176
|
+
# into the active `next` sink so `ExpressionTyper#type_block_body` joins it with the body's fall-through tail
|
|
3177
|
+
# (issue #841). The node itself is recorded alongside the type because the sink also collects `next`s belonging to
|
|
3178
|
+
# a nested block / loop / def; the consumer filters by identity.
|
|
3179
|
+
#
|
|
3180
|
+
# A `next` on a provably dead branch is never reached by the evaluator at all (`eval_if` / `eval_unless` skip the
|
|
3181
|
+
# dead arm), so the join is flow-sensitive for free: `[1, 2].map { |x| next nil if x.nil?; x.to_s }` keeps its
|
|
3182
|
+
# exact per-element answer.
|
|
3183
|
+
def eval_next(node)
|
|
3184
|
+
sink = Thread.current[NEXT_SINK_KEY]
|
|
3185
|
+
sink << [node, jump_value_type(node)] if sink
|
|
3017
3186
|
[Type::Combinator.bot, scope]
|
|
3018
3187
|
end
|
|
3019
3188
|
|
|
@@ -3022,26 +3191,28 @@ module Rigor
|
|
|
3022
3191
|
# would drop (`flag = true; break` -> `flag` is `false | true` after the loop). nil sink = a `break` not inside an
|
|
3023
3192
|
# inferred loop body (a block targeting a method, or top-level) — left to the existing escaping-block / no-op
|
|
3024
3193
|
# handling.
|
|
3194
|
+
#
|
|
3195
|
+
# Issue #853: the value it carries out belongs to the yielding CALL, so it is recorded into the separate
|
|
3196
|
+
# break-value sink for `ExpressionTyper#call_dispatch_type_for` to union in. Both sinks are optional and
|
|
3197
|
+
# independent — a loop body collects scopes while an enclosing call collects values from the same walk.
|
|
3025
3198
|
def eval_break(node)
|
|
3026
3199
|
sink = Thread.current[BREAK_SINK_KEY]
|
|
3027
3200
|
sink << [node, scope] if sink
|
|
3201
|
+
value_sink = Thread.current[BREAK_VALUE_SINK_KEY]
|
|
3202
|
+
value_sink << [node, jump_value_type(node)] if value_sink
|
|
3028
3203
|
[Type::Combinator.bot, scope]
|
|
3029
3204
|
end
|
|
3030
3205
|
|
|
3031
|
-
|
|
3206
|
+
# The value a `return` / `next` / `break` carries out of the construct it leaves. A bare jump carries nil; a
|
|
3207
|
+
# single argument carries its own type; `return a, b, c` (and `next` / `break` alike) packs `[a, b, c]`, so the
|
|
3208
|
+
# corresponding Tuple is contributed element-by-element. The argument is evaluated under the entry scope and the
|
|
3209
|
+
# resulting scope discarded — control leaves here, so nothing the argument binds is observable downstream.
|
|
3210
|
+
def jump_value_type(node)
|
|
3032
3211
|
args = node.arguments&.arguments || []
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
sink << Type::Combinator.constant_of(nil)
|
|
3038
|
-
elsif args.size == 1
|
|
3039
|
-
type, = sub_eval(args.first, scope)
|
|
3040
|
-
sink << type
|
|
3041
|
-
else
|
|
3042
|
-
element_types = args.map { |arg| sub_eval(arg, scope).first }
|
|
3043
|
-
sink << Type::Combinator.tuple_of(*element_types)
|
|
3044
|
-
end
|
|
3212
|
+
return Type::Combinator.constant_of(nil) if args.empty?
|
|
3213
|
+
return sub_eval(args.first, scope).first if args.size == 1
|
|
3214
|
+
|
|
3215
|
+
Type::Combinator.tuple_of(*args.map { |arg| sub_eval(arg, scope).first })
|
|
3045
3216
|
end
|
|
3046
3217
|
|
|
3047
3218
|
def sub_eval(node, with_scope, class_context: @class_context, lexical_nesting: @lexical_nesting)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
# The String half of {MutationWidening}'s straight-line seam (issue #617 residue (4)).
|
|
8
|
+
#
|
|
9
|
+
# `s = +"ab"; s << "c"; s == "ab"` folded `flow.always-truthy-condition` on correct code: the seam
|
|
10
|
+
# responded to `Tuple` / `HashShape` / empty-witness carriers only, while a `Constant[String]` is just as
|
|
11
|
+
# falsified by an in-place mutator as a `Tuple` is by `<<`. The ADR-56 block-capture path already widened
|
|
12
|
+
# the same binding, so the two seams disagreed about one carrier.
|
|
13
|
+
#
|
|
14
|
+
# It lives beside `MutationWidening` rather than inside it because the tables here answer a different
|
|
15
|
+
# question from the Array / Hash ones: those partition a carrier's SHAPE evidence, this one partitions a
|
|
16
|
+
# VALUE pin and an emptiness proof.
|
|
17
|
+
module StringMutation
|
|
18
|
+
# Receiver-mutating String methods only. The non-bang siblings (`upcase` vs `upcase!`, `sub` vs `sub!`)
|
|
19
|
+
# return a new String and leave the receiver's value intact, so they stay precise.
|
|
20
|
+
MUTATORS = %i[
|
|
21
|
+
<< concat insert prepend replace clear []= slice!
|
|
22
|
+
sub! gsub! tr! tr_s! delete! squeeze! succ! next!
|
|
23
|
+
upcase! downcase! capitalize! swapcase! reverse!
|
|
24
|
+
strip! lstrip! rstrip! chomp! chop! force_encoding
|
|
25
|
+
].to_set.freeze
|
|
26
|
+
|
|
27
|
+
# The {MUTATORS} that can leave the receiver EMPTY, and so retract a `non-empty-string` refinement. The
|
|
28
|
+
# rest are appenders or same-length rewrites: `s << x` / `s.prepend(x)` / `s.upcase!` cannot empty a
|
|
29
|
+
# string that already holds a character, so the refinement is still provable afterwards.
|
|
30
|
+
EMPTYING_MUTATORS = %i[
|
|
31
|
+
replace clear slice! []=
|
|
32
|
+
sub! gsub! delete! squeeze! strip! lstrip! rstrip! chomp! chop!
|
|
33
|
+
].to_set.freeze
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
# `Constant["ab"]` under a String mutator → the bare `String` nominal. The pinned value is what the
|
|
38
|
+
# mutation falsifies, and nothing else about the carrier survives it, so there is no `values: :keep`
|
|
39
|
+
# half to preserve the way a `Tuple`'s surviving slots have one: an appended, substituted or
|
|
40
|
+
# case-folded string is a different string.
|
|
41
|
+
#
|
|
42
|
+
# Only a String-valued `Constant` widens. Every other literal (`Integer`, `Symbol`, `Range`) can only
|
|
43
|
+
# reach this arm through a name in the Array or Hash table and not in {MUTATORS}, and answering for it
|
|
44
|
+
# would erase a value no in-place mutator on that class can change.
|
|
45
|
+
#
|
|
46
|
+
# `nil` — no widening — for every other input, which is the contract `MutationWidening#widen_for_mutator`
|
|
47
|
+
# signals a decline with.
|
|
48
|
+
def widen_constant(constant, method_name)
|
|
49
|
+
return nil unless constant?(constant)
|
|
50
|
+
return nil unless MUTATORS.include?(method_name)
|
|
51
|
+
|
|
52
|
+
Type::Combinator.nominal_of("String")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def constant?(type)
|
|
56
|
+
type.is_a?(Type::Constant) && type.value.is_a?(String)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -55,10 +55,10 @@ module Rigor
|
|
|
55
55
|
node.is_a?(Prism::ModuleNode) || node.is_a?(Prism::SingletonClassNode)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# @param root
|
|
59
|
-
# @param layout_lookup
|
|
58
|
+
# @param root — the local-variable scope to scan.
|
|
59
|
+
# @param layout_lookup — a `String -> Array[Symbol] | nil` resolver mapping a constant receiver name to its
|
|
60
60
|
# struct member list.
|
|
61
|
-
# @return
|
|
61
|
+
# @return the fold-safe local names.
|
|
62
62
|
def fold_safe_locals(root, layout_lookup)
|
|
63
63
|
return EMPTY if root.nil?
|
|
64
64
|
|
|
@@ -208,10 +208,9 @@ module Rigor
|
|
|
208
208
|
# Nested `def` / `class` / `module` bodies are skipped: their statements do not run during THIS body's
|
|
209
209
|
# evaluation. Blocks are descended into — they share `self`.
|
|
210
210
|
#
|
|
211
|
-
# @param
|
|
212
|
-
# @
|
|
213
|
-
# @
|
|
214
|
-
# @yieldreturn [Boolean] whether that sibling method is itself self-fold-safe.
|
|
211
|
+
# @param member_names — the receiver carrier's member names.
|
|
212
|
+
# @yieldparam name — an unrecognised self-call selector.
|
|
213
|
+
# @yieldreturn whether that sibling method is itself self-fold-safe.
|
|
215
214
|
def self_fold_safe_body?(body, member_names, &sibling_pure)
|
|
216
215
|
return false if body.nil?
|
|
217
216
|
|
|
@@ -22,8 +22,7 @@ module Rigor
|
|
|
22
22
|
class SyntheticMethodIndex
|
|
23
23
|
attr_reader :entries, :class_names
|
|
24
24
|
|
|
25
|
-
# @param
|
|
26
|
-
# @param class_names [Array<String>, Set<String>] names of classes the substrate synthesises wholesale (ADR-36
|
|
25
|
+
# @param class_names — names of classes the substrate synthesises wholesale (ADR-36
|
|
27
26
|
# nested-class emission — the variant subclasses that have no RBS/source declaration of their own). Recorded so
|
|
28
27
|
# `Environment#class_known?` can resolve them as classes (their constant reference + `.new` dispatch) even
|
|
29
28
|
# though nothing else in the type universe declares them. Tier B/C method emissions leave this empty (their
|
|
@@ -40,26 +40,24 @@ module Rigor
|
|
|
40
40
|
module SyntheticMethodScanner # rubocop:disable Metrics/ModuleLength
|
|
41
41
|
module_function
|
|
42
42
|
|
|
43
|
-
# @param
|
|
44
|
-
# @param paths [Array<String>] absolute paths to the project
|
|
43
|
+
# @param paths — absolute paths to the project
|
|
45
44
|
# source files to scan.
|
|
46
|
-
# @param environment
|
|
45
|
+
# @param environment — used for
|
|
47
46
|
# inheritance resolution against RBS-known classes
|
|
48
47
|
# (ActiveRecord::Base, Dry::Struct, etc.) that aren't
|
|
49
48
|
# declared in project source.
|
|
50
|
-
# @param fact_store
|
|
49
|
+
# @param fact_store —
|
|
51
50
|
# the per-run cross-plugin fact store. ADR-18 lookups
|
|
52
51
|
# (`Plugin::Macro::HeredocTemplate::Emit#returns_from_arg`)
|
|
53
52
|
# consult this at scan time to resolve per-call-site
|
|
54
53
|
# return types from published facts; without it, those
|
|
55
54
|
# emit rows fall back to their static `returns:` (or
|
|
56
55
|
# `"untyped"` → `Dynamic[Top]`).
|
|
57
|
-
# @param buffer
|
|
56
|
+
# @param buffer —
|
|
58
57
|
# editor-mode buffer binding. When set, reads for the
|
|
59
58
|
# logical path resolve to the buffer's physical path so
|
|
60
59
|
# the pre-pass sees the in-flight bytes instead of the
|
|
61
60
|
# on-disk copy.
|
|
62
|
-
# @return [Rigor::Inference::SyntheticMethodIndex]
|
|
63
61
|
def scan(plugin_registry:, paths:, environment: nil, fact_store: nil, buffer: nil)
|
|
64
62
|
templates = collect_templates(plugin_registry)
|
|
65
63
|
registries = collect_trait_registries(plugin_registry)
|