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
|
@@ -55,35 +55,36 @@ module Rigor
|
|
|
55
55
|
# knowledge the single-file run does not have) must not mask a kill the shipped oracle would report.
|
|
56
56
|
Baseline = Data.define(:own, :dependents)
|
|
57
57
|
|
|
58
|
-
# @param
|
|
59
|
-
# @param
|
|
60
|
-
# @param project_scan [Rigor::Analysis::ProjectScan] built once by the caller; adopted per analysis
|
|
58
|
+
# @param environment — built once by the caller.
|
|
59
|
+
# @param project_scan — built once by the caller; adopted per analysis
|
|
61
60
|
# through `prebuilt:`, exactly as {DiagnosticOracle} does.
|
|
62
|
-
# @param paths
|
|
61
|
+
# @param paths — the measured file set, in canonical order (the seed's span: a class
|
|
63
62
|
# declared outside it stays unknown, as it does for Tier 1's seed and for {DiscoverySeed}).
|
|
64
|
-
# @param dependents
|
|
65
|
-
# @param seed_bundles
|
|
66
|
-
# @param discovery_seed
|
|
63
|
+
# @param dependents — {DependencyClosure} map, restricted to `paths`.
|
|
64
|
+
# @param seed_bundles — {DiscoverySeed.bundles} over the same `paths`.
|
|
65
|
+
# @param discovery_seed — the `discovery-seeded-mutation-sites` seed when that feature is also
|
|
67
66
|
# adopted, nil otherwise. It goes to the delegated {DiagnosticOracle} verbatim, so the mutated file's
|
|
68
67
|
# verdict is byte-for-byte the verdict that feature combination produces without this one; its
|
|
69
68
|
# `param_inferred_types` slot additionally rides the per-mutant closure seed, so an admitted site is
|
|
70
69
|
# judged with the knowledge that admitted it (issue #260's amended decision). The table is not
|
|
71
70
|
# refreshed per mutant — the collector is a whole-project pre-pass, and one mutated method body does
|
|
72
71
|
# not justify re-running it thousands of times.
|
|
72
|
+
# @param rebuild_session — issue #790 — a callable returning a FRESH `[environment, project_scan]`
|
|
73
|
+
# pair, invoked after a run this oracle refused for an analyzer defect. nil means the caller has no
|
|
74
|
+
# way to rebuild; see {#recovering} for what the oracle then does instead.
|
|
75
|
+
# rubocop:disable Metrics/ParameterLists -- the eight are independent collaborators the caller builds
|
|
76
|
+
# once each; an options object would only put a name in front of the same list.
|
|
73
77
|
def initialize(configuration:, environment:, project_scan:, paths:, dependents:, seed_bundles:,
|
|
74
|
-
discovery_seed: nil)
|
|
78
|
+
discovery_seed: nil, rebuild_session: nil)
|
|
79
|
+
# rubocop:enable Metrics/ParameterLists
|
|
75
80
|
@configuration = configuration
|
|
76
|
-
@environment = environment
|
|
77
|
-
@project_scan = project_scan
|
|
78
81
|
@paths = paths
|
|
79
82
|
@dependents = dependents
|
|
80
83
|
@seed_bundles = seed_bundles
|
|
81
84
|
@discovery_seed = discovery_seed
|
|
85
|
+
@rebuild_session = rebuild_session
|
|
82
86
|
@param_inferred_types = discovery_seed && discovery_seed[:param_inferred_types]
|
|
83
|
-
|
|
84
|
-
configuration: configuration, environment: environment, project_scan: project_scan,
|
|
85
|
-
discovery_seed: discovery_seed
|
|
86
|
-
)
|
|
87
|
+
adopt_session(environment, project_scan)
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
# The clean baselines a mutant must add a diagnostic to: the mutated file's (the shipped oracle's own,
|
|
@@ -91,21 +92,25 @@ module Rigor
|
|
|
91
92
|
# never per mutant. The dependents' half is bound through the same buffer machinery a mutant is, so the
|
|
92
93
|
# clean and mutant runs of the closure differ in exactly one input — the bytes.
|
|
93
94
|
def baseline(source:, path:)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
recovering do
|
|
96
|
+
Baseline.new(
|
|
97
|
+
own: @single.baseline(source: source, path: path),
|
|
98
|
+
dependents: dependents_signatures(source, path)
|
|
99
|
+
)
|
|
100
|
+
end
|
|
98
101
|
end
|
|
99
102
|
|
|
100
103
|
# Killed iff the mutant introduces a diagnostic the baseline did not carry — in the mutated file (the
|
|
101
104
|
# shipped verdict), or, failing that, in any dependent of it (what this feature adds).
|
|
102
105
|
def killed?(mutant_source:, path:, baseline:)
|
|
103
|
-
|
|
106
|
+
recovering do
|
|
107
|
+
next true if @single.killed?(mutant_source: mutant_source, path: path, baseline: baseline.own)
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
dependents = @dependents[path] || []
|
|
110
|
+
next false if dependents.empty?
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
dependents_signatures(mutant_source, path).any? { |sig| !baseline.dependents.include?(sig) }
|
|
113
|
+
end
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
# The file set a kill is looked for in: the mutated file plus its measured dependents.
|
|
@@ -115,6 +120,45 @@ module Rigor
|
|
|
115
120
|
|
|
116
121
|
private
|
|
117
122
|
|
|
123
|
+
# Issue #790 — a refused mutant is one mutant; a POISONED Environment is every mutant after it.
|
|
124
|
+
#
|
|
125
|
+
# The #784 seam records its failure on the Environment and {Environment::HktRegistryHolder} memoises
|
|
126
|
+
# the degraded registry, while {Environment::FailureSlot} is first-write-wins. So the one mutant that
|
|
127
|
+
# made the scan raise leaves an Environment that reports `rbs.coverage.hkt-scan-failed` on every later
|
|
128
|
+
# run and never re-attempts the scan — the row lands on BOTH sides of the next mutant's set
|
|
129
|
+
# difference, and every later mutant is measured over the degraded universe. Pre-#788 the raise
|
|
130
|
+
# propagated instead of being memoised, so the next demand retried the build and the poisoning did not
|
|
131
|
+
# exist; recovering it is this method.
|
|
132
|
+
#
|
|
133
|
+
# REBUILD rather than reset the slots. A reset would be cheaper, but it has to enumerate every holder
|
|
134
|
+
# the degraded registry fed — the memo itself, the failure slot, and the constant-type cache whose
|
|
135
|
+
# entries were folded under it — and a holder added to {Rigor::Environment} later would silently not
|
|
136
|
+
# be reset, with nothing to go red. The cost objection a reset answers is a rebuild PER MUTANT; this
|
|
137
|
+
# one is paid only after a defect is actually observed, which on a healthy sweep is never.
|
|
138
|
+
#
|
|
139
|
+
# With no `rebuild_session:` the oracle keeps the poisoned Environment and every later mutant is
|
|
140
|
+
# refused by the same guard. That is the deliberate fallback: a sweep that reports `harness_errors`
|
|
141
|
+
# says it could not measure, where the pre-#790 behaviour said "survived" about a universe it had
|
|
142
|
+
# silently degraded.
|
|
143
|
+
def recovering
|
|
144
|
+
yield
|
|
145
|
+
rescue AnalyzerCrashed => e
|
|
146
|
+
adopt_session(*@rebuild_session.call) if e.analyzer_defect? && @rebuild_session
|
|
147
|
+
raise
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The delegated single-file oracle is rebuilt with the pair, not handed the new Environment after the
|
|
151
|
+
# fact: it holds its own reference, and an oracle whose two halves disagree about which Environment
|
|
152
|
+
# they measure over is the same split-universe bug one layer down.
|
|
153
|
+
def adopt_session(environment, project_scan)
|
|
154
|
+
@environment = environment
|
|
155
|
+
@project_scan = project_scan
|
|
156
|
+
@single = DiagnosticOracle.new(
|
|
157
|
+
configuration: @configuration, environment: environment, project_scan: project_scan,
|
|
158
|
+
discovery_seed: @discovery_seed
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
118
162
|
# The diagnostic signatures the dependents of `path` report while `source` stands in for it. Empty (and
|
|
119
163
|
# analysis-free) when nothing depends on the file — 120 of Rigor's own 349 `lib` files.
|
|
120
164
|
def dependents_signatures(source, path)
|
|
@@ -187,7 +231,7 @@ module Rigor
|
|
|
187
231
|
configuration: @configuration, environment: @environment, prebuilt: @project_scan,
|
|
188
232
|
cache_store: nil, collect_stats: false, buffer: buffer, discovery_seed: seed, analyze_only: paths
|
|
189
233
|
).run(paths),
|
|
190
|
-
context: "ClosureKillOracle closure analysis of #{paths.join(', ')}"
|
|
234
|
+
context: "ClosureKillOracle closure analysis of #{paths.join(', ')}", environment: @environment
|
|
191
235
|
)
|
|
192
236
|
end
|
|
193
237
|
end
|
|
@@ -24,14 +24,13 @@ module Rigor
|
|
|
24
24
|
module DependencyClosure
|
|
25
25
|
module_function
|
|
26
26
|
|
|
27
|
-
# @param paths
|
|
28
|
-
# @param
|
|
29
|
-
# @param
|
|
30
|
-
# @param cache_store [Rigor::Cache::Store, nil] threaded to the recording run only (its RBS-env and
|
|
27
|
+
# @param paths — the measured file set, in caller order.
|
|
28
|
+
# @param environment — built once by the caller.
|
|
29
|
+
# @param cache_store — threaded to the recording run only (its RBS-env and
|
|
31
30
|
# plugin-producer tiers); the per-mutant analyses stay `cache_store: nil` regardless.
|
|
32
|
-
# @param workers
|
|
31
|
+
# @param workers — fork-pool workers for the recording pass (the pool records per worker and
|
|
33
32
|
# marshals the records back, so a pooled graph equals the sequential one).
|
|
34
|
-
# @return
|
|
33
|
+
# @return frozen `path => sorted dependents`, restricted to `paths`.
|
|
35
34
|
def build(paths:, configuration:, environment:, cache_store: nil, workers: 0)
|
|
36
35
|
runner = Analysis::Runner.new(
|
|
37
36
|
configuration: configuration, environment: environment, cache_store: cache_store,
|
|
@@ -45,8 +44,6 @@ module Rigor
|
|
|
45
44
|
# not being measured, so re-analysing it would report a diagnostic against a file the run never
|
|
46
45
|
# baselined. Self-edges are dropped (the mutated file is the closure's own head), and each list is
|
|
47
46
|
# sorted so a `--threshold` gate reads the same number whatever order the recording pass finished in.
|
|
48
|
-
#
|
|
49
|
-
# @return [Hash{String => Array<String>}]
|
|
50
47
|
def index(dependents, paths)
|
|
51
48
|
measured = Set.new(paths)
|
|
52
49
|
paths.to_h do |path|
|
|
@@ -17,7 +17,7 @@ module Rigor
|
|
|
17
17
|
# disk write). Passing `prebuilt:` disables the run-result cache (whose key digests the *disk* file), so a
|
|
18
18
|
# mutant is never served a stale clean hit.
|
|
19
19
|
class DiagnosticOracle
|
|
20
|
-
# @param discovery_seed
|
|
20
|
+
# @param discovery_seed — issue #260 — the cross-file discovery tables (see {DiscoverySeed})
|
|
21
21
|
# the per-mutant analysis is seeded with, threaded through to `Runner.new(discovery_seed:)`. Without
|
|
22
22
|
# it the runner's `prebuilt:` path carries frozen-empty discovery tables, so a receiver whose class is
|
|
23
23
|
# declared in a *sibling* file reads `Dynamic` and NO mutation at that site can produce a diagnostic —
|
|
@@ -53,7 +53,7 @@ module Rigor
|
|
|
53
53
|
configuration: @configuration, environment: @environment, prebuilt: @project_scan,
|
|
54
54
|
cache_store: nil, collect_stats: false, discovery_seed: @discovery_seed
|
|
55
55
|
).run_source(source: source, path: path),
|
|
56
|
-
context: "DiagnosticOracle re-analysis of #{path}"
|
|
56
|
+
context: "DiagnosticOracle re-analysis of #{path}", environment: @environment
|
|
57
57
|
)
|
|
58
58
|
end
|
|
59
59
|
end
|
|
@@ -30,12 +30,12 @@ module Rigor
|
|
|
30
30
|
module DiscoverySeed
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param paths
|
|
33
|
+
# @param paths — the measured file set; the seed spans these files only, exactly as Tier 1's
|
|
34
34
|
# seed does. A class declared outside them stays unknown.
|
|
35
|
-
# @param environment
|
|
36
|
-
# @param target_ruby
|
|
37
|
-
# @param workers
|
|
38
|
-
# @return
|
|
35
|
+
# @param environment — the plugin-aware environment, built once by the caller.
|
|
36
|
+
# @param target_ruby — Prism parse version for the parameter-inference pre-pass.
|
|
37
|
+
# @param workers — worker count for that pre-pass (0 keeps it sequential).
|
|
38
|
+
# @return frozen seed tables; empty when the paths yield nothing.
|
|
39
39
|
def build(paths:, environment:, target_ruby:, workers: 0)
|
|
40
40
|
tables = discovery_tables(paths)
|
|
41
41
|
params = Inference::ParameterInferenceCollector.collect(
|
|
@@ -51,8 +51,8 @@ module Rigor
|
|
|
51
51
|
# builds. Built ONCE on the parent (before {CLI::MutationForkScan} forks, so children copy-on-write
|
|
52
52
|
# inherit it), and cheap: ≈0.36s over Rigor's own 349-file `lib`.
|
|
53
53
|
#
|
|
54
|
-
# @param paths
|
|
55
|
-
# @return
|
|
54
|
+
# @param paths — the measured file set, in canonical (caller) order.
|
|
55
|
+
# @return per-path bundles, the input {#tables_for_buffer} re-folds.
|
|
56
56
|
def bundles(paths:)
|
|
57
57
|
Inference::ScopeIndexer.discovered_project_index_incremental(paths, seed_bundles: {}).fetch(:bundles)
|
|
58
58
|
end
|
|
@@ -69,10 +69,10 @@ module Rigor
|
|
|
69
69
|
#
|
|
70
70
|
# ≈15ms over 349 files (one re-walk plus a whole-set fold), against ≈210ms for one mutant's analysis.
|
|
71
71
|
#
|
|
72
|
-
# @param paths
|
|
73
|
-
# @param bundles
|
|
74
|
-
# @param buffer
|
|
75
|
-
# @return
|
|
72
|
+
# @param paths — the measured file set, in the same order {#bundles} was built from.
|
|
73
|
+
# @param bundles — that bundle set.
|
|
74
|
+
# @param buffer — the mutant binding (logical path → mutant bytes).
|
|
75
|
+
# @return frozen seed tables.
|
|
76
76
|
def tables_for_buffer(paths:, bundles:, buffer:)
|
|
77
77
|
index = Inference::ScopeIndexer.discovered_project_index_incremental(
|
|
78
78
|
paths, seed_bundles: bundles, buffer: buffer
|
|
@@ -15,14 +15,12 @@ module Rigor
|
|
|
15
15
|
module KillSignature
|
|
16
16
|
module_function
|
|
17
17
|
|
|
18
|
-
# @
|
|
19
|
-
# @return [Array] the comparison key.
|
|
18
|
+
# @return the comparison key.
|
|
20
19
|
def of(diagnostic)
|
|
21
20
|
[diagnostic.rule, diagnostic.path, diagnostic.line, diagnostic.column, diagnostic.message]
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
# @
|
|
25
|
-
# @return [Set<Array>] the signature set of `diagnostics`.
|
|
23
|
+
# @return the signature set of `diagnostics`.
|
|
26
24
|
def signatures_of(diagnostics)
|
|
27
25
|
diagnostics.to_set { |diagnostic| of(diagnostic) }
|
|
28
26
|
end
|
|
@@ -19,15 +19,13 @@ module Rigor
|
|
|
19
19
|
module MeasurementIntegrity
|
|
20
20
|
module_function
|
|
21
21
|
|
|
22
|
-
# @return
|
|
22
|
+
# @return false only when nothing could be measured AND something failed trying.
|
|
23
23
|
def measured?(total:, harness_errors:)
|
|
24
24
|
!(total.zero? && harness_errors.positive?)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# The report-level companion: a project ratio computed over nothing, where something went wrong.
|
|
28
28
|
# Distinct from an empty project, which is legitimately vacuous.
|
|
29
|
-
#
|
|
30
|
-
# @return [Boolean]
|
|
31
29
|
def ratio_unmeasurable?(grand_total:, unmeasured_files:)
|
|
32
30
|
grand_total.zero? && unmeasured_files.positive?
|
|
33
31
|
end
|
|
@@ -89,17 +89,15 @@ module Rigor
|
|
|
89
89
|
UNIDENTIFIED_ENGINE = "the engine's own source tree could not be digested"
|
|
90
90
|
|
|
91
91
|
class << self
|
|
92
|
-
# @param
|
|
93
|
-
# @param roots [Array<String>] the analysis roots to look for a snapshot under, most-specific first
|
|
92
|
+
# @param roots — the analysis roots to look for a snapshot under, most-specific first
|
|
94
93
|
# (the command's own path arguments, then the configured `paths:`).
|
|
95
|
-
# @param project_scan
|
|
96
|
-
# @param
|
|
97
|
-
# @param
|
|
98
|
-
# @param seed_inputs [Array<String>, nil] the files the {DiscoverySeed} was built over when it is
|
|
94
|
+
# @param project_scan — the prepared whole-project scan.
|
|
95
|
+
# @param feature_ids — the ADOPTED bleeding-edge ids that change this measurement.
|
|
96
|
+
# @param seed_inputs — the files the {DiscoverySeed} was built over when it is
|
|
99
97
|
# active, nil when it is not. The CLI stays the only place that knows a feature id exists.
|
|
100
|
-
# @param bypass_reason
|
|
98
|
+
# @param bypass_reason — a caller-side reason to run uncached (`--no-cache`, the closure
|
|
101
99
|
# oracle). Reported verbatim.
|
|
102
|
-
# @return
|
|
100
|
+
# @return enabled, or a disabled instance carrying `#reason`.
|
|
103
101
|
def build(configuration:, roots:, project_scan:, sampling:, feature_ids:, seed_inputs: nil,
|
|
104
102
|
bypass_reason: nil)
|
|
105
103
|
return disabled(bypass_reason) if bypass_reason
|
|
@@ -263,7 +261,6 @@ module Rigor
|
|
|
263
261
|
end
|
|
264
262
|
|
|
265
263
|
# The cached result for `path`, or nil on any miss (including "this cache is disabled").
|
|
266
|
-
# @return [MutationScanner::FileResult, nil]
|
|
267
264
|
def fetch(path)
|
|
268
265
|
return nil unless enabled?
|
|
269
266
|
# A path the snapshot never analysed has no recorded `deps[A]`, which means "depends on every project
|
|
@@ -87,25 +87,25 @@ module Rigor
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# @param
|
|
91
|
-
# @param
|
|
92
|
-
# @param
|
|
93
|
-
# @param limit [Integer, nil] optional per-file mutation cap (sampled with
|
|
90
|
+
# @param environment — pre-built once by the caller
|
|
91
|
+
# @param project_scan — pre-built once
|
|
92
|
+
# @param limit — optional per-file mutation cap (sampled with
|
|
94
93
|
# `seed`); nil analyses every type-relevant mutation (deterministic).
|
|
95
|
-
# @param seed
|
|
96
|
-
# @param oracle
|
|
97
|
-
# defaults to the {DiagnosticOracle} (the
|
|
98
|
-
#
|
|
94
|
+
# @param seed — RNG seed for the optional sample.
|
|
95
|
+
# @param oracle — the kill oracle (ADR-69 Seam 1) — anything answering
|
|
96
|
+
# `baseline` and `killed?`; defaults to the {DiagnosticOracle} (the
|
|
97
|
+
# ADR-62/63 behaviour).
|
|
98
|
+
# @param site_selector — which sites to mutate (ADR-69
|
|
99
99
|
# Seam 2). `:biteable` (default) keeps only concrete-type sites Rigor can
|
|
100
100
|
# bite; `:all` also mutates Dynamic-receiver dispatch sites — use only
|
|
101
101
|
# with a {TestSuiteOracle} (the fused overlay), never the diagnostic path.
|
|
102
|
-
# @param base_scope
|
|
102
|
+
# @param base_scope — the scope site selection judges
|
|
103
103
|
# anchor types against, built once by the caller (see
|
|
104
104
|
# {Mutator#anchor_base_scope}). `nil` — the default — keeps the bare
|
|
105
105
|
# single-file empty scope. A caller that seeds cross-file discovery
|
|
106
106
|
# passes it here; this class stays free of {Rigor::Configuration} and of
|
|
107
107
|
# bleeding-edge feature ids, which live one layer up in the CLI.
|
|
108
|
-
# @param discovery_seed
|
|
108
|
+
# @param discovery_seed — issue #260 — the SAME cross-file table
|
|
109
109
|
# set `base_scope` was built from, threaded to the default
|
|
110
110
|
# {DiagnosticOracle} so an admitted cross-file site is one the oracle can
|
|
111
111
|
# also kill at. Pass both or neither: a `base_scope` without it measures
|
|
@@ -127,9 +127,8 @@ module Rigor
|
|
|
127
127
|
)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
# @param path
|
|
131
|
-
# @param source
|
|
132
|
-
# @return [FileResult]
|
|
130
|
+
# @param path — the file to measure (used as the in-memory bind path)
|
|
131
|
+
# @param source — the file's source; read from disk when nil
|
|
133
132
|
def scan_file(path, source: nil)
|
|
134
133
|
source ||= File.read(path, encoding: Encoding::UTF_8)
|
|
135
134
|
kept = kept_mutations(source, path)
|
|
@@ -156,8 +155,6 @@ module Rigor
|
|
|
156
155
|
# mutant the type checker did **not** kill, asks `test_oracle` whether the project's test suite catches it.
|
|
157
156
|
# The expensive suite run is paid only for type-survivors (the gradual short-circuit), so the cost is
|
|
158
157
|
# proportional to the protection hole.
|
|
159
|
-
# @param test_oracle [TestSuiteOracle]
|
|
160
|
-
# @return [FusedFileResult]
|
|
161
158
|
def scan_file_fused(path, test_oracle:, source: nil)
|
|
162
159
|
source ||= File.read(path, encoding: Encoding::UTF_8)
|
|
163
160
|
kept = kept_mutations(source, path)
|
|
@@ -127,6 +127,7 @@ module Rigor
|
|
|
127
127
|
when Type::Constant then CONSTANT_CLASSES[type.value.class] || type.value.class.name
|
|
128
128
|
when Type::Refined, Type::Difference then concrete_class_name(type.base)
|
|
129
129
|
when Type::IntegerRange then "Integer"
|
|
130
|
+
when Type::FloatRange then "Float"
|
|
130
131
|
when Type::App then concrete_class_name(type.bound)
|
|
131
132
|
end
|
|
132
133
|
end
|
|
@@ -171,7 +172,7 @@ module Rigor
|
|
|
171
172
|
# only removes provably-Dynamic sites. Returns [kept, dropped_count]. Builds the scope index from THIS
|
|
172
173
|
# mutator's parse so anchor node identity matches the keys.
|
|
173
174
|
#
|
|
174
|
-
# @param base_scope
|
|
175
|
+
# @param base_scope — a pre-seeded scope to judge anchors against (see
|
|
175
176
|
# {#anchor_base_scope}); nil builds the bare empty scope, which is the shipped default.
|
|
176
177
|
def filter_by_type(mutations, environment:, path:, base_scope: nil)
|
|
177
178
|
base = anchor_base_scope(environment, path, base_scope)
|
|
@@ -16,8 +16,8 @@ module Rigor
|
|
|
16
16
|
# standard mutation-testing hazard the `ensure` cannot cover; callers running this in CI accept that, as
|
|
17
17
|
# `mutant` / Stryker do.)
|
|
18
18
|
class TestSuiteOracle
|
|
19
|
-
# @param command
|
|
20
|
-
# @param runner
|
|
19
|
+
# @param command — the test command (the runner hook)
|
|
20
|
+
# @param runner — `runner.call(command) -> true iff the suite
|
|
21
21
|
# passed`. Defaults to shelling out via `system`.
|
|
22
22
|
def initialize(command:, runner: nil)
|
|
23
23
|
@command = command
|
|
@@ -31,9 +31,9 @@ module Rigor
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# Killed iff the mutant turns the suite red. Restores `original` afterward.
|
|
34
|
-
# @param path
|
|
35
|
-
# @param original
|
|
36
|
-
# @param mutant_source
|
|
34
|
+
# @param path — the file to (temporarily) overwrite with the mutant
|
|
35
|
+
# @param original — the clean bytes to restore
|
|
36
|
+
# @param mutant_source — the mutated bytes to test against
|
|
37
37
|
def killed?(path:, original:, mutant_source:)
|
|
38
38
|
File.write(path, mutant_source)
|
|
39
39
|
!@runner.call(@command)
|
|
@@ -31,8 +31,9 @@ module Rigor
|
|
|
31
31
|
# - `IncompatibleSignature` — a provided method's signature violates the interface contract (return widened,
|
|
32
32
|
# or a parameter narrowed). Same rule, signature-specific message.
|
|
33
33
|
# - `UnresolvedInterface` — the named interface is not loaded (a typo, or the defining library / `sig` set is
|
|
34
|
-
# not on the RBS load path). Surfaces as `dynamic.rbs-extended.unresolved` `:
|
|
35
|
-
#
|
|
34
|
+
# not on the RBS load path). Surfaces as `dynamic.rbs-extended.unresolved` at `:warning` (#928): the
|
|
35
|
+
# other directive parsers fail soft to `:info`, but a name the author asserted against must not pass
|
|
36
|
+
# silently — the catalog ships, so an unresolved role is a typo or a missing `sig` set.
|
|
36
37
|
#
|
|
37
38
|
# Fail-soft throughout: a class whose own definition cannot be built (RBS error) is skipped rather than
|
|
38
39
|
# reported.
|
|
@@ -60,7 +61,7 @@ module Rigor
|
|
|
60
61
|
results
|
|
61
62
|
end
|
|
62
63
|
|
|
63
|
-
# @return
|
|
64
|
+
# @return zero or more records for one (class, interface) pair.
|
|
64
65
|
def check_one(rbs_loader, class_name, interface_name, location)
|
|
65
66
|
interface_def = resolve_interface(rbs_loader, class_name, interface_name)
|
|
66
67
|
if interface_def.nil?
|
|
@@ -48,10 +48,9 @@ module Rigor
|
|
|
48
48
|
|
|
49
49
|
module_function
|
|
50
50
|
|
|
51
|
-
# @param sources
|
|
51
|
+
# @param sources — `[buffer name, RBS source]` pairs — the project's
|
|
52
52
|
# `.rbs` files, plus the virtual entries rbs-inline and plugin `source_rbs` synthesis contribute.
|
|
53
|
-
# @param registry
|
|
54
|
-
# @return [Result]
|
|
53
|
+
# @param registry — the vocabulary an unknown label is judged against.
|
|
55
54
|
#
|
|
56
55
|
# The `ANNOTATION_HINT` routing test runs here, before any parse: a source with no honoured
|
|
57
56
|
# payload can contribute neither an envelope nor an unresolved report, so a signature tree with
|
|
@@ -94,11 +93,10 @@ module Rigor
|
|
|
94
93
|
# class discovery knows", which is a project fact; a gem's class-level tag would have to
|
|
95
94
|
# distribute over a definition set this reader does not have.
|
|
96
95
|
#
|
|
97
|
-
# @param loader
|
|
96
|
+
# @param loader — the run's loader; it owns the env walk
|
|
98
97
|
# ({Rigor::Environment::RbsLoader#each_annotated_method_member}), so the environment itself never
|
|
99
98
|
# leaves it.
|
|
100
|
-
# @
|
|
101
|
-
# @return [Hash{String => Rigor::Effects::Envelope}] keyed `Class#m` / `Class.m`
|
|
99
|
+
# @return keyed `Class#m` / `Class.m`
|
|
102
100
|
def from_loader(loader:, registry:)
|
|
103
101
|
out = {}
|
|
104
102
|
loader.each_annotated_method_member do |class_name, member|
|
|
@@ -9,18 +9,23 @@ module Rigor
|
|
|
9
9
|
# Owns three event streams:
|
|
10
10
|
#
|
|
11
11
|
# - `#unresolved_payloads` — `rigor:v1:*` directive payloads the resolver could not turn into a
|
|
12
|
-
# {Rigor::Type}. Surface as `dynamic.rbs-extended.unresolved` `:info` diagnostics
|
|
12
|
+
# {Rigor::Type}. Surface as `dynamic.rbs-extended.unresolved` `:info` diagnostics, except an
|
|
13
|
+
# unresolved `conforms-to` interface, which the aggregator stamps `:warning` (#928).
|
|
13
14
|
# - `#lossy_projections` — shape-projection type functions (`pick_of` / `omit_of` / `partial_of` /
|
|
14
15
|
# `required_of` / `readonly_of`) applied to a carrier that does not preserve shape information (anything
|
|
15
16
|
# other than `Type::HashShape` / `Type::Tuple`). Surface as `dynamic.shape.lossy-projection` `:info`
|
|
16
17
|
# diagnostics.
|
|
17
18
|
# - `#hkt_directive_errors` — malformed `rigor:v1:hkt_register` / `rigor:v1:hkt_define` directives the
|
|
18
19
|
# ADR-20 parser declined. Surface as `dynamic.rbs-extended.hkt-directive-invalid` `:info` diagnostics.
|
|
20
|
+
# - `#deprecated_forms` — payload spellings the grammar still accepts for one deprecation window but no
|
|
21
|
+
# longer displays (ADR-109 WD3: the PHPStan-style `int<a, b>`). Surface as
|
|
22
|
+
# `dynamic.rbs-extended.deprecated-form` `:info` diagnostics naming the replacement.
|
|
19
23
|
#
|
|
20
24
|
# Mutable through the run; consumed once by {Rigor::Analysis::Runner} at end-of-run. Each event is
|
|
21
25
|
# deduplicated by its whole entry — `(payload, path, line, column)` for unresolved, `(head, path, line,
|
|
22
|
-
# column)` for lossy-projection, `(message, path, line, column)` for an hkt directive
|
|
23
|
-
# annotation read from many call
|
|
26
|
+
# column)` for lossy-projection, `(message, path, line, column)` for an hkt directive, `(payload,
|
|
27
|
+
# replacement, path, line, column)` for a deprecated form — so a single annotation read from many call
|
|
28
|
+
# sites yields one diagnostic.
|
|
24
29
|
#
|
|
25
30
|
# The reporter is intentionally thread-safe via a coarse `Mutex` because the inference engine may read the
|
|
26
31
|
# same method definition from multiple files in parallel; the critical sections are short (Array#include? +
|
|
@@ -41,6 +46,7 @@ module Rigor
|
|
|
41
46
|
UnresolvedEntry = Data.define(:payload, :path, :line, :column)
|
|
42
47
|
LossyProjectionEntry = Data.define(:head, :path, :line, :column)
|
|
43
48
|
HktDirectiveEntry = Data.define(:message, :path, :line, :column)
|
|
49
|
+
DeprecatedFormEntry = Data.define(:payload, :replacement, :path, :line, :column)
|
|
44
50
|
|
|
45
51
|
# Flattens an `RBS::Location` (or anything answering the same readers) to the `(path, line, column)`
|
|
46
52
|
# triple every entry carries. `column` is 1-based, since `RBS::Location#start_column` is 0-based and
|
|
@@ -49,9 +55,6 @@ module Rigor
|
|
|
49
55
|
#
|
|
50
56
|
# Fail-soft by construction, like every parser that calls it: a location that raises while being read
|
|
51
57
|
# costs the entry its position, never the run.
|
|
52
|
-
#
|
|
53
|
-
# @param source_location [RBS::Location, nil]
|
|
54
|
-
# @return [Array(String, nil, Integer, nil, Integer, nil)]
|
|
55
58
|
def self.position_of(source_location)
|
|
56
59
|
return [nil, nil, nil] if source_location.nil?
|
|
57
60
|
|
|
@@ -68,15 +71,16 @@ module Rigor
|
|
|
68
71
|
@unresolved_payloads = []
|
|
69
72
|
@lossy_projections = []
|
|
70
73
|
@hkt_directive_errors = []
|
|
74
|
+
@deprecated_forms = []
|
|
71
75
|
@mutex = Mutex.new
|
|
72
76
|
end
|
|
73
77
|
|
|
74
|
-
# @return
|
|
78
|
+
# @return frozen snapshot of the accumulated unresolved-payload events.
|
|
75
79
|
def unresolved_payloads
|
|
76
80
|
@mutex.synchronize { @unresolved_payloads.dup.freeze }
|
|
77
81
|
end
|
|
78
82
|
|
|
79
|
-
# @return
|
|
83
|
+
# @return frozen snapshot of the accumulated lossy-projection events.
|
|
80
84
|
def lossy_projections
|
|
81
85
|
@mutex.synchronize { @lossy_projections.dup.freeze }
|
|
82
86
|
end
|
|
@@ -109,7 +113,7 @@ module Rigor
|
|
|
109
113
|
end
|
|
110
114
|
end
|
|
111
115
|
|
|
112
|
-
# @return
|
|
116
|
+
# @return frozen snapshot of the accumulated hkt-directive failures.
|
|
113
117
|
def hkt_directive_errors
|
|
114
118
|
@mutex.synchronize { @hkt_directive_errors.dup.freeze }
|
|
115
119
|
end
|
|
@@ -132,11 +136,33 @@ module Rigor
|
|
|
132
136
|
end
|
|
133
137
|
end
|
|
134
138
|
|
|
139
|
+
# @return frozen snapshot of the accumulated deprecated-form events.
|
|
140
|
+
def deprecated_forms
|
|
141
|
+
@mutex.synchronize { @deprecated_forms.dup.freeze }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Records a `dynamic.rbs-extended.deprecated-form` event (ADR-109 WD3): `payload` is the spelling the
|
|
145
|
+
# author wrote (`int<5, 10>`), `replacement` the spelling the same type displays as (`Integer[5..10]`),
|
|
146
|
+
# so the row can say exactly what to write instead. The position triple is read exactly as
|
|
147
|
+
# {#record_unresolved}'s is, and every String is frozen for the pool drain.
|
|
148
|
+
def record_deprecated_form(payload:, replacement:, path: nil, line: nil, column: nil)
|
|
149
|
+
entry = DeprecatedFormEntry.new(
|
|
150
|
+
payload: frozen_text(payload), replacement: frozen_text(replacement),
|
|
151
|
+
path: frozen_text(path), line: line, column: column
|
|
152
|
+
)
|
|
153
|
+
@mutex.synchronize do
|
|
154
|
+
return if @deprecated_forms.include?(entry)
|
|
155
|
+
|
|
156
|
+
@deprecated_forms << entry
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
135
160
|
# True when no events have accumulated. Used by callers that want to skip the diagnostic-emission pass
|
|
136
161
|
# entirely on the common no-event path.
|
|
137
162
|
def empty?
|
|
138
163
|
@mutex.synchronize do
|
|
139
|
-
@unresolved_payloads.empty? && @lossy_projections.empty? && @hkt_directive_errors.empty?
|
|
164
|
+
@unresolved_payloads.empty? && @lossy_projections.empty? && @hkt_directive_errors.empty? &&
|
|
165
|
+
@deprecated_forms.empty?
|
|
140
166
|
end
|
|
141
167
|
end
|
|
142
168
|
|