rigortype 0.4.6 → 0.4.8
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 +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -5,6 +5,7 @@ require "prism"
|
|
|
5
5
|
require "tmpdir"
|
|
6
6
|
|
|
7
7
|
require_relative "../environment"
|
|
8
|
+
require_relative "../run_scoped_slots"
|
|
8
9
|
require_relative "../scope"
|
|
9
10
|
require_relative "../cache/store"
|
|
10
11
|
require_relative "../cache/rbs_descriptor"
|
|
@@ -63,8 +64,10 @@ module Rigor
|
|
|
63
64
|
|
|
64
65
|
# ADR-89 WD2 — bounds on the persisted return summaries (per-def observed call keys, and total defs
|
|
65
66
|
# summarised) so the incremental snapshot stays small. A def observed under more keys than the per-def
|
|
66
|
-
# cap keeps
|
|
67
|
-
#
|
|
67
|
+
# cap keeps none of them (issue #3116): the keys past the cap are unknown, so equal returns at the first
|
|
68
|
+
# eight prove nothing about the ninth key's reader. A def past the total cap keeps none either. Both are
|
|
69
|
+
# harvested as nil ({#return_summaries}), which the session stores as no summary at all, so the def's symbol
|
|
70
|
+
# dependents always re-check — the conservative direction.
|
|
68
71
|
RETURN_SUMMARY_KEYS_PER_DEF = 8
|
|
69
72
|
RETURN_SUMMARY_TOTAL_CAP = 4000
|
|
70
73
|
|
|
@@ -87,11 +90,10 @@ module Rigor
|
|
|
87
90
|
# forbids Rigor demanding in its place) and the HKT-scan outcome (whose producer is an environment a
|
|
88
91
|
# nothing-changed recheck otherwise has no reason to build). {IncrementalSession} persists what a full
|
|
89
92
|
# run answers here and hands it back to the next narrowed run.
|
|
93
|
+
#
|
|
94
|
+
# Which rows those are is declared, not listed: every {RunLevelFacts} fact marked `replay`.
|
|
90
95
|
def run_level_rows
|
|
91
|
-
Cache::IncrementalSnapshot::RunLevelRows.new(
|
|
92
|
-
definition_build_failures: @snapshots.definition_build_failures,
|
|
93
|
-
hkt_scan_failure: @snapshots.hkt_scan_failure
|
|
94
|
-
)
|
|
96
|
+
Cache::IncrementalSnapshot::RunLevelRows.new(**@snapshots.to_h.slice(*RunLevelFacts.replayable_names))
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
# ADR-46 — the per-file cross-file read records this run captured (empty unless
|
|
@@ -280,8 +282,7 @@ module Rigor
|
|
|
280
282
|
# @param explain — surface fail-soft fallback events as `:info` diagnostics.
|
|
281
283
|
# @param cache_store — the persistent cache the runner exposes to producers
|
|
282
284
|
# (`RbsConstantTable` and successors). Pass `nil` to disable caching for this run; the CLI's
|
|
283
|
-
# `--no-cache` flag wires `nil` through.
|
|
284
|
-
# slices route real producers through it.
|
|
285
|
+
# `--no-cache` flag wires `nil` through.
|
|
285
286
|
# @param workers — ADR-15 Phase 4b — when greater than zero, per-file analysis dispatches
|
|
286
287
|
# across a pool of N workers. Default `0` keeps the sequential code path bit-for-bit unchanged.
|
|
287
288
|
# Controlled via the `RIGOR_RACTOR_WORKERS` env var or `.rigor.yml` `parallel.workers:` (Phase 4c,
|
|
@@ -352,8 +353,8 @@ module Rigor
|
|
|
352
353
|
@prebuilt = prebuilt
|
|
353
354
|
@environment_override = environment
|
|
354
355
|
# ADR-46 slice 1 — opt-in cross-file dependency recording. Off by default; when true,
|
|
355
|
-
# `analyze_file` records each file's cross-file reads into `file_dependencies
|
|
356
|
-
# cache
|
|
356
|
+
# `analyze_file` records each file's cross-file reads into `file_dependencies`; the incremental
|
|
357
|
+
# cache consumes them.
|
|
357
358
|
@record_dependencies = record_dependencies
|
|
358
359
|
# ADR-24 slice 4a — opt-in unresolved-implicit-self-call recording. Off by default; when true,
|
|
359
360
|
# `analyze_file` activates the engine choke-point recorder and collects each file's misses into
|
|
@@ -513,6 +514,13 @@ module Rigor
|
|
|
513
514
|
# entry across a run boundary (LSP re-check, ADR-62 warm loop); nil until the first run so
|
|
514
515
|
# runner-less probes keep the per-file fallback.
|
|
515
516
|
@run_generation = nil
|
|
517
|
+
# The return memo bucket the last `run` filled, kept for {#return_summaries} once the run has released the
|
|
518
|
+
# thread's slot; nil before the first run.
|
|
519
|
+
@return_memo = nil
|
|
520
|
+
# The `[path, node_id] => Prism::DefNode` table the last `run`'s {Inference::DefNodeResolver} filled, kept for
|
|
521
|
+
# the same reader.
|
|
522
|
+
@resolved_def_nodes = nil
|
|
523
|
+
@probe_reads = {}.freeze
|
|
516
524
|
build_collaborators
|
|
517
525
|
end
|
|
518
526
|
|
|
@@ -526,6 +534,9 @@ module Rigor
|
|
|
526
534
|
# errors. The Environment is built once at run start through `Environment.for_project` so all files
|
|
527
535
|
# share the same RBS load.
|
|
528
536
|
def run(paths = @configuration.paths)
|
|
537
|
+
# ADR-129 WD5 (#2785) — a mismatch left on the thread by typing after an earlier run (its return summaries, a
|
|
538
|
+
# language-server hover) fails here rather than reaching this run's first file as a crash row.
|
|
539
|
+
Inference::MemoAudit.raise_undrained!
|
|
529
540
|
# One per-run file-digest memo spans the whole run, so a path is SHA-256'd at most once across the
|
|
530
541
|
# run-diagnostics dependency descriptor, its `fresh?` validation, the RBS signature tree, and every
|
|
531
542
|
# plugin producer's watched-glob validation (they overlap heavily on the warm path). The nested ADR-85
|
|
@@ -534,7 +545,17 @@ module Rigor
|
|
|
534
545
|
# ADR-87 WD1 — `cache.validation` (or the RIGOR_STRICT_VALIDATION env, which wins) selects the
|
|
535
546
|
# freshness path for this run; the `auto` default resolves strict in CI (#190), stat-first elsewhere.
|
|
536
547
|
Cache::FileDigest.with_run(strict: @configuration.cache_validation_strict?) do
|
|
537
|
-
Inference::DefNodeResolver.with_run
|
|
548
|
+
Inference::DefNodeResolver.with_run do
|
|
549
|
+
run_analysis(paths)
|
|
550
|
+
ensure
|
|
551
|
+
# Issue #3160 — the memo keys an unchanged file's def on the node the resolver minted for its handle.
|
|
552
|
+
@resolved_def_nodes = Inference::DefNodeResolver.resolved_nodes
|
|
553
|
+
end
|
|
554
|
+
ensure
|
|
555
|
+
# ADR-89 WD2 — {#return_summaries} reads the return memo after the run, so the runner keeps the bucket the
|
|
556
|
+
# run filled before the thread's run-scoped slots are released (issues #2412, #2399).
|
|
557
|
+
@return_memo = Inference::ExpressionTyper.current_return_memo
|
|
558
|
+
RunScopedSlots.release
|
|
538
559
|
end
|
|
539
560
|
end
|
|
540
561
|
|
|
@@ -548,6 +569,21 @@ module Rigor
|
|
|
548
569
|
end
|
|
549
570
|
private :reset_analysis_attribution
|
|
550
571
|
|
|
572
|
+
# Issue #2392 — the sequential half of the per-run reset, beside {#reset_analysis_attribution}: the
|
|
573
|
+
# dependency graph and per-file effect collections a `--workers 0` run records, and the effect-slot state a
|
|
574
|
+
# served run leaves behind. The loader is the one that matters: a served run's collections loader would
|
|
575
|
+
# otherwise feed the next analysing run the previous run's collections. Runs before any `adopt_*`, which the
|
|
576
|
+
# served lanes call later in the same run, so an adopted slot is this run's own.
|
|
577
|
+
def reset_sequential_run_tables
|
|
578
|
+
@file_dependencies = {}
|
|
579
|
+
@file_effects = {}
|
|
580
|
+
@cached_effect_sources = nil
|
|
581
|
+
@cached_effect_ancestry = nil
|
|
582
|
+
@cached_collections_loader = nil
|
|
583
|
+
@effects_served_from_cache = false
|
|
584
|
+
end
|
|
585
|
+
private :reset_sequential_run_tables
|
|
586
|
+
|
|
551
587
|
def run_analysis(paths)
|
|
552
588
|
Inference::MethodDispatcher::FileFolding.fold_platform_specific_paths =
|
|
553
589
|
@configuration.fold_platform_specific_paths
|
|
@@ -561,6 +597,10 @@ module Rigor
|
|
|
561
597
|
@project_discovery_expansion = project_discovery_expansion(paths, expansion)
|
|
562
598
|
@discovery_widened = widen_discovery_to_project?(paths)
|
|
563
599
|
@snapshots.reset_for_run
|
|
600
|
+
# Issue #2392 — the coordinator's run-accumulated tables (disclosures, trust refusals, the dependency and
|
|
601
|
+
# effect graphs) are per run too: a reused Runner must not re-emit or sum the previous run's rows.
|
|
602
|
+
@pool_coordinator.reset_for_run
|
|
603
|
+
reset_sequential_run_tables
|
|
564
604
|
# Per-run reset of the deferred-discovery memo (see `#ensure_project_discovery`).
|
|
565
605
|
@project_discovery_done = false
|
|
566
606
|
# #392 — re-derive the template units each run: their source files can change between two runs of
|
|
@@ -762,15 +802,29 @@ module Rigor
|
|
|
762
802
|
end
|
|
763
803
|
|
|
764
804
|
# ADR-89 WD2 — the per-method observed-key return summaries the run's ADR-84 return memo just captured.
|
|
765
|
-
# For each project method
|
|
766
|
-
# incremental session persists: `keys` the observed `[receiver, arg_types]` type tuples, `returns` their
|
|
805
|
+
# For each project method the run observed, a bounded `{ keys:, returns:, effects:, guard_constants: }` summary
|
|
806
|
+
# the incremental session persists: `keys` the observed `[receiver, arg_types]` type tuples, `returns` their
|
|
767
807
|
# `describe(:short)` descriptors, `effects` the content-mutated parameter positions (a caller-visible
|
|
768
|
-
# arg-flooring surface)
|
|
808
|
+
# arg-flooring surface), `guard_constants` the published-constant spellings its body reads ({#guard_constants}).
|
|
809
|
+
# Only meaningful right after a run populated the memo (baseline / recheck),
|
|
769
810
|
# before the bucket rolls. Keys are held live here; the session Marshals them for the snapshot.
|
|
770
811
|
#
|
|
771
|
-
#
|
|
812
|
+
# Issue #3160 — the session reads the harvest as EVERY key the files this run analysed observed each def
|
|
813
|
+
# under, so whatever cannot be that says so instead of looking like a short key list:
|
|
814
|
+
#
|
|
815
|
+
# - a def observed under keys this run cannot list maps to nil: more keys than
|
|
816
|
+
# {RETURN_SUMMARY_KEYS_PER_DEF} (issue #3116), a call whose result the memo refused to store, or a def
|
|
817
|
+
# past {RETURN_SUMMARY_TOTAL_CAP};
|
|
818
|
+
# - a def absent from the result was observed by no analysed file, wherever it is defined: an unchanged
|
|
819
|
+
# file's def is found through the node its {Inference::DefHandle} resolved to;
|
|
820
|
+
# - the whole result is nil for a pool run, whose workers typed the calls in memos this process never sees.
|
|
821
|
+
#
|
|
822
|
+
# @return the summaries keyed by `[path, "Class#method"]` (or `"Class.method"`), each holding `keys:`,
|
|
823
|
+
# `returns:` and `effects:`, or nil; nil for a pool run.
|
|
772
824
|
def return_summaries
|
|
773
|
-
|
|
825
|
+
return nil if pool_mode?
|
|
826
|
+
|
|
827
|
+
memo = Inference::ExpressionTyper.harvest_return_memo(@return_memo)
|
|
774
828
|
return {} if memo.empty?
|
|
775
829
|
|
|
776
830
|
result = {}
|
|
@@ -779,37 +833,89 @@ module Rigor
|
|
|
779
833
|
"#", memo, effects_evaluator)
|
|
780
834
|
collect_return_summaries(result, @project_discovered_singleton_def_nodes,
|
|
781
835
|
@project_discovered_singleton_def_sources, ".", memo, effects_evaluator)
|
|
836
|
+
# ADR-129 WD5 (#2785) — typing after the run, under `content_effects`' rescue.
|
|
837
|
+
Inference::MemoAudit.raise_undrained!
|
|
782
838
|
result
|
|
783
839
|
end
|
|
784
840
|
|
|
785
841
|
# Folds one def-node/def-source table pair's memo entries into the return-summary result under
|
|
786
|
-
# `separator`.
|
|
787
|
-
#
|
|
788
|
-
#
|
|
789
|
-
# the snapshot). Bounded per def and in total so the snapshot stays small.
|
|
842
|
+
# `separator`. The node the memo keyed is the identity every cross-file caller resolved through: the live
|
|
843
|
+
# def node of a cold / re-walked file, or the one the resolver minted for an unchanged file's
|
|
844
|
+
# {Inference::DefHandle} ({#observed_def_node}).
|
|
790
845
|
def collect_return_summaries(result, nodes, sources, separator, memo, effects_evaluator)
|
|
846
|
+
summarised = result.count { |_pair, summary| summary }
|
|
791
847
|
nodes.each do |class_name, methods|
|
|
792
|
-
methods.each do |method_name,
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
entries = memo[node]
|
|
796
|
-
next if entries.nil? || entries.empty?
|
|
848
|
+
methods.each do |method_name, slot|
|
|
849
|
+
node = observed_def_node(slot)
|
|
850
|
+
next unless memo.key?(node)
|
|
797
851
|
|
|
798
852
|
path_line = sources.dig(class_name, method_name)
|
|
799
853
|
next if path_line.nil?
|
|
800
854
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
result[[path_line.split(":", 2).first, "#{class_name}#{separator}#{method_name}"]] = {
|
|
805
|
-
keys: capped.map { |entry| [entry.receiver, entry.arg_types] },
|
|
806
|
-
returns: capped.map { |entry| entry.result.describe(:short) },
|
|
807
|
-
effects: content_effects(effects_evaluator, node)
|
|
808
|
-
}
|
|
855
|
+
summary = summarised < RETURN_SUMMARY_TOTAL_CAP ? return_summary(memo[node], effects_evaluator, node) : nil
|
|
856
|
+
summarised += 1 if summary
|
|
857
|
+
result[[path_line.split(":", 2).first, "#{class_name}#{separator}#{method_name}"]] = summary
|
|
809
858
|
end
|
|
810
859
|
end
|
|
811
860
|
end
|
|
812
861
|
|
|
862
|
+
# The node the run's return memo keyed `slot`'s def on, or nil when no call resolved it: the live node, or the
|
|
863
|
+
# one the run's resolver minted for a {Inference::DefHandle}.
|
|
864
|
+
def observed_def_node(slot)
|
|
865
|
+
return slot unless slot.is_a?(Inference::DefHandle)
|
|
866
|
+
|
|
867
|
+
@resolved_def_nodes&.[]([slot.path, slot.node_id])
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
# One def's summary, or nil when `entries` are not every key it was observed under: the memo refused to store
|
|
871
|
+
# a call of it (so `entries` may be empty), or there are more than {RETURN_SUMMARY_KEYS_PER_DEF} (issue
|
|
872
|
+
# #3116). A splat call's key (issue #1237) is an entry like any other and counts toward the cap.
|
|
873
|
+
def return_summary(entries, effects_evaluator, node)
|
|
874
|
+
return nil if entries.empty? || entries.size > RETURN_SUMMARY_KEYS_PER_DEF
|
|
875
|
+
return nil if Inference::ExpressionTyper.return_memo_refused?(@return_memo, node)
|
|
876
|
+
|
|
877
|
+
{ keys: entries.map { |entry| return_summary_key(entry) },
|
|
878
|
+
returns: entries.map { |entry| entry.result.describe(:short) },
|
|
879
|
+
effects: content_effects(effects_evaluator, node),
|
|
880
|
+
guard_constants: guard_constants(node) }
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
# Issue #3186 — the constant spellings in `node`'s body that the truthiness rule's one interprocedural hop
|
|
884
|
+
# ({CheckRules::PublishedConstantGuard}) could answer true for, or nil when they cannot be listed. A reader's
|
|
885
|
+
# `flow.always-truthy-condition` withholds when a def it calls reads a constant another file published,
|
|
886
|
+
# whatever the def returns, so the ADR-89 WD2 gate compares these as well as the returns; nil never compares
|
|
887
|
+
# equal, so it keeps the def's readers.
|
|
888
|
+
#
|
|
889
|
+
# The hop asks `Scope#published_constant?`, which answers true only for a spelling whose last segment the
|
|
890
|
+
# project publishes or the reader's own file assigns from a published constant, and each of those is a segment
|
|
891
|
+
# some project file writes. So the spellings whose last segment no file writes (a class name, as a rule) are
|
|
892
|
+
# left out: they answer false for every reader, and keeping them would fail the gate on every edit that adds a
|
|
893
|
+
# callee. The filter is this run's write census, so a census that moved fails the comparison.
|
|
894
|
+
def guard_constants(node)
|
|
895
|
+
written = constant_write_segments
|
|
896
|
+
CheckRules::PublishedConstantGuard.body_constant_spellings(node.body).select do |spelling|
|
|
897
|
+
written.include?(spelling.split("::").last)
|
|
898
|
+
end
|
|
899
|
+
rescue StandardError
|
|
900
|
+
nil
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
# The last segments of every constant name the run's write census holds, a wildcard key's included.
|
|
904
|
+
def constant_write_segments
|
|
905
|
+
unless @constant_write_segments_of.equal?(@project_constant_writes)
|
|
906
|
+
@constant_write_segments_of = @project_constant_writes
|
|
907
|
+
@constant_write_segments = @project_constant_writes.each_key.to_set { |name| name.split("::").last }
|
|
908
|
+
end
|
|
909
|
+
@constant_write_segments
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
# One observed call key: `[receiver, arg_types]`, or, for an entry a splat call stored (issue #1237),
|
|
913
|
+
# `[receiver, arg_types, true]`, which {#evaluate_spec_returns} re-drives with no positional correspondence.
|
|
914
|
+
def return_summary_key(entry)
|
|
915
|
+
key = [entry.receiver, entry.arg_types]
|
|
916
|
+
entry.positionals_unknown ? key << true : key
|
|
917
|
+
end
|
|
918
|
+
|
|
813
919
|
# The content-mutated parameter positions of `node`, or `[]` if the computation raises (defensive: the
|
|
814
920
|
# effects surface only routes attention, never soundness — a missing set reads as "no floor").
|
|
815
921
|
def content_effects(effects_evaluator, node)
|
|
@@ -828,15 +934,49 @@ module Rigor
|
|
|
828
934
|
#
|
|
829
935
|
# @param paths — analysis roots (nil → the configuration's `paths:`).
|
|
830
936
|
# @param specs — each `{ class_name:, method_name:, singleton:, keys: [[receiver, args], …] }`.
|
|
831
|
-
# @return
|
|
937
|
+
# @return the re-evaluated return descriptors, one per key, keyed by `[class_name, method_name, singleton]`.
|
|
832
938
|
def evaluate_return_types(paths, specs)
|
|
833
939
|
return {} if specs.empty?
|
|
834
940
|
|
|
941
|
+
@probe_reads = {}
|
|
942
|
+
@probe_plugin_reads = {}
|
|
943
|
+
@probe_disclosures = {}
|
|
944
|
+
@probe_guard_constants = {}
|
|
835
945
|
Cache::FileDigest.with_run(strict: @configuration.cache_validation_strict?) do
|
|
836
|
-
Inference::DefNodeResolver.with_run
|
|
946
|
+
Inference::DefNodeResolver.with_run do
|
|
947
|
+
evaluate_return_types_setup(paths, specs)
|
|
948
|
+
ensure
|
|
949
|
+
@resolved_def_nodes = Inference::DefNodeResolver.resolved_nodes
|
|
950
|
+
end
|
|
837
951
|
end
|
|
952
|
+
ensure
|
|
953
|
+
# Issue #3180 — as {#run} does: {#return_summaries} then answers for what the probe observed.
|
|
954
|
+
@return_memo = Inference::ExpressionTyper.current_return_memo
|
|
955
|
+
RunScopedSlots.release
|
|
838
956
|
end
|
|
839
957
|
|
|
958
|
+
# Issue #3180 — what the last {#evaluate_return_types} read while re-evaluating each spec's def at its keys: the
|
|
959
|
+
# spec triple => a {DependencyRecorder::Record} of every def, class and name the evaluation read, the def's own
|
|
960
|
+
# file included (nothing is filtered as a self-read). The incremental session gives these edges to the readers
|
|
961
|
+
# a passing gate skips, since they now reach whatever the edited def reaches. Empty before the first probe.
|
|
962
|
+
attr_reader :probe_reads
|
|
963
|
+
|
|
964
|
+
# Issue #3187 — what the plugins did while the last {#evaluate_return_types} re-evaluated each spec's def, as
|
|
965
|
+
# {#analysis_reads} and {#analysis_disclosures} hold for an analysed file: the spec triple => the boundary rows
|
|
966
|
+
# its evaluation read ({Plugin::IoBoundary.attributing}), and => the run-scoped rows it registered
|
|
967
|
+
# ({RunDisclosureCredit}). A triple whose evaluation read or registered nothing has no key. The incremental
|
|
968
|
+
# session credits the reads to the readers a passing gate skips, as it does {#probe_reads}; a registration
|
|
969
|
+
# keeps the def's readers instead, since no skipped reader could be credited with it, or have it retracted.
|
|
970
|
+
attr_reader :probe_plugin_reads, :probe_disclosures
|
|
971
|
+
|
|
972
|
+
# Issue #3186 — the spec triple => {#guard_constants} of the def the last {#evaluate_return_types} re-evaluated,
|
|
973
|
+
# under that probe's write census. A triple whose def the probe did not find has no key.
|
|
974
|
+
attr_reader :probe_guard_constants
|
|
975
|
+
|
|
976
|
+
# The consumer {#evaluate_spec_returns} records under. It is no file's path, so no read is dropped as a self-read.
|
|
977
|
+
PROBE_CONSUMER = ""
|
|
978
|
+
private_constant :PROBE_CONSUMER
|
|
979
|
+
|
|
840
980
|
# Builds the discovery index + environment (a real run's prologue, minus per-file analysis) and
|
|
841
981
|
# re-evaluates each spec's def. Extracted so {#evaluate_return_types}'s `with_run` wrappers stay thin.
|
|
842
982
|
def evaluate_return_types_setup(paths, specs)
|
|
@@ -872,10 +1012,43 @@ module Rigor
|
|
|
872
1012
|
return spec[:keys].map { nil } if node.nil?
|
|
873
1013
|
|
|
874
1014
|
scope = seed_project_scope(Scope.empty(environment: environment, source_path: spec[:path]))
|
|
875
|
-
spec[:
|
|
876
|
-
|
|
877
|
-
|
|
1015
|
+
triple = [spec[:class_name], spec[:method_name], spec[:singleton]]
|
|
1016
|
+
@probe_guard_constants[triple] = guard_constants(node)
|
|
1017
|
+
returns = nil
|
|
1018
|
+
attributed_probe(triple) do
|
|
1019
|
+
record_guard_spelling_edges(node)
|
|
1020
|
+
returns = spec[:keys].map do |(receiver, arg_types, positionals_unknown)|
|
|
1021
|
+
result = scope.user_method_return(node, receiver, arg_types,
|
|
1022
|
+
positionals_unknown: positionals_unknown || false)
|
|
1023
|
+
result&.describe(:short)
|
|
1024
|
+
end
|
|
1025
|
+
end
|
|
1026
|
+
returns
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
# Issue #3199 — the `constant:` edge for every spelling the truthiness guard's hop would scan in `node`'s body,
|
|
1030
|
+
# recorded into the probe's reads, which every reader the gate skips receives. {#guard_constants} leaves out a
|
|
1031
|
+
# spelling whose last segment no file writes, so an edit that starts probing one (`defined?(B::X)` before any
|
|
1032
|
+
# file assigns `B::X`) passes the gate; the skipped reader's guard never asks about it, and without this edge a
|
|
1033
|
+
# file that later publishes the constant would re-check nobody.
|
|
1034
|
+
def record_guard_spelling_edges(node)
|
|
1035
|
+
CheckRules::PublishedConstantGuard.body_constant_spellings(node.body).each do |spelling|
|
|
1036
|
+
DependencyRecorder.read_last_segment(:constant, spelling)
|
|
1037
|
+
end
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# Runs one spec's evaluation with its dependency reads recorded ({#probe_reads}) and its plugin reads and
|
|
1041
|
+
# run-scoped registrations attributed to it ({#probe_plugin_reads}, {#probe_disclosures}), as
|
|
1042
|
+
# {#analyze_file_attributed} does for a file.
|
|
1043
|
+
def attributed_probe(triple, &)
|
|
1044
|
+
reads = []
|
|
1045
|
+
disclosures = RunDisclosureCredit.collect do
|
|
1046
|
+
@probe_reads[triple] = Plugin::IoBoundary.attributing(reads) do
|
|
1047
|
+
DependencyRecorder.record_for(PROBE_CONSUMER, &)
|
|
1048
|
+
end
|
|
878
1049
|
end
|
|
1050
|
+
@probe_plugin_reads[triple] = reads.uniq.freeze unless reads.empty?
|
|
1051
|
+
@probe_disclosures[triple] = disclosures unless disclosures.empty?
|
|
879
1052
|
end
|
|
880
1053
|
|
|
881
1054
|
# ADR-45 — unchanged-project fast path. Serves the whole run's (pre-severity-profile) diagnostics
|
|
@@ -891,7 +1064,10 @@ module Rigor
|
|
|
891
1064
|
# #392 — `source_files:` is what a plugin's `source_rbs_synthesizer` is offered at env-build time,
|
|
892
1065
|
# and a template unit is not Ruby the synthesiser can read (rbs-inline would be handed `.rbx`
|
|
893
1066
|
# bytes). The `.rb` expansion is the right set here; the units join the ANALYSED set only.
|
|
894
|
-
|
|
1067
|
+
# Issue #2883 — not eager: a warm result-cache HIT never types, so it must not build the RBS environment;
|
|
1068
|
+
# {#assemble_run_diagnostics} builds it on the analysis path.
|
|
1069
|
+
environment = @pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files),
|
|
1070
|
+
eager: false)
|
|
895
1071
|
# Lazy-files descriptor: the cache KEY reads only `gems` + `configs`; the RBS signature-tree `files`
|
|
896
1072
|
# are digested solely by `run_dependency_descriptor` on a MISS, so a warm HIT never walks the tree.
|
|
897
1073
|
rbs_descriptor = if environment&.rbs_loader
|
|
@@ -1021,17 +1197,12 @@ module Rigor
|
|
|
1021
1197
|
nil
|
|
1022
1198
|
end
|
|
1023
1199
|
|
|
1024
|
-
# The write half, run only after a miss, so
|
|
1025
|
-
# collections the analysis just produced — and the table the analysis already closed over them, so
|
|
1026
|
-
# a warm hit re-runs neither the merge nor the fixpoint — validated against the same post-run
|
|
1027
|
-
# dependency descriptor the diagnostics slot records. Fail-soft — a collection that will not
|
|
1028
|
-
# Marshal (which nothing in {Effects::FileCollection} should be, and the fork pool already proves
|
|
1029
|
-
# per file) costs the next run its warm start and nothing else.
|
|
1030
|
-
# The write half, run only after a miss, so neither block recomputes anything. Two entries under one
|
|
1200
|
+
# The write half, run only after a miss, so nothing here recomputes anything. Two entries under one
|
|
1031
1201
|
# key descriptor and one dependency descriptor (#482): the summary a warm run serves from, and the
|
|
1032
1202
|
# collections the incremental and fail-soft paths read. The summary is written LAST, so a run
|
|
1033
1203
|
# interrupted between the two leaves a collections entry that the next run's summary miss simply
|
|
1034
|
-
# re-collects — never a summary whose collections are absent.
|
|
1204
|
+
# re-collects — never a summary whose collections are absent. A collection that will not Marshal
|
|
1205
|
+
# costs the next run its warm start and nothing else.
|
|
1035
1206
|
def store_effect_collections(descriptor, expansion, rbs_descriptor)
|
|
1036
1207
|
return if descriptor.nil?
|
|
1037
1208
|
|
|
@@ -1170,6 +1341,8 @@ module Rigor
|
|
|
1170
1341
|
end
|
|
1171
1342
|
|
|
1172
1343
|
def assemble_run_diagnostics(expansion, environment: nil)
|
|
1344
|
+
# Issue #2883 — the analysis path builds the RBS environment before anything types (ADR-129 G1').
|
|
1345
|
+
environment&.prepare_for_typing!
|
|
1173
1346
|
# Force the deferred cross-file discovery pre-pass on the analysis (miss) path. Memoised, so the
|
|
1174
1347
|
# eager force in `#run` (recording / subset modes) makes this a no-op. A warm cache HIT never calls
|
|
1175
1348
|
# `assemble_run_diagnostics`, so it never runs the two whole-project parse passes. Runs over the FULL
|
|
@@ -1233,40 +1406,13 @@ module Rigor
|
|
|
1233
1406
|
end
|
|
1234
1407
|
private :template_unit_failure_diagnostics
|
|
1235
1408
|
|
|
1236
|
-
#
|
|
1237
|
-
#
|
|
1238
|
-
#
|
|
1239
|
-
#
|
|
1240
|
-
#
|
|
1241
|
-
# run.
|
|
1409
|
+
# Every remaining post-analysis stream `#assemble_run_diagnostics` appends — the `rbs.coverage.*`
|
|
1410
|
+
# build-failure ladder (widest consequence first: quarantine, env build, definition build #696, HKT scan
|
|
1411
|
+
# #784), its notices, the conformance rows, the reporter streams, the plugin run disclosures (#1051)
|
|
1412
|
+
# and, last, the trust-refusal envelope (#959) — in the contract order ADR-116 WD3's
|
|
1413
|
+
# {RunLevelFacts} declares. Read after `analyze_targets`, so every worker payload has been merged.
|
|
1242
1414
|
def post_analysis_diagnostic_streams
|
|
1243
|
-
|
|
1244
|
-
diagnostics += @diagnostic_aggregator.rbs_environment_build_failed_diagnostics
|
|
1245
|
-
# Issue #696 — after its env-wide twin and before the synthesized-namespace notice: the four
|
|
1246
|
-
# `rbs.coverage.*` build conditions surface widest-consequence first, and their relative order is
|
|
1247
|
-
# the diagnostic output contract.
|
|
1248
|
-
diagnostics += @diagnostic_aggregator.rbs_definition_build_failed_diagnostics
|
|
1249
|
-
# Issue #784 — last of the four, deliberately: it loses only the IMPLICIT HKT registrations a
|
|
1250
|
-
# `type` alias would have contributed, never a class's own declared method surface, so it is the
|
|
1251
|
-
# narrowest-consequence rung on the same ladder.
|
|
1252
|
-
diagnostics += @diagnostic_aggregator.rbs_hkt_scan_failed_diagnostics
|
|
1253
|
-
# The synthesized-namespace notice, then (#610) the plugin-signature stand-down: the outcome that
|
|
1254
|
-
# AVOIDED a definition-build failure, so the quietest row on that ladder.
|
|
1255
|
-
diagnostics += @diagnostic_aggregator.rbs_coverage_notice_diagnostics
|
|
1256
|
-
diagnostics += @diagnostic_aggregator.conforms_to_diagnostics
|
|
1257
|
-
diagnostics += @diagnostic_aggregator.rbs_extended_reporter_diagnostics
|
|
1258
|
-
diagnostics += @diagnostic_aggregator.boundary_cross_diagnostics
|
|
1259
|
-
diagnostics += @diagnostic_aggregator.source_rbs_synthesis_diagnostics
|
|
1260
|
-
# Issue #1051 — read after `analyze_targets` for the same reason the refusal stream below is: a
|
|
1261
|
-
# plugin may register a disclosure from `#prepare` (the usual case) or while analysing a file, and
|
|
1262
|
-
# both have to be in before the table is harvested. Ordered before the refusals so the run-level
|
|
1263
|
-
# plugin rows read plugin-authored notice first, engine-authored envelope second.
|
|
1264
|
-
diagnostics += @diagnostic_aggregator.plugin_run_disclosure_diagnostics
|
|
1265
|
-
# Issue #959 — read LAST (after `analyze_targets`, which already ran before this method is called),
|
|
1266
|
-
# so every per-file plugin call has had its chance to record a refusal on its (per-plugin-instance
|
|
1267
|
-
# memoised) `IoBoundary`, alongside the prepare-time refusals the same boundary instance may
|
|
1268
|
-
# already carry.
|
|
1269
|
-
diagnostics + @diagnostic_aggregator.plugin_trust_refusal_diagnostics
|
|
1415
|
+
@diagnostic_aggregator.run_level_diagnostics
|
|
1270
1416
|
end
|
|
1271
1417
|
private :post_analysis_diagnostic_streams
|
|
1272
1418
|
|
|
@@ -1445,7 +1591,7 @@ module Rigor
|
|
|
1445
1591
|
# #979 — the signature ROOTS' listings, one glob row each. `rbs_descriptor.files` covers only the
|
|
1446
1592
|
# `.rbs` files that existed while the run read them, so without these a signature file written after
|
|
1447
1593
|
# the run left the slot validating fresh and the warm run answered without it.
|
|
1448
|
-
globs = rbs_descriptor.globs + template_unit_glob_entries + discovery_glob_entries
|
|
1594
|
+
globs = rbs_descriptor.globs + template_unit_glob_entries + discovery_glob_entries + bundle_sig_glob_entries
|
|
1449
1595
|
boundary = plugin_boundary_rows
|
|
1450
1596
|
entries.concat(boundary.files)
|
|
1451
1597
|
globs.concat(boundary.globs).concat(producer_watch_glob_entries)
|
|
@@ -1583,7 +1729,9 @@ module Rigor
|
|
|
1583
1729
|
# change time it cannot read, and passes over a `pinned` one.
|
|
1584
1730
|
BaselineRows = Data.define(:owned, :pinned)
|
|
1585
1731
|
|
|
1586
|
-
|
|
1732
|
+
# A loader's two readers {Cache::RbsDescriptor} takes signature roots from. `core_root` (issue #2517) is the
|
|
1733
|
+
# selected core, which the slot key names by path only, so its files are `owned` rows like the project's own.
|
|
1734
|
+
SignatureRoots = Struct.new(:signature_paths, :core_root)
|
|
1587
1735
|
private_constant :SignatureRoots
|
|
1588
1736
|
|
|
1589
1737
|
# @param files — the run's analysed-path set.
|
|
@@ -1591,6 +1739,9 @@ module Rigor
|
|
|
1591
1739
|
return nil if pool_mode? || !@collect_stats || @analyzed_files.empty?
|
|
1592
1740
|
|
|
1593
1741
|
own, pinned = signature_rows
|
|
1742
|
+
# Issue #2987 — the bundle's `sig/` listing is identified by the gems' directory, outside the project, so a
|
|
1743
|
+
# `pinned` row: the write guard passes over it where it would refuse an `owned` one it cannot stat.
|
|
1744
|
+
pinned = Cache::Descriptor.new(files: pinned.files, globs: (pinned.globs + bundle_sig_glob_entries).uniq)
|
|
1594
1745
|
BaselineRows.new(
|
|
1595
1746
|
owned: Cache::Descriptor.new(
|
|
1596
1747
|
files: own.files + kind_entries(signature_root_candidates) +
|
|
@@ -1604,8 +1755,9 @@ module Rigor
|
|
|
1604
1755
|
|
|
1605
1756
|
# The signature tree's rows, the project's own and the rest.
|
|
1606
1757
|
def signature_rows
|
|
1607
|
-
roots = SignatureRoots.new(@snapshots.signature_paths)
|
|
1608
|
-
own = signature_root_candidates
|
|
1758
|
+
roots = SignatureRoots.new(@snapshots.signature_paths, @configuration.core_root)
|
|
1759
|
+
own = (signature_root_candidates + [@configuration.core_root].compact)
|
|
1760
|
+
.map { |root| File.join(File.absolute_path(root.to_s), "") }
|
|
1609
1761
|
own_files, other_files = Cache::RbsDescriptor.file_entries(roots, comparator: :stat)
|
|
1610
1762
|
.partition { |row| within?(row.path, own) }
|
|
1611
1763
|
own_globs, other_globs = Cache::RbsDescriptor.glob_entries(roots).partition { |row| within?(row.root, own) }
|
|
@@ -1618,12 +1770,12 @@ module Rigor
|
|
|
1618
1770
|
roots.any? { |root| directory.start_with?(root) }
|
|
1619
1771
|
end
|
|
1620
1772
|
|
|
1621
|
-
# The project's own signature roots, whether or not they exist:
|
|
1622
|
-
#
|
|
1623
|
-
#
|
|
1624
|
-
# itself covers it appearing.
|
|
1773
|
+
# The project's own signature roots, whether or not they exist:
|
|
1774
|
+
# {Environment.project_signature_root_candidates} — the configured `signature_paths:`, or the `sig`
|
|
1775
|
+
# directory it defaults to when none are configured (issue #1619). The signature rows' listing rows
|
|
1776
|
+
# cover a file appearing under a root that existed; only a row for the root itself covers it appearing.
|
|
1625
1777
|
def signature_root_candidates
|
|
1626
|
-
@configuration.signature_paths
|
|
1778
|
+
Environment.project_signature_root_candidates(@configuration.signature_paths)
|
|
1627
1779
|
end
|
|
1628
1780
|
|
|
1629
1781
|
# One names-mode listing row per directory the discovery widening globbed, so a file appearing there is seen
|
|
@@ -1638,6 +1790,22 @@ module Rigor
|
|
|
1638
1790
|
end
|
|
1639
1791
|
end
|
|
1640
1792
|
|
|
1793
|
+
# Issue #2987 — one names-mode listing row per bundle `sig/` layout ({Environment::BundleSigDiscovery.discover}'s
|
|
1794
|
+
# two globs). The signature rows list only the directories the loader read, so a gem that GAINS a `sig/`
|
|
1795
|
+
# (or loses it) while its version and lock entry stand still sits in none of them, and the run-result
|
|
1796
|
+
# slot kept serving the environment built without (or with) it.
|
|
1797
|
+
def bundle_sig_glob_entries
|
|
1798
|
+
root = Environment::BundleSigDiscovery.resolve_bundle_path(
|
|
1799
|
+
bundle_path: @configuration.bundler_bundle_path, project_root: Dir.pwd,
|
|
1800
|
+
auto_detect: @configuration.bundler_auto_detect
|
|
1801
|
+
)
|
|
1802
|
+
return [] if root.nil?
|
|
1803
|
+
|
|
1804
|
+
Environment::BundleSigDiscovery::SIG_GLOBS.map do |pattern|
|
|
1805
|
+
Cache::Descriptor::GlobEntry.compute(root: root.to_s, pattern: pattern, mode: :names)
|
|
1806
|
+
end
|
|
1807
|
+
end
|
|
1808
|
+
|
|
1641
1809
|
def unanalysed_pre_eval_entries(files)
|
|
1642
1810
|
analysed = files.to_set { |path| File.absolute_path(path) }
|
|
1643
1811
|
outside = @configuration.pre_eval.reject { |path| analysed.include?(File.absolute_path(path)) }
|
|
@@ -1658,6 +1826,7 @@ module Rigor
|
|
|
1658
1826
|
end
|
|
1659
1827
|
private :plugin_boundary_rows, :producer_watch_glob_entries, :kind_entries, :root_presence_entries,
|
|
1660
1828
|
:analysis_reads_attributed?, :signature_root_candidates, :discovery_glob_entries,
|
|
1829
|
+
:bundle_sig_glob_entries,
|
|
1661
1830
|
:unanalysed_pre_eval_entries, :signature_rows, :within?
|
|
1662
1831
|
|
|
1663
1832
|
# Issue #684 — the project files this run DISCOVERED but did not analyse. A widened run's diagnostics
|
|
@@ -2031,7 +2200,7 @@ module Rigor
|
|
|
2031
2200
|
rescue ArgumentError => e
|
|
2032
2201
|
floor = self.class.prism_supported_floor
|
|
2033
2202
|
Diagnostic.new(
|
|
2034
|
-
path:
|
|
2203
|
+
path: @configuration.config_display_path, line: 1, column: 1,
|
|
2035
2204
|
message: "target_ruby #{@configuration.target_ruby.inspect} is not supported by this Rigor build " \
|
|
2036
2205
|
"(Prism accepts #{floor} and newer). Set target_ruby to your project's Ruby version " \
|
|
2037
2206
|
"(>= #{floor}) — read it from Gemfile.lock's `RUBY VERSION` or .ruby-version. " \
|
|
@@ -2056,8 +2225,7 @@ module Rigor
|
|
|
2056
2225
|
# Editor mode § "Scope choice — option A". Under `buffer:` non-nil the per-file analysis emits
|
|
2057
2226
|
# diagnostics ONLY for the buffer's logical path; the rest of `paths:` is consumed by the
|
|
2058
2227
|
# project-wide pre-passes (synthetic methods, project-patched methods, plugin facts) but contributes
|
|
2059
|
-
# no per-file diagnostics.
|
|
2060
|
-
# (full project + incremental cache) is queued.
|
|
2228
|
+
# no per-file diagnostics. Option B (#146) narrows to the closure instead; see {#target_files}.
|
|
2061
2229
|
#
|
|
2062
2230
|
# The buffer's logical path is added to the file list even if it's not under `paths:` — per design §
|
|
2063
2231
|
# "Failure envelope": "--instead-of=Y with Y not under any paths: directory → treated as a valid
|
|
@@ -2090,7 +2258,13 @@ module Rigor
|
|
|
2090
2258
|
# The logical path joins even when it is not on disk under `paths:` (design § "Failure envelope"),
|
|
2091
2259
|
# the same allowance option A makes below.
|
|
2092
2260
|
files = files.select { |path| @analyze_only.include?(path) }
|
|
2093
|
-
|
|
2261
|
+
# Issue #2233 — a buffer that is a claimed template joins as its unit, appended by
|
|
2262
|
+
# {#template_unit_targets}, never as a `.rb` file: the logical path in its absolute spelling would
|
|
2263
|
+
# otherwise analyse the same template twice. The registry is loaded by now, which is why the test is here
|
|
2264
|
+
# and not in the incremental session, where it is not.
|
|
2265
|
+
if @buffer && @analyze_only.include?(@buffer.logical_path) && !template_path?(@buffer.logical_path)
|
|
2266
|
+
files |= [@buffer.logical_path]
|
|
2267
|
+
end
|
|
2094
2268
|
return template_unit_targets(files)
|
|
2095
2269
|
end
|
|
2096
2270
|
return template_unit_targets(files) if @buffer.nil?
|
|
@@ -2126,6 +2300,14 @@ module Rigor
|
|
|
2126
2300
|
end
|
|
2127
2301
|
public :template_unit_paths
|
|
2128
2302
|
|
|
2303
|
+
# Issue #2233 — whether `path` is a template of this run: a claimed glob matches it, or it names a unit. The
|
|
2304
|
+
# runner keeps the buffer's logical path out of the targets when this is true, so a template buffer is
|
|
2305
|
+
# analysed once, as a unit ({#template_unit_targets} appends it), and never also as a plain `.rb` file.
|
|
2306
|
+
def template_path?(path)
|
|
2307
|
+
template_units.claims?(path) ||
|
|
2308
|
+
template_unit_paths.include?(Analysis::TemplateUnitPaths.relative(path, Dir.pwd))
|
|
2309
|
+
end
|
|
2310
|
+
|
|
2129
2311
|
# Editor mode (`buffer:` non-nil) auto-flips the cache store to `read_only: true` so multiple
|
|
2130
2312
|
# debounced editor invocations against the same project don't churn the on-disk cache or race on
|
|
2131
2313
|
# schema-version writes. Cache reads continue unchanged; misses still run the producer block but the
|
|
@@ -2179,14 +2361,7 @@ module Rigor
|
|
|
2179
2361
|
pool_mode: -> { pool_mode? },
|
|
2180
2362
|
cached_plugin_prepare_diagnostics: -> { @cached_plugin_prepare_diagnostics },
|
|
2181
2363
|
pre_eval_diagnostics_from_scanner: -> { @pre_eval_diagnostics_from_scanner },
|
|
2182
|
-
|
|
2183
|
-
quarantined_signatures_snapshot: -> { @snapshots.quarantined_signatures },
|
|
2184
|
-
signature_standdowns_snapshot: -> { @snapshots.signature_standdowns },
|
|
2185
|
-
vendored_standdowns_snapshot: -> { @snapshots.vendored_standdowns },
|
|
2186
|
-
env_build_failure_snapshot: -> { @snapshots.env_build_failure },
|
|
2187
|
-
definition_build_failures_snapshot: -> { @snapshots.definition_build_failures },
|
|
2188
|
-
hkt_scan_failure_snapshot: -> { @snapshots.hkt_scan_failure },
|
|
2189
|
-
conformance_results_snapshot: -> { @snapshots.conformance_results },
|
|
2364
|
+
snapshots: @snapshots,
|
|
2190
2365
|
pooled_run_disclosures: -> { @pool_coordinator.collected_run_disclosures },
|
|
2191
2366
|
served_run_disclosures: -> { @served_run_disclosures },
|
|
2192
2367
|
pooled_trust_refusals: -> { @pool_coordinator.collected_trust_refusals }
|
|
@@ -2520,14 +2695,14 @@ module Rigor
|
|
|
2520
2695
|
@discovery_seed ? @discovery_seed.dup : {}
|
|
2521
2696
|
end
|
|
2522
2697
|
|
|
2523
|
-
# Seeds the
|
|
2524
|
-
#
|
|
2525
|
-
#
|
|
2698
|
+
# Seeds the parameter-inference and constant-publication tables, so a pooled `WorkerSession` scope resolves
|
|
2699
|
+
# identically to the sequential path (ADR-15 sequential equivalence). The parameter-inference table is absent
|
|
2700
|
+
# unless its opt-in ran; the constant tables are seeded whenever the run has project constants.
|
|
2526
2701
|
#
|
|
2527
2702
|
# - ADR-67 WD6a `param_inferred_types`: the call-site parameter-inference table, populated only when the
|
|
2528
2703
|
# `parameter_inference:` gate ran the pre-pass.
|
|
2529
|
-
# - Issue #352 `in_source_constants`: the `pre_eval:` constant publication,
|
|
2530
|
-
#
|
|
2704
|
+
# - Issue #352 `in_source_constants`: the `pre_eval:` constant publication, merged with the project's
|
|
2705
|
+
# literal constant table (#644, see {#merged_seed_constants}).
|
|
2531
2706
|
#
|
|
2532
2707
|
# Extracted to keep {#project_scope_seed_tables} under the complexity budget.
|
|
2533
2708
|
def seed_opt_in_pre_pass_tables(tables)
|
|
@@ -2655,6 +2830,14 @@ module Rigor
|
|
|
2655
2830
|
# `DependencyRecorder.active? == false`. Recording is observational, so diagnostics are byte-identical
|
|
2656
2831
|
# either way.
|
|
2657
2832
|
def analyze_file(path, environment)
|
|
2833
|
+
diagnostics = analyze_file_attributed(path, environment)
|
|
2834
|
+
return diagnostics unless Inference::MemoAudit.enabled?
|
|
2835
|
+
|
|
2836
|
+
# ADR-129 WD5 (#2785) — the audit's mismatches this file raised, a swallowed one included.
|
|
2837
|
+
CrashSignature.with_swallowed(path, diagnostics, Inference::MemoAudit.drain)
|
|
2838
|
+
end
|
|
2839
|
+
|
|
2840
|
+
def analyze_file_attributed(path, environment)
|
|
2658
2841
|
return analyze_with_effects(path, environment) unless @record_dependencies
|
|
2659
2842
|
|
|
2660
2843
|
# ADR-45 WD2 (#1507) — every plugin read this file's analysis makes is attributed to it, so the
|
|
@@ -2735,9 +2918,11 @@ module Rigor
|
|
|
2735
2918
|
# `self_call_misses` feeds the `call.self-undefined-method` collector; the recorder is inert unless
|
|
2736
2919
|
# the rule is active or `record_self_calls:` opted in.
|
|
2737
2920
|
index = nil
|
|
2921
|
+
TypeOfMemo.begin_file
|
|
2738
2922
|
self_call_record = with_self_call_recording(path) do
|
|
2739
2923
|
index = Inference::ScopeIndexer.index(parse_result.value, default_scope: scope, widen_clauses: true)
|
|
2740
2924
|
end
|
|
2925
|
+
TypeOfMemo.phase!(:rule)
|
|
2741
2926
|
self_call_misses = self_call_record ? self_call_record.calls : []
|
|
2742
2927
|
diagnostics = rule_and_plugin_diagnostics(path, parse_result, scope, index, self_call_misses)
|
|
2743
2928
|
notify_file_indexed(path, parse_result, scope, index)
|
|
@@ -2752,16 +2937,18 @@ module Rigor
|
|
|
2752
2937
|
severity: :error
|
|
2753
2938
|
)
|
|
2754
2939
|
]
|
|
2755
|
-
rescue StandardError => e
|
|
2940
|
+
rescue StandardError, SystemStackError => e
|
|
2756
2941
|
[
|
|
2757
2942
|
Diagnostic.new(
|
|
2758
2943
|
path: path,
|
|
2759
2944
|
line: 1,
|
|
2760
2945
|
column: 1,
|
|
2761
|
-
message: CrashSignature.
|
|
2946
|
+
message: CrashSignature.contained_message!(e, parse_result),
|
|
2762
2947
|
severity: :error
|
|
2763
2948
|
)
|
|
2764
2949
|
]
|
|
2950
|
+
ensure
|
|
2951
|
+
TypeOfMemo.end_file
|
|
2765
2952
|
end
|
|
2766
2953
|
|
|
2767
2954
|
# ADR-113 WD2 — the `on_file_indexed` observer. It reads; an error inside it never changes this file's
|
|
@@ -2778,7 +2965,9 @@ module Rigor
|
|
|
2778
2965
|
# output and the plugin diagnostics are then built from that single walk's results.
|
|
2779
2966
|
def rule_and_plugin_diagnostics(path, parse_result, scope, index, self_call_misses)
|
|
2780
2967
|
root = parse_result.value
|
|
2781
|
-
node_collectors = CheckRules.build_node_collectors(
|
|
2968
|
+
node_collectors = CheckRules.build_node_collectors(
|
|
2969
|
+
path, index, root, plugin_api_surface: CheckRules::PluginApiSurface.active?(@configuration)
|
|
2970
|
+
)
|
|
2782
2971
|
node_results = node_rule_results_by_plugin(path, root, scope, node_collectors, index)
|
|
2783
2972
|
diagnostics = CheckRules.diagnose(
|
|
2784
2973
|
path: path,
|