rigortype 0.3.7 → 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 +96 -15
- 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 +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- 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 +69 -13
- 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 +100 -29
- 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 +316 -40
- 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 +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- 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 +70 -0
- 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_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- 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 +31 -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 +28 -0
- 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 +613 -113
- data/lib/rigor/environment.rb +189 -27
- 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 +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- 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 +67 -14
- 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 +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- 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 +181 -102
- 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 +17 -11
- 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 +11 -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 +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -27,10 +27,13 @@ module Rigor
|
|
|
27
27
|
:discovered_superclasses,
|
|
28
28
|
:discovered_header_nestings,
|
|
29
29
|
:discovered_includes,
|
|
30
|
+
:discovered_extends,
|
|
30
31
|
:discovered_class_sources,
|
|
31
32
|
:constant_sources,
|
|
32
33
|
:published_constant_names,
|
|
33
34
|
:local_constant_names,
|
|
35
|
+
:published_constant_alias_names,
|
|
36
|
+
:published_constant_ivars,
|
|
34
37
|
:data_member_layouts,
|
|
35
38
|
:struct_member_layouts,
|
|
36
39
|
:param_inferred_types,
|
|
@@ -52,6 +55,11 @@ module Rigor
|
|
|
52
55
|
# either kind.
|
|
53
56
|
METHOD_KIND_BOTH = :both
|
|
54
57
|
|
|
58
|
+
# Issue #728 — the key a `discovered_header_nestings` bucket stores its class-wide chain under, beside
|
|
59
|
+
# the per-ancestor-name entries. `nil` is used because every other key in a bucket is an ancestor name
|
|
60
|
+
# as written, and no ancestor name can be nil.
|
|
61
|
+
UNKEYED_HEADER_NESTING = nil
|
|
62
|
+
|
|
55
63
|
# The shared all-empty index `Scope.empty` (and every scope that never sees a seeding pass) points at — one
|
|
56
64
|
# allocation per process.
|
|
57
65
|
EMPTY = new(
|
|
@@ -86,8 +94,20 @@ module Rigor
|
|
|
86
94
|
# name — the peel is the nested spelling's answer, and it searched an `Admin::Base` that a compact
|
|
87
95
|
# `class Admin::Widget < Base` written at the top level never reaches. An absent entry means "not
|
|
88
96
|
# recorded" and keeps the peel, so a scope that never saw a declaration walk is unchanged.
|
|
97
|
+
#
|
|
98
|
+
# Issue #728 — the value is keyed by the ancestor NAME the site wrote, because a class's declaration
|
|
99
|
+
# sites need not share a cref: `class Foo < Base` at the top level and `class ::Foo; include Helper`
|
|
100
|
+
# inside `module Outer` resolve `Base` and `Helper` in different ones, and a single chain per class
|
|
101
|
+
# gave `Outer` to both. {UNKEYED_HEADER_NESTING} holds the union over every ancestor-naming site, for
|
|
102
|
+
# a name no site recorded under its own key.
|
|
89
103
|
discovered_header_nestings: EMPTY_TABLE,
|
|
90
104
|
discovered_includes: EMPTY_TABLE,
|
|
105
|
+
# Issue #898 — the singleton-side twin of `discovered_includes`: `{qualified class or module name =>
|
|
106
|
+
# [module names it `extend`s, as written]}`, built by the same `ScopeIndexer` walk that #526 already
|
|
107
|
+
# ran to fold an extended module's instance defs onto the extending class's singleton. #526 consumed
|
|
108
|
+
# the table inside the indexer and threw it away; `Narrowing` needs it to survive onto the scope,
|
|
109
|
+
# because `Singleton[C]`'s ancestry is exactly what `extend` writes and nothing else records it.
|
|
110
|
+
discovered_extends: EMPTY_TABLE,
|
|
91
111
|
discovered_class_sources: EMPTY_TABLE,
|
|
92
112
|
# Issue #644 — `{qualified constant name => Set[declaring file]}`, the write attribution behind the
|
|
93
113
|
# cross-file value-constant table. Read only by `Scope#record_constant_dependency` during ADR-46
|
|
@@ -102,6 +122,16 @@ module Rigor
|
|
|
102
122
|
# author cannot see. Both empty outside a runner-seeded scope, which makes the guard a no-op there.
|
|
103
123
|
published_constant_names: EMPTY_NAME_SET,
|
|
104
124
|
local_constant_names: EMPTY_NAME_SET,
|
|
125
|
+
# Issue #667 — the two per-file tables that carry a published constant's provenance across a COPY,
|
|
126
|
+
# which the name sets above cannot: they answer about a reference's spelling, and a copy has none.
|
|
127
|
+
# `published_constant_alias_names` is the last segments of the constants THIS FILE assigns straight
|
|
128
|
+
# from a foreign published one (`MODE2 = AppConfig::MODE`), which the local-declaration exemption
|
|
129
|
+
# would otherwise release. `published_constant_ivars` is `{class name => Set[ivar name]}` for an ivar
|
|
130
|
+
# whose class-ivar seed is such a copy (`@mode = AppConfig::MODE` in `initialize`), stamped onto the
|
|
131
|
+
# flow carrier at method entry. Both are seeded by `ScopeIndexer.index` and only when the project
|
|
132
|
+
# published something, so a project with no cross-file value constants pays no walk.
|
|
133
|
+
published_constant_alias_names: EMPTY_NAME_SET,
|
|
134
|
+
published_constant_ivars: EMPTY_TABLE,
|
|
105
135
|
data_member_layouts: EMPTY_TABLE,
|
|
106
136
|
struct_member_layouts: EMPTY_TABLE,
|
|
107
137
|
# ADR-67 WD3 — the call-site parameter-inference table, keyed by `[class_name, method_name, kind]` (the
|
data/lib/rigor/scope.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Rigor
|
|
|
21
21
|
attr_reader :environment, :locals, :fact_store, :self_type,
|
|
22
22
|
:ivars, :cvars, :globals,
|
|
23
23
|
:indexed_narrowings, :method_chain_narrowings,
|
|
24
|
-
:declaration_sourced,
|
|
24
|
+
:declaration_sourced, :published_constant_sourced,
|
|
25
25
|
:source_path, :discovery, :struct_fold_safe_locals,
|
|
26
26
|
:opaque_block_self, :lexical_nesting,
|
|
27
27
|
:dynamic_origins, :local_origins, :ivar_origins,
|
|
@@ -49,12 +49,21 @@ module Rigor
|
|
|
49
49
|
def discovered_method_visibilities = @discovery.discovered_method_visibilities
|
|
50
50
|
def discovered_superclasses = @discovery.discovered_superclasses
|
|
51
51
|
def discovered_includes = @discovery.discovered_includes
|
|
52
|
+
def discovered_extends = @discovery.discovered_extends
|
|
52
53
|
def discovered_class_sources = @discovery.discovered_class_sources
|
|
53
54
|
# Issue #644 — `{qualified constant name => Set[declaring file]}`; seeded only on an ADR-46 recording run.
|
|
54
55
|
def constant_sources = @discovery.constant_sources
|
|
55
56
|
def published_constant_names = @discovery.published_constant_names
|
|
56
57
|
def local_constant_names = @discovery.local_constant_names
|
|
57
58
|
|
|
59
|
+
# Issue #667 — the instance-variable names of `class_name` whose class-ivar seed comes from a foreign
|
|
60
|
+
# published constant (`@mode = AppConfig::MODE` in `initialize`, read in a sibling method).
|
|
61
|
+
# `StatementEvaluator#seed_instance_ivars` stamps the flow mark for these at method-body entry, the way
|
|
62
|
+
# ADR-58's seed stamps its own; a method-local write or narrowing then drops it through `with_ivar`.
|
|
63
|
+
def published_constant_ivars_for(class_name)
|
|
64
|
+
@discovery.published_constant_ivars[class_name] || EMPTY_PUBLISHED_CONSTANT_IVARS
|
|
65
|
+
end
|
|
66
|
+
|
|
58
67
|
# Issue #644 — true when the constant reference `name` (as written: `MODE` or `AppConfig::MODE`) is
|
|
59
68
|
# published by the cross-file value-constant table and is NOT assigned by the file being analysed: a
|
|
60
69
|
# value this file's author cannot see the declaration of. {Analysis::CheckRules::PublishedConstantGuard}
|
|
@@ -76,14 +85,27 @@ module Rigor
|
|
|
76
85
|
# over already-qualified names rather than a second lexical ladder: the engine's ladder
|
|
77
86
|
# (`Reflection.resolve_constant_type`) answers with a TYPE and not with the candidate that won, and
|
|
78
87
|
# reimplementing the walk here is the very thing that made the receiver resolution wrong once already.
|
|
88
|
+
#
|
|
89
|
+
# Issue #667 — a third answer sits beside those two. `MODE2 = AppConfig::MODE` is assigned by this file,
|
|
90
|
+
# so the exemption above releases it, yet its value is still one the author never saw: the alias only
|
|
91
|
+
# renames the foreign declaration. `published_constant_alias_names` holds the last segments of exactly
|
|
92
|
+
# those aliases, and matching on the last segment over-answers in the WITHHOLDING direction, which is the
|
|
93
|
+
# safe one.
|
|
79
94
|
def published_constant?(name)
|
|
80
95
|
names = @discovery.published_constant_names
|
|
96
|
+
return true if published_constant_alias?(name)
|
|
81
97
|
return false if names.empty?
|
|
82
98
|
return false unless names.include?(name.split("::").last)
|
|
83
99
|
|
|
84
100
|
!locally_declared_constant?(name)
|
|
85
101
|
end
|
|
86
102
|
|
|
103
|
+
def published_constant_alias?(name)
|
|
104
|
+
aliases = @discovery.published_constant_alias_names
|
|
105
|
+
!aliases.empty? && aliases.include?(name.split("::").last)
|
|
106
|
+
end
|
|
107
|
+
private :published_constant_alias?
|
|
108
|
+
|
|
87
109
|
def locally_declared_constant?(reference_name)
|
|
88
110
|
local = @discovery.local_constant_names
|
|
89
111
|
return false if local.empty?
|
|
@@ -153,9 +175,21 @@ module Rigor
|
|
|
153
175
|
# threaded by reference through transitions; reset per method body (a fresh entry scope drops it), so the
|
|
154
176
|
# name keys never collide across bodies.
|
|
155
177
|
EMPTY_ORIGINS = {}.freeze
|
|
178
|
+
# Issue #667 — the set of variable references currently bound to a value COPIED out of a constant the
|
|
179
|
+
# project published and this file does not declare. Members are frozen `[kind, name]` pairs, the same
|
|
180
|
+
# spelling {EMPTY_DECLARATION_SOURCED} uses, but a DELIBERATELY SEPARATE carrier: this mark joins by
|
|
181
|
+
# UNION where ADR-58's kinds intersect, and it answers a different question (what a value's *constancy*
|
|
182
|
+
# rests on, not what a binding's *optionality* rests on) for a different consumer. Folding it into the
|
|
183
|
+
# ADR-58 set would be the third establishing transition its *Non-transitivity* passage reserves as a
|
|
184
|
+
# decision, on a Set that already carries two opposite join policies.
|
|
185
|
+
EMPTY_PUBLISHED_CONSTANT_SOURCED = Set.new.freeze
|
|
186
|
+
# Issue #667 — the empty answer of {#published_constant_ivars_for}, so a class with no such ivar (every
|
|
187
|
+
# class in a project that publishes nothing) allocates none.
|
|
188
|
+
EMPTY_PUBLISHED_CONSTANT_IVARS = Set.new.freeze
|
|
156
189
|
private_constant :EMPTY_VAR_BINDINGS, :EMPTY_INDEXED_NARROWINGS,
|
|
157
190
|
:EMPTY_CHAIN_NARROWINGS, :EMPTY_DECLARATION_SOURCED,
|
|
158
|
-
:EMPTY_FOLD_SAFE, :EMPTY_ORIGINS
|
|
191
|
+
:EMPTY_FOLD_SAFE, :EMPTY_ORIGINS, :EMPTY_PUBLISHED_CONSTANT_SOURCED,
|
|
192
|
+
:EMPTY_PUBLISHED_CONSTANT_IVARS
|
|
159
193
|
|
|
160
194
|
class << self
|
|
161
195
|
def empty(environment: Environment.default, source_path: nil)
|
|
@@ -207,6 +241,7 @@ module Rigor
|
|
|
207
241
|
indexed_narrowings: EMPTY_INDEXED_NARROWINGS,
|
|
208
242
|
method_chain_narrowings: EMPTY_CHAIN_NARROWINGS,
|
|
209
243
|
declaration_sourced: EMPTY_DECLARATION_SOURCED,
|
|
244
|
+
published_constant_sourced: EMPTY_PUBLISHED_CONSTANT_SOURCED,
|
|
210
245
|
source_path: nil,
|
|
211
246
|
struct_fold_safe_locals: EMPTY_FOLD_SAFE,
|
|
212
247
|
opaque_block_self: false,
|
|
@@ -231,6 +266,7 @@ module Rigor
|
|
|
231
266
|
@indexed_narrowings = indexed_narrowings
|
|
232
267
|
@method_chain_narrowings = method_chain_narrowings
|
|
233
268
|
@declaration_sourced = declaration_sourced
|
|
269
|
+
@published_constant_sourced = published_constant_sourced
|
|
234
270
|
@source_path = source_path
|
|
235
271
|
@struct_fold_safe_locals = struct_fold_safe_locals
|
|
236
272
|
@opaque_block_self = opaque_block_self
|
|
@@ -314,6 +350,10 @@ module Rigor
|
|
|
314
350
|
indexed_narrowings: new_indexed_narrowings,
|
|
315
351
|
method_chain_narrowings: new_chain_narrowings,
|
|
316
352
|
declaration_sourced: drop_declaration_sourced_for(:local, name),
|
|
353
|
+
# Issue #667 — rebinding is flow-live for the published-constant mark too: the new value need
|
|
354
|
+
# not be a copy of anything. `with_published_constant_mark` re-stamps afterward when the write's
|
|
355
|
+
# rvalue is one.
|
|
356
|
+
published_constant_sourced: drop_published_constant_sourced_for(:local, name),
|
|
317
357
|
local_origins: drop_origin(@local_origins, name),
|
|
318
358
|
optimistic_locals: drop_origin(@optimistic_locals, name))
|
|
319
359
|
end
|
|
@@ -419,6 +459,7 @@ module Rigor
|
|
|
419
459
|
indexed_narrowings: new_indexed_narrowings,
|
|
420
460
|
method_chain_narrowings: new_chain_narrowings,
|
|
421
461
|
declaration_sourced: drop_declaration_sourced_for(:ivar, name),
|
|
462
|
+
published_constant_sourced: drop_published_constant_sourced_for(:ivar, name),
|
|
422
463
|
ivar_origins: drop_origin(@ivar_origins, name),
|
|
423
464
|
optimistic_ivars: drop_origin(@optimistic_ivars, name))
|
|
424
465
|
end
|
|
@@ -445,6 +486,22 @@ module Rigor
|
|
|
445
486
|
rebuild(declaration_sourced: add_declaration_sourced(:local, name))
|
|
446
487
|
end
|
|
447
488
|
|
|
489
|
+
# Issue #667 — record that `name` is currently bound to a value copied out of a foreign published
|
|
490
|
+
# constant. Always applied AFTER the `with_local` / `with_ivar` that binds the value (both drop the mark
|
|
491
|
+
# unconditionally), exactly as {#with_local_declaration_mark} is.
|
|
492
|
+
def with_published_constant_mark(kind, name)
|
|
493
|
+
rebuild(published_constant_sourced: add_published_constant_sourced(kind, name))
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Issue #667 — true when `(kind, name)`'s current binding is a copy of a constant the project published
|
|
497
|
+
# and this file does not declare. Asked through
|
|
498
|
+
# {Analysis::CheckRules::PublishedConstantGuard}, never directly from a rule.
|
|
499
|
+
def published_constant_sourced?(kind, name)
|
|
500
|
+
return false if @published_constant_sourced.empty?
|
|
501
|
+
|
|
502
|
+
@published_constant_sourced.include?([kind.to_sym, name.to_sym])
|
|
503
|
+
end
|
|
504
|
+
|
|
448
505
|
# ADR-58 WD1 — true when `(kind, name)`'s binding optionality is purely declaration-sourced (no flow-live
|
|
449
506
|
# write/narrowing has touched it).
|
|
450
507
|
def declaration_sourced?(kind, name)
|
|
@@ -744,6 +801,34 @@ module Rigor
|
|
|
744
801
|
@discovery.discovered_includes[class_name.to_s] || []
|
|
745
802
|
end
|
|
746
803
|
|
|
804
|
+
# Issue #898 — the module names `extend`ed onto `class_name`'s SINGLETON, as written, gathered up the
|
|
805
|
+
# as-written superclass chain because a singleton class inherits its superclass's singleton class
|
|
806
|
+
# (`class Base; extend Comparable; end; class Widget < Base; end` leaves `Widget.is_a?(Comparable)`
|
|
807
|
+
# true). Empty for a class the project never extends, and for every scope that saw no seeding pass.
|
|
808
|
+
#
|
|
809
|
+
# This is EVIDENCE FOR a singleton ancestor, never against one: the walk sees only what a constant
|
|
810
|
+
# argument to a receiverless `extend` (or, since #915, a receiverless `include` / `prepend` inside a
|
|
811
|
+
# `class << self` body) written inside a declaration body spells, so a runtime `Widget.extend(m)` and an
|
|
812
|
+
# `extend` in a file outside the analysed set are both absent from it, as is an `extend` declared only
|
|
813
|
+
# in RBS — that one reaches the same consumer through `Environment#singleton_extended_modules` instead.
|
|
814
|
+
# Its one consumer ({Inference::Narrowing.narrow_class}) therefore reads it
|
|
815
|
+
# only to WITHHOLD a `Bot`, and never to assert that a guard matches.
|
|
816
|
+
def singleton_extends_of(class_name)
|
|
817
|
+
table = @discovery.discovered_extends
|
|
818
|
+
return [] if table.empty?
|
|
819
|
+
|
|
820
|
+
names = []
|
|
821
|
+
current = class_name.to_s
|
|
822
|
+
seen = {}
|
|
823
|
+
while current && !seen[current] && seen.size <= ANCESTOR_WALK_LIMIT
|
|
824
|
+
seen[current] = true
|
|
825
|
+
record_class_dependency(current) if Analysis::DependencyRecorder.active?
|
|
826
|
+
names.concat(table[current] || [])
|
|
827
|
+
current = @discovery.discovered_superclasses[current]
|
|
828
|
+
end
|
|
829
|
+
names.uniq
|
|
830
|
+
end
|
|
831
|
+
|
|
747
832
|
# ADR-24 slice 2 — the user-side ancestor walk: resolves `method_name` against `class_name`'s own `def`s,
|
|
748
833
|
# then breadth-first through its included / prepended modules (mixins first, as Ruby orders them) and its
|
|
749
834
|
# superclass chain. Returns `[def_node, owner]`, or `[nil, nil]` when nothing in the project defines it.
|
|
@@ -810,9 +895,56 @@ module Rigor
|
|
|
810
895
|
[nil, nil]
|
|
811
896
|
end
|
|
812
897
|
|
|
898
|
+
# Issue #633 — the ancestors a project class reaches that the project itself does NOT declare: the
|
|
899
|
+
# `< StandardError` / `< Array` superclasses and the `include Comparable` mixins the two walks above
|
|
900
|
+
# deliberately drop, gathered breadth-first over the project ancestry so an inherited edge counts too.
|
|
901
|
+
#
|
|
902
|
+
# Each entry is the CANDIDATE LIST for one as-written name ({#ancestor_name_candidates}: the nesting
|
|
903
|
+
# spellings first, the bare name last), not a resolved class — resolving it means asking the RBS
|
|
904
|
+
# environment, which this frozen-index walk does not read. The caller takes the first candidate its own
|
|
905
|
+
# oracle knows and ignores the rest, the same "most-qualified first" order the project-side resolution
|
|
906
|
+
# uses.
|
|
907
|
+
def external_ancestor_name_candidates(class_name, name_memo: {})
|
|
908
|
+
groups = []
|
|
909
|
+
queue = [class_name.to_s]
|
|
910
|
+
seen = {}
|
|
911
|
+
visited = 0
|
|
912
|
+
until queue.empty?
|
|
913
|
+
current = queue.shift
|
|
914
|
+
next if current.nil? || seen[current]
|
|
915
|
+
|
|
916
|
+
seen[current] = true
|
|
917
|
+
visited += 1
|
|
918
|
+
break if visited > ANCESTOR_WALK_LIMIT
|
|
919
|
+
|
|
920
|
+
collect_external_ancestors(current, queue, groups, name_memo)
|
|
921
|
+
end
|
|
922
|
+
groups
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
# One node of {#external_ancestor_name_candidates}: the project-declared ancestors continue the walk,
|
|
926
|
+
# everything else is reported as a candidate list.
|
|
927
|
+
def collect_external_ancestors(current, queue, groups, name_memo)
|
|
928
|
+
raw_names = includes_of(current).dup
|
|
929
|
+
raw_super = superclass_of(current)
|
|
930
|
+
raw_names << raw_super if raw_super
|
|
931
|
+
raw_names.each do |raw|
|
|
932
|
+
resolved = resolve_ancestor_class_name(current, raw, name_memo)
|
|
933
|
+
if resolved
|
|
934
|
+
queue.push(resolved)
|
|
935
|
+
else
|
|
936
|
+
groups << ancestor_name_candidates(current, raw)
|
|
937
|
+
end
|
|
938
|
+
end
|
|
939
|
+
end
|
|
940
|
+
private :collect_external_ancestors
|
|
941
|
+
|
|
813
942
|
# The BFS node cap; a hierarchy past it gives up rather than walking unboundedly (ADR-41 WD4).
|
|
814
943
|
ANCESTOR_WALK_LIMIT = 100
|
|
815
944
|
|
|
945
|
+
EMPTY_HEADER_NESTING = [].freeze
|
|
946
|
+
private_constant :EMPTY_HEADER_NESTING
|
|
947
|
+
|
|
816
948
|
def ancestor_walk_gave_up
|
|
817
949
|
Inference::BudgetTrace.hit(Inference::BudgetTrace::ANCESTOR_WALK_LIMIT)
|
|
818
950
|
[nil, nil]
|
|
@@ -929,10 +1061,25 @@ module Rigor
|
|
|
929
1061
|
return [raw.delete_prefix("::")] if raw.start_with?("::")
|
|
930
1062
|
|
|
931
1063
|
recorded = @discovery.discovered_header_nestings[subclass_qualified.to_s]
|
|
932
|
-
entries = recorded
|
|
1064
|
+
entries = recorded ? recorded_header_nesting(recorded, raw) : peeled_header_nesting(subclass_qualified)
|
|
933
1065
|
entries.map { |entry| "#{entry}::#{raw_ancestor}" } << raw_ancestor.to_s
|
|
934
1066
|
end
|
|
935
1067
|
|
|
1068
|
+
# Issue #728 — the chain of the declaration site that WROTE `raw`, which is the cref Ruby resolves that
|
|
1069
|
+
# one name in. A class's sites need not agree: `class Foo < Base` at the top level and a rooted
|
|
1070
|
+
# `class ::Foo; include Helper; end` inside `module Outer` are two sites of `Foo`, and the union of
|
|
1071
|
+
# their chains put `Outer::Base` — a class Ruby never looks at — ahead of `::Base` for the superclass
|
|
1072
|
+
# the top-level site wrote. That is a WRONG CLASS, not a wider candidate list.
|
|
1073
|
+
#
|
|
1074
|
+
# The unkeyed entry is the union, and answers a name no site recorded under its own key: an `include`
|
|
1075
|
+
# attributed to this class from OUTSIDE its declaration (`Recv.class_eval { include M }`, which
|
|
1076
|
+
# `Inference::ScopeIndexer#walk_class_includes` names but the header walk cannot see), or a dynamic
|
|
1077
|
+
# mixin argument. It is the pre-#728 answer, so those are unchanged rather than degraded.
|
|
1078
|
+
def recorded_header_nesting(bucket, raw)
|
|
1079
|
+
bucket[raw] || bucket[DiscoveryIndex::UNKEYED_HEADER_NESTING] || EMPTY_HEADER_NESTING
|
|
1080
|
+
end
|
|
1081
|
+
private :recorded_header_nesting
|
|
1082
|
+
|
|
936
1083
|
# The pre-#682 reading of a qualified class name: every proper prefix of it, innermost first, as if the
|
|
937
1084
|
# class had been declared one `module` keyword per segment.
|
|
938
1085
|
def peeled_header_nesting(subclass_qualified)
|
|
@@ -974,6 +1121,21 @@ module Rigor
|
|
|
974
1121
|
end
|
|
975
1122
|
private :record_class_dependency
|
|
976
1123
|
|
|
1124
|
+
# Issue #639 — the EXISTENCE edge for a bare class reference, `class:<last segment>`, the hit-side twin of
|
|
1125
|
+
# the miss-side key `Inference::ExpressionTyper` already records. ADR-46 slice 1c left the existence hit
|
|
1126
|
+
# edgeless on the reasoning that a file merely referencing `Post` depends on `Post`'s methods, not on its
|
|
1127
|
+
# bare existence; that stops holding the moment the DECLARING FILE IS DELETED, because existence is then
|
|
1128
|
+
# exactly what changed and a reference-only consumer had recorded nothing to be re-checked by.
|
|
1129
|
+
#
|
|
1130
|
+
# NAME-keyed rather than a positive edge to the declaring file, which is the distinction the ADR's cost
|
|
1131
|
+
# argument turns on: the declared-class set of a file does not move when a method body in it is edited,
|
|
1132
|
+
# so a bare referent is re-checked when the class appears or disappears and at no other time. A file edge
|
|
1133
|
+
# would have re-checked every bare referent of every class the file declares on any edit to it.
|
|
1134
|
+
def record_class_existence(class_name)
|
|
1135
|
+
segment = class_name.to_s.delete_prefix("::").split("::").last
|
|
1136
|
+
Analysis::DependencyRecorder.read_name(:class, segment) if segment
|
|
1137
|
+
end
|
|
1138
|
+
|
|
977
1139
|
# Issue #644 — the positive ADR-46 edge for a cross-file VALUE constant. `Reflection.constant_type_at`
|
|
978
1140
|
# calls this the moment a candidate resolves through `in_source_constants`, so the reader depends on the
|
|
979
1141
|
# file that wrote the constant: editing the literal, or deleting the file, re-checks the reader. Recorded
|
|
@@ -1125,7 +1287,8 @@ module Rigor
|
|
|
1125
1287
|
@globals == other.globals &&
|
|
1126
1288
|
@indexed_narrowings == other.indexed_narrowings &&
|
|
1127
1289
|
@method_chain_narrowings == other.method_chain_narrowings &&
|
|
1128
|
-
@declaration_sourced == other.declaration_sourced
|
|
1290
|
+
@declaration_sourced == other.declaration_sourced &&
|
|
1291
|
+
@published_constant_sourced == other.published_constant_sourced
|
|
1129
1292
|
end
|
|
1130
1293
|
alias eql? ==
|
|
1131
1294
|
|
|
@@ -1142,6 +1305,7 @@ module Rigor
|
|
|
1142
1305
|
indexed_narrowings: @indexed_narrowings,
|
|
1143
1306
|
method_chain_narrowings: @method_chain_narrowings,
|
|
1144
1307
|
declaration_sourced: @declaration_sourced,
|
|
1308
|
+
published_constant_sourced: @published_constant_sourced,
|
|
1145
1309
|
source_path: @source_path,
|
|
1146
1310
|
struct_fold_safe_locals: @struct_fold_safe_locals,
|
|
1147
1311
|
opaque_block_self: @opaque_block_self,
|
|
@@ -1163,6 +1327,7 @@ module Rigor
|
|
|
1163
1327
|
indexed_narrowings: indexed_narrowings,
|
|
1164
1328
|
method_chain_narrowings: method_chain_narrowings,
|
|
1165
1329
|
declaration_sourced: declaration_sourced,
|
|
1330
|
+
published_constant_sourced: published_constant_sourced,
|
|
1166
1331
|
source_path: source_path,
|
|
1167
1332
|
struct_fold_safe_locals: struct_fold_safe_locals,
|
|
1168
1333
|
opaque_block_self: opaque_block_self,
|
|
@@ -1208,6 +1373,12 @@ module Rigor
|
|
|
1208
1373
|
# path made the binding flow-live (a method-local nil write / failed-guard narrowing), the merge is
|
|
1209
1374
|
# flow-live and `possible-nil-receiver` fires as before.
|
|
1210
1375
|
declaration_sourced: join_declaration_sourced(other),
|
|
1376
|
+
# Issue #667 — UNION, the opposite of the line above, which is the first reason this mark does not
|
|
1377
|
+
# ride the ADR-58 Set. The mark only ever WITHHOLDS a firing, so keeping it when either arm bound the
|
|
1378
|
+
# name from a published constant is the false-positive-safe merge: `if c then m = MODE else m = MODE2
|
|
1379
|
+
# end; m == :x` must not warn because one arm's copy is invisible to the reader's author. ADR-67
|
|
1380
|
+
# WD6b's `:inferred_param` taint takes the same direction for the same reason.
|
|
1381
|
+
published_constant_sourced: join_published_constant_sourced(other),
|
|
1211
1382
|
source_path: source_path,
|
|
1212
1383
|
# Issue #589 — the fold-safe set MUST survive a merge. It was simply absent from this constructor
|
|
1213
1384
|
# call, so it fell back to the empty default and every `if` / `while` in a method silently revoked
|
|
@@ -1339,6 +1510,34 @@ module Rigor
|
|
|
1339
1510
|
end
|
|
1340
1511
|
|
|
1341
1512
|
# ADR-58 WD1 — set/clear the declaration-sourced provenance mark.
|
|
1513
|
+
# Issue #667 — the #667 carrier's add/drop pair. Deliberately not reusing
|
|
1514
|
+
# {#add_declaration_sourced} / {#drop_declaration_sourced_for}: the two sets are joined by opposite
|
|
1515
|
+
# policies, so sharing the storage would make every future edit to either one a decision about both.
|
|
1516
|
+
def add_published_constant_sourced(kind, name)
|
|
1517
|
+
ref = [kind.to_sym, name.to_sym].freeze
|
|
1518
|
+
return @published_constant_sourced if @published_constant_sourced.include?(ref)
|
|
1519
|
+
|
|
1520
|
+
(@published_constant_sourced.dup << ref).freeze
|
|
1521
|
+
end
|
|
1522
|
+
|
|
1523
|
+
def drop_published_constant_sourced_for(kind, name)
|
|
1524
|
+
return @published_constant_sourced if @published_constant_sourced.empty?
|
|
1525
|
+
|
|
1526
|
+
ref = [kind.to_sym, name.to_sym]
|
|
1527
|
+
return @published_constant_sourced unless @published_constant_sourced.include?(ref)
|
|
1528
|
+
|
|
1529
|
+
(@published_constant_sourced - [ref]).freeze
|
|
1530
|
+
end
|
|
1531
|
+
|
|
1532
|
+
def join_published_constant_sourced(other)
|
|
1533
|
+
mine = @published_constant_sourced
|
|
1534
|
+
theirs = other.published_constant_sourced
|
|
1535
|
+
return mine if mine.equal?(theirs) || theirs.empty?
|
|
1536
|
+
return theirs if mine.empty?
|
|
1537
|
+
|
|
1538
|
+
(mine | theirs).freeze
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1342
1541
|
def add_declaration_sourced(kind, name)
|
|
1343
1542
|
ref = [kind.to_sym, name.to_sym]
|
|
1344
1543
|
return @declaration_sourced if @declaration_sourced.include?(ref)
|