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
|
@@ -84,7 +84,29 @@ module Rigor
|
|
|
84
84
|
# `[]`, deserialises cleanly, and would send every unchanged file's top-level helper back to the peel
|
|
85
85
|
# on a warm run while a cold run resolved at the top level — the `--verify-incremental` divergence 14
|
|
86
86
|
# was bumped for, in the same table.
|
|
87
|
-
|
|
87
|
+
# 18: issue #728 re-keys each seed bundle's `header_nestings` VALUE from one chain per class to one
|
|
88
|
+
# chain per ancestor NAME the class's sites wrote, plus an unkeyed chain holding the old union. A
|
|
89
|
+
# pre-18 blob's Array value is not a Hash and the resolver's per-name lookup would raise on it, so the
|
|
90
|
+
# gate has to reject it — and even a reader that tolerated the old shape would serve the per-class
|
|
91
|
+
# union for every unchanged file, which is the answer this issue exists to stop giving.
|
|
92
|
+
# 19: issue #668 re-keys a constant write through a base no name reaches — `k::LIMIT = 7` — from the
|
|
93
|
+
# bare last segment to the wildcard `*::LIMIT`, in `constant_decls` and in each seed bundle's
|
|
94
|
+
# `constant_writes` census alike. A pre-19 blob's `LIMIT` key deserialises cleanly and reads as an
|
|
95
|
+
# ordinary top-level write, so every unchanged file would keep publishing the `Foo::LIMIT` the write
|
|
96
|
+
# may already have replaced while a cold run made it gradual — the `--verify-incremental` divergence
|
|
97
|
+
# 14 was bumped for.
|
|
98
|
+
# 20: issue #722 residue 2 adds each seed bundle's compact-header re-anchor census, without which a
|
|
99
|
+
# warm fold sees no candidate for an unchanged file and keeps serving the pre-fix `Wrap::Outer::Leaf`
|
|
100
|
+
# for a `class Outer::Leaf` that reopens `::Outer::Leaf`.
|
|
101
|
+
# 21: issue #915 widens the seed bundle's `extends` table to a singleton-body `include` / `prepend`;
|
|
102
|
+
# a pre-21 bundle for an unchanged file would keep the narrower record and still report the
|
|
103
|
+
# `case Widget when M` arm unreachable.
|
|
104
|
+
# 22: issues #796 / #794 add `run_level_rows` — the cold run's `definition-build-failed` set and its
|
|
105
|
+
# HKT-scan outcome, the two rows a narrowed run cannot re-derive from the files it analyses. A pre-22
|
|
106
|
+
# blob has no such section and would deserialise it as nil, which reads as "nothing cached" and is
|
|
107
|
+
# therefore not misread — but a snapshot written by an engine that did not yet REPLAY them was also
|
|
108
|
+
# written by one whose narrowed runs under-reported, so the gate rejects it and the next run is cold.
|
|
109
|
+
SCHEMA = 22
|
|
88
110
|
|
|
89
111
|
# The persisted per-file state.
|
|
90
112
|
# `cache` maps an analyzed file to its diagnostics.
|
|
@@ -136,11 +158,21 @@ module Rigor
|
|
|
136
158
|
# collection was off). It is a SEPARATE gate from the global `fingerprint`: the `effects:` block is
|
|
137
159
|
# deliberately absent from `Configuration#to_h`, so turning collection on invalidates no diagnostics,
|
|
138
160
|
# and a vocabulary / catalogue / `effects:` change must invalidate the summaries alone.
|
|
161
|
+
# Issues #796 / #794 — the run-level rows a narrowed run replays instead of re-deriving; see
|
|
162
|
+
# docs/internal-spec/cache.md § "Run-level rows the snapshot carries" for why exactly these two are
|
|
163
|
+
# fresh under the fingerprint and why no other `.rigor.yml`-level row may join them.
|
|
139
164
|
Payload = Data.define(:cache, :sources, :digests, :analyzed,
|
|
140
165
|
:symbol_sources, :ancestry_sources, :symbol_fingerprints,
|
|
141
166
|
:missing, :class_decls, :constant_decls, :seed_bundles, :plugin_fact_digest,
|
|
142
167
|
:return_summaries, :param_table,
|
|
143
|
-
:effect_collections, :effects_identity)
|
|
168
|
+
:effect_collections, :effects_identity, :run_level_rows)
|
|
169
|
+
|
|
170
|
+
# Issues #796 / #794 — the two run-level rows the snapshot carries. `definition_build_failures` is the
|
|
171
|
+
# reported per-class detail list ({Environment::RbsLoader#definition_build_failures}); `hkt_scan_failure`
|
|
172
|
+
# is the `[error_class, first_line, frame, stage]` tuple ({Rigor::Environment#hkt_scan_failure}), nil
|
|
173
|
+
# when the scan succeeded — which is why the presence of this object, not the tuple, is what says a
|
|
174
|
+
# replay is available.
|
|
175
|
+
RunLevelRows = Data.define(:definition_build_failures, :hkt_scan_failure)
|
|
144
176
|
|
|
145
177
|
# The global fingerprint that gates a snapshot load: a digest of the inputs whose change requires a full
|
|
146
178
|
# rebuild — the engine version + schema, the engine's own SOURCE when the version does not pin it, the
|
|
@@ -222,8 +254,8 @@ module Rigor
|
|
|
222
254
|
# The fingerprint that matched is returned alongside the payload so the caller can mix it into ITS own
|
|
223
255
|
# cache key — the snapshot's identity is exactly what "these dependency edges came from that world" means.
|
|
224
256
|
#
|
|
225
|
-
# @param fingerprints
|
|
226
|
-
# @return
|
|
257
|
+
# @param fingerprints — candidates, most-specific first.
|
|
258
|
+
# @return `[matched fingerprint, payload]`, or nil on any miss.
|
|
227
259
|
def load_any(fingerprints:)
|
|
228
260
|
data = read_data
|
|
229
261
|
return nil if data.nil?
|
|
@@ -259,7 +291,8 @@ module Rigor
|
|
|
259
291
|
return_summaries: data[:return_summaries] || {},
|
|
260
292
|
param_table: data[:param_table] || {},
|
|
261
293
|
effect_collections: data[:effect_collections] || {},
|
|
262
|
-
effects_identity: data[:effects_identity]
|
|
294
|
+
effects_identity: data[:effects_identity],
|
|
295
|
+
run_level_rows: data[:run_level_rows]
|
|
263
296
|
)
|
|
264
297
|
end
|
|
265
298
|
private :payload_from
|
|
@@ -283,7 +316,8 @@ module Rigor
|
|
|
283
316
|
return_summaries: payload.return_summaries,
|
|
284
317
|
param_table: payload.param_table,
|
|
285
318
|
effect_collections: payload.effect_collections,
|
|
286
|
-
effects_identity: payload.effects_identity
|
|
319
|
+
effects_identity: payload.effects_identity,
|
|
320
|
+
run_level_rows: payload.run_level_rows
|
|
287
321
|
)
|
|
288
322
|
blob = Zlib::Deflate.deflate(raw)
|
|
289
323
|
tmp = "#{@path}.#{Process.pid}.tmp"
|
|
@@ -20,9 +20,6 @@ module Rigor
|
|
|
20
20
|
class RbsClassAncestorTable < RbsCacheProducer
|
|
21
21
|
PRODUCER_ID = "rbs.class_ancestor_table"
|
|
22
22
|
|
|
23
|
-
# @param loader [Rigor::Environment::RbsLoader]
|
|
24
|
-
# @param store [Rigor::Cache::Store]
|
|
25
|
-
# @return [Hash{String => Array<String>}]
|
|
26
23
|
def self.compute(loader)
|
|
27
24
|
table = {}
|
|
28
25
|
loader.each_known_class_name do |name|
|
|
@@ -20,9 +20,6 @@ module Rigor
|
|
|
20
20
|
class RbsClassTypeParamNames < RbsCacheProducer
|
|
21
21
|
PRODUCER_ID = "rbs.class_type_param_names"
|
|
22
22
|
|
|
23
|
-
# @param loader [Rigor::Environment::RbsLoader]
|
|
24
|
-
# @param store [Rigor::Cache::Store]
|
|
25
|
-
# @return [Hash{String => Array<Symbol>}]
|
|
26
23
|
def self.compute(loader)
|
|
27
24
|
table = {}
|
|
28
25
|
loader.each_known_class_name do |name|
|
|
@@ -15,9 +15,6 @@ module Rigor
|
|
|
15
15
|
class RbsConstantTable < RbsCacheProducer
|
|
16
16
|
PRODUCER_ID = "rbs.constant_type_table"
|
|
17
17
|
|
|
18
|
-
# @param loader [Rigor::Environment::RbsLoader]
|
|
19
|
-
# @param store [Rigor::Cache::Store]
|
|
20
|
-
# @return [Hash{String => Rigor::Type}]
|
|
21
18
|
def self.compute(loader)
|
|
22
19
|
table = {}
|
|
23
20
|
loader.each_constant_decl do |name, entry|
|
|
@@ -12,13 +12,11 @@ module Rigor
|
|
|
12
12
|
# slots, so factoring the construction here keeps the producers small and ensures invalidation behaves
|
|
13
13
|
# identically across them.
|
|
14
14
|
module RbsDescriptor
|
|
15
|
-
# @param loader [Rigor::Environment::RbsLoader]
|
|
16
|
-
# @return [Rigor::Cache::Descriptor]
|
|
17
15
|
def self.build(loader)
|
|
18
16
|
Descriptor.new(
|
|
19
17
|
gems: [rbs_gem_entry],
|
|
20
18
|
files: file_entries(loader),
|
|
21
|
-
configs: config_entries(loader)
|
|
19
|
+
configs: config_entries(loader) + env_only_config_entries(loader)
|
|
22
20
|
)
|
|
23
21
|
end
|
|
24
22
|
|
|
@@ -26,8 +24,9 @@ module Rigor
|
|
|
26
24
|
# `gems` + `configs` ({Runner#run_key_descriptor}); the RBS signature-tree `files` are read solely on a
|
|
27
25
|
# MISS, by the dependency descriptor ({Runner#run_dependency_descriptor}). So a warm HIT never digests
|
|
28
26
|
# the (large, vendored) RBS tree. {RunDescriptor} is NOT a {Descriptor} — it is never composed, hashed,
|
|
29
|
-
# or `==`'d, only its
|
|
30
|
-
# / `configs`
|
|
27
|
+
# or `==`'d, only its four readers are consulted — so deferring `files` costs no soundness, and `gems`
|
|
28
|
+
# / `configs` match {.build}'s shared slots (the key is unchanged; the one slot the env key adds on
|
|
29
|
+
# top, {.env_only_config_entries}, is never read here).
|
|
31
30
|
def self.build_run(loader)
|
|
32
31
|
RunDescriptor.new(loader: loader, gems: [rbs_gem_entry], configs: config_entries(loader))
|
|
33
32
|
end
|
|
@@ -38,19 +37,47 @@ module Rigor
|
|
|
38
37
|
[libraries_entry(loader.libraries), virtual_rbs_entry(loader)].compact
|
|
39
38
|
end
|
|
40
39
|
|
|
40
|
+
# Issue #610 — the slots only the env-cache KEY reads, on top of {.config_entries}. Which of the
|
|
41
|
+
# loader's `signature_paths:` are DEFERRED (a bundled plugin's `sig/`, loaded last and allowed to
|
|
42
|
+
# stand down against a colliding generic arity) changes the env built from byte-identical files, so
|
|
43
|
+
# the partition belongs in the key: moving a plugin's `sig/` between `plugins:` and an explicit
|
|
44
|
+
# `signature_paths:` entry changes no file, and an env cached before the producer honoured the
|
|
45
|
+
# partition must read as a miss once. Kept OUT of {.config_entries} deliberately — the run-result
|
|
46
|
+
# key already digests the whole resolved configuration (`RunCacheKey`), and its boot-slimming probe
|
|
47
|
+
# reconstructs {.config_entries} without loading a plugin, so an entry there that it cannot rebuild
|
|
48
|
+
# would turn the warm fast lane into a permanent miss on every project that enables a bundled plugin.
|
|
49
|
+
def self.env_only_config_entries(loader)
|
|
50
|
+
[deferred_signature_paths_entry(loader)].compact
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.deferred_signature_paths_entry(loader)
|
|
54
|
+
return nil unless loader.respond_to?(:deferred_signature_paths)
|
|
55
|
+
|
|
56
|
+
deferred = Array(loader.deferred_signature_paths).map(&:to_s).sort
|
|
57
|
+
return nil if deferred.empty?
|
|
58
|
+
|
|
59
|
+
Descriptor::ConfigEntry.new(
|
|
60
|
+
key: "rbs.deferred_signature_paths",
|
|
61
|
+
value_hash: Digest::SHA256.hexdigest(deferred.join("\n"))
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private_class_method :deferred_signature_paths_entry
|
|
66
|
+
|
|
41
67
|
# Public (ADR-87 WD4) so the boot-slimming run-cache probe reconstructs the identical `gems` +
|
|
42
68
|
# `rbs.libraries` key slots the runner writes, from the config-derived library list, without a loader.
|
|
43
69
|
def self.rbs_gem_entry
|
|
44
70
|
Descriptor::GemEntry.new(name: "rbs", requirement: ">= 0", locked: ::RBS::VERSION.to_s)
|
|
45
71
|
end
|
|
46
72
|
|
|
47
|
-
# @param comparator
|
|
73
|
+
# @param comparator — `:digest` (default) for the env-cache KEY descriptor ({.build}), where the
|
|
48
74
|
# value must be deterministic; `:stat` (ADR-87 WD1) for the validation-only run-dependency descriptor
|
|
49
75
|
# ({RunDescriptor#files}), where the stat tier short-circuits the SHA-256 on an unmoved file.
|
|
50
76
|
def self.file_entries(loader, comparator: :digest)
|
|
51
77
|
roots = loader.signature_paths +
|
|
52
78
|
Rigor::Environment::RbsLoader.vendored_gem_sig_paths +
|
|
53
|
-
Rigor::Environment::RbsLoader.core_overlay_sig_paths
|
|
79
|
+
Rigor::Environment::RbsLoader.core_overlay_sig_paths +
|
|
80
|
+
Rigor::Environment::RbsLoader.capability_role_sig_paths
|
|
54
81
|
roots.flat_map do |root|
|
|
55
82
|
next [] unless root.directory?
|
|
56
83
|
|
|
@@ -65,7 +92,40 @@ module Rigor
|
|
|
65
92
|
end
|
|
66
93
|
end
|
|
67
94
|
|
|
68
|
-
#
|
|
95
|
+
# Issue #979 — the directory-LISTING rows for the signature roots, one {Descriptor::GlobEntry} per
|
|
96
|
+
# root over `**/*.rbs`. {.file_entries} answers "did any signature file I read change", which is a
|
|
97
|
+
# question only about files that existed when the run recorded its dependencies; a `sig/roles.rbs`
|
|
98
|
+
# written afterwards is in no row, so the ADR-45 run-result slot validated fresh and the warm run kept
|
|
99
|
+
# serving diagnostics computed without it. A glob row is the same edge {Plugin::IoBoundary} records
|
|
100
|
+
# for a listed directory (#954): re-globbing on the next run sees the appearance, and one row per root
|
|
101
|
+
# (not per file) keeps the cost a single `Dir.glob`.
|
|
102
|
+
#
|
|
103
|
+
# The rows are `:names` mode, not the default `:stat`: the question they add is which signature files
|
|
104
|
+
# EXIST, and every edit to one of them is already carried by that file's own ADR-87 WD1 `:stat` row —
|
|
105
|
+
# which survives a moved stat tuple by falling back to its recorded content digest. A stat-mode glob
|
|
106
|
+
# would not, so a `git checkout`, a `bundle install`, or a CI run with a restored cache over a fresh
|
|
107
|
+
# checkout — none of which changes a byte of RBS — would turn every warm run into a full re-analysis.
|
|
108
|
+
# Names mode also reads no stat, so it neither pays for nor perturbs the per-run validation-stat memo.
|
|
109
|
+
#
|
|
110
|
+
# Scoped to the loader's own roots — the project's `signature_paths:` (including the auto-detected
|
|
111
|
+
# `sig/`), the bundled-plugin `sig/` trees (ADR-25), the bundle walk, `rbs collection`, the ADR-72
|
|
112
|
+
# overlays. Rigor's own `data/` trees ({RbsLoader.vendored_gem_sig_paths}, `.core_overlay_sig_paths`,
|
|
113
|
+
# `.capability_role_sig_paths`) are engine-owned: a file appears under one only when the engine tree is
|
|
114
|
+
# edited, which normally arrives with the `lib/` change that reads it and so re-keys every
|
|
115
|
+
# computed-value slot through {EngineSource}. They get file rows and no glob — a glob there would cost
|
|
116
|
+
# every project a stat walk of the vendored gem-signature tree per warm run to catch an
|
|
117
|
+
# engine-development edit.
|
|
118
|
+
def self.glob_entries(loader)
|
|
119
|
+
# Deduplicated by path: a root reachable twice (an explicit `signature_paths:` entry that is also a
|
|
120
|
+
# bundled plugin's `sig/`, #610) would otherwise contribute two rows whose stat signatures race.
|
|
121
|
+
loader.signature_paths.uniq(&:to_s).filter_map do |root|
|
|
122
|
+
next unless root.directory?
|
|
123
|
+
|
|
124
|
+
Descriptor::GlobEntry.compute(root: root.to_s, pattern: File.join("**", "*.rbs"), mode: :names)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @param library_names — the loader's merged library list (or, on the WD4 probe
|
|
69
129
|
# path, `Environment::DEFAULT_LIBRARIES + config.libraries` reconstructed without a loader).
|
|
70
130
|
def self.libraries_entry(library_names)
|
|
71
131
|
sorted = library_names.map(&:to_s).sort
|
|
@@ -93,10 +153,11 @@ module Rigor
|
|
|
93
153
|
|
|
94
154
|
private_class_method :virtual_rbs_entry
|
|
95
155
|
|
|
96
|
-
# The lazy-files run descriptor {RbsDescriptor.build_run} returns. Exposes the
|
|
156
|
+
# The lazy-files run descriptor {RbsDescriptor.build_run} returns. Exposes the four readers the
|
|
97
157
|
# run-diagnostics cache consults — `gems` + `configs` are supplied eagerly (they feed the cache KEY,
|
|
98
|
-
# and are cheap); `files` (the RBS signature-tree digests
|
|
99
|
-
# first access and memoised, so a warm HIT
|
|
158
|
+
# and are cheap); `files` (the RBS signature-tree digests) and `globs` (#979, the per-root listing
|
|
159
|
+
# rows) are read only on a MISS, computed once on first access and memoised, so a warm HIT pays for
|
|
160
|
+
# neither.
|
|
100
161
|
class RunDescriptor
|
|
101
162
|
attr_reader :gems, :configs
|
|
102
163
|
|
|
@@ -106,6 +167,13 @@ module Rigor
|
|
|
106
167
|
@configs = configs
|
|
107
168
|
end
|
|
108
169
|
|
|
170
|
+
# Issue #979 — one directory-listing row per signature root, so a `.rbs` file APPEARING under one
|
|
171
|
+
# (or vanishing from it) invalidates the run-result slot. Memoised for the same reason `files` is:
|
|
172
|
+
# a collecting run asks twice about the same post-run world.
|
|
173
|
+
def globs
|
|
174
|
+
@globs ||= RbsDescriptor.glob_entries(@loader)
|
|
175
|
+
end
|
|
176
|
+
|
|
109
177
|
def files
|
|
110
178
|
# ADR-87 WD1 — this descriptor is validated (never a cache key), so the RBS signature tree rides the
|
|
111
179
|
# stat-then-digest `:stat` tier: a warm run stat-checks the (large, vendored) tree instead of
|
|
@@ -22,14 +22,18 @@ module Rigor
|
|
|
22
22
|
class RbsEnvironment < RbsCacheProducer
|
|
23
23
|
PRODUCER_ID = "rbs.environment"
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
25
|
+
# Issue #610 — the deferred (plugin-contributed) subset rides through too. This is the build path every
|
|
26
|
+
# cached run takes: a cache store is the CLI default and only `--no-cache` reaches the loader's own
|
|
27
|
+
# `build_env`, so a list threaded through the loader but not through here ran the arity stand-down on
|
|
28
|
+
# no real `rigor check` at all — 0.3.8 shipped exactly that, behind a green gate built over a
|
|
29
|
+
# store-less loader. Any input `build_env_for` takes MUST be passed from here as well.
|
|
28
30
|
def self.compute(loader)
|
|
29
31
|
Rigor::Environment::RbsLoader.build_env_for(
|
|
30
32
|
libraries: loader.libraries,
|
|
31
33
|
signature_paths: loader.signature_paths,
|
|
32
|
-
virtual_rbs: loader.respond_to?(:virtual_rbs) ? loader.virtual_rbs : []
|
|
34
|
+
virtual_rbs: loader.respond_to?(:virtual_rbs) ? loader.virtual_rbs : [],
|
|
35
|
+
deferred_signature_paths:
|
|
36
|
+
loader.respond_to?(:deferred_signature_paths) ? loader.deferred_signature_paths : []
|
|
33
37
|
)
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -18,9 +18,6 @@ module Rigor
|
|
|
18
18
|
class RbsKnownClassNames < RbsCacheProducer
|
|
19
19
|
PRODUCER_ID = "rbs.known_class_names"
|
|
20
20
|
|
|
21
|
-
# @param loader [Rigor::Environment::RbsLoader]
|
|
22
|
-
# @param store [Rigor::Cache::Store]
|
|
23
|
-
# @return [Set<String>]
|
|
24
21
|
def self.compute(loader)
|
|
25
22
|
names = Set.new
|
|
26
23
|
loader.each_known_class_name { |name| names << name }
|
data/lib/rigor/cache/store.rb
CHANGED
|
@@ -65,8 +65,8 @@ module Rigor
|
|
|
65
65
|
|
|
66
66
|
VALID_PRODUCER_ID = /\A[a-z][a-z0-9._-]*\z/
|
|
67
67
|
|
|
68
|
-
# @param root
|
|
69
|
-
# @param read_only
|
|
68
|
+
# @param root — cache root directory.
|
|
69
|
+
# @param read_only — when true, every disk-side side-effect is suppressed: `fetch_or_compute`
|
|
70
70
|
# still reads existing entries (hits, gated on a current `schema_version.txt` marker — see
|
|
71
71
|
# {#ensure_schema_version!}) and still runs the producer block on miss, but it does NOT write the
|
|
72
72
|
# produced value to disk, does NOT update the marker, and does NOT touch the on-disk root directory.
|
|
@@ -107,7 +107,7 @@ module Rigor
|
|
|
107
107
|
|
|
108
108
|
attr_reader :root
|
|
109
109
|
|
|
110
|
-
# @return
|
|
110
|
+
# @return whether this Store suppresses disk writes (`schema_version.txt`, entry creation).
|
|
111
111
|
# Reads are unaffected.
|
|
112
112
|
def read_only?
|
|
113
113
|
@read_only
|
|
@@ -118,7 +118,7 @@ module Rigor
|
|
|
118
118
|
# specific instance rather than the on-disk cache state. Disk-level state is reported separately by
|
|
119
119
|
# {.disk_inventory}.
|
|
120
120
|
#
|
|
121
|
-
# @return
|
|
121
|
+
# @return `{ hits:, misses:, writes:, by_producer: { id => { hits:, misses:, writes: } } }`
|
|
122
122
|
def stats
|
|
123
123
|
@monitor.synchronize do
|
|
124
124
|
per_producer = @by_producer.transform_values { |counts| counts.dup.freeze }.freeze
|
|
@@ -130,7 +130,7 @@ module Rigor
|
|
|
130
130
|
# `rigor check --cache-stats` to surface cache size and per-producer entry counts without depending on
|
|
131
131
|
# in-process counters (which only reflect the current run).
|
|
132
132
|
#
|
|
133
|
-
# @return
|
|
133
|
+
# @return `{ root:, schema_version:, total_entries:, total_bytes:, producers: [{ id:, entries:,
|
|
134
134
|
# bytes: }, ...] }`. When the root does not exist or has no schema-version marker, `schema_version` is
|
|
135
135
|
# nil and the producer list is empty.
|
|
136
136
|
#
|
|
@@ -176,21 +176,21 @@ module Rigor
|
|
|
176
176
|
end
|
|
177
177
|
private_class_method :collect_producers
|
|
178
178
|
|
|
179
|
-
# @param producer_id
|
|
180
|
-
# @param generation_cap
|
|
179
|
+
# @param producer_id — stable cache namespace; only `[a-z][a-z0-9._-]*` is accepted.
|
|
180
|
+
# @param generation_cap — how many generations of this producer survive a compaction
|
|
181
181
|
# pass — a positive `Integer` for a whole-project producer (one live entry, older ones unreachable),
|
|
182
182
|
# or {UNBOUNDED_GENERATIONS} for a producer with many simultaneously-live entries. REQUIRED, and
|
|
183
183
|
# sourced from the producer's own declaration (`RbsCacheProducer.generation_cap`,
|
|
184
184
|
# `RunCacheKey::GENERATION_CAP`, `Plugin::Base.producer generation_cap:`) rather than invented at the
|
|
185
185
|
# call site. See {#evict!}.
|
|
186
|
-
# @param params
|
|
187
|
-
# @param descriptor
|
|
188
|
-
# @param serialize
|
|
186
|
+
# @param params — producer inputs; mixed into the cache key via {Descriptor#cache_key_for}.
|
|
187
|
+
# @param descriptor — the invalidation descriptor for the value being cached.
|
|
188
|
+
# @param serialize — optional callable that turns the producer's return value into a binary
|
|
189
189
|
# `String`. Defaults to `Marshal.dump(value).b`. Producers whose return values are not
|
|
190
190
|
# `Marshal`-clean (RBS-native objects with `RBS::Location` members, raw `IO`, …) MUST provide a
|
|
191
191
|
# serialiser. The pair `(serialize, deserialize)` MUST round-trip — a producer that reads with one
|
|
192
192
|
# strategy and writes with another corrupts its own cache slice.
|
|
193
|
-
# @param deserialize
|
|
193
|
+
# @param deserialize — optional callable that turns bytes back into the producer's value.
|
|
194
194
|
# Defaults to `Marshal.load`. Any exception (`StandardError`) raised by the deserialiser is treated as
|
|
195
195
|
# a cache miss — the entry is considered corrupt, the producer block reruns, and the next write
|
|
196
196
|
# overwrites it. This is consistent with the fault-tolerance contract for the default `Marshal.load`
|
|
@@ -394,10 +394,22 @@ module Rigor
|
|
|
394
394
|
|
|
395
395
|
# Reads and validates one entry file. Any failure (missing, short, bad magic, bad version, bad
|
|
396
396
|
# checksum, unmarshal-able) returns nil so the caller treats it as a cache miss.
|
|
397
|
+
#
|
|
398
|
+
# The `ENOENT` rescue covers the gap between the existence check and the open: a concurrent process
|
|
399
|
+
# compacting or clearing the root can unlink the file in between, and a file that is gone reads as the
|
|
400
|
+
# same miss the existence check above already reports for one that was never there (issue #807). It is
|
|
401
|
+
# deliberately narrow — every other `SystemCallError` (a permission error, an I/O error) still raises,
|
|
402
|
+
# so a genuinely broken filesystem stays visible instead of degrading into an endless recompute. A
|
|
403
|
+
# present-but-corrupt file is not touched by this either: it still goes through the envelope and
|
|
404
|
+
# payload checks below.
|
|
397
405
|
def read_entry(path, deserialize: nil)
|
|
398
406
|
return nil unless File.file?(path)
|
|
399
407
|
|
|
400
|
-
|
|
408
|
+
begin
|
|
409
|
+
bytes = File.binread(path)
|
|
410
|
+
rescue Errno::ENOENT
|
|
411
|
+
return nil
|
|
412
|
+
end
|
|
401
413
|
return nil unless envelope_valid?(bytes)
|
|
402
414
|
|
|
403
415
|
body = bytes.byteslice(HEADER.bytesize, bytes.bytesize - HEADER.bytesize - 32)
|
|
@@ -486,22 +498,34 @@ module Rigor
|
|
|
486
498
|
def atomically_replace(path, body)
|
|
487
499
|
File.open(path, File::RDWR | File::CREAT, 0o644) do |lock_fd|
|
|
488
500
|
lock_fd.flock(File::LOCK_EX)
|
|
489
|
-
|
|
490
|
-
begin
|
|
491
|
-
File.open(tmp, "wb") do |f|
|
|
492
|
-
f.write(body)
|
|
493
|
-
f.fsync
|
|
494
|
-
end
|
|
495
|
-
File.rename(tmp, path)
|
|
496
|
-
ensure
|
|
497
|
-
# A failed write/rename must not leak its temp file — rely on the 1-hour `cleanup_stale_temp_files`
|
|
498
|
-
# sweep only as a backstop for crashes that skip this ensure entirely.
|
|
499
|
-
unlink_entry(tmp) if File.exist?(tmp)
|
|
500
|
-
end
|
|
501
|
+
publish_by_rename(path, body)
|
|
501
502
|
fsync_directory(File.dirname(path))
|
|
502
503
|
end
|
|
503
504
|
end
|
|
504
505
|
|
|
506
|
+
# The rename-into-place half of {#atomically_replace}, without the destination lock: writes `body` to a
|
|
507
|
+
# sibling temp file, fsyncs it, and renames it over `path`. POSIX makes the rename atomic within one
|
|
508
|
+
# filesystem, so an unlocked concurrent reader sees either the previous contents in full or the new
|
|
509
|
+
# ones in full — never a truncated or half-written file.
|
|
510
|
+
#
|
|
511
|
+
# {#repair_writable_marker!} uses this half alone. It needs the atomicity (its reader is another
|
|
512
|
+
# process's constructor, which does not lock) but not the ordering a lock would buy: every writer of
|
|
513
|
+
# `schema_version.txt` writes the same value, so there is no last-writer-wins question to settle.
|
|
514
|
+
def publish_by_rename(path, body)
|
|
515
|
+
tmp = "#{path}.tmp.#{Process.pid}.#{SecureRandom.hex(4)}"
|
|
516
|
+
begin
|
|
517
|
+
File.open(tmp, "wb") do |f|
|
|
518
|
+
f.write(body)
|
|
519
|
+
f.fsync
|
|
520
|
+
end
|
|
521
|
+
File.rename(tmp, path)
|
|
522
|
+
ensure
|
|
523
|
+
# A failed write/rename must not leak its temp file — rely on the 1-hour `cleanup_stale_temp_files`
|
|
524
|
+
# sweep only as a backstop for crashes that skip this ensure entirely.
|
|
525
|
+
unlink_entry(tmp) if File.exist?(tmp)
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
505
529
|
# Checks (and, for a writable store, repairs) the `schema_version.txt` marker, and reports whether the
|
|
506
530
|
# disk tier is usable for the rest of this Store's lifetime. The result is memoized in `@disk_available`
|
|
507
531
|
# — one check per Store is enough; a benign double-check under a thread race would just repeat
|
|
@@ -515,8 +539,6 @@ module Rigor
|
|
|
515
539
|
# (permission, disk full, deleted root) also reports unavailable, degrading this Store instance to
|
|
516
540
|
# in-memory-memo-only for the rest of its lifetime — the producer block still runs, its result still
|
|
517
541
|
# lands in `@memo`, and disk reads/writes are simply skipped.
|
|
518
|
-
#
|
|
519
|
-
# @return [Boolean]
|
|
520
542
|
def ensure_schema_version!
|
|
521
543
|
return @disk_available unless @disk_available.nil?
|
|
522
544
|
|
|
@@ -532,25 +554,48 @@ module Rigor
|
|
|
532
554
|
false
|
|
533
555
|
end
|
|
534
556
|
|
|
557
|
+
# The marker is PUBLISHED BY RENAME rather than written in place. Its reader is another process's
|
|
558
|
+
# constructor, which takes no lock, so an `O_TRUNC`-then-write would expose a window in which the file
|
|
559
|
+
# exists and is empty; a reader landing there concludes the marker disagrees and clears the whole cache
|
|
560
|
+
# root — under, among others, a sibling that is between its own `File.file?` and `File.binread`, which
|
|
561
|
+
# is the `Errno::ENOENT` of issue #807. Two `rigor check` processes over one fresh `.rigor/cache` are
|
|
562
|
+
# enough to hit it.
|
|
563
|
+
#
|
|
564
|
+
# An EMPTY marker is treated as an ABSENT one — rewritten, never cleared on. Emptiness carries no
|
|
565
|
+
# information: it is what a torn write by an older Rigor looks like, what a crash between the create
|
|
566
|
+
# and the write leaves, and what this method's own clear-then-rewrite passes through. Answering an
|
|
567
|
+
# ambiguous signal by destroying a cache root is the wrong trade, and declining grants the entries
|
|
568
|
+
# below no trust they did not already have — a marker that is simply MISSING over existing entries is
|
|
569
|
+
# already kept and served (see the spec's writable-store rules), and `clear_cache_root!` removes the
|
|
570
|
+
# marker before rewriting it, so it produces that state itself.
|
|
535
571
|
def repair_writable_marker!
|
|
536
572
|
FileUtils.mkdir_p(@root)
|
|
537
573
|
marker = File.join(@root, "schema_version.txt")
|
|
538
574
|
current = self.class.schema_marker_value
|
|
539
575
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
return true if on_disk == current
|
|
576
|
+
on_disk = read_marker(marker)
|
|
577
|
+
return true if on_disk == current
|
|
543
578
|
|
|
544
|
-
|
|
545
|
-
end
|
|
579
|
+
clear_cache_root! unless on_disk.nil? || on_disk.empty?
|
|
546
580
|
|
|
547
581
|
FileUtils.mkdir_p(@root)
|
|
548
|
-
|
|
582
|
+
publish_by_rename(marker, "#{current}\n")
|
|
583
|
+
fsync_directory(@root)
|
|
549
584
|
true
|
|
550
585
|
rescue StandardError
|
|
551
586
|
false
|
|
552
587
|
end
|
|
553
588
|
|
|
589
|
+
# @return the marker's content, or nil when there is none to read — including the case of a concurrent
|
|
590
|
+
# {#clear_cache_root!} removing it between the check and the open.
|
|
591
|
+
def read_marker(path)
|
|
592
|
+
return nil unless File.file?(path)
|
|
593
|
+
|
|
594
|
+
File.read(path).strip
|
|
595
|
+
rescue Errno::ENOENT
|
|
596
|
+
nil
|
|
597
|
+
end
|
|
598
|
+
|
|
554
599
|
def clear_cache_root!
|
|
555
600
|
Dir.children(@root).each do |entry|
|
|
556
601
|
FileUtils.rm_rf(File.join(@root, entry))
|
|
@@ -15,7 +15,7 @@ require_relative "../inference/scope_indexer"
|
|
|
15
15
|
require_relative "../inference/statement_evaluator"
|
|
16
16
|
require_relative "prism_colorizer"
|
|
17
17
|
require_relative "command"
|
|
18
|
-
require_relative "
|
|
18
|
+
require_relative "../project_environment"
|
|
19
19
|
|
|
20
20
|
module Rigor
|
|
21
21
|
class CLI
|
|
@@ -43,7 +43,7 @@ module Rigor
|
|
|
43
43
|
# paging stays off because the CLI may itself sit in a pipeline.
|
|
44
44
|
BAT_ARGS = %w[--language=ruby --style=plain --paging=never --color=always].freeze
|
|
45
45
|
|
|
46
|
-
# @return
|
|
46
|
+
# @return CLI exit status.
|
|
47
47
|
def run
|
|
48
48
|
options = parse_options
|
|
49
49
|
file = @argv.shift
|
|
@@ -132,10 +132,10 @@ module Rigor
|
|
|
132
132
|
|
|
133
133
|
# The plugin-aware environment for the annotated file, so a `#=> <type>` matches what `rigor check`
|
|
134
134
|
# infers on that line — including types synthesized from the file's own inline RBS annotations (the file
|
|
135
|
-
# is threaded as the synthesizer's `source_files:`; see {
|
|
135
|
+
# is threaded as the synthesizer's `source_files:`; see {ProjectEnvironment}).
|
|
136
136
|
def base_scope(configuration, file)
|
|
137
137
|
Scope.empty(
|
|
138
|
-
environment:
|
|
138
|
+
environment: ProjectEnvironment.build(configuration: configuration, source_files: [file])
|
|
139
139
|
)
|
|
140
140
|
end
|
|
141
141
|
|
|
@@ -220,8 +220,7 @@ module Rigor
|
|
|
220
220
|
@scope_index = scope_index
|
|
221
221
|
end
|
|
222
222
|
|
|
223
|
-
# @
|
|
224
|
-
# @return [Hash{Integer => Rigor::Type}] 1-indexed line => type.
|
|
223
|
+
# @return 1-indexed line => type.
|
|
225
224
|
def collect(program)
|
|
226
225
|
by_line = {}
|
|
227
226
|
each_statement(program) do |statement|
|