rigortype 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +96 -15
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +69 -13
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +100 -29
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +70 -0
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +31 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +28 -0
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +613 -113
- data/lib/rigor/environment.rb +189 -27
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +67 -14
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +181 -102
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +17 -11
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +11 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -42,9 +42,16 @@ module Rigor
|
|
|
42
42
|
# prepare row is appended to every sequential run, it would refuse every run for the life of the
|
|
43
43
|
# process.
|
|
44
44
|
# - `:rbs_build` — the analysis ran to completion and every rule fired, over a type universe missing one
|
|
45
|
-
# class (`rbs.coverage.definition-build-failed`)
|
|
46
|
-
# (`rbs.coverage.environment-build-failed`)
|
|
47
|
-
#
|
|
45
|
+
# class (`rbs.coverage.definition-build-failed`), all of them
|
|
46
|
+
# (`rbs.coverage.environment-build-failed`), or only the HKT registrations the `type` aliases and the
|
|
47
|
+
# loaded plugins' manifests would have contributed (`rbs.coverage.hkt-scan-failed`, issues #784 and
|
|
48
|
+
# #791). The first two are degradations
|
|
49
|
+
# the user causes and the diagnostic itself reports; a project can sit on one for a release while it
|
|
50
|
+
# fixes its `sig/`. The third is NOT the user's: post-#783 the scan raising is an analyzer defect, and
|
|
51
|
+
# the overlay stage is a defect in Rigor or in a plugin the user installed, never in their `sig/`. It
|
|
52
|
+
# still belongs here rather than under `:check_rule`, because the question this classification
|
|
53
|
+
# answers is "may a consumer still read the run's diagnostics?" — and it may: every rule fired. What
|
|
54
|
+
# differs is whether the run is a valid measurement OF RIGOR, which is {.analyzer_defect?}'s question.
|
|
48
55
|
#
|
|
49
56
|
# The two consumer tiers therefore differ on purpose. The ADR-69 kill oracles arm `:check_rule` only:
|
|
50
57
|
# refusing a run they could still have measured is the same "manufactures work" error as scoring an
|
|
@@ -63,10 +70,11 @@ module Rigor
|
|
|
63
70
|
PLUGIN_SOURCE_FAMILY = :plugin_loader
|
|
64
71
|
PLUGIN_RULE = "runtime-error"
|
|
65
72
|
|
|
66
|
-
# The `rbs.coverage.*` rules that mean declared types went missing from this run —
|
|
67
|
-
#
|
|
68
|
-
# in `docs/type-specification/diagnostic-policy.md`.
|
|
73
|
+
# The `rbs.coverage.*` rules that mean declared types went missing from this run — the implicit-HKT
|
|
74
|
+
# scan over `type` aliases (#784), a per-class definition build (#696), and the env-wide collapse.
|
|
75
|
+
# Ordered widest consequence last, the way the rows sit in `docs/type-specification/diagnostic-policy.md`.
|
|
69
76
|
RBS_BUILD_FAILURE_RULES = %w[
|
|
77
|
+
rbs.coverage.hkt-scan-failed
|
|
70
78
|
rbs.coverage.definition-build-failed
|
|
71
79
|
rbs.coverage.environment-build-failed
|
|
72
80
|
].freeze
|
|
@@ -75,10 +83,55 @@ module Rigor
|
|
|
75
83
|
# `:rbs_build` both leave a readable run behind — see the class doc.
|
|
76
84
|
DISCARDS_FILE_ANALYSIS_REASON = :check_rule
|
|
77
85
|
|
|
86
|
+
# The `:rbs_build` rules whose cause is Rigor, not the user's `sig/` (issue #784). Readable for the
|
|
87
|
+
# user — every rule fired — but NOT a valid measurement of Rigor itself, so a harness that scores
|
|
88
|
+
# Rigor's behaviour should treat one of these as a crash finding the way it treats `:check_rule`, or
|
|
89
|
+
# a mutant that re-breaks the HKT scan (#776 was one) scores as a measurement over a silently degraded
|
|
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.
|
|
97
|
+
ANALYZER_DEFECT_RULES = %w[
|
|
98
|
+
rbs.coverage.hkt-scan-failed
|
|
99
|
+
].freeze
|
|
100
|
+
|
|
78
101
|
module_function
|
|
79
102
|
|
|
80
|
-
#
|
|
81
|
-
#
|
|
103
|
+
# The message `Runner#analyze_file_body` / `WorkerSession#analyze_body` fold a raised `StandardError`
|
|
104
|
+
# into. Built here, not at either rescue site, so the two twins cannot drift (issue #665) — and so
|
|
105
|
+
# the appended crash frame is derived identically on the sequential and pooled paths. Keeps the
|
|
106
|
+
# {CHECK_RULE_MESSAGE_PREFIX} prefix every consumer matches on; the frame is a trailing hint.
|
|
107
|
+
def check_rule_message(error)
|
|
108
|
+
base = "#{CHECK_RULE_MESSAGE_PREFIX}: #{error.class}: #{error.message}"
|
|
109
|
+
frame = crash_frame(error)
|
|
110
|
+
frame ? "#{base} (#{frame})" : base
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The first `lib/rigor/` backtrace frame — the raise site, path made repo-relative so it reads the
|
|
114
|
+
# same whether Rigor runs from a checkout or an installed gem. A LOCATION, not an attribution: a
|
|
115
|
+
# bundled or third-party plugin also lives under `lib/rigor/<plugin>/`, so a frame here says where
|
|
116
|
+
# the raise was, never whose defect it is. Falls back to the raw top frame when the crash is entirely
|
|
117
|
+
# inside a dependency, and to nil when there is no backtrace at all.
|
|
118
|
+
def crash_frame(error)
|
|
119
|
+
frames = error.backtrace
|
|
120
|
+
return nil if frames.nil? || frames.empty?
|
|
121
|
+
|
|
122
|
+
relativize_frame(frames.find { |f| f.include?("/lib/rigor/") } || frames.first)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The repo-relative half of {.crash_frame}, pulled out on its own so a second caller — the #784
|
|
126
|
+
# `rbs.coverage.hkt-scan-failed` diagnostic, which stores its raw frame across a `Marshal` boundary
|
|
127
|
+
# (the fork pool) rather than deriving it fresh from a live `Exception` — can relativize the frame it
|
|
128
|
+
# already has without re-deriving `.crash_frame`'s "which frame" choice. Nil-safe, and a no-op (`sub`
|
|
129
|
+
# never matches) on a frame with no `lib/rigor/` segment at all.
|
|
130
|
+
def relativize_frame(frame)
|
|
131
|
+
frame&.sub(%r{\A.*/(lib/rigor/)}, '\1')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @return `:check_rule`, `:plugin`, `:rbs_build`, or nil for an ordinary diagnostic.
|
|
82
135
|
def reason(diagnostic)
|
|
83
136
|
return :check_rule if diagnostic.message.to_s.start_with?(CHECK_RULE_MESSAGE_PREFIX)
|
|
84
137
|
return :plugin if plugin_isolation_row?(diagnostic)
|
|
@@ -90,17 +143,20 @@ module Rigor
|
|
|
90
143
|
# True when `diagnostic` is the rescue row that REPLACED a file's analysis — the only shape after which
|
|
91
144
|
# the run's diagnostics say nothing about the code. NOT a general "something went wrong" predicate; see
|
|
92
145
|
# the class doc for why `:plugin` and `:rbs_build` are excluded.
|
|
93
|
-
#
|
|
94
|
-
# @param diagnostic [Rigor::Analysis::Diagnostic]
|
|
95
146
|
def discards_file_analysis?(diagnostic)
|
|
96
147
|
reason(diagnostic) == DISCARDS_FILE_ANALYSIS_REASON
|
|
97
148
|
end
|
|
98
149
|
|
|
150
|
+
# True when `diagnostic` reports a failure inside Rigor that left the run readable but invalid as a
|
|
151
|
+
# measurement of Rigor — see {ANALYZER_DEFECT_RULES}. Orthogonal to {.discards_file_analysis?}: the
|
|
152
|
+
# user-facing tier reads the run; the Rigor-measuring tier should refuse it (the mutation fuzz does,
|
|
153
|
+
# the ADR-69 kill oracle does not yet — #790).
|
|
154
|
+
def analyzer_defect?(diagnostic)
|
|
155
|
+
ANALYZER_DEFECT_RULES.include?(diagnostic.rule)
|
|
156
|
+
end
|
|
157
|
+
|
|
99
158
|
# A one-line "<reason> at <path>:<line>: <message>" for a failure message, so whoever reads the raise
|
|
100
159
|
# sees which shape fired and where without re-deriving it.
|
|
101
|
-
#
|
|
102
|
-
# @param diagnostic [Rigor::Analysis::Diagnostic]
|
|
103
|
-
# @return [String]
|
|
104
160
|
def describe(diagnostic)
|
|
105
161
|
"#{reason(diagnostic) || :unknown} at #{diagnostic.path}:#{diagnostic.line}: #{diagnostic.message}"
|
|
106
162
|
end
|
|
@@ -25,7 +25,7 @@ module Rigor
|
|
|
25
25
|
@mutex = Mutex.new
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# @return
|
|
28
|
+
# @return frozen snapshot of the recorded boundary-cross events. Each entry is a Data
|
|
29
29
|
# with `class_name` (String), `method_name` (Symbol), `gem_name` (String), and `rbs_display` (String
|
|
30
30
|
# — the authoritative-side type's human-facing form, embedded into the diagnostic message).
|
|
31
31
|
def entries
|
|
@@ -30,8 +30,6 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param entry [Rigor::Configuration::Dependencies::Entry]
|
|
34
|
-
# @return [Resolved, Unresolvable]
|
|
35
33
|
def resolve(entry)
|
|
36
34
|
spec = locate_gem_spec(entry.gem)
|
|
37
35
|
return Unresolvable.new(gem_name: entry.gem, reason: :not_in_bundle) if spec.nil?
|
|
@@ -13,19 +13,19 @@ module Rigor
|
|
|
13
13
|
attr_reader :resolved_gems, :unresolvable, :method_catalog, :budget_exceeded,
|
|
14
14
|
:class_to_gem, :budget_overrun_strategy, :gem_modes
|
|
15
15
|
|
|
16
|
-
# @param method_catalog
|
|
16
|
+
# @param method_catalog — => Symbol}] the flat
|
|
17
17
|
# `(class_name, method_name) → :instance | :singleton` table produced by {Walker.walk}, aggregated
|
|
18
18
|
# across every resolved gem in the run. The Index itself stays gem-agnostic — the per-gem
|
|
19
19
|
# attribution that slice 3's cache descriptor needs lives on `Resolved`, not here.
|
|
20
|
-
# @param budget_exceeded
|
|
20
|
+
# @param budget_exceeded — gem names whose {Walker} run hit the per-gem catalog cap
|
|
21
21
|
# (slice 4). The Runner consumes this list to emit one `dynamic.dependency-source.budget-exceeded`
|
|
22
22
|
# warning per gem.
|
|
23
|
-
# @param class_to_gem
|
|
23
|
+
# @param class_to_gem — reverse lookup `class_name → gem_name` (slice 5b). Built
|
|
24
24
|
# first-write-wins: when two opt-in gems re-open the same class, the first gem owns it. The
|
|
25
25
|
# dispatcher consults this map under the `:dependency_silence` budget overrun strategy so call
|
|
26
26
|
# sites on a budget-exceeded gem's classes degrade to `Dynamic[top]` instead of falling through to
|
|
27
27
|
# the user-class fallback.
|
|
28
|
-
# @param gem_modes
|
|
28
|
+
# @param gem_modes — per-gem mode table (`gem_name → :disabled | :when_missing |
|
|
29
29
|
# :full`). ADR-10 slice 5c consults this through {#mode_for} to identify call sites where
|
|
30
30
|
# gem-source and RBS both contribute under `mode: :full`. The map is keyed on `gem_name` (not
|
|
31
31
|
# class) because re-opened classes belong to the first gem they appeared in per `class_to_gem`;
|
|
@@ -56,7 +56,7 @@ module Rigor
|
|
|
56
56
|
end
|
|
57
57
|
private :normalize_catalog
|
|
58
58
|
|
|
59
|
-
# @return
|
|
59
|
+
# @return the gem that owns `class_name` (first-write-wins); `nil` when the class isn't
|
|
60
60
|
# in any opt-in gem's catalog.
|
|
61
61
|
def gem_for(class_name)
|
|
62
62
|
@class_to_gem[class_name]
|
|
@@ -28,8 +28,7 @@ module Rigor
|
|
|
28
28
|
module ReturnTypeHeuristic
|
|
29
29
|
module_function
|
|
30
30
|
|
|
31
|
-
# @
|
|
32
|
-
# @return [Rigor::Type, nil] heuristic return type, or `nil` when the body's tail expression doesn't
|
|
31
|
+
# @return heuristic return type, or `nil` when the body's tail expression doesn't
|
|
33
32
|
# match any of the recognised shapes.
|
|
34
33
|
def extract(def_node)
|
|
35
34
|
body = def_node.body
|
|
@@ -55,12 +55,12 @@ module Rigor
|
|
|
55
55
|
|
|
56
56
|
module_function
|
|
57
57
|
|
|
58
|
-
# @param gem_dir
|
|
59
|
-
# @param roots
|
|
58
|
+
# @param gem_dir — absolute path to the gem's installation directory.
|
|
59
|
+
# @param roots — subdirectory names within the gem to walk (defaults to `["lib"]` per
|
|
60
60
|
# `Configuration::Dependencies::Entry`).
|
|
61
|
-
# @param budget
|
|
61
|
+
# @param budget — per-gem catalog cap (method-definition count). When unset, defaults
|
|
62
62
|
# to `UNBOUNDED` for backwards-compatible test paths.
|
|
63
|
-
# @return
|
|
63
|
+
# @return frozen wrapper carrying the catalog (`Hash{[class_name, method_name] =>
|
|
64
64
|
# :instance | :singleton}`) and a `truncated?` flag set when the walker stopped harvesting because
|
|
65
65
|
# the budget was reached. Methods of identical name on the same class with different kinds (rare;
|
|
66
66
|
# private API mostly) carry the kind that wins the per-class first walk.
|
|
@@ -52,17 +52,16 @@ module Rigor
|
|
|
52
52
|
# otherwise rebuild (the probe loaded the plugins to key the entry, so it already knows it).
|
|
53
53
|
Served = Data.define(:table, :sources, :registry, :plugin_facts)
|
|
54
54
|
|
|
55
|
-
# @param configuration
|
|
56
|
-
# @param cache_root [String, nil]
|
|
55
|
+
# @param configuration — with effects already enabled by the caller.
|
|
57
56
|
def initialize(configuration:, cache_root:)
|
|
58
57
|
@configuration = configuration
|
|
59
58
|
@cache_root = cache_root
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
# @param paths
|
|
61
|
+
# @param paths — the analysed set — `configuration.paths` unioned with any path
|
|
63
62
|
# arguments, exactly what {CLI::EffectsCommand#analyze} hands the runner, because the analysed
|
|
64
63
|
# set is part of the diagnostics key.
|
|
65
|
-
# @return
|
|
64
|
+
# @return nil to decline, on any failure whatsoever.
|
|
66
65
|
def serve(paths)
|
|
67
66
|
return nil if @cache_root.nil? || !@configuration.effects_check?
|
|
68
67
|
|
|
@@ -19,8 +19,7 @@ module Rigor
|
|
|
19
19
|
|
|
20
20
|
module_function
|
|
21
21
|
|
|
22
|
-
# @
|
|
23
|
-
# @return [Boolean] true when the parsed source looks like an ERB template (parse errors expected;
|
|
22
|
+
# @return true when the parsed source looks like an ERB template (parse errors expected;
|
|
24
23
|
# analysis should skip).
|
|
25
24
|
def template?(parse_result)
|
|
26
25
|
source = parse_result.source.source
|
|
@@ -18,9 +18,14 @@ module Rigor
|
|
|
18
18
|
].freeze
|
|
19
19
|
|
|
20
20
|
class Target < Data.define(:kind, :name)
|
|
21
|
+
# #775 — a target is an immutable value keyed by its name, and the local target is built on every
|
|
22
|
+
# binding (`Scope#with_local` invalidates through it) and every local-fact read: ~350k times on
|
|
23
|
+
# the lib self-check for a few thousand distinct names. One shared instance per name.
|
|
21
24
|
def self.local(name)
|
|
22
|
-
|
|
25
|
+
name = name.to_sym
|
|
26
|
+
@local_targets[name] ||= new(kind: :local, name: name)
|
|
23
27
|
end
|
|
28
|
+
@local_targets = {}
|
|
24
29
|
|
|
25
30
|
def initialize(kind:, name:)
|
|
26
31
|
super(kind: kind.to_sym, name: name)
|
|
@@ -120,6 +120,22 @@ module Rigor
|
|
|
120
120
|
appeared.freeze
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
+
# Issue #639 — the SYMMETRIC reading of {appeared_classes}: the names whose declaration in a file
|
|
124
|
+
# appeared OR VANISHED. Appearance alone answered the miss side, which was the only side recording a
|
|
125
|
+
# `class:` edge; a bare reference that RESOLVED now records one too, and what invalidates it is the
|
|
126
|
+
# declaration going away. `paths` MUST therefore include REMOVED files — a removed file has no
|
|
127
|
+
# after-state, so its whole before-set vanished — which is the same reason
|
|
128
|
+
# {changed_constant_publications} diffs over `changed + removed`.
|
|
129
|
+
def changed_class_declarations(paths, decls_before, decls_after)
|
|
130
|
+
moved = Set.new
|
|
131
|
+
paths.each do |path|
|
|
132
|
+
before = decls_before[path] || Set.new
|
|
133
|
+
after = decls_after[path] || Set.new
|
|
134
|
+
moved.merge(after ^ before)
|
|
135
|
+
end
|
|
136
|
+
moved.freeze
|
|
137
|
+
end
|
|
138
|
+
|
|
123
139
|
# Issue #644 — the qualified constant names whose PUBLICATION could have moved in this edit: the names
|
|
124
140
|
# whose per-file census descriptor differs between the before- and after-state of any file in `paths`.
|
|
125
141
|
# The value-constant twin of {appeared_classes}, and deliberately NOT its analogue — a class either
|
|
@@ -42,22 +42,22 @@ module Rigor
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @param paths
|
|
45
|
+
# @param paths — explicit analysis roots; nil (the default) uses the configuration's
|
|
46
46
|
# `paths:`.
|
|
47
|
-
# @param environment
|
|
47
|
+
# @param environment — optional shared environment to thread into each internal
|
|
48
48
|
# Runner. Long-lived callers and specs can use this to avoid rebuilding the same RBS universe for
|
|
49
49
|
# every baseline / recheck / oracle run.
|
|
50
|
-
# @param cache_store
|
|
50
|
+
# @param cache_store — ADR-85 WD1 — the persistent cache each internal Runner
|
|
51
51
|
# exposes to the RBS-env and plugin-producer tiers. A cross-process `--incremental` recheck otherwise
|
|
52
52
|
# rebuilt a fresh runner with no store, so every plugin `#prepare` producer (the ADR-9/#74/ADR-60 WD3
|
|
53
53
|
# record-and-validate caches) recomputed per invocation — 86% of a Rails warm incremental. Threading
|
|
54
54
|
# the store lets those producers serve from disk. `nil` (the default) preserves the pre-#85 behaviour
|
|
55
55
|
# the specs assert; the whole-run ADR-45 result cache stays disabled on these runs
|
|
56
56
|
# (`Runner#run_result_cacheable?` excludes `record_dependencies` / `analyze_only`).
|
|
57
|
-
# @param plugin_requirer
|
|
57
|
+
# @param plugin_requirer — optional gem-require hook threaded into each internal Runner
|
|
58
58
|
# (mirrors {Runner}'s parameter). nil (the default, and what the CLI passes) uses `Kernel.require`;
|
|
59
59
|
# embedders and specs inject a fake so a test plugin registers without touching the real load path.
|
|
60
|
-
# @param workers
|
|
60
|
+
# @param workers — ADR-46 — the resolved fork-pool worker count threaded into every internal
|
|
61
61
|
# analyzer, so a `--incremental` recheck's closure re-analysis parallelises exactly like the standard
|
|
62
62
|
# `check` path (the recon's audit: `--workers` / `RIGOR_RACTOR_WORKERS` / `parallel.workers:` were
|
|
63
63
|
# silently ignored because `build_runner` passed no `workers:`). 0 (the default, and what the specs and
|
|
@@ -112,7 +112,7 @@ module Rigor
|
|
|
112
112
|
# and a missing invalidation edge is impossible by construction — the reason this is a table diff
|
|
113
113
|
# and not the caller→callee edge recording #204 first sketched.
|
|
114
114
|
@param_table = {}
|
|
115
|
-
|
|
115
|
+
reset_carried_state
|
|
116
116
|
# ADR-88 WD1 — the plugin fact-surface digest computed for THIS invocation (nil until a
|
|
117
117
|
# `#run_incremental` pass runs / a plugin-free project) and the reporting flags a caller (the CLI
|
|
118
118
|
# banner + `--cache-stats`) reads after `#run_incremental`. `@last_runner` is the analysis runner the
|
|
@@ -172,8 +172,9 @@ module Rigor
|
|
|
172
172
|
# ADR-103 WD13 / #382 — a baseline collects every file, so its collections are the whole world.
|
|
173
173
|
@effect_collections = runner.effect_collections_by_path
|
|
174
174
|
@effects_identity = current_effects_identity
|
|
175
|
-
@cache = per_file(
|
|
175
|
+
@cache = per_file(runner.per_file_diagnostics)
|
|
176
176
|
@digests = @analyzed.to_h { |path| [path, pack_digest(path)] }
|
|
177
|
+
@run_level_rows = runner.run_level_rows
|
|
177
178
|
diagnostics
|
|
178
179
|
end
|
|
179
180
|
|
|
@@ -197,12 +198,13 @@ module Rigor
|
|
|
197
198
|
# The freshly collected table is handed to the runner so the run seeds from the SAME table the diff
|
|
198
199
|
# was decided on (and the collector runs once per recheck, not twice).
|
|
199
200
|
runner = build_runner(analyze_only: analyze_set, record_dependencies: true,
|
|
200
|
-
param_inferred_types: fresh_params
|
|
201
|
+
param_inferred_types: fresh_params,
|
|
202
|
+
restored_run_level_rows: @run_level_rows)
|
|
201
203
|
fresh = run_runner(runner).diagnostics
|
|
202
204
|
@last_runner = runner # ADR-88 WD1 — the post-hoc fact-surface fingerprint reads this prepared registry.
|
|
203
205
|
reused = (current & previous) - affected.to_a
|
|
204
206
|
merged = fresh + reused.flat_map { |path| @cache[path] || [] }
|
|
205
|
-
absorb(runner,
|
|
207
|
+
absorb(runner, current, analyze_set, removed)
|
|
206
208
|
@param_table = fresh_params
|
|
207
209
|
Recheck.new(diagnostics: merged, changed: changed.to_set, added: added.to_set,
|
|
208
210
|
removed: removed.to_set, affected: affected, reused: reused.to_set)
|
|
@@ -407,10 +409,13 @@ module Rigor
|
|
|
407
409
|
end
|
|
408
410
|
|
|
409
411
|
# The current project file set (cheap directory expansion, no analysis), used to detect files added /
|
|
410
|
-
# removed since the last run.
|
|
412
|
+
# removed since the last run. Also where an editor buffer's binding is re-spelled onto the set, since
|
|
413
|
+
# this is where the run first learns how it spells its own members.
|
|
411
414
|
def current_files
|
|
412
415
|
runner = build_runner
|
|
413
|
-
@paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
|
|
416
|
+
files = @paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
|
|
417
|
+
rebind_buffer(files)
|
|
418
|
+
files
|
|
414
419
|
end
|
|
415
420
|
|
|
416
421
|
# Verification engine (the `--verify-incremental` gate): with NO source edit, re-analyze `subset` fresh
|
|
@@ -422,7 +427,8 @@ module Rigor
|
|
|
422
427
|
affected = subset.to_set
|
|
423
428
|
# ADR-67 WD6c lift — seed the subset run from the baseline's own table so the verification engine
|
|
424
429
|
# exercises the exact seeds the served cache entries were computed under (and skips a re-collect).
|
|
425
|
-
runner = build_runner(analyze_only: affected, param_inferred_types: @param_table
|
|
430
|
+
runner = build_runner(analyze_only: affected, param_inferred_types: @param_table,
|
|
431
|
+
restored_run_level_rows: @run_level_rows)
|
|
426
432
|
fresh = run_runner(runner).diagnostics
|
|
427
433
|
reused = @analyzed - affected.to_a
|
|
428
434
|
fresh + reused.flat_map { |path| @cache[path] || [] }
|
|
@@ -559,6 +565,9 @@ module Rigor
|
|
|
559
565
|
restore_structural_declarations(payload)
|
|
560
566
|
@return_summaries = payload.return_summaries || {}
|
|
561
567
|
@param_table = payload.param_table || {} # ADR-67 WD6c lift — the seeds the cache was built under.
|
|
568
|
+
# Issues #796 / #794 — absent (nil) in a snapshot written before schema 22 is impossible (the SCHEMA
|
|
569
|
+
# gate rejects one), so nil here means the writing run genuinely had nothing to say.
|
|
570
|
+
@run_level_rows = payload.run_level_rows
|
|
562
571
|
# ADR-103 WD13 / #382 — the effects sidecar rides its own identity, so a snapshot whose summaries
|
|
563
572
|
# were collected under a different vocabulary / catalogue / `effects:` block restores as EMPTY
|
|
564
573
|
# rather than as stale rows. {#run_incremental} turns that emptiness into a full baseline, because
|
|
@@ -592,7 +601,8 @@ module Rigor
|
|
|
592
601
|
return_summaries: marshal_safe_return_summaries,
|
|
593
602
|
param_table: marshal_safe_param_table,
|
|
594
603
|
effect_collections: marshal_safe_effect_collections,
|
|
595
|
-
effects_identity: @effects_identity
|
|
604
|
+
effects_identity: @effects_identity,
|
|
605
|
+
run_level_rows: @run_level_rows
|
|
596
606
|
)
|
|
597
607
|
end
|
|
598
608
|
|
|
@@ -606,6 +616,15 @@ module Rigor
|
|
|
606
616
|
@effects_identity = nil
|
|
607
617
|
end
|
|
608
618
|
|
|
619
|
+
# The state this session carries ACROSS processes in the snapshot rather than deriving each run: the
|
|
620
|
+
# effects sidecar above, and (issues #796 / #794) the run-level rows the last FULL run answered — the
|
|
621
|
+
# `definition-build-failed` set and the HKT-scan outcome, nil until a full run has produced them, and
|
|
622
|
+
# handed to every narrowed run this session builds, none of which can derive them from its own files.
|
|
623
|
+
def reset_carried_state
|
|
624
|
+
reset_effect_state
|
|
625
|
+
@run_level_rows = nil
|
|
626
|
+
end
|
|
627
|
+
|
|
609
628
|
# Whether the effects half of a restored snapshot permits reuse. Collection off is vacuously true —
|
|
610
629
|
# the session keeps no collections, writes none, and the pre-#382 reuse decision stands unchanged.
|
|
611
630
|
def effects_reuse_permitted?(payload)
|
|
@@ -684,18 +703,21 @@ module Rigor
|
|
|
684
703
|
# Fold a #recheck's fresh results back into the cache + graph so the session is correct across
|
|
685
704
|
# multiple edits: the analyzed set gets fresh diagnostics + digests + dependency edges, removed files
|
|
686
705
|
# are evicted from every map, and the analyzed-file list advances to `current`.
|
|
687
|
-
def absorb(runner,
|
|
706
|
+
def absorb(runner, current, analyze_set, removed)
|
|
688
707
|
removed.each { |path| forget(path) }
|
|
689
708
|
@analyzed = current
|
|
690
709
|
# ADR-85 WD2 — the recheck's discovery folded the restored bundles and refreshed them (changed files
|
|
691
710
|
# re-walked, removed files dropped, added files built), so adopt the runner's current set wholesale.
|
|
692
711
|
@seed_bundles = runner.seed_bundles
|
|
693
|
-
fresh_by_file = per_file(
|
|
712
|
+
fresh_by_file = per_file(runner.per_file_diagnostics)
|
|
694
713
|
analyze_set.each do |path|
|
|
695
714
|
@cache[path] = fresh_by_file[path] || []
|
|
696
715
|
@digests[path] = pack_digest(path)
|
|
697
716
|
end
|
|
698
717
|
absorb_dependency_graph(runner)
|
|
718
|
+
# Issues #796 / #794 — the recheck's own snapshots already carry the replayed rows folded together
|
|
719
|
+
# with anything its closure demanded for itself, so this is the same union the next run replays.
|
|
720
|
+
@run_level_rows = runner.run_level_rows
|
|
699
721
|
refresh_return_summaries(runner, analyze_set)
|
|
700
722
|
refresh_effect_collections(runner, analyze_set)
|
|
701
723
|
end
|
|
@@ -803,20 +825,24 @@ module Rigor
|
|
|
803
825
|
end
|
|
804
826
|
|
|
805
827
|
# ADR-46 slice 3 — the consumers to re-check because a symbol that appeared in a changed file resolves
|
|
806
|
-
# a prior missed lookup
|
|
807
|
-
# would satisfy (`toplevel:foo` for a
|
|
808
|
-
# negative-dependents of those keys.
|
|
828
|
+
# a prior missed lookup, and (issue #639) because a class DECLARATION appeared or vanished. Maps each
|
|
829
|
+
# appeared `"ClassName#method"` to the negative-dependency key it would satisfy (`toplevel:foo` for a
|
|
830
|
+
# top-level def, `method:C#m` otherwise), then unions the recorded negative-dependents of those keys.
|
|
809
831
|
def negative_affected(changed, removed, new_fingerprints, new_class_decls, new_constant_decls)
|
|
810
832
|
appeared_methods = Incremental.appeared_symbols(changed, @symbol_fingerprints, new_fingerprints)
|
|
811
|
-
|
|
833
|
+
# Issue #639 — a class DISAPPEARING satisfies the `class:` kind too, now that a resolved bare
|
|
834
|
+
# reference records the edge and not only a failed one; removed files join the diff for it.
|
|
835
|
+
moved_classes = Incremental.changed_class_declarations(
|
|
836
|
+
changed + removed, @class_decls, new_class_decls
|
|
837
|
+
)
|
|
812
838
|
# Issue #644 — a constant whose PUBLICATION moved satisfies the `constant:` kind, keyed on the same
|
|
813
|
-
# last segment the consumer recorded.
|
|
814
|
-
# second declarer restores the precise answer, and no other producer input can see that.
|
|
839
|
+
# last segment the consumer recorded. Removed files join this diff (as they do the class one above):
|
|
840
|
+
# deleting a second declarer restores the precise answer, and no other producer input can see that.
|
|
815
841
|
moved_constants = Incremental.changed_constant_publications(
|
|
816
842
|
changed + removed, @constant_decls, new_constant_decls
|
|
817
843
|
)
|
|
818
844
|
keys = appeared_methods.map { |symbol| negative_key_for(symbol) }
|
|
819
|
-
keys.concat(
|
|
845
|
+
keys.concat(moved_classes.map { |klass| "class:#{klass.split('::').last}" })
|
|
820
846
|
keys.concat(moved_constants.map { |name| "constant:#{name.split('::').last}" })
|
|
821
847
|
Incremental.negative_closure(keys, @negative_dependents)
|
|
822
848
|
end
|
|
@@ -984,9 +1010,21 @@ module Rigor
|
|
|
984
1010
|
@paths ? runner.run(@paths) : runner.run
|
|
985
1011
|
end
|
|
986
1012
|
|
|
987
|
-
# Group diagnostics by
|
|
988
|
-
#
|
|
989
|
-
#
|
|
1013
|
+
# Group the runner's PER-FILE diagnostics by path, keeping only analyzed project files.
|
|
1014
|
+
#
|
|
1015
|
+
# The argument is `Runner#per_file_diagnostics` — what `PoolCoordinator#analyze_files` returned, already
|
|
1016
|
+
# severity-resolved as the run's stream is (this cache serves reused files without re-stamping, so a
|
|
1017
|
+
# raw row would resurrect an `:off` rule and lose every override) — never the run's full stream. Every
|
|
1018
|
+
# run-level row is regenerated by every run and must not be served
|
|
1019
|
+
# from this cache, and "run-level" is a property of where a row was PRODUCED, not of where it is
|
|
1020
|
+
# positioned: the `.rigor.yml` rows the slice below drops are the common case, but
|
|
1021
|
+
# `effect.annotations-unchecked` sits at the first annotated file and the two `source-rbs-*` rows sit
|
|
1022
|
+
# at the source file that carried the annotation, and all three are produced once per run off the
|
|
1023
|
+
# environment and its reporters. Slicing the full stream cached them, and once a narrowed run's
|
|
1024
|
+
# environment carried the whole project's synthesized RBS (#793) each was regenerated for a file the
|
|
1025
|
+
# recheck did not analyse and met its cached twin in the merge — two rows, `--verify-incremental` red,
|
|
1026
|
+
# on the default configuration with the bundled rbs-inline plugin (#788 rounds 5 and 6; a rule-id
|
|
1027
|
+
# denylist missed the second pair). Feeding only the per-file stream makes the split structural.
|
|
990
1028
|
def per_file(diagnostics)
|
|
991
1029
|
diagnostics.group_by(&:path).slice(*@analyzed)
|
|
992
1030
|
end
|
|
@@ -1001,18 +1039,51 @@ module Rigor
|
|
|
1001
1039
|
candidates.reject { |path| stat_fresh?(path) }
|
|
1002
1040
|
end
|
|
1003
1041
|
|
|
1004
|
-
# A bound buffer's logical path is never fresh: the bytes to analyse live in the editor's temp file, and
|
|
1005
|
-
# the recorded entry describes the file on disk. Re-analysing it when the two happen to agree costs one
|
|
1006
|
-
# file; trusting the stat tuple would serve the editor its own stale diagnostics.
|
|
1007
1042
|
def buffer_path?(path)
|
|
1008
1043
|
!@buffer.nil? && path == @buffer.logical_path
|
|
1009
1044
|
end
|
|
1010
1045
|
|
|
1046
|
+
# Editor mode (#146) — the bound buffer's logical path is fresh exactly when the EDITOR's bytes still
|
|
1047
|
+
# hash to the digest the snapshot recorded for the file they stand in for. The recorded stat tuple
|
|
1048
|
+
# describes the file on disk, which an edit in the editor never moves, so validating through it serves
|
|
1049
|
+
# the editor its own pre-buffer answers (#960); hashing the buffer makes the substituted file changed
|
|
1050
|
+
# whenever it differs, and keeps an untouched buffer out of the closure. Anything unreadable or
|
|
1051
|
+
# unrecorded reads as changed, the conservative direction the rest of this tier takes.
|
|
1052
|
+
def buffer_fresh?(path)
|
|
1053
|
+
recorded = Cache::FileDigest.content_digest(@digests[path])
|
|
1054
|
+
return false if recorded.nil?
|
|
1055
|
+
|
|
1056
|
+
Cache::FileDigest.hexdigest(@buffer.resolve(path)) == recorded
|
|
1057
|
+
rescue StandardError
|
|
1058
|
+
false
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
# Editor mode (#146 / #960) — `--instead-of` is spelled the way the editor spells it (absolute, or
|
|
1062
|
+
# `./`-prefixed), while the analysed set is spelled the way the run's path arguments produce. A binding
|
|
1063
|
+
# whose logical path matches no member of that set substitutes nothing at all: every reader keeps
|
|
1064
|
+
# reading the file on disk and the run answers as if no buffer had been passed. Re-spell the binding
|
|
1065
|
+
# onto the set's own member, identified by real path, once the set is known.
|
|
1066
|
+
def rebind_buffer(files)
|
|
1067
|
+
return if @buffer.nil? || files.include?(@buffer.logical_path)
|
|
1068
|
+
|
|
1069
|
+
target = real_path(@buffer.logical_path)
|
|
1070
|
+
return if target.nil?
|
|
1071
|
+
|
|
1072
|
+
match = files.find { |path| real_path(path) == target }
|
|
1073
|
+
@buffer = BufferBinding.new(logical_path: match, physical_path: @buffer.physical_path) if match
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
def real_path(path)
|
|
1077
|
+
File.realpath(path)
|
|
1078
|
+
rescue StandardError
|
|
1079
|
+
nil
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1011
1082
|
# True when `path`'s recorded stat entry proves it unchanged since the last analysis. Any stat / parse
|
|
1012
1083
|
# failure (missing entry, unreadable / vanished file) reads as NOT fresh (→ re-analyse), preserving the
|
|
1013
1084
|
# prior `digest(path) != recorded` "changed" semantics for a file that cannot be validated.
|
|
1014
1085
|
def stat_fresh?(path)
|
|
1015
|
-
return
|
|
1086
|
+
return buffer_fresh?(path) if buffer_path?(path)
|
|
1016
1087
|
|
|
1017
1088
|
entry = @digests[path]
|
|
1018
1089
|
return false if entry.nil?
|
|
@@ -79,7 +79,6 @@ module Rigor
|
|
|
79
79
|
#
|
|
80
80
|
# Keeping the opaque decision here, rather than at each cache's call site, means a new consumer cannot
|
|
81
81
|
# key on `digest` while forgetting that an opaque surface makes it meaningless.
|
|
82
|
-
# @return [String, nil]
|
|
83
82
|
def self.key_digest(registry)
|
|
84
83
|
result = from_registry(registry)
|
|
85
84
|
result.opaque? ? nil : result.digest.to_s
|
|
@@ -29,13 +29,11 @@ module Rigor
|
|
|
29
29
|
# bucket on trust.
|
|
30
30
|
Undecidable = Data.define(:fqn, :path, :line, :reason)
|
|
31
31
|
|
|
32
|
-
# @param declarations
|
|
33
|
-
# @param references [Array<Scan::Reference>]
|
|
34
|
-
# @param root_fqns [Enumerable<String>] declarations that are entry points regardless of who references
|
|
32
|
+
# @param root_fqns — declarations that are entry points regardless of who references
|
|
35
33
|
# them (config-declared globs in this slice; plugin-supplied roots are #349).
|
|
36
|
-
# @param foreign
|
|
34
|
+
# @param foreign — predicate answering "is this FQN owned by something outside the project?" —
|
|
37
35
|
# a reopened gem or stdlib class must never be a candidate (WD6). Defaults to "nothing is foreign".
|
|
38
|
-
# @param dynamic_uses
|
|
36
|
+
# @param dynamic_uses — sites where a constant is reached by name at runtime.
|
|
39
37
|
# A literal-argument site contributes a real reference; a dynamic one taints a namespace (WD4).
|
|
40
38
|
def initialize(declarations:, references:, root_fqns: [], dynamic_uses: [], foreign: ->(_fqn) { false })
|
|
41
39
|
@declarations = declarations
|
|
@@ -83,14 +83,15 @@ module Rigor
|
|
|
83
83
|
# Loads the project's configured plugins, runs every `#prepare`, and returns the union of every
|
|
84
84
|
# published `:reachability_roots` and `:reachability_references` fact.
|
|
85
85
|
#
|
|
86
|
-
# @param configuration
|
|
87
|
-
# @param plugin_requirer
|
|
88
|
-
# `Analysis::Runner` exposes, so a spec can register a plugin class without publishing a
|
|
89
|
-
#
|
|
86
|
+
# @param configuration — the loaded project configuration.
|
|
87
|
+
# @param plugin_requirer — how a plugin gem is brought into the process, called with the gem name. The
|
|
88
|
+
# same seam `Analysis::Runner` exposes, so a spec can register a plugin class without publishing a
|
|
89
|
+
# gem.
|
|
90
|
+
# @param cache_store — when given, each plugin's `#prepare` producers read
|
|
90
91
|
# and write the same ADR-60 record-and-validate slots they use under `rigor check`, instead of
|
|
91
92
|
# recomputing from scratch — a routes parse or a factory discovery is a validated cache read on
|
|
92
93
|
# every invocation after the first. Nil keeps the historical recompute-always behaviour.
|
|
93
|
-
# @return
|
|
94
|
+
# @return sorted and de-duplicated. Empty whenever the project declares no plugins, no
|
|
94
95
|
# plugin contributes anything, or anything at all goes wrong.
|
|
95
96
|
def collect(configuration:, plugin_requirer: ->(name) { require name }, cache_store: nil)
|
|
96
97
|
return Contribution.empty if configuration.plugins.empty?
|
|
@@ -46,8 +46,8 @@ module Rigor
|
|
|
46
46
|
File.fnmatch?(pattern, path, File::FNM_PATHNAME)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# @param relative_paths
|
|
50
|
-
# @return
|
|
49
|
+
# @param relative_paths — paths relative to `root`.
|
|
50
|
+
# @return those that belong to the project itself.
|
|
51
51
|
def own(relative_paths, root)
|
|
52
52
|
prefixes = submodule_prefixes(root)
|
|
53
53
|
relative_paths.grep_v(VENDOR_DIRS).reject { |rel| prefixes.any? { |prefix| rel.start_with?(prefix) } }
|
|
@@ -70,11 +70,6 @@ module Rigor
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# @param path [String] the file's path, as the report should render it.
|
|
74
|
-
# @param source [String] the file's bytes.
|
|
75
|
-
# @param target_ruby [String, nil] Prism version string, threaded from the project configuration.
|
|
76
|
-
# @return [Result, nil] nil when the file does not parse (a parse error is the analyzer's business, not
|
|
77
|
-
# this scan's — it simply contributes nothing rather than half a file).
|
|
78
73
|
# A constant name is ASCII by construction, so a byte sequence that is not valid UTF-8 cannot be one.
|
|
79
74
|
# Dropping it is both correct and the only safe answer: carrying it forward crashed the whole run on
|
|
80
75
|
# the first `String#sub` downstream, which is how this surfaced — `rigor unused` on Rigor's own
|
|
@@ -86,6 +81,11 @@ module Rigor
|
|
|
86
81
|
name.valid_encoding? ? name : nil
|
|
87
82
|
end
|
|
88
83
|
|
|
84
|
+
# @param path — the file's path, as the report should render it.
|
|
85
|
+
# @param source — the file's bytes.
|
|
86
|
+
# @param target_ruby — Prism version string, threaded from the project configuration.
|
|
87
|
+
# @return nil when the file does not parse (a parse error is the analyzer's business, not
|
|
88
|
+
# this scan's — it simply contributes nothing rather than half a file).
|
|
89
89
|
def self.call(path:, source:, target_ruby: nil)
|
|
90
90
|
parsed = if target_ruby
|
|
91
91
|
Prism.parse(source, filepath: path,
|