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
|
@@ -32,14 +32,21 @@ module Rigor
|
|
|
32
32
|
# The class-length budget is relaxed (as on `Rigor::CLI` itself) because `check` aggregates several independent
|
|
33
33
|
# concerns that are clearer read together than split across micro-classes.
|
|
34
34
|
class CheckCommand < Command # rubocop:disable Metrics/ClassLength
|
|
35
|
-
#
|
|
35
|
+
# Issue #812 — `--fail-on=SEVERITY` raises the exit-status bar above the default (`:error`-only).
|
|
36
|
+
# Orders the three severities a diagnostic can carry once it survives {SeverityStamp} (`:off` rows are
|
|
37
|
+
# dropped upstream and never reach a `Result`, so they are not a member here).
|
|
38
|
+
FAIL_ON_RANK = { info: 0, warning: 1, error: 2 }.freeze
|
|
39
|
+
|
|
40
|
+
# @return CLI exit status.
|
|
36
41
|
#
|
|
37
42
|
# Deferred YJIT enablement (Runtime::Jit) is armed by `CLI#dispatch` for every command, this
|
|
38
43
|
# one included, before any analysis work runs.
|
|
39
|
-
def run # rubocop:disable Metrics/AbcSize
|
|
44
|
+
def run # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
40
45
|
# ADR-87 WD4 — parse options + resolve config WITHOUT the inference engine, so the run-cache hit probe
|
|
41
46
|
# can run first. The heavy engine (`load_check_dependencies`) loads only on a miss / non-cacheable run.
|
|
42
47
|
options = parse_check_options
|
|
48
|
+
return CLI::EXIT_USAGE unless valid_fail_on_option?(options)
|
|
49
|
+
|
|
43
50
|
buffer = Options.resolve_buffer_binding(options, err: @err)
|
|
44
51
|
return CLI::EXIT_USAGE if buffer == :usage_error
|
|
45
52
|
|
|
@@ -68,7 +75,8 @@ module Rigor
|
|
|
68
75
|
result = apply_baseline_filter(raw_result, configuration, options)
|
|
69
76
|
|
|
70
77
|
coverage = compute_coverage(runner, configuration, options)
|
|
71
|
-
write_result(result, options.fetch(:format), coverage: coverage, config_warnings: config_warnings
|
|
78
|
+
write_result(result, options.fetch(:format), coverage: coverage, config_warnings: config_warnings,
|
|
79
|
+
fail_on: options.fetch(:fail_on))
|
|
72
80
|
emit_ci_detected_output(result, options)
|
|
73
81
|
write_run_stats(result.stats) if result.stats
|
|
74
82
|
write_trace_appendices
|
|
@@ -77,11 +85,34 @@ module Rigor
|
|
|
77
85
|
|
|
78
86
|
exit_code = result.success? ? 0 : 1
|
|
79
87
|
exit_code = 1 if baseline_strict_violation?(raw_result.diagnostics, configuration, options)
|
|
88
|
+
exit_code = 1 if fail_on_violation?(result.diagnostics, options)
|
|
80
89
|
exit_code
|
|
81
90
|
end
|
|
82
91
|
|
|
83
92
|
private
|
|
84
93
|
|
|
94
|
+
# True when `diagnostics` (already baseline-filtered and severity-resolved — the same list
|
|
95
|
+
# `Result#success?` reads) carries a row at or above the `--fail-on` threshold. With the default
|
|
96
|
+
# `:error` threshold this is exactly `!diagnostics.any?(&:error?)`, i.e. a no-op over the existing exit
|
|
97
|
+
# code — `--fail-on` only ever makes a run *stricter*, never masks an `:error` the old exit code caught.
|
|
98
|
+
def fail_on_violation?(diagnostics, options)
|
|
99
|
+
threshold = FAIL_ON_RANK.fetch(options.fetch(:fail_on))
|
|
100
|
+
diagnostics.any? { |diagnostic| FAIL_ON_RANK.fetch(diagnostic.severity, FAIL_ON_RANK[:error]) >= threshold }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# True when `options[:fail_on]` (an arbitrary user-typed Symbol at this point — parsing only lower-cases
|
|
104
|
+
# and interns it, see `parse_check_options`) is one of {FAIL_ON_RANK}'s keys; writes a usage note to
|
|
105
|
+
# `@err` and returns false otherwise. Validated as a plain post-parse check rather than raised from
|
|
106
|
+
# inside the `opts.on` block: OptionParser rescues a `ParseError` raised from a switch's own block and
|
|
107
|
+
# rebuilds it as a generic `"invalid argument: --fail-on=<value>"`, discarding any custom message — this
|
|
108
|
+
# way the usage note actually names the accepted values.
|
|
109
|
+
def valid_fail_on_option?(options)
|
|
110
|
+
return true if FAIL_ON_RANK.key?(options.fetch(:fail_on))
|
|
111
|
+
|
|
112
|
+
@err.puts("rigor: invalid --fail-on value: #{options.fetch(:fail_on)} (expected error, warning, or info)")
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
|
|
85
116
|
# ADR-87 WD4 — attempt the boot-slimming run-cache hit. Returns the cached {Analysis::Result} (severity
|
|
86
117
|
# profile applied, no stats — matching a cache-served `Runner#run`) on a hit, or nil to fall through to
|
|
87
118
|
# the full engine path. Only an ordinary sequential check whose result IS cache-served the same way is
|
|
@@ -129,11 +160,13 @@ module Rigor
|
|
|
129
160
|
# gate reads the same diagnostics the full path would.
|
|
130
161
|
def finalize_cache_hit(raw_result, configuration, options, config_warnings)
|
|
131
162
|
result = apply_baseline_filter(raw_result, configuration, options)
|
|
132
|
-
write_result(result, options.fetch(:format), config_warnings: config_warnings
|
|
163
|
+
write_result(result, options.fetch(:format), config_warnings: config_warnings,
|
|
164
|
+
fail_on: options.fetch(:fail_on))
|
|
133
165
|
emit_ci_detected_output(result, options)
|
|
134
166
|
|
|
135
167
|
exit_code = result.success? ? 0 : 1
|
|
136
168
|
exit_code = 1 if baseline_strict_violation?(raw_result.diagnostics, configuration, options)
|
|
169
|
+
exit_code = 1 if fail_on_violation?(result.diagnostics, options)
|
|
137
170
|
exit_code
|
|
138
171
|
end
|
|
139
172
|
|
|
@@ -233,8 +266,8 @@ module Rigor
|
|
|
233
266
|
|
|
234
267
|
result = apply_baseline_filter(Analysis::Result.new(diagnostics: diagnostics, stats: nil), configuration,
|
|
235
268
|
options)
|
|
236
|
-
write_result(result, options.fetch(:format))
|
|
237
|
-
result.success? ?
|
|
269
|
+
write_result(result, options.fetch(:format), fail_on: options.fetch(:fail_on))
|
|
270
|
+
!result.success? || fail_on_violation?(result.diagnostics, options) ? 1 : 0
|
|
238
271
|
end
|
|
239
272
|
|
|
240
273
|
# Editor mode option B (#146) — `--incremental` plus an editor buffer. The whole project is in scope with
|
|
@@ -260,8 +293,8 @@ module Rigor
|
|
|
260
293
|
filtered = apply_baseline_filter(
|
|
261
294
|
Analysis::Result.new(diagnostics: result.diagnostics, stats: nil), configuration, options
|
|
262
295
|
)
|
|
263
|
-
write_result(filtered, options.fetch(:format))
|
|
264
|
-
filtered.success? ?
|
|
296
|
+
write_result(filtered, options.fetch(:format), fail_on: options.fetch(:fail_on))
|
|
297
|
+
!filtered.success? || fail_on_violation?(filtered.diagnostics, options) ? 1 : 0
|
|
265
298
|
end
|
|
266
299
|
|
|
267
300
|
# ADR-88 WD1 — a one-line stderr note when the plugin fact surface (an ADR-9 fact, an ADR-60 producer
|
|
@@ -439,6 +472,10 @@ module Rigor
|
|
|
439
472
|
baseline: :unset,
|
|
440
473
|
# ADR-22 slice 5 — `--baseline-strict` CI gate: fail the run on any baseline drift, in either direction.
|
|
441
474
|
baseline_strict: false,
|
|
475
|
+
# Issue #812 — `--fail-on=SEVERITY` raises the exit-status bar: the run exits non-zero when a
|
|
476
|
+
# diagnostic AT OR ABOVE this severity survives baseline filtering and severity resolution, not only
|
|
477
|
+
# on `:error` (today's default and behaviour). See `FAIL_ON_RANK`.
|
|
478
|
+
fail_on: :error,
|
|
442
479
|
# ADR-32 WD10 carry-over — `--treat-all-as-inline-rbs` forces the `rigor-rbs-inline` plugin into the loaded
|
|
443
480
|
# plugin set with `require_magic_comment: false` so a single ad-hoc `rigor check` invocation treats every
|
|
444
481
|
# analysed file as inline-RBS without the user editing `.rigor.yml`. Intended for single-file / ad-hoc CI use;
|
|
@@ -508,6 +545,12 @@ module Rigor
|
|
|
508
545
|
"fail the run on any baseline drift (CI gate)") do
|
|
509
546
|
options[:baseline_strict] = true
|
|
510
547
|
end
|
|
548
|
+
opts.on("--fail-on=SEVERITY",
|
|
549
|
+
"exit non-zero on a diagnostic at or above SEVERITY: error (default), warning, or info") do |value|
|
|
550
|
+
# Validated post-parse (`valid_fail_on_option?`, called from `run`) rather than here — see its
|
|
551
|
+
# comment for why an unrecognised value is not raised from inside this block.
|
|
552
|
+
options[:fail_on] = value.to_s.downcase.to_sym
|
|
553
|
+
end
|
|
511
554
|
opts.on("--treat-all-as-inline-rbs",
|
|
512
555
|
"force-load rigor-rbs-inline with require_magic_comment: false") do
|
|
513
556
|
options[:treat_all_as_inline_rbs] = true
|
|
@@ -661,7 +704,6 @@ module Rigor
|
|
|
661
704
|
@err.puts(" body evals: #{counts[bt::MEMO_BODY_EVALS]}")
|
|
662
705
|
@err.puts(" non-stored: on-stack #{counts[bt::MEMO_REFUSE_ON_STACK]} " \
|
|
663
706
|
"unroll-in-flight #{counts[bt::MEMO_REFUSE_UNROLL]} " \
|
|
664
|
-
"consult-tainted #{counts[bt::MEMO_REFUSE_CONSULT_TAINTED]} " \
|
|
665
707
|
"transient-tainted #{counts[bt::MEMO_REFUSE_TRANSIENT]}")
|
|
666
708
|
write_memo_signature_table
|
|
667
709
|
end
|
|
@@ -841,10 +883,15 @@ module Rigor
|
|
|
841
883
|
format == "text" ? @out : @err
|
|
842
884
|
end
|
|
843
885
|
|
|
844
|
-
def write_result(result, format, coverage: nil, config_warnings: [])
|
|
886
|
+
def write_result(result, format, coverage: nil, config_warnings: [], fail_on: :error)
|
|
845
887
|
case format
|
|
846
888
|
when "json"
|
|
847
889
|
payload = enrich_json(result.to_h)
|
|
890
|
+
# Issue #812 — the exit code can be stricter than `payload["success"]` (which stays the `:error`-only
|
|
891
|
+
# reading `Result#success?` always gave, per the other consumers already reading it); a JSON
|
|
892
|
+
# consumer that wants to know WHY a run exited non-zero reads this alongside it rather than
|
|
893
|
+
# re-deriving `--fail-on`'s value from the invocation.
|
|
894
|
+
payload["fail_on"] = fail_on.to_s
|
|
848
895
|
payload["coverage"] = coverage_payload(coverage) if coverage
|
|
849
896
|
payload["config_warnings"] = config_warnings.map(&:to_h) unless config_warnings.empty?
|
|
850
897
|
@out.puts(JSON.pretty_generate(payload))
|
|
@@ -22,7 +22,7 @@ module Rigor
|
|
|
22
22
|
# The outcome of {.attempt} — the best-effort variant. Exactly one of the two is non-nil, so a caller can never
|
|
23
23
|
# read "the check could not run" as "the check ran clean": `result` is nil precisely when `error` is set.
|
|
24
24
|
Outcome = Data.define(:result, :error) do
|
|
25
|
-
# @return
|
|
25
|
+
# @return true when an analysis actually completed.
|
|
26
26
|
def ran?
|
|
27
27
|
!result.nil?
|
|
28
28
|
end
|
|
@@ -42,12 +42,9 @@ module Rigor
|
|
|
42
42
|
|
|
43
43
|
# Runs the analysis the way `rigor check` does and returns the runner + its raw result.
|
|
44
44
|
#
|
|
45
|
-
# @param
|
|
46
|
-
# @param
|
|
47
|
-
# @param
|
|
48
|
-
# @param buffer [Rigor::Analysis::BufferBinding, nil]
|
|
49
|
-
# @param cache_root [String, nil] nil falls back to the configuration's cache path.
|
|
50
|
-
# @return [Invocation]
|
|
45
|
+
# @param options — at least `:no_cache`, `:explain`, `:stats`, `:workers` (see {CheckRunnerFactory.build}).
|
|
46
|
+
# @param paths — analysed paths; nil falls back to the configuration's `paths:`.
|
|
47
|
+
# @param cache_root — nil falls back to the configuration's cache path.
|
|
51
48
|
def run(configuration:, options:, paths: nil, buffer: nil, cache_root: nil)
|
|
52
49
|
require_relative "check_runner_factory"
|
|
53
50
|
runner = CheckRunnerFactory.build(
|
|
@@ -68,10 +65,8 @@ module Rigor
|
|
|
68
65
|
# routing hint is outside the false-positive envelope, but a crash in it is not — `describe` must stay a command
|
|
69
66
|
# an agent can run freely.
|
|
70
67
|
#
|
|
71
|
-
# @param config_path
|
|
72
|
-
# @param options
|
|
73
|
-
# @param paths [Array<String>, nil]
|
|
74
|
-
# @return [Outcome]
|
|
68
|
+
# @param config_path — path to the config file; nil uses {Configuration.discover}.
|
|
69
|
+
# @param options — runner options (defaults to {DEEP_OPTIONS}).
|
|
75
70
|
def attempt(config_path: nil, options: DEEP_OPTIONS, paths: nil)
|
|
76
71
|
require_relative "../configuration"
|
|
77
72
|
configuration = Configuration.load(config_path)
|
|
@@ -14,12 +14,8 @@ module Rigor
|
|
|
14
14
|
|
|
15
15
|
# Builds a runner matching the configuration/plugin/cache resolution that `rigor check` itself uses.
|
|
16
16
|
#
|
|
17
|
-
# @param
|
|
18
|
-
# @param options [Hash] parsed CLI options (must contain at least
|
|
17
|
+
# @param options — parsed CLI options (must contain at least
|
|
19
18
|
# `:no_cache`, `:explain`, `:stats`, `:workers`)
|
|
20
|
-
# @param buffer [Rigor::Analysis::BufferBinding, nil]
|
|
21
|
-
# @param cache_root [String]
|
|
22
|
-
# @return [Rigor::Analysis::Runner]
|
|
23
19
|
def build(configuration:, options:, buffer:, cache_root:)
|
|
24
20
|
cache_store = if options.fetch(:no_cache)
|
|
25
21
|
nil
|
|
@@ -53,7 +53,7 @@ module Rigor
|
|
|
53
53
|
# `--test-command`.
|
|
54
54
|
DEFAULT_TEST_COMMAND = %w[bundle exec rake].freeze
|
|
55
55
|
|
|
56
|
-
# @return
|
|
56
|
+
# @return CLI exit status.
|
|
57
57
|
#
|
|
58
58
|
# Deferred YJIT enablement (Runtime::Jit) is armed by `CLI#dispatch` for every command, this
|
|
59
59
|
# one included: a scan long enough to amortize JIT compile cost enables mid-flight; a short one
|
|
@@ -48,8 +48,6 @@ module Rigor
|
|
|
48
48
|
# Both measurement-integrity warnings, and the escalation rule between them, live in
|
|
49
49
|
# {MeasurementIntegrityWarning}. Only the floor stays here: it is this command's policy, and
|
|
50
50
|
# `coverage_command_spec` reads it off this module.
|
|
51
|
-
#
|
|
52
|
-
# @param report [MutationProtectionReport, FusedProtectionReport]
|
|
53
51
|
def warn_harness_errors(report)
|
|
54
52
|
MeasurementIntegrityWarning.emit(report, err: @err, floor: HARNESS_ERROR_WARN_FLOOR)
|
|
55
53
|
end
|
|
@@ -113,10 +111,21 @@ module Rigor
|
|
|
113
111
|
cache_store: context.cache_store, workers: workers
|
|
114
112
|
),
|
|
115
113
|
seed_bundles: Protection::DiscoverySeed.bundles(paths: paths),
|
|
116
|
-
discovery_seed: seed
|
|
114
|
+
discovery_seed: seed,
|
|
115
|
+
rebuild_session: -> { rebuilt_session(context) }
|
|
117
116
|
)
|
|
118
117
|
end
|
|
119
118
|
|
|
119
|
+
# Issue #790 — how the oracle replaces an Environment a mutant degraded. {LanguageServer::ProjectContext}
|
|
120
|
+
# already has the operation: `invalidate!` drops the cached environment AND the project scan together,
|
|
121
|
+
# which is the pair the oracle measures over, and the next reader rebuilds both. Under
|
|
122
|
+
# {MutationForkScan} this runs in a forked child against its copy-on-write context, so one file's
|
|
123
|
+
# defect never costs the parent or its siblings a rebuild.
|
|
124
|
+
def rebuilt_session(context)
|
|
125
|
+
context.invalidate!
|
|
126
|
+
[context.environment, context.project_scan]
|
|
127
|
+
end
|
|
128
|
+
|
|
120
129
|
# The scope {Protection::Mutator} judges a mutation site's receiver against, derived from the same seed
|
|
121
130
|
# the oracle gets. nil (the gate off, or an empty seed) keeps the bare `Scope.empty` per file.
|
|
122
131
|
def mutation_base_scope(seed, environment)
|
|
@@ -21,9 +21,7 @@ module Rigor
|
|
|
21
21
|
module CoverageScan
|
|
22
22
|
module_function
|
|
23
23
|
|
|
24
|
-
# @param files
|
|
25
|
-
# @param configuration [Rigor::Configuration]
|
|
26
|
-
# @return [Rigor::CLI::CoverageReport]
|
|
24
|
+
# @param files — explicit `.rb` file paths to scan.
|
|
27
25
|
#
|
|
28
26
|
# Issue #513 — the environment is PLUGIN-AWARE (`LanguageServer::ProjectContext`, the same builder
|
|
29
27
|
# `--protection` uses), not the bare RBS environment this path carried until 2026-09-01. Without the
|
|
@@ -57,8 +55,6 @@ module Rigor
|
|
|
57
55
|
# the precision lens must mirror the walk it describes (`check` leaves the table empty unless
|
|
58
56
|
# `parameter_inference:` is on, ADR-67 WD6a), while `--protection` seeds it unconditionally by ADR-67's own
|
|
59
57
|
# wiring — it only ever reclassifies sites it already counted.
|
|
60
|
-
#
|
|
61
|
-
# @return [Rigor::Scope]
|
|
62
58
|
def discovery_seeded_scope(files:, configuration:, environment:, parameter_inference:, workers: nil)
|
|
63
59
|
base = Scope.empty(environment: environment)
|
|
64
60
|
seed = Protection::DiscoverySeed.discovery_tables(files).dup
|
data/lib/rigor/cli/doc_links.rb
CHANGED
|
@@ -36,9 +36,9 @@ module Rigor
|
|
|
36
36
|
|
|
37
37
|
module_function
|
|
38
38
|
|
|
39
|
-
# @param body
|
|
40
|
-
# @param from
|
|
41
|
-
# @return
|
|
39
|
+
# @param body — the page as it is stored.
|
|
40
|
+
# @param from — its absolute path, so relative targets resolve.
|
|
41
|
+
# @return the page with every relative link turned into a `rigor docs` key.
|
|
42
42
|
def rewrite(body, from:)
|
|
43
43
|
dir = File.dirname(from)
|
|
44
44
|
body.gsub(MARKDOWN_LINK) do
|
|
@@ -52,7 +52,7 @@ module Rigor
|
|
|
52
52
|
HANDBOOK_ROOT = File.join(DOCS_ROOT, "handbook")
|
|
53
53
|
LLMS_INDEX = File.join(DOCS_ROOT, "llms.txt")
|
|
54
54
|
|
|
55
|
-
# @return
|
|
55
|
+
# @return CLI exit status.
|
|
56
56
|
def run
|
|
57
57
|
case @argv.first
|
|
58
58
|
when nil
|
|
@@ -136,7 +136,7 @@ module Rigor
|
|
|
136
136
|
# Resolve a query to a single doc. Exact relative-path and prefixed-basename aliases are unique; a short
|
|
137
137
|
# (prefix-stripped) name is accepted only when one category owns it.
|
|
138
138
|
#
|
|
139
|
-
# @return
|
|
139
|
+
# @return the doc entry, or an error exit status
|
|
140
140
|
# after the error has been written to `@err`.
|
|
141
141
|
def resolve_doc(query)
|
|
142
142
|
# A key printed by a rendered page may carry the section it pointed at; resolve the page and say
|
|
@@ -7,9 +7,11 @@ require_relative "../configuration"
|
|
|
7
7
|
require_relative "../config_audit"
|
|
8
8
|
require_relative "../analysis/baseline"
|
|
9
9
|
require_relative "../analysis/result"
|
|
10
|
+
require_relative "../environment/bundle_sig_discovery"
|
|
10
11
|
require_relative "../plugin"
|
|
11
12
|
require_relative "../plugin/loader"
|
|
12
13
|
require_relative "../plugin/services"
|
|
14
|
+
require_relative "../plugin_gap_advisory"
|
|
13
15
|
require_relative "../reflection"
|
|
14
16
|
require_relative "../type/combinator"
|
|
15
17
|
require_relative "check_invocation"
|
|
@@ -33,17 +35,12 @@ module Rigor
|
|
|
33
35
|
CHECK_RBS = "rbs_environment"
|
|
34
36
|
CHECK_PLUGINS = "plugins"
|
|
35
37
|
CHECK_BASELINE = "baseline"
|
|
36
|
-
|
|
38
|
+
CHECK_PLUGIN_GAP = "plugin_gap"
|
|
37
39
|
CHECK_GEMFILE = "gemfile_install"
|
|
38
40
|
CHECK_PLUGIN_SKEW = "plugin_skew"
|
|
41
|
+
CHECK_BUNDLE_LAYOUT = "bundle_layout"
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
RAILS_PLUGIN_MARKERS = %w[
|
|
42
|
-
rigor-activerecord rigor-actionpack rigor-actionmailer rigor-activejob
|
|
43
|
-
rigor-rails-routes rigor-rails-i18n rigor-actioncable rigor-activestorage rigor-rails
|
|
44
|
-
].freeze
|
|
45
|
-
|
|
46
|
-
# @return [Integer] CLI exit status.
|
|
43
|
+
# @return CLI exit status.
|
|
47
44
|
def run
|
|
48
45
|
options = parse_options
|
|
49
46
|
configuration = Configuration.load(options.fetch(:config))
|
|
@@ -70,8 +67,8 @@ module Rigor
|
|
|
70
67
|
# 5. Baseline drift check.
|
|
71
68
|
findings.concat(check_baseline(configuration, result))
|
|
72
69
|
|
|
73
|
-
# 6.
|
|
74
|
-
findings.concat(
|
|
70
|
+
# 6. Gems locked that a bundled plugin models, with that plugin not enabled (ADR-96 WD2).
|
|
71
|
+
findings.concat(check_plugin_gap(configuration))
|
|
75
72
|
|
|
76
73
|
# 7. Rigor itself resolved as a project dependency.
|
|
77
74
|
findings.concat(check_gemfile_install)
|
|
@@ -79,6 +76,9 @@ module Rigor
|
|
|
79
76
|
# 8. A bundled plugin loaded from a different rigortype installation than the engine (#194 slice 3).
|
|
80
77
|
findings.concat(check_plugin_skew(registry))
|
|
81
78
|
|
|
79
|
+
# 9. The project's gems are installed somewhere Rigor cannot see (ADR-25 carry-over).
|
|
80
|
+
findings.concat(check_bundle_layout(configuration))
|
|
81
|
+
|
|
82
82
|
report(findings, options.fetch(:format))
|
|
83
83
|
findings.any? { |f| f[:status] == :fail } ? 1 : 0
|
|
84
84
|
rescue OptionParser::InvalidArgument => e
|
|
@@ -249,6 +249,46 @@ module Rigor
|
|
|
249
249
|
File.expand_path(path)
|
|
250
250
|
end
|
|
251
251
|
|
|
252
|
+
# ADR-25 carry-over — the plain `bundle install` layout. {Environment::BundleSigDiscovery} detects a
|
|
253
|
+
# bundle root only from paths the PROJECT records: `.bundle/config`'s `BUNDLE_PATH`, an in-tree
|
|
254
|
+
# `vendor/bundle`, or the user-global Bundler config. The default layout records none of those — the
|
|
255
|
+
# gems live in the active Ruby's `GEM_HOME`, which is a property of the project's toolchain, and
|
|
256
|
+
# ADR-27 keeps Rigor from running that toolchain to ask. So detection is not a bounded probe Rigor can
|
|
257
|
+
# add; what it CAN do is stop the state being silent. Without this the user sees gem-shipped `sig/`
|
|
258
|
+
# simply not taking effect, with nothing anywhere naming the reason.
|
|
259
|
+
#
|
|
260
|
+
# Warn, never fail: the layout is the Ruby default and is perfectly correct — only ~3% of gems ship
|
|
261
|
+
# `sig/` at all, so most projects lose nothing by it.
|
|
262
|
+
def check_bundle_layout(configuration)
|
|
263
|
+
return [] unless File.file?(File.join(Dir.pwd, "Gemfile.lock"))
|
|
264
|
+
return [] if resolved_bundle_root(configuration)
|
|
265
|
+
|
|
266
|
+
[
|
|
267
|
+
{
|
|
268
|
+
check: CHECK_BUNDLE_LAYOUT,
|
|
269
|
+
status: :warn,
|
|
270
|
+
message: "Gems are installed in the active Ruby's default gem home, which Rigor cannot locate — " \
|
|
271
|
+
"no gem-shipped `sig/` is being discovered",
|
|
272
|
+
hint: "Rigor reads your project as data and never runs its toolchain, so it can only find a " \
|
|
273
|
+
"bundle whose location the project records (`.bundle/config`'s `BUNDLE_PATH`, an in-tree " \
|
|
274
|
+
"`vendor/bundle`, or your global Bundler config). Point it at the install root with " \
|
|
275
|
+
"`bundler.bundle_path:` in `.rigor.yml`, run `bundle config set --local path vendor/bundle` " \
|
|
276
|
+
"and reinstall, or supply signatures with `rbs collection install` instead."
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def resolved_bundle_root(configuration)
|
|
282
|
+
Environment::BundleSigDiscovery.resolve_bundle_path(
|
|
283
|
+
bundle_path: configuration.bundler_bundle_path,
|
|
284
|
+
project_root: Dir.pwd,
|
|
285
|
+
auto_detect: configuration.bundler_auto_detect
|
|
286
|
+
)
|
|
287
|
+
rescue StandardError
|
|
288
|
+
# A doctor check must never be the thing that breaks the command it is diagnosing.
|
|
289
|
+
nil
|
|
290
|
+
end
|
|
291
|
+
|
|
252
292
|
def check_baseline(configuration, result)
|
|
253
293
|
path = configuration.baseline_path
|
|
254
294
|
return [] if path.nil? || !File.exist?(path)
|
|
@@ -287,38 +327,45 @@ module Rigor
|
|
|
287
327
|
parts.join(", ")
|
|
288
328
|
end
|
|
289
329
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
330
|
+
# ADR-96 WD2 — the plugin-gap advisory, read off each bundled plugin's `target_gems:` rather than the
|
|
331
|
+
# Rails-only tables it replaces. Per plugin, so a Rails app that enabled only `rigor-activerecord` now
|
|
332
|
+
# hears about the other seven; `:warn`, because declining a plugin is a legitimate choice and must not
|
|
333
|
+
# fail the command forever. The one `:fail` is today's behaviour generalised: the project depends on
|
|
334
|
+
# gems Rigor models and enables none of the plugins that model them, which is genuinely unconfigured.
|
|
335
|
+
# Both branches read the lockfile's `DEPENDENCIES` section, never its resolved graph: a transitive
|
|
336
|
+
# `minitest` or `i18n` rides along in nearly every Rails lock, and failing `doctor` on one would fail a
|
|
337
|
+
# correctly configured project.
|
|
338
|
+
def check_plugin_gap(configuration)
|
|
339
|
+
return [] if Configuration.discover.nil?
|
|
340
|
+
|
|
341
|
+
gaps = PluginGapAdvisory.gaps(project_root: Dir.pwd, plugins: configuration.plugins)
|
|
342
|
+
return [] if gaps.empty?
|
|
343
|
+
|
|
344
|
+
if PluginGapAdvisory.unconfigured?(project_root: Dir.pwd, plugins: configuration.plugins)
|
|
345
|
+
[unconfigured_plugin_finding(gaps)]
|
|
346
|
+
else
|
|
347
|
+
gaps.map { |gap| plugin_gap_finding(gap) }
|
|
348
|
+
end
|
|
302
349
|
end
|
|
303
350
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return false unless File.file?(lock) && file_mentions_any?(lock, RAILS_LOCK_MARKERS)
|
|
313
|
-
|
|
314
|
-
!file_mentions_any?(config_path, RAILS_PLUGIN_MARKERS)
|
|
351
|
+
def unconfigured_plugin_finding(gaps)
|
|
352
|
+
{
|
|
353
|
+
check: CHECK_PLUGIN_GAP,
|
|
354
|
+
status: :fail,
|
|
355
|
+
message: "#{gaps.size} bundled plugin(s) model gems this project depends on and none is enabled " \
|
|
356
|
+
"(#{gaps.map(&:plugin_gem).join(', ')})",
|
|
357
|
+
hint: "Add the plugins for your stack to `.rigor.yml` `plugins:` so framework calls resolve."
|
|
358
|
+
}
|
|
315
359
|
end
|
|
316
360
|
|
|
317
|
-
def
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
361
|
+
def plugin_gap_finding(gap)
|
|
362
|
+
{
|
|
363
|
+
check: CHECK_PLUGIN_GAP,
|
|
364
|
+
status: :warn,
|
|
365
|
+
message: "#{gap.locked_gems.join(', ')} #{gap.locked_gems.one? ? 'is a dependency' : 'are dependencies'} " \
|
|
366
|
+
"of this project and #{gap.plugin_gem} models it, but it is not enabled",
|
|
367
|
+
hint: "Add `#{gap.plugin_gem}` to `.rigor.yml` `plugins:`, or leave it out deliberately."
|
|
368
|
+
}
|
|
322
369
|
end
|
|
323
370
|
|
|
324
371
|
# Rigor is a tool, not a library (ADR-27): resolving it as one of the project's own dependencies pins the
|
|
@@ -41,7 +41,7 @@ module Rigor
|
|
|
41
41
|
class EffectsCommand < Command
|
|
42
42
|
USAGE = "Usage: rigor effects [options] [paths]"
|
|
43
43
|
|
|
44
|
-
# @return
|
|
44
|
+
# @return CLI exit status: 0 on success, 1 on `check` drift, 64 on a usage error.
|
|
45
45
|
def run
|
|
46
46
|
verb = @argv.first
|
|
47
47
|
return run_verb(verb) if EffectsSnapshotCommand::VERBS.include?(verb)
|
|
@@ -231,7 +231,7 @@ module Rigor
|
|
|
231
231
|
# project does — still analyses it. Inside a project the argument is already under `paths:` and the
|
|
232
232
|
# union is the configured set unchanged.
|
|
233
233
|
#
|
|
234
|
-
# @return
|
|
234
|
+
# @return the table, and which
|
|
235
235
|
# file each unit was defined in — the map {EffectsReport} needs to answer a path argument.
|
|
236
236
|
def analyze(configuration, scope)
|
|
237
237
|
require_relative "../analysis/runner"
|
|
@@ -50,12 +50,12 @@ module Rigor
|
|
|
50
50
|
REGENERATION_CLOSING_LINE = "Run `rigor effects update` to regenerate the record under the current " \
|
|
51
51
|
"rules."
|
|
52
52
|
|
|
53
|
-
# @param sources
|
|
53
|
+
# @param sources — `Runner#effect_sources` — where each unit is defined.
|
|
54
54
|
# A drift row names `file:line` so a reviewer does not have to search for the method (#435). The
|
|
55
55
|
# file rides the cached summary entry and is free; the line is resolved by parsing the row's own
|
|
56
56
|
# file ({Effects::DefinitionLines}), which is why a fresh report — no rows — still parses nothing
|
|
57
57
|
# and the whole-project parse ADR-104 removed from this command stays removed.
|
|
58
|
-
# @param lines
|
|
58
|
+
# @param lines — seam for the specs; the default parses on demand.
|
|
59
59
|
def initialize(out:, path:, sources: nil, lines: Effects::DefinitionLines.new)
|
|
60
60
|
@out = out
|
|
61
61
|
@path = path
|
|
@@ -121,7 +121,7 @@ module Rigor
|
|
|
121
121
|
# method that no longer exists was not defined by it. Such a row renders exactly as it did before
|
|
122
122
|
# this suffix existed.
|
|
123
123
|
#
|
|
124
|
-
# @return
|
|
124
|
+
# @return `[relative path, line]` per file, the line nil when that
|
|
125
125
|
# file spells the key with no `def` — an accessor, or a definition the nesting cannot name.
|
|
126
126
|
def sources_of(symbol)
|
|
127
127
|
Array(@sources[symbol]).map do |path|
|
|
@@ -51,7 +51,7 @@ module Rigor
|
|
|
51
51
|
@verb = verb
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# @return
|
|
54
|
+
# @return 0 fresh / written, 1 on drift under `check`, 64 on a usage error.
|
|
55
55
|
def run
|
|
56
56
|
options = parse_options
|
|
57
57
|
return usage_error("unsupported format: #{options.fetch(:format)}") unless
|
|
@@ -4,6 +4,7 @@ require "json"
|
|
|
4
4
|
require "optionparser"
|
|
5
5
|
|
|
6
6
|
require_relative "../analysis/rule_catalog"
|
|
7
|
+
require_relative "../sig_gen/skip_reason_catalog"
|
|
7
8
|
require_relative "command"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
@@ -18,7 +19,7 @@ module Rigor
|
|
|
18
19
|
class ExplainCommand < Command
|
|
19
20
|
USAGE = "Usage: rigor explain [options] [<rule>]"
|
|
20
21
|
|
|
21
|
-
# @return
|
|
22
|
+
# @return CLI exit status.
|
|
22
23
|
def run
|
|
23
24
|
options = parse_options
|
|
24
25
|
|
|
@@ -28,6 +29,12 @@ module Rigor
|
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
token = @argv.shift
|
|
32
|
+
skip_reason = SigGen::SkipReasonCatalog.resolve(token)
|
|
33
|
+
if skip_reason
|
|
34
|
+
render_skip_reason(skip_reason, options.fetch(:format))
|
|
35
|
+
return 0
|
|
36
|
+
end
|
|
37
|
+
|
|
31
38
|
entries = Analysis::RuleCatalog.resolve(token)
|
|
32
39
|
if entries.empty?
|
|
33
40
|
@err.puts("Unknown rule: #{token}")
|
|
@@ -68,6 +75,32 @@ module Rigor
|
|
|
68
75
|
@out.puts("")
|
|
69
76
|
@out.puts("Run `rigor explain <rule>` for the full description.")
|
|
70
77
|
@out.puts("Family wildcards (`call`, `flow`, `assert`, `dump`, `def`) print every rule under that prefix.")
|
|
78
|
+
@out.puts("")
|
|
79
|
+
@out.puts("`rigor sig-gen` skip reasons:")
|
|
80
|
+
@out.puts("")
|
|
81
|
+
SigGen::SkipReasonCatalog.all.each do |entry|
|
|
82
|
+
@out.puts(" #{entry.id.ljust(33)} #{entry.summary}")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def render_skip_reason(entry, format)
|
|
87
|
+
if format == "json"
|
|
88
|
+
@out.puts(JSON.pretty_generate(entry.to_h))
|
|
89
|
+
return
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@out.puts(entry.id)
|
|
93
|
+
@out.puts("=" * entry.id.length)
|
|
94
|
+
@out.puts("")
|
|
95
|
+
@out.puts(entry.summary)
|
|
96
|
+
@out.puts("")
|
|
97
|
+
@out.puts("A `rigor sig-gen` skip reason, not a diagnostic rule: it reports a signature that was NOT")
|
|
98
|
+
@out.puts("written, so it has no severity and nothing to suppress.")
|
|
99
|
+
@out.puts("")
|
|
100
|
+
@out.puts(entry.explanation)
|
|
101
|
+
@out.puts("")
|
|
102
|
+
@out.puts("What to do:")
|
|
103
|
+
@out.puts(" #{entry.next_step}")
|
|
71
104
|
end
|
|
72
105
|
|
|
73
106
|
def render_entries(entries, format)
|
|
@@ -12,7 +12,13 @@ module Rigor
|
|
|
12
12
|
# Framing (ADR-63 / ADR-62 Criterion A, extended): the payload is the **attribution** — which protection axis is
|
|
13
13
|
# missing — never raw survival. An unprotected site is "add protection here", never "your code is broken".
|
|
14
14
|
# `harness_errors` (#264) — see {Rigor::CLI::FileEffectiveness}; defaults to 0 for the same reason.
|
|
15
|
-
|
|
15
|
+
# A `class ... < Data.define(...)` body, not the `Data.define(...) do ... end` shorthand: Steep
|
|
16
|
+
# resolves a `def` inside the shorthand block against the *lexically enclosing* class (here,
|
|
17
|
+
# `Rigor::CLI`, which declares its own differently-shaped `initialize`) rather than the anonymous
|
|
18
|
+
# Data subclass being defined — a `sig/` declaration for `FusedFileProtection` cannot fix this,
|
|
19
|
+
# since the misattribution happens before any signature is consulted. The explicit `class` keyword
|
|
20
|
+
# is a real namespace boundary Steep's block walker respects; behaviour is identical either way.
|
|
21
|
+
class FusedFileProtection < Data.define(:path, :type_killed, :test_killed, :unprotected, :ratio, :harness_errors)
|
|
16
22
|
def initialize(path:, type_killed:, test_killed:, unprotected:, ratio:, harness_errors: 0)
|
|
17
23
|
super
|
|
18
24
|
end
|