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
|
@@ -50,11 +50,18 @@ module Rigor
|
|
|
50
50
|
# passage is the precedent for stopping at one hop rather than building a provenance channel through
|
|
51
51
|
# the return memo.
|
|
52
52
|
#
|
|
53
|
-
#
|
|
54
|
-
# into an ivar (`@mode = MODE` in `initialize`), or
|
|
55
|
-
# (`MODE2 = AppConfig::MODE`).
|
|
56
|
-
#
|
|
57
|
-
#
|
|
53
|
+
# - a value COPIED out of such a constant before the predicate reads it (issue #667) — into a local
|
|
54
|
+
# (`m = AppConfig::MODE; m == :x`), into an ivar (`@mode = AppConfig::MODE` in `initialize`), or
|
|
55
|
+
# through a same-file alias (`MODE2 = AppConfig::MODE`). A copy has no spelling for the root walk to
|
|
56
|
+
# recognise, so the provenance is stamped on the value where the copy happens and travels with it:
|
|
57
|
+
# the first two on the `Scope` flow carrier `published_constant_sourced` (dropped by any rebinding,
|
|
58
|
+
# unioned at a join), the alias on the per-file `published_constant_alias_names` census table, which
|
|
59
|
+
# needs no flow machinery at all because a constant write is not flow. This walk only READS those
|
|
60
|
+
# marks — the widening ADR-58's *Non-transitivity* passage forbids is inferring one AT a consumer,
|
|
61
|
+
# and the establishing transitions are named in
|
|
62
|
+
# `docs/internal-spec/inference-engine.md` § "The truthiness withholding".
|
|
63
|
+
#
|
|
64
|
+
# Deliberately NOT covered: two interprocedural hops (`live? -> prod? -> MODE`).
|
|
58
65
|
module PublishedConstantGuard
|
|
59
66
|
# A defensive depth cap against a pathological chain (the walk is otherwise linear in chain length).
|
|
60
67
|
MAX_DEPTH = 64
|
|
@@ -66,13 +73,15 @@ module Rigor
|
|
|
66
73
|
|
|
67
74
|
module_function
|
|
68
75
|
|
|
69
|
-
# @return
|
|
76
|
+
# @return true when the rule MUST withhold its firing.
|
|
70
77
|
def rooted?(node, scope, depth = 0)
|
|
71
78
|
return false if node.nil? || depth > MAX_DEPTH || scope.nil?
|
|
72
79
|
|
|
73
80
|
case node
|
|
74
81
|
when Prism::ConstantReadNode then scope.published_constant?(node.name.to_s)
|
|
75
82
|
when Prism::ConstantPathNode then published_path?(node, scope)
|
|
83
|
+
when Prism::LocalVariableReadNode then scope.published_constant_sourced?(:local, node.name)
|
|
84
|
+
when Prism::InstanceVariableReadNode then scope.published_constant_sourced?(:ivar, node.name)
|
|
76
85
|
when Prism::CallNode then rooted_call?(node, scope, depth)
|
|
77
86
|
else rooted_through_composition?(node, scope, depth)
|
|
78
87
|
end
|
|
@@ -137,7 +137,7 @@ module Rigor
|
|
|
137
137
|
# first segment appears here as KNOWN and stays silent: these ids are legitimate suppression /
|
|
138
138
|
# `severity_overrides:` vocabulary the light rule-id table cannot enumerate (plugins load dynamically;
|
|
139
139
|
# aggregator ids live in the engine-heavy runner), so under-warning is the FP-safe direction.
|
|
140
|
-
NON_CHECK_DIAGNOSTIC_FAMILIES = %w[rbs_extended dynamic rbs pre-eval plugin].freeze
|
|
140
|
+
NON_CHECK_DIAGNOSTIC_FAMILIES = %w[rbs_extended dynamic rbs pre-eval plugin plugin_trust].freeze
|
|
141
141
|
|
|
142
142
|
# Bare (dot-less) diagnostic ids the engine emits outside the catalogue (see the `rule:` literals in
|
|
143
143
|
# `Analysis::Runner` / `Runner::DiagnosticAggregator`). A token equal to one of these is treated as
|
|
@@ -40,11 +40,26 @@ module Rigor
|
|
|
40
40
|
|
|
41
41
|
CLASS_OR_MODULE_NODE_CLASSES = [Prism::ClassNode, Prism::ModuleNode].freeze
|
|
42
42
|
|
|
43
|
+
# Issue #909 — the receivers whose block form opens a class body that is NOT the lexically enclosing
|
|
44
|
+
# class: `Class.new do … end`, `Module.new`, `Struct.new`, `Data.define`. A `def` in such a block
|
|
45
|
+
# belongs to the anonymous class the call returns, so its ivar writes are a different store from the
|
|
46
|
+
# enclosing class's.
|
|
47
|
+
DETACHED_CLASS_FACTORIES = {
|
|
48
|
+
Class: %i[new],
|
|
49
|
+
Module: %i[new],
|
|
50
|
+
Struct: %i[new],
|
|
51
|
+
Data: %i[define]
|
|
52
|
+
}.freeze
|
|
53
|
+
private_constant :DETACHED_CLASS_FACTORIES
|
|
54
|
+
|
|
55
|
+
EMPTY_METHODS = [].freeze
|
|
56
|
+
private_constant :EMPTY_METHODS
|
|
57
|
+
|
|
43
58
|
# The immutable per-node descent context. Recomputed for each child from its parent's context as the
|
|
44
59
|
# walk descends; collectors read only the fields they declared a need for.
|
|
45
|
-
Context = Data.define(:in_loop_or_block, :qualified_prefix, :inside_def) do
|
|
60
|
+
Context = Data.define(:in_loop_or_block, :qualified_prefix, :inside_def, :detached_ivar_facet) do
|
|
46
61
|
def self.root
|
|
47
|
-
new(in_loop_or_block: false, qualified_prefix: [], inside_def: false)
|
|
62
|
+
new(in_loop_or_block: false, qualified_prefix: [], inside_def: false, detached_ivar_facet: false)
|
|
48
63
|
end
|
|
49
64
|
end
|
|
50
65
|
|
|
@@ -58,9 +73,13 @@ module Rigor
|
|
|
58
73
|
# - `:inside_def` (`inside_def`) — lexically inside a `DefNode`; IvarWrite collects only at a `def`
|
|
59
74
|
# that sits directly in a class / module body (its legacy walk `return`s at the first `def` it
|
|
60
75
|
# meets, so a nested def is never reached).
|
|
76
|
+
# - `:detached_ivar_facet` (`detached_ivar_facet`) — under a `class << self` body or an
|
|
77
|
+
# anonymous-class factory block, where the enclosing class name does not name the ivar store the
|
|
78
|
+
# body writes to.
|
|
61
79
|
GATE_CONTEXT_PREDICATES = {
|
|
62
80
|
loop_or_block: :in_loop_or_block,
|
|
63
|
-
inside_def: :inside_def
|
|
81
|
+
inside_def: :inside_def,
|
|
82
|
+
detached_ivar_facet: :detached_ivar_facet
|
|
64
83
|
}.freeze
|
|
65
84
|
|
|
66
85
|
# A per-node driver over a fixed collector set: holds the compiled `node_class => [[collector,
|
|
@@ -124,14 +143,30 @@ module Rigor
|
|
|
124
143
|
LOOP_OR_BLOCK_NODE_CLASSES.any? { |klass| node.is_a?(klass) }
|
|
125
144
|
inside_def = context.inside_def || node.is_a?(Prism::DefNode)
|
|
126
145
|
qualified_prefix = extend_prefix(node, context.qualified_prefix)
|
|
146
|
+
detached_ivar_facet = context.detached_ivar_facet || detached_ivar_facet?(node)
|
|
127
147
|
|
|
128
148
|
Context.new(
|
|
129
149
|
in_loop_or_block: in_loop_or_block,
|
|
130
150
|
qualified_prefix: qualified_prefix,
|
|
131
|
-
inside_def: inside_def
|
|
151
|
+
inside_def: inside_def,
|
|
152
|
+
detached_ivar_facet: detached_ivar_facet
|
|
132
153
|
)
|
|
133
154
|
end
|
|
134
155
|
|
|
156
|
+
# True for a node whose descendants write ivars to a store the enclosing class name does not name:
|
|
157
|
+
# a `class << self` body (the class object's own ivars) or a call that builds an anonymous class
|
|
158
|
+
# from a block. Every singleton-class body counts, `class << Konstant` included: whichever object's
|
|
159
|
+
# singleton it opens, it is not the instance facet the enclosing prefix names.
|
|
160
|
+
def detached_ivar_facet?(node)
|
|
161
|
+
return true if node.is_a?(Prism::SingletonClassNode)
|
|
162
|
+
return false unless node.is_a?(Prism::CallNode) && node.block.is_a?(Prism::BlockNode)
|
|
163
|
+
|
|
164
|
+
receiver = node.receiver
|
|
165
|
+
return false unless receiver.is_a?(Prism::ConstantReadNode)
|
|
166
|
+
|
|
167
|
+
DETACHED_CLASS_FACTORIES.fetch(receiver.name, EMPTY_METHODS).include?(node.name)
|
|
168
|
+
end
|
|
169
|
+
|
|
135
170
|
private
|
|
136
171
|
|
|
137
172
|
def collector_gates(collector)
|
|
@@ -58,7 +58,7 @@ module Rigor
|
|
|
58
58
|
# Legacy single-collector walk — the oracle the shadow harness (`RIGOR_SHADOW_RULE_WALK=1`) compares
|
|
59
59
|
# the shared {RuleWalk} traversal against. Tracks the lexical class / module prefix itself, exactly
|
|
60
60
|
# as {RuleWalk} threads `qualified_prefix`.
|
|
61
|
-
# @return
|
|
61
|
+
# @return one entry per provably-shadowed rescue clause.
|
|
62
62
|
def collect(root)
|
|
63
63
|
walk(root, [])
|
|
64
64
|
@results.freeze
|
|
@@ -42,7 +42,7 @@ module Rigor
|
|
|
42
42
|
DEFENSIVE_EXIT_CALLS = %i[raise fail throw abort exit].freeze
|
|
43
43
|
|
|
44
44
|
# @!attribute kind
|
|
45
|
-
# @return
|
|
45
|
+
# @return `:disjoint` (this clause's type is disjoint
|
|
46
46
|
# from the still-possible subject), `:prior_exhaustion` (an
|
|
47
47
|
# earlier clause already covered the subject), or
|
|
48
48
|
# `:exhausted_else` (every subject value is covered, so the
|
|
@@ -63,7 +63,7 @@ module Rigor
|
|
|
63
63
|
|
|
64
64
|
# Legacy single-collector walk — kept as the oracle the ADR-53 Track B equivalence harness compares
|
|
65
65
|
# {RuleWalk} against; deleted when Track B completes.
|
|
66
|
-
# @return
|
|
66
|
+
# @return one entry per provably-dead `when` clause.
|
|
67
67
|
def collect(root)
|
|
68
68
|
walk(root, in_loop_or_block: false)
|
|
69
69
|
@results.freeze
|
|
@@ -52,7 +52,6 @@ module Rigor
|
|
|
52
52
|
# Legacy single-collector walk — kept as the oracle the ADR-53 Track B equivalence harness compares
|
|
53
53
|
# {RuleWalk} against. Walks the whole subtree once, checking each consumer node's value slots.
|
|
54
54
|
# Returns one {Result} per value-context use of a recovered-`void` call.
|
|
55
|
-
# @return [Array<Result>]
|
|
56
55
|
def collect(root)
|
|
57
56
|
walk(root)
|
|
58
57
|
@results.freeze
|
|
@@ -83,11 +83,8 @@ module Rigor
|
|
|
83
83
|
# have already produced `scope_index` through
|
|
84
84
|
# `Rigor::Inference::ScopeIndexer.index(root, default_scope:)`.
|
|
85
85
|
#
|
|
86
|
-
# @param path
|
|
86
|
+
# @param path — used to populate
|
|
87
87
|
# `Diagnostic#path`; the rule does not open files.
|
|
88
|
-
# @param root [Prism::Node]
|
|
89
|
-
# @param scope_index [Hash{Prism::Node => Rigor::Scope}]
|
|
90
|
-
# @return [Array<Rigor::Analysis::Diagnostic>]
|
|
91
88
|
#
|
|
92
89
|
# ADR-53 B4 — when `node_collectors` is supplied, the converged
|
|
93
90
|
# {Plugin::NodeRuleWalk} traversal has already populated the built-in
|
|
@@ -490,7 +487,7 @@ module Rigor
|
|
|
490
487
|
/\A#\s*rigor:(?<marker>disable-(?!file(?![\w-]))[\w-]+|enable(?:-[\w-]+)?)(?![\w-])(?<rest>.*)/
|
|
491
488
|
private_constant :UNKNOWN_SUPPRESSION_MARKER
|
|
492
489
|
|
|
493
|
-
# @return
|
|
490
|
+
# @return pair of
|
|
494
491
|
# `(line_suppressions, file_suppressions)`. Line
|
|
495
492
|
# suppressions are keyed by source line number; file
|
|
496
493
|
# suppressions apply to every line.
|
|
@@ -717,7 +714,7 @@ module Rigor
|
|
|
717
714
|
# because the real type is empty (the real `DRb` has
|
|
718
715
|
# `start_service`), so enumerating it to prove a call
|
|
719
716
|
# "undefined" would be a false positive.
|
|
720
|
-
return nil if unenumerable_receiver?(class_name, scope)
|
|
717
|
+
return nil if unenumerable_receiver?(receiver_type, class_name, scope)
|
|
721
718
|
|
|
722
719
|
# Slice 7 phase 12 — suppress when the user has
|
|
723
720
|
# declared the method in source (`def` /
|
|
@@ -976,10 +973,32 @@ module Rigor
|
|
|
976
973
|
# the scalar rule enumerated `Module`'s own RBS instead and reported them.
|
|
977
974
|
#
|
|
978
975
|
# Kept beside `unbounded_receiver_surface?` rather than folded into it: that predicate has eight
|
|
979
|
-
# other callers
|
|
980
|
-
#
|
|
981
|
-
|
|
982
|
-
|
|
976
|
+
# other callers, and #742 had no evidence that any of them spoke on these receivers.
|
|
977
|
+
#
|
|
978
|
+
# Issue #879 supplied it for exactly one — `call.wrong-arity`, which decided `v.hexdigest(1, 2, 3)`
|
|
979
|
+
# against `Digest::Instance`'s declaration on the same run where `v.no_such_method_zzz` was declined
|
|
980
|
+
# as unenumerable. One receiver, one run, two answers about whether that surface can be read. The
|
|
981
|
+
# reporter could make no other caller speak: `raise` on a module- or `Class`-typed operand is silent,
|
|
982
|
+
# and `Class`-typed `.new` with a wrong arity is silent too, so the other seven stay on
|
|
983
|
+
# `unbounded_receiver_surface?` until something demonstrates otherwise.
|
|
984
|
+
#
|
|
985
|
+
# Arity takes {#unenumerable_receiver_shape?}, not this whole predicate: ADR-26's open receivers are
|
|
986
|
+
# handled for the signature-reading rules by {#unauthoritative_inherited_signature?}, which is
|
|
987
|
+
# deliberately narrower — a method the open class DECLARES itself keeps its arity check.
|
|
988
|
+
def unenumerable_receiver?(receiver_type, class_name, scope)
|
|
989
|
+
unenumerable_receiver_shape?(receiver_type, class_name, scope) ||
|
|
990
|
+
unbounded_receiver_surface?(class_name, scope)
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
# The receiver shapes whose method surface cannot be enumerated at all, whatever the environment
|
|
994
|
+
# declares: a generic metaclass value, and an instance-side mixin-module type — an instance of some
|
|
995
|
+
# unknown class that includes the module, which may declare the name with any arity of its own
|
|
996
|
+
# (#739's reasoning, applied to the signature rather than to the method's existence).
|
|
997
|
+
#
|
|
998
|
+
# Instance-side only, for #739's reason: `Singleton[M]` on a namespace module is a real, enumerable
|
|
999
|
+
# surface, and `M.helper(1, 2, 3)` must keep firing.
|
|
1000
|
+
def unenumerable_receiver_shape?(receiver_type, class_name, scope)
|
|
1001
|
+
METACLASS_ARMS.include?(class_name) || module_mixin_receiver?(receiver_type, scope)
|
|
983
1002
|
end
|
|
984
1003
|
|
|
985
1004
|
# Issue #746 — a class that includes a module the environment does not know. `SudoMode::Form`
|
|
@@ -1030,7 +1049,7 @@ module Rigor
|
|
|
1030
1049
|
# about it instead of bailing. `Type::Refined` carries string-family
|
|
1031
1050
|
# refinements (`lowercase-string`, …) over an explicit `.base`;
|
|
1032
1051
|
# `Type::IntegerRange` carries the bounded-int refinements
|
|
1033
|
-
# (`non-negative-int`, `positive-int`, `
|
|
1052
|
+
# (`non-negative-int`, `positive-int`, `Integer[1..5]`), every one an
|
|
1034
1053
|
# Integer; `Type::Difference` (`A - B`) carries the non-empty /
|
|
1035
1054
|
# non-zero refinements (`non-empty-string` = `String - ""`,
|
|
1036
1055
|
# `non-empty-array` = `Array - []`, `non-zero-int` = `Integer - 0`)
|
|
@@ -1038,6 +1057,7 @@ module Rigor
|
|
|
1038
1057
|
# base (minuend) class dispatches.
|
|
1039
1058
|
when Type::Refined, Type::Difference then concrete_class_name(type.base)
|
|
1040
1059
|
when Type::IntegerRange then "Integer"
|
|
1060
|
+
when Type::FloatRange then "Float"
|
|
1041
1061
|
end
|
|
1042
1062
|
end
|
|
1043
1063
|
|
|
@@ -1097,7 +1117,11 @@ module Rigor
|
|
|
1097
1117
|
# audit), so declaring even its reader surface makes it RBS-known — and, without this, every member
|
|
1098
1118
|
# the declaration omits would become a false `call.undefined-method` for every project that locks
|
|
1099
1119
|
# activesupport, the overwhelming majority of which never opt into the plugin at all.
|
|
1100
|
-
|
|
1120
|
+
# `ActiveSupport::Concern` joined it in #534 item 7. `class_methods do … end` synthesizes a
|
|
1121
|
+
# `ClassMethods` constant and `append_features` / `prepend_features` are part of the real surface,
|
|
1122
|
+
# none of which the two-line declaration enumerates — and a concern module is the receiver of that
|
|
1123
|
+
# surface in every Rails app, so the same argument applies with the same force.
|
|
1124
|
+
GEM_OVERLAY_OPEN_RECEIVERS = Set["ActiveSupport::Duration", "ActiveSupport::Concern"].freeze
|
|
1101
1125
|
private_constant :GEM_OVERLAY_OPEN_RECEIVERS
|
|
1102
1126
|
|
|
1103
1127
|
# ADR-26 — whether `class_name` is declared "open" by a
|
|
@@ -1441,6 +1465,12 @@ module Rigor
|
|
|
1441
1465
|
class_name = concrete_class_name(receiver_type)
|
|
1442
1466
|
return nil if class_name.nil?
|
|
1443
1467
|
|
|
1468
|
+
# Issue #879 — the receiver whose surface `call.undefined-method` already refuses to enumerate.
|
|
1469
|
+
# Deciding an arity against a declaration this rule cannot prove the receiver actually runs is the
|
|
1470
|
+
# same unsoundness in signature form: the includer may declare the name with a wider arity, and
|
|
1471
|
+
# then the call is correct code.
|
|
1472
|
+
return nil if unenumerable_receiver_shape?(receiver_type, class_name, scope)
|
|
1473
|
+
|
|
1444
1474
|
kind = receiver_type.is_a?(Type::Singleton) ? :singleton : :instance
|
|
1445
1475
|
# `Struct.new(:a, :b).new(...)` chained: the inner
|
|
1446
1476
|
# `Struct.new(...)` is an anonymous Struct *subclass* whose
|
|
@@ -1458,6 +1488,7 @@ module Rigor
|
|
|
1458
1488
|
|
|
1459
1489
|
method_def = trustworthy_signature(receiver_type, class_name, call_node, scope)
|
|
1460
1490
|
return nil if method_def.nil?
|
|
1491
|
+
return nil if undeclared_constructor?(class_name, call_node, kind, method_def)
|
|
1461
1492
|
|
|
1462
1493
|
arity_envelope = compute_arity_envelope(method_def)
|
|
1463
1494
|
return nil if arity_envelope.nil?
|
|
@@ -1469,6 +1500,25 @@ module Rigor
|
|
|
1469
1500
|
build_arity_diagnostic(path, call_node, class_name, min, max, actual)
|
|
1470
1501
|
end
|
|
1471
1502
|
|
|
1503
|
+
# Issue #917 — no loaded RBS declares a constructor for this class. The definition builder
|
|
1504
|
+
# synthesizes `.new` from the nearest `initialize` in the ancestry, and when nothing declares
|
|
1505
|
+
# one that is `BasicObject#initialize: () -> void`, so `.new` derives a nullary envelope that
|
|
1506
|
+
# is an artifact of the fallback rather than a statement about the class. `Gem::Specification`
|
|
1507
|
+
# is declared in full by the vendored rubygems sigs except for its constructor, and every
|
|
1508
|
+
# argument at the real one was reported. The rule needs "this class declares a nullary
|
|
1509
|
+
# constructor", and only a `defined_in` naming a class other than `BasicObject` is that
|
|
1510
|
+
# evidence — except on `BasicObject` and `Object` themselves, where the declaration is their
|
|
1511
|
+
# own and `.new` really does take no arguments.
|
|
1512
|
+
def undeclared_constructor?(class_name, call_node, kind, method_def)
|
|
1513
|
+
return false unless kind == :singleton && call_node.name == :new
|
|
1514
|
+
return false unless method_def.respond_to?(:defined_in)
|
|
1515
|
+
|
|
1516
|
+
defined_in = method_def.defined_in
|
|
1517
|
+
return false unless defined_in.nil? || strip_root(defined_in.to_s) == "BasicObject"
|
|
1518
|
+
|
|
1519
|
+
!%w[BasicObject Object].include?(strip_root(class_name))
|
|
1520
|
+
end
|
|
1521
|
+
|
|
1472
1522
|
# True for the outer `.new` of a chained `Struct.new(...).new`:
|
|
1473
1523
|
# `class_name`/`kind` already pin the receiver to
|
|
1474
1524
|
# `Singleton[Struct]`, and the receiver node is itself a
|
|
@@ -2385,8 +2435,10 @@ module Rigor
|
|
|
2385
2435
|
# overload requires the dispatcher's full
|
|
2386
2436
|
# acceptance plumbing.
|
|
2387
2437
|
# - The selected overload must have NO
|
|
2388
|
-
# rest_positionals
|
|
2389
|
-
#
|
|
2438
|
+
# rest_positionals and NO trailing positionals —
|
|
2439
|
+
# the two shapes under which argument N no longer
|
|
2440
|
+
# binds to positional parameter N. Keyword
|
|
2441
|
+
# parameters do not disqualify it (#673).
|
|
2390
2442
|
# - The call must use plain positional arguments
|
|
2391
2443
|
# (no splat / kw / block-pass / forwarded).
|
|
2392
2444
|
# - Per-argument: skip when EITHER side is `Dynamic`
|
|
@@ -2478,13 +2530,46 @@ module Rigor
|
|
|
2478
2530
|
# (a single-concrete-class argument every overload rejects). See
|
|
2479
2531
|
# {#multi_overload_argument_mismatch}.
|
|
2480
2532
|
def argument_mismatch(method_types, call_node, scope, param_overrides)
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2533
|
+
mismatch =
|
|
2534
|
+
if method_types.size == 1
|
|
2535
|
+
first_argument_mismatch(method_types.first, call_node, scope, param_overrides)
|
|
2536
|
+
else
|
|
2537
|
+
multi_overload_argument_mismatch(method_types, call_node, scope, param_overrides)
|
|
2538
|
+
end
|
|
2539
|
+
return nil if mismatch && keyword_bearing?(method_types) && nil_tainted?(mismatch[:actual])
|
|
2540
|
+
|
|
2541
|
+
mismatch
|
|
2542
|
+
end
|
|
2543
|
+
|
|
2544
|
+
# Issue #673 widened {#argument_check_eligible?} to admit keyword-bearing signatures, whose
|
|
2545
|
+
# POSITIONAL arguments had gone unchecked. The widening is staged, and this is the half held
|
|
2546
|
+
# back: on such a signature only an argument refuted on its CLASS fires; one carrying `nil`
|
|
2547
|
+
# does not.
|
|
2548
|
+
#
|
|
2549
|
+
# Measured, not assumed. Across redmine, mastodon, tdiary-core, faraday, liquid and
|
|
2550
|
+
# rubocop-ast the widening produced exactly two new verdicts, both on correct code and both
|
|
2551
|
+
# resting on nullability: `Addrinfo.foreach(host, nil, nil, :STREAM)` (redmine — a nil service
|
|
2552
|
+
# is legal at runtime; rbs declares the parameter `String | ::Integer`) and
|
|
2553
|
+
# `Nokogiri::HTML5(html)` guarded by `return if html.nil?` one line above (mastodon — the guard
|
|
2554
|
+
# is on a method call, so the type stays `Dynamic[top]?`). Zero class-refuted new verdicts were
|
|
2555
|
+
# false. Nullability is the property a hand-written signature most often over-declares, so it
|
|
2556
|
+
# stays behind the old envelope until it has evidence of its own.
|
|
2557
|
+
#
|
|
2558
|
+
# Signatures WITHOUT keywords are untouched: `"a" + nil` fires exactly as before.
|
|
2559
|
+
def keyword_bearing?(method_types)
|
|
2560
|
+
method_types.any? do |method_type|
|
|
2561
|
+
function = method_type.type
|
|
2562
|
+
next false unless function.respond_to?(:required_keywords)
|
|
2563
|
+
|
|
2564
|
+
!function.required_keywords.empty? || !function.optional_keywords.empty? ||
|
|
2565
|
+
!function.rest_keywords.nil?
|
|
2485
2566
|
end
|
|
2486
2567
|
end
|
|
2487
2568
|
|
|
2569
|
+
def nil_tainted?(actual)
|
|
2570
|
+
nil_member?(actual) || (actual.is_a?(Type::Union) && union_contains_nil?(actual))
|
|
2571
|
+
end
|
|
2572
|
+
|
|
2488
2573
|
# Multi-overload argument-type-mismatch. The dispatcher's per-overload
|
|
2489
2574
|
# acceptance plumbing is not run here; instead the FP-safe shape mirrors
|
|
2490
2575
|
# the "absent on every arm" union-undefined-method rule: an argument is
|
|
@@ -2509,8 +2594,8 @@ module Rigor
|
|
|
2509
2594
|
arguments = call_node.arguments&.arguments || []
|
|
2510
2595
|
arguments.each_with_index do |arg, index|
|
|
2511
2596
|
arg_type = scope.type_of(arg)
|
|
2512
|
-
params =
|
|
2513
|
-
next if params.nil?
|
|
2597
|
+
params = checkable_overload_params(method_types, index, param_overrides, scope)
|
|
2598
|
+
next if params.nil?
|
|
2514
2599
|
|
|
2515
2600
|
mismatch =
|
|
2516
2601
|
if nil_member?(arg_type) # pure nil only — not a `T | nil` union
|
|
@@ -2523,6 +2608,19 @@ module Rigor
|
|
|
2523
2608
|
nil
|
|
2524
2609
|
end
|
|
2525
2610
|
|
|
2611
|
+
# The parameter set at `index` that a verdict may rest on, or nil when there is none — the two
|
|
2612
|
+
# reasons being arity divergence (an overload with no param there, which is `call.wrong-arity`'s
|
|
2613
|
+
# concern) and an overload whose param names a class carrying no definition ({#stub_typed_param?},
|
|
2614
|
+
# issue #661). Both channels below fire only when EVERY overload rejects, so either one is enough
|
|
2615
|
+
# to unseat that premise.
|
|
2616
|
+
def checkable_overload_params(method_types, index, param_overrides, scope)
|
|
2617
|
+
params = overload_positional_params(method_types, index)
|
|
2618
|
+
return nil if params.nil?
|
|
2619
|
+
return nil if params.any? { |param| stub_typed_param?(param, param_overrides, scope) }
|
|
2620
|
+
|
|
2621
|
+
params
|
|
2622
|
+
end
|
|
2623
|
+
|
|
2526
2624
|
# The nil channel: a pure `nil` argument no overload admits (ADR-58
|
|
2527
2625
|
# parity excuses a declaration-sourced ivar nil).
|
|
2528
2626
|
def nil_arg_overload_mismatch(arg, arg_type, params, param_overrides, scope)
|
|
@@ -2568,6 +2666,53 @@ module Rigor
|
|
|
2568
2666
|
param.map_type { |type| type.sub(substitution) }
|
|
2569
2667
|
end
|
|
2570
2668
|
|
|
2669
|
+
# ADR-26 WD8's PARAMETER-side twin (issue #661). `sig/app.rbs` declaring
|
|
2670
|
+
# `(ActiveSupport::TimeWithZone) -> void` in a project that never brings ActiveSupport's RBS in
|
|
2671
|
+
# names a class that carries no definition anywhere in the environment. Rigor still refuted
|
|
2672
|
+
# `App.take_twz(Time.now)` against it — `expected ActiveSupport::TimeWithZone, got Time` on a call
|
|
2673
|
+
# that works — because the acceptance walk compares nominals and `Time`'s ancestry does not list a
|
|
2674
|
+
# name nothing declares.
|
|
2675
|
+
#
|
|
2676
|
+
# WD8 already declines the two signature rules on such a RECEIVER, on the ground that there is
|
|
2677
|
+
# nothing authoritative to check the call against. It is the same one position over: with no
|
|
2678
|
+
# ancestry there is no fact that could make an argument satisfy the parameter either, so the
|
|
2679
|
+
# rejection is the missing signature restated as an error rather than a finding about the program.
|
|
2680
|
+
# Under AGENTS.md's false-positives-outrank-worst-case rule the honest answer is to decline, and
|
|
2681
|
+
# the report it costs is one the analyzer never had evidence for.
|
|
2682
|
+
#
|
|
2683
|
+
# Both shapes the missing name arrives in are covered, deliberately as ONE criterion. A name an
|
|
2684
|
+
# INSTANCE-method signature references is minted as an empty stub so the class builds
|
|
2685
|
+
# ({Environment::RbsLoader.stub_missing_referenced_types}) and reads back as RBS-known; the same
|
|
2686
|
+
# name in a SINGLETON signature is not, because that pass mirrors rbs's own membership test and
|
|
2687
|
+
# `validate_type_params` never reaches singleton members. That asymmetry is an artifact of which
|
|
2688
|
+
# pass ran, not of what is knowable, and the issue's own repro is the singleton half.
|
|
2689
|
+
#
|
|
2690
|
+
# A `rigor:v1:param` override is a real Rigor type the author wrote against the call site's own
|
|
2691
|
+
# method, so it is consulted first and checked normally.
|
|
2692
|
+
def stub_typed_param?(param, param_overrides, scope)
|
|
2693
|
+
return false if param_overrides[param.name]
|
|
2694
|
+
|
|
2695
|
+
undefined_param_class?(param.type, scope)
|
|
2696
|
+
end
|
|
2697
|
+
|
|
2698
|
+
# Only the arms a verdict can rest on directly: a union member, an optional's inner type, an
|
|
2699
|
+
# expanded alias, and the class instance / singleton itself. Deliberately NOT recursive into type
|
|
2700
|
+
# ARGUMENTS — `Array[Missing]` is still refuted by an `Integer` argument on the `Array` alone, and
|
|
2701
|
+
# declining there would be a lost report bought for nothing.
|
|
2702
|
+
def undefined_param_class?(rbs_type, scope)
|
|
2703
|
+
case rbs_type
|
|
2704
|
+
when RBS::Types::Union then rbs_type.types.any? { |member| undefined_param_class?(member, scope) }
|
|
2705
|
+
when RBS::Types::Optional then undefined_param_class?(rbs_type.type, scope)
|
|
2706
|
+
when RBS::Types::Alias
|
|
2707
|
+
expanded = scope.environment&.rbs_loader&.expand_type_alias(rbs_type)
|
|
2708
|
+
!expanded.nil? && undefined_param_class?(expanded, scope)
|
|
2709
|
+
when RBS::Types::ClassInstance, RBS::Types::ClassSingleton
|
|
2710
|
+
name = rbs_type.name
|
|
2711
|
+
synthesized_stub_receiver?(name, scope) || !Rigor::Reflection.rbs_class_known?(name.to_s, scope: scope)
|
|
2712
|
+
else false
|
|
2713
|
+
end
|
|
2714
|
+
end
|
|
2715
|
+
|
|
2571
2716
|
# The class names whose instances `nil` IS — `NilClass` and every
|
|
2572
2717
|
# ancestor. A parameter typed as any other class instance rejects nil.
|
|
2573
2718
|
NIL_COMPATIBLE_CLASS_NAMES = %w[NilClass Object BasicObject Kernel].to_set.freeze
|
|
@@ -2770,6 +2915,8 @@ module Rigor
|
|
|
2770
2915
|
# the original translated-acceptance check, with a `rigor:v1:param`
|
|
2771
2916
|
# override taking precedence over the RBS-declared type.
|
|
2772
2917
|
def single_argument_mismatch(param, arg, scope, param_overrides)
|
|
2918
|
+
return nil if stub_typed_param?(param, param_overrides, scope)
|
|
2919
|
+
|
|
2773
2920
|
arg_type = scope.type_of(arg)
|
|
2774
2921
|
|
|
2775
2922
|
if nil_member?(arg_type)
|
|
@@ -2824,17 +2971,29 @@ module Rigor
|
|
|
2824
2971
|
Inference::Acceptance.accepts(param_type, non_nil, mode: :gradual).yes?
|
|
2825
2972
|
end
|
|
2826
2973
|
|
|
2974
|
+
# What this rule needs is that argument N binds to positional
|
|
2975
|
+
# parameter N, so the only disqualifiers are the two shapes that
|
|
2976
|
+
# break that mapping: a rest positional (arguments past it belong
|
|
2977
|
+
# to the rest, not to a later parameter) and trailing positionals
|
|
2978
|
+
# (the last arguments bind backwards from the end).
|
|
2979
|
+
#
|
|
2980
|
+
# Issue #673 — a keyword parameter used to disqualify the whole
|
|
2981
|
+
# signature, which silently unchecked the POSITIONAL arguments of
|
|
2982
|
+
# every keyword-bearing signature in the bundle:
|
|
2983
|
+
# `time.beginning_of_week("monday")` fired while
|
|
2984
|
+
# `time.next_week("monday")`, `(?Symbol, ?same_time: bool)`, did not,
|
|
2985
|
+
# though both raise the same way at runtime. Keywords cannot shift a
|
|
2986
|
+
# positional argument's index, and a call that PASSES keywords never
|
|
2987
|
+
# reaches here at all — `plain_positional_call?` rejects a
|
|
2988
|
+
# `KeywordHashNode` argument (`**opts` included) before the signature
|
|
2989
|
+
# is consulted.
|
|
2827
2990
|
def argument_check_eligible?(function)
|
|
2828
2991
|
# See `arity_eligible?`: `UntypedFunction` lacks
|
|
2829
2992
|
# the per-arity accessors. Treat it as ineligible
|
|
2830
2993
|
# for argument-type-mismatch diagnostics.
|
|
2831
2994
|
return false unless function.respond_to?(:required_keywords)
|
|
2832
2995
|
|
|
2833
|
-
function.rest_positionals.nil? &&
|
|
2834
|
-
function.required_keywords.empty? &&
|
|
2835
|
-
function.optional_keywords.empty? &&
|
|
2836
|
-
function.rest_keywords.nil? &&
|
|
2837
|
-
function.trailing_positionals.empty?
|
|
2996
|
+
function.rest_positionals.nil? && function.trailing_positionals.empty?
|
|
2838
2997
|
end
|
|
2839
2998
|
|
|
2840
2999
|
def translate_param_type(rbs_type, environment)
|
|
@@ -2911,6 +3070,14 @@ module Rigor
|
|
|
2911
3070
|
#
|
|
2912
3071
|
# - Skips methods without an RBS declaration. The rule has no contract to compare against for
|
|
2913
3072
|
# source-only methods.
|
|
3073
|
+
# - Skips a `def` whose only declaration was resolved through an ANCESTOR. `Reflection`'s lookup
|
|
3074
|
+
# reads the fully resolved method table, so a signature written about a base class answers for
|
|
3075
|
+
# every subclass that inherits the name; comparing an override's body against it reports the
|
|
3076
|
+
# override wrong for disagreeing with a contract that was never written about it (#744, and
|
|
3077
|
+
# the four redmine sites in #856). ADR-35 already specifies this rule as comparing a method's
|
|
3078
|
+
# OWN declared return, and the sibling override rules gate on `defined_on?` — this one did not.
|
|
3079
|
+
# The inherited-declaration signal belongs to the ADR-35 family, which requires both sides
|
|
3080
|
+
# authored. Decided in ADR-110 WD3.
|
|
2914
3081
|
# - Skips methods whose enclosing class isn't a `Type::Singleton` self_type that we can name
|
|
2915
3082
|
# (top-level / module-level methods land outside the rule).
|
|
2916
3083
|
# - Skips methods whose body's last expression is absent or types as `Dynamic[top]` (the
|
|
@@ -2978,6 +3145,7 @@ module Rigor
|
|
|
2978
3145
|
Reflection.singleton_method_definition(self_type.class_name, def_node.name, scope: scope)
|
|
2979
3146
|
end
|
|
2980
3147
|
return nil if method_def.nil?
|
|
3148
|
+
return nil unless defined_on?(method_def, self_type.class_name)
|
|
2981
3149
|
|
|
2982
3150
|
override = Rigor::RbsExtended.read_return_type_override(method_def, environment: scope.environment)
|
|
2983
3151
|
return override if override
|
|
@@ -3156,9 +3324,15 @@ module Rigor
|
|
|
3156
3324
|
names
|
|
3157
3325
|
end
|
|
3158
3326
|
|
|
3327
|
+
# Issue #732 — the predicate is asked of the SCOPE, not re-implemented here. This rule kept its own
|
|
3328
|
+
# three-table copy until #723 widened the scope's with `discovered_methods` and left this one behind,
|
|
3329
|
+
# so the two disagreed about exactly one shape (a project class with no instance def, no superclass
|
|
3330
|
+
# and no include — `class Base; def self.build = :built; end`) with nothing observing the fork. #682
|
|
3331
|
+
# made `Scope` the single owner of the candidate ORDER; this makes it the single owner of the
|
|
3332
|
+
# predicate that filters those candidates too.
|
|
3159
3333
|
def resolve_override_ancestor_name(scope, subclass_qualified, raw_ancestor)
|
|
3160
3334
|
resolved = scope.ancestor_name_candidates(subclass_qualified, raw_ancestor)
|
|
3161
|
-
.find { |candidate| known_user_class?(
|
|
3335
|
+
.find { |candidate| scope.known_user_class?(candidate) }
|
|
3162
3336
|
return resolved if resolved
|
|
3163
3337
|
|
|
3164
3338
|
# ADR-46 slice 3 — the override checker reads the class graph
|
|
@@ -3172,12 +3346,6 @@ module Rigor
|
|
|
3172
3346
|
nil
|
|
3173
3347
|
end
|
|
3174
3348
|
|
|
3175
|
-
def known_user_class?(scope, name)
|
|
3176
|
-
scope.discovered_superclasses.key?(name) ||
|
|
3177
|
-
scope.discovered_def_nodes.key?(name) ||
|
|
3178
|
-
scope.discovered_includes.key?(name)
|
|
3179
|
-
end
|
|
3180
|
-
|
|
3181
3349
|
def build_override_visibility_diagnostic(path, def_node, parent_class, parent_visibility, override_visibility)
|
|
3182
3350
|
Diagnostic.from_name_loc(
|
|
3183
3351
|
def_node,
|
|
@@ -87,12 +87,13 @@ module Rigor
|
|
|
87
87
|
# user — every rule fired — but NOT a valid measurement of Rigor itself, so a harness that scores
|
|
88
88
|
# Rigor's behaviour should treat one of these as a crash finding the way it treats `:check_rule`, or
|
|
89
89
|
# a mutant that re-breaks the HKT scan (#776 was one) scores as a measurement over a silently degraded
|
|
90
|
-
# universe.
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# the
|
|
95
|
-
# and
|
|
90
|
+
# universe. Both Rigor-measuring harnesses consult it (#790): `tool/mutation`'s fuzz crash detector
|
|
91
|
+
# and {Protection::AnalysisGuard}, the ADR-69 kill oracles' refusal. Neither reads it alone — the row
|
|
92
|
+
# is severity-stamped like any other, so `severity_overrides:` can drop it from a run's diagnostics
|
|
93
|
+
# entirely, and each harness also reads {Rigor::Environment#hkt_scan_failure}, the pre-severity record
|
|
94
|
+
# the row is derived from. Both also replace their Environment after a defect: the registry build is
|
|
95
|
+
# memoised and the failure slot is first-write-wins, so an Environment that degraded once stays
|
|
96
|
+
# degraded for every later mutant.
|
|
96
97
|
ANALYZER_DEFECT_RULES = %w[
|
|
97
98
|
rbs.coverage.hkt-scan-failed
|
|
98
99
|
].freeze
|
|
@@ -103,9 +104,6 @@ module Rigor
|
|
|
103
104
|
# into. Built here, not at either rescue site, so the two twins cannot drift (issue #665) — and so
|
|
104
105
|
# the appended crash frame is derived identically on the sequential and pooled paths. Keeps the
|
|
105
106
|
# {CHECK_RULE_MESSAGE_PREFIX} prefix every consumer matches on; the frame is a trailing hint.
|
|
106
|
-
#
|
|
107
|
-
# @param error [StandardError]
|
|
108
|
-
# @return [String]
|
|
109
107
|
def check_rule_message(error)
|
|
110
108
|
base = "#{CHECK_RULE_MESSAGE_PREFIX}: #{error.class}: #{error.message}"
|
|
111
109
|
frame = crash_frame(error)
|
|
@@ -117,9 +115,6 @@ module Rigor
|
|
|
117
115
|
# bundled or third-party plugin also lives under `lib/rigor/<plugin>/`, so a frame here says where
|
|
118
116
|
# the raise was, never whose defect it is. Falls back to the raw top frame when the crash is entirely
|
|
119
117
|
# inside a dependency, and to nil when there is no backtrace at all.
|
|
120
|
-
#
|
|
121
|
-
# @param error [Exception]
|
|
122
|
-
# @return [String, nil]
|
|
123
118
|
def crash_frame(error)
|
|
124
119
|
frames = error.backtrace
|
|
125
120
|
return nil if frames.nil? || frames.empty?
|
|
@@ -132,15 +127,11 @@ module Rigor
|
|
|
132
127
|
# (the fork pool) rather than deriving it fresh from a live `Exception` — can relativize the frame it
|
|
133
128
|
# already has without re-deriving `.crash_frame`'s "which frame" choice. Nil-safe, and a no-op (`sub`
|
|
134
129
|
# never matches) on a frame with no `lib/rigor/` segment at all.
|
|
135
|
-
#
|
|
136
|
-
# @param frame [String, nil]
|
|
137
|
-
# @return [String, nil]
|
|
138
130
|
def relativize_frame(frame)
|
|
139
131
|
frame&.sub(%r{\A.*/(lib/rigor/)}, '\1')
|
|
140
132
|
end
|
|
141
133
|
|
|
142
|
-
# @
|
|
143
|
-
# @return [Symbol, nil] `:check_rule`, `:plugin`, `:rbs_build`, or nil for an ordinary diagnostic.
|
|
134
|
+
# @return `:check_rule`, `:plugin`, `:rbs_build`, or nil for an ordinary diagnostic.
|
|
144
135
|
def reason(diagnostic)
|
|
145
136
|
return :check_rule if diagnostic.message.to_s.start_with?(CHECK_RULE_MESSAGE_PREFIX)
|
|
146
137
|
return :plugin if plugin_isolation_row?(diagnostic)
|
|
@@ -152,8 +143,6 @@ module Rigor
|
|
|
152
143
|
# True when `diagnostic` is the rescue row that REPLACED a file's analysis — the only shape after which
|
|
153
144
|
# the run's diagnostics say nothing about the code. NOT a general "something went wrong" predicate; see
|
|
154
145
|
# the class doc for why `:plugin` and `:rbs_build` are excluded.
|
|
155
|
-
#
|
|
156
|
-
# @param diagnostic [Rigor::Analysis::Diagnostic]
|
|
157
146
|
def discards_file_analysis?(diagnostic)
|
|
158
147
|
reason(diagnostic) == DISCARDS_FILE_ANALYSIS_REASON
|
|
159
148
|
end
|
|
@@ -162,17 +151,12 @@ module Rigor
|
|
|
162
151
|
# measurement of Rigor — see {ANALYZER_DEFECT_RULES}. Orthogonal to {.discards_file_analysis?}: the
|
|
163
152
|
# user-facing tier reads the run; the Rigor-measuring tier should refuse it (the mutation fuzz does,
|
|
164
153
|
# the ADR-69 kill oracle does not yet — #790).
|
|
165
|
-
#
|
|
166
|
-
# @param diagnostic [Rigor::Analysis::Diagnostic]
|
|
167
154
|
def analyzer_defect?(diagnostic)
|
|
168
155
|
ANALYZER_DEFECT_RULES.include?(diagnostic.rule)
|
|
169
156
|
end
|
|
170
157
|
|
|
171
158
|
# A one-line "<reason> at <path>:<line>: <message>" for a failure message, so whoever reads the raise
|
|
172
159
|
# sees which shape fired and where without re-deriving it.
|
|
173
|
-
#
|
|
174
|
-
# @param diagnostic [Rigor::Analysis::Diagnostic]
|
|
175
|
-
# @return [String]
|
|
176
160
|
def describe(diagnostic)
|
|
177
161
|
"#{reason(diagnostic) || :unknown} at #{diagnostic.path}:#{diagnostic.line}: #{diagnostic.message}"
|
|
178
162
|
end
|