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
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../typing_state"
|
|
4
|
+
require_relative "../memo_audit"
|
|
5
|
+
|
|
3
6
|
module Rigor
|
|
4
7
|
# {Reflection}'s constant-ancestor walk ([#354](https://github.com/rigortype/rigor/issues/354)), split out for
|
|
5
8
|
# the reader rather than for the loader: the whole-spelling ladder's step 2 (`reflection.rb`) and the
|
|
@@ -7,13 +10,23 @@ module Rigor
|
|
|
7
10
|
# name's owner inherits constants from, and this file answers that once for both. Reopening the module
|
|
8
11
|
# keeps the walk behind the facade its callers already read.
|
|
9
12
|
module Reflection
|
|
10
|
-
# #354 —
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
# #354 — the per-run ancestor-scope memo. See {.ancestor_constant_scopes}. ADR-129 WD1 — a run-scoped Memo.
|
|
14
|
+
ANCESTOR_SCOPES = TypingState.memo(
|
|
15
|
+
:ancestor_constant_scopes,
|
|
16
|
+
run_scoped: true, audit: "ancestor constant scopes",
|
|
17
|
+
reason: "one [discovery, scopes by class name] slot keyed on the discovery index's identity, as the " \
|
|
18
|
+
"resolution chain the scopes are read from is (#2848)"
|
|
19
|
+
)
|
|
20
|
+
private_constant :ANCESTOR_SCOPES
|
|
21
|
+
|
|
22
|
+
# The recording run's slot. See {.recorded_ancestor_constant_scopes}. ADR-129 WD1 — a run-scoped Memo.
|
|
23
|
+
RECORDED_ANCESTOR_SCOPES = TypingState.memo(
|
|
24
|
+
:recorded_ancestor_constant_scopes,
|
|
25
|
+
run_scoped: true, audit: "recorded ancestor constant scopes",
|
|
26
|
+
reason: "one [discovery, scopes by class name] slot keyed on the discovery index's identity; a hit replays " \
|
|
27
|
+
"the chain's edges, so the key is exact"
|
|
28
|
+
)
|
|
29
|
+
private_constant :RECORDED_ANCESTOR_SCOPES
|
|
17
30
|
|
|
18
31
|
module_function
|
|
19
32
|
|
|
@@ -35,11 +48,13 @@ module Rigor
|
|
|
35
48
|
# constant it owns resolves only if the bare name reaches it at step 3: widening to the superclass's
|
|
36
49
|
# RBS graph is a separate question with its own FP surface. See {.external_constant_owner}.
|
|
37
50
|
#
|
|
38
|
-
# Memoised
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
51
|
+
# Memoised because step 2 runs on every constant reference whose lexical candidates all miss — which
|
|
52
|
+
# is the common case for a core-class reference (`String` inside `class Foo`). The bucket keys on the
|
|
53
|
+
# identity of the discovery index, as the `Scope::ResolutionChain` memo it reads does: the chain's
|
|
54
|
+
# order depends on the analysing file's own view of the include order (a class declared in two files
|
|
55
|
+
# orders its `include`s from the reader's file first), so a run-generation key served one file's
|
|
56
|
+
# order to another (#2848). A re-run in one process (LSP, ADR-62 warm loop) builds a new index, so it
|
|
57
|
+
# cannot hit stale entries either.
|
|
43
58
|
def ancestor_constant_scopes(class_name, scope)
|
|
44
59
|
ancestor_constant_worlds(class_name, scope).first
|
|
45
60
|
end
|
|
@@ -85,32 +100,76 @@ module Rigor
|
|
|
85
100
|
# every call.
|
|
86
101
|
return recorded_ancestor_constant_scopes(class_name, scope) if Analysis::DependencyRecorder.active?
|
|
87
102
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
# ADR-129 G2 — an audit recomputation bypasses the memo (neither read nor written).
|
|
104
|
+
if Inference::MemoAudit.enabled? && Inference::MemoAudit.suspended?
|
|
105
|
+
return compute_ancestor_constant_scopes(class_name, scope)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
discovery = scope.discovery
|
|
109
|
+
state = TypingState.current
|
|
110
|
+
slot = state[ANCESTOR_SCOPES]
|
|
111
|
+
unless slot && slot[0].equal?(discovery)
|
|
112
|
+
slot = [discovery, {}]
|
|
113
|
+
state[ANCESTOR_SCOPES] = slot
|
|
93
114
|
end
|
|
94
115
|
bucket = slot[1]
|
|
95
|
-
|
|
116
|
+
worlds = bucket[class_name]
|
|
117
|
+
if worlds.nil?
|
|
118
|
+
return bucket[class_name] = Inference::MemoAudit.key_audit("ancestor constant scopes") do
|
|
119
|
+
compute_ancestor_constant_scopes(class_name, scope)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
return worlds unless Inference::MemoAudit.enabled?
|
|
123
|
+
|
|
124
|
+
Inference::MemoAudit.check("ancestor constant scopes", audit_view(worlds), class_name) do
|
|
125
|
+
audit_view(compute_ancestor_constant_scopes(class_name, scope))
|
|
126
|
+
end
|
|
127
|
+
worlds
|
|
96
128
|
end
|
|
97
129
|
private_class_method :ancestor_constant_worlds
|
|
98
130
|
|
|
131
|
+
# `[scopes, chain]` compared by content: a recomputation builds a new chain object, equal to the stored one
|
|
132
|
+
# entry for entry when the key holds every input.
|
|
133
|
+
def audit_view(worlds) = [worlds[0], worlds[1].audit_signature]
|
|
134
|
+
private_class_method :audit_view
|
|
135
|
+
|
|
136
|
+
# A recomputation for the audit, its dependency reads withheld: the hit it checks files its own edges, so the
|
|
137
|
+
# recorded set stays what an unaudited run records.
|
|
138
|
+
def fresh_ancestor_constant_scopes(class_name, scope)
|
|
139
|
+
Analysis::DependencyRecorder.withhold { compute_ancestor_constant_scopes(class_name, scope) }.first
|
|
140
|
+
end
|
|
141
|
+
private_class_method :fresh_ancestor_constant_scopes
|
|
142
|
+
|
|
99
143
|
# The recording run's memo. The answer is every project entry of the chain, so its edges are the
|
|
100
144
|
# root's and every entry's — what `Scope::ResolutionChain#record` files, which a computation does and a
|
|
101
145
|
# hit replays, in the same order. One slot keyed on the discovery index: the answer and the edges are
|
|
102
146
|
# both read from it, so the key is exact, and the slot never pins more than the index it serves.
|
|
103
147
|
def recorded_ancestor_constant_scopes(class_name, scope)
|
|
148
|
+
# ADR-129 G2 — an audit recomputation bypasses the memo (neither read nor written).
|
|
149
|
+
if Inference::MemoAudit.enabled? && Inference::MemoAudit.suspended?
|
|
150
|
+
return compute_ancestor_constant_scopes(class_name, scope)
|
|
151
|
+
end
|
|
152
|
+
|
|
104
153
|
discovery = scope.discovery
|
|
105
|
-
|
|
154
|
+
state = TypingState.current
|
|
155
|
+
slot = state[RECORDED_ANCESTOR_SCOPES]
|
|
106
156
|
unless slot && slot[0].equal?(discovery)
|
|
107
157
|
slot = [discovery, {}]
|
|
108
|
-
|
|
158
|
+
state[RECORDED_ANCESTOR_SCOPES] = slot
|
|
109
159
|
end
|
|
110
160
|
bucket = slot[1]
|
|
111
161
|
worlds = bucket[class_name]
|
|
112
|
-
|
|
162
|
+
if worlds.nil?
|
|
163
|
+
return bucket[class_name] = Inference::MemoAudit.key_audit("recorded ancestor constant scopes") do
|
|
164
|
+
compute_ancestor_constant_scopes(class_name, scope)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
113
167
|
|
|
168
|
+
if Inference::MemoAudit.enabled?
|
|
169
|
+
Inference::MemoAudit.check("recorded ancestor constant scopes", audit_view(worlds), class_name) do
|
|
170
|
+
audit_view(fresh_ancestor_constant_scopes(class_name, scope))
|
|
171
|
+
end
|
|
172
|
+
end
|
|
114
173
|
chain = Scope::ResolutionChain.for(scope, class_name.to_s, :instance, :constants)
|
|
115
174
|
chain.record(scope)
|
|
116
175
|
record_external_owner_edges(chain)
|
|
@@ -228,7 +287,7 @@ module Rigor
|
|
|
228
287
|
end
|
|
229
288
|
private_class_method :ancestry_unknown_before?
|
|
230
289
|
|
|
231
|
-
# Ruby places these after every entry of a chain, so a constant they own is the top level's
|
|
290
|
+
# Ruby places these after every entry of a chain, so a constant they own is the top level's.
|
|
232
291
|
IMPLICIT_ANCESTORS = %w[Object Kernel BasicObject].freeze
|
|
233
292
|
private_constant :IMPLICIT_ANCESTORS
|
|
234
293
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
# Not required on its own: `rigor/inference/declaration_walk` loads it once the walk exists, and
|
|
6
|
+
# {Reflection.class_declarations_in} loads that entry point on first use. Keeping the walk out of `Reflection`'s own
|
|
7
|
+
# load keeps the engine-free paths engine-free (ADR-104).
|
|
8
|
+
module Rigor
|
|
9
|
+
module Reflection
|
|
10
|
+
# The walk's collector behind {.class_declarations_in}; the plugin never sees it.
|
|
11
|
+
class DeclarationCollector
|
|
12
|
+
include Inference::DeclarationWalk::Collector
|
|
13
|
+
|
|
14
|
+
EMPTY_NESTING = [].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :classes
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@classes = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def on_declaration(node, context, body_context)
|
|
23
|
+
if node.is_a?(Prism::ClassNode)
|
|
24
|
+
name = body_context.prefix.join("::")
|
|
25
|
+
@classes << ClassDeclaration.new(name: name, nesting: frozen_nesting(context), node: node) unless name.empty?
|
|
26
|
+
end
|
|
27
|
+
Inference::DeclarationWalk::DESCEND
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# The walk's chain, frozen: the root's `[]` is one array shared by every top-level declaration.
|
|
33
|
+
def frozen_nesting(context)
|
|
34
|
+
nesting = context.nesting || EMPTY_NESTING
|
|
35
|
+
nesting.frozen? ? nesting : nesting.dup.freeze
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# {DeclarationCollector} that also forwards every call. A subclass, because the walk dispatches only the events a
|
|
40
|
+
# collector's class overrides, so a classes-only run pays for no call event.
|
|
41
|
+
class DeclarationCallCollector < DeclarationCollector
|
|
42
|
+
def initialize(on_call)
|
|
43
|
+
super()
|
|
44
|
+
@on_call = on_call
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def on_call(node, context)
|
|
48
|
+
owner = context.prefix.join("::")
|
|
49
|
+
@on_call.call(DeclaredCall.new(node: node, owner: owner.empty? ? nil : owner))
|
|
50
|
+
Inference::DeclarationWalk::DESCEND
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
private_constant :DeclarationCollector, :DeclarationCallCollector
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
# {Reflection}'s per-file declaration walk for plugins (ADR-128, #2708): {.class_declarations_in} answers which
|
|
7
|
+
# classes a parsed file declares under their `Module.nesting`-correct names, and hands each call the walk visits
|
|
8
|
+
# to a plugin together with the class or module its position reaches. It replaces a plugin's own
|
|
9
|
+
# `DeclarationWalk::Collector`, so the walk's context, its control constants and its collector protocol stay
|
|
10
|
+
# internal.
|
|
11
|
+
module Reflection
|
|
12
|
+
# One `class` header of a file, as {.class_declarations_in} reports it.
|
|
13
|
+
#
|
|
14
|
+
# - `name` — the qualified name the header's body is keyed under (`"Admin::User"` for `class User` in
|
|
15
|
+
# `module Admin`, and for `class Admin::User`); never empty.
|
|
16
|
+
# - `nesting` — `Module.nesting` where the header is written, innermost first, not counting the class itself
|
|
17
|
+
# (`["Admin"]` for `class User` in `module Admin`; `[]` at the top level). Frozen.
|
|
18
|
+
# - `node` — the `Prism::ClassNode`, for its `superclass` and `body`.
|
|
19
|
+
ClassDeclaration = Data.define(:name, :nesting, :node)
|
|
20
|
+
|
|
21
|
+
# One call the walk visited, as {.class_declarations_in} yields it.
|
|
22
|
+
#
|
|
23
|
+
# - `node` — the `Prism::CallNode`.
|
|
24
|
+
# - `owner` — the qualified name of the innermost `class` / `module` the call is lexically inside, or nil where
|
|
25
|
+
# none is (the top level, or below a bare header in a `class << self` body).
|
|
26
|
+
DeclaredCall = Data.define(:node, :owner)
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
# ADR-128, #2708 — the classes a parsed file declares, in the order their headers are written. A class
|
|
31
|
+
# reopened in the file appears once per header. A `module`, a `class << self` and a class no name reaches
|
|
32
|
+
# (`Class.new { … }`, a header below `class << self`) are not reported.
|
|
33
|
+
#
|
|
34
|
+
# With a block, the walk also yields a {DeclaredCall} for the calls the engine's declaration walk visits, in
|
|
35
|
+
# source order, outer calls before the calls inside them. Those are the calls in statements and in the bodies of
|
|
36
|
+
# `class` / `module` / `class << self` / blocks, at any depth. The owner is lexical, not `self`: a call in
|
|
37
|
+
# `class << self` or in a `Class.new { … }` block reports the class around it.
|
|
38
|
+
#
|
|
39
|
+
# The block does not see every call node of the file. The walk does not enter, so the block never sees:
|
|
40
|
+
#
|
|
41
|
+
# - a class or module header's own expressions: the superclass (`class Foo < Struct.new(:a)`) and the constant
|
|
42
|
+
# path (`class foo::Bar`);
|
|
43
|
+
# - the factory call a constant write binds (`K = Class.new { … }`), whose receiver, arguments and block body the
|
|
44
|
+
# walk does enter;
|
|
45
|
+
# - the tail of such a write (`K = Class.new { … }.freeze` yields what is inside the block, not `freeze`), and
|
|
46
|
+
# the constant-path parent of a write target (`P` in `P::K = …`).
|
|
47
|
+
#
|
|
48
|
+
# `root` is a `Prism::Node`, typically `Prism.parse(source).value`.
|
|
49
|
+
def class_declarations_in(root, &on_call)
|
|
50
|
+
# Loaded on first use, not with `Reflection`: the walk heads the analysis engine, which the engine-free
|
|
51
|
+
# paths (a warm `rigor effects`) load `Reflection` without. The walk's entry point loads the collectors.
|
|
52
|
+
require_relative "../inference/declaration_walk"
|
|
53
|
+
collector = on_call ? DeclarationCallCollector.new(on_call) : DeclarationCollector.new
|
|
54
|
+
Inference::DeclarationWalk.run(root, [collector], Inference::DeclarationWalk::Context.root(nesting: []))
|
|
55
|
+
collector.classes
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
# {Reflection}'s ancestor facade for plugins (ADR-128 WD1), split out for the reader rather than for the
|
|
5
|
+
# loader: {.project_method_defined} turns the two `Scope` ancestor walks into one `Trinary` answer. Reopening
|
|
6
|
+
# the module keeps it behind the facade its callers already read.
|
|
7
|
+
module Reflection
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
PROJECT_METHOD_KINDS = %i[instance singleton].freeze
|
|
11
|
+
PROJECT_METHOD_SOURCES = %i[def discovered either].freeze
|
|
12
|
+
private_constant :PROJECT_METHOD_KINDS, :PROJECT_METHOD_SOURCES
|
|
13
|
+
|
|
14
|
+
# ADR-128 WD1 — does the project define `method_name` on `class_name` or on an ancestor the project itself
|
|
15
|
+
# declares? Answers a {Trinary}: `yes` where the walk found a definer, `no` where it found none and its
|
|
16
|
+
# `ResolutionChain` was not truncated, and `maybe` where the chain was truncated at its budget before it found one
|
|
17
|
+
# (ADR-41 WD4), so a definer past the cut cannot be ruled out. It replaces a plugin's direct call to the
|
|
18
|
+
# two `Scope` walks, whose budget answers differ and are not a promise to plugin authors.
|
|
19
|
+
#
|
|
20
|
+
# - `kind:` is `:instance` or `:singleton`; each walks its own chain.
|
|
21
|
+
# - `source:` names the walk outright, with that walk's order and settling:
|
|
22
|
+
# - `:def` — the first project `def` (`Scope#user_def_through_ancestors`, or `#singleton_def_through_ancestors`
|
|
23
|
+
# for `kind: :singleton`). It does not count `attr_*` or `define_method`.
|
|
24
|
+
# - `:discovered` — the discovery table (`Scope#discovered_method_through_ancestors?`), which also counts
|
|
25
|
+
# `attr_*` and `define_method`.
|
|
26
|
+
# - `:either` — their union as a three-valued OR: `yes` wins, then `maybe`, then `no`.
|
|
27
|
+
#
|
|
28
|
+
# A site that tested the `:def` walk's old answer tests `.yes?` (that walk read a cut as absent); a site that
|
|
29
|
+
# tested the `:discovered` walk's tests `!no?` (that walk read a cut as present).
|
|
30
|
+
def project_method_defined(class_name, method_name, kind:, source:, scope:)
|
|
31
|
+
raise ArgumentError, "unknown kind #{kind.inspect}" unless PROJECT_METHOD_KINDS.include?(kind)
|
|
32
|
+
raise ArgumentError, "unknown source #{source.inspect}" unless PROJECT_METHOD_SOURCES.include?(source)
|
|
33
|
+
|
|
34
|
+
case source
|
|
35
|
+
when :def then project_def_defined(class_name, method_name, kind, scope)
|
|
36
|
+
when :discovered then project_discovered_defined(class_name, method_name, kind, scope)
|
|
37
|
+
else project_either_defined(class_name, method_name, kind, scope)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Does RBS declare `method_name` on `class_name` or on an ancestor the project's own ancestry reaches? A
|
|
42
|
+
# project class (`class ArticlesController < ActionController::Base`) is rarely in RBS itself; its RBS-known
|
|
43
|
+
# ancestors are the EXTERNAL entries of its `ResolutionChain`, which the RBS-only reads
|
|
44
|
+
# ({.instance_method_definition}) cannot reach from the project class's name. This walks the chain and asks RBS
|
|
45
|
+
# of each entry (every candidate spelling of an external one), on the side the entry sits.
|
|
46
|
+
#
|
|
47
|
+
# Answers a {Trinary}: `yes` when an entry's RBS declares the method, `no` when none does and the chain was
|
|
48
|
+
# not truncated, `maybe` when the chain was cut at its budget (ADR-41 WD4) before one did. `kind:` is
|
|
49
|
+
# `:instance` or `:singleton`; a `nil` `class_name` answers `no`.
|
|
50
|
+
def rbs_method_defined(class_name, method_name, kind:, scope:)
|
|
51
|
+
chain = ancestry_chain(class_name, kind, scope)
|
|
52
|
+
return Trinary.no if chain.nil?
|
|
53
|
+
|
|
54
|
+
found = chain.search(scope) do |entry|
|
|
55
|
+
chain_entry_names(entry).any? { |name| !rbs_method_on_side(name, method_name, entry.side, scope).nil? }
|
|
56
|
+
end
|
|
57
|
+
chain_answer(found, chain)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Does the project ancestry of `class_name` reach any of `ancestor_names` (qualified names, a leading `::`
|
|
61
|
+
# ignored)? An external ancestor counts by any candidate spelling, so a superclass the project names but
|
|
62
|
+
# does not declare (`ActionController::Base`, with no RBS loaded) is found. Same `Trinary` and `kind:` as
|
|
63
|
+
# {.rbs_method_defined}.
|
|
64
|
+
def ancestry_reaches(class_name, ancestor_names, kind:, scope:)
|
|
65
|
+
chain = ancestry_chain(class_name, kind, scope)
|
|
66
|
+
return Trinary.no if chain.nil?
|
|
67
|
+
|
|
68
|
+
wanted = ancestor_names.map { |name| name.to_s.delete_prefix("::") }
|
|
69
|
+
found = chain.search(scope) do |entry|
|
|
70
|
+
chain_entry_names(entry).any? { |name| wanted.include?(name.to_s.delete_prefix("::")) }
|
|
71
|
+
end
|
|
72
|
+
chain_answer(found, chain)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def ancestry_chain(class_name, kind, scope)
|
|
76
|
+
raise ArgumentError, "unknown kind #{kind.inspect}" unless PROJECT_METHOD_KINDS.include?(kind)
|
|
77
|
+
return nil if class_name.nil?
|
|
78
|
+
|
|
79
|
+
Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
|
|
80
|
+
end
|
|
81
|
+
private_class_method :ancestry_chain
|
|
82
|
+
|
|
83
|
+
def chain_answer(found, chain)
|
|
84
|
+
return Trinary.yes if found
|
|
85
|
+
|
|
86
|
+
chain.truncated? ? Trinary.maybe : Trinary.no
|
|
87
|
+
end
|
|
88
|
+
private_class_method :chain_answer
|
|
89
|
+
|
|
90
|
+
def chain_entry_names(entry)
|
|
91
|
+
entry.external? ? entry.candidates : [entry.name]
|
|
92
|
+
end
|
|
93
|
+
private_class_method :chain_entry_names
|
|
94
|
+
|
|
95
|
+
def rbs_method_on_side(class_name, method_name, side, scope)
|
|
96
|
+
if side == :singleton
|
|
97
|
+
singleton_method_definition(class_name, method_name, scope: scope)
|
|
98
|
+
else
|
|
99
|
+
instance_method_definition(class_name, method_name, scope: scope)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
private_class_method :rbs_method_on_side
|
|
103
|
+
|
|
104
|
+
# `yes` wins, then `maybe`, then `no`. The early return is a short-circuit and carries no answer of its own:
|
|
105
|
+
# `yes.or(x)` is `yes`, so removing it changes only which walks run, and so which dependencies they read.
|
|
106
|
+
def project_either_defined(class_name, method_name, kind, scope)
|
|
107
|
+
by_def = project_def_defined(class_name, method_name, kind, scope)
|
|
108
|
+
return by_def if by_def.yes?
|
|
109
|
+
|
|
110
|
+
by_def.or(project_discovered_defined(class_name, method_name, kind, scope))
|
|
111
|
+
end
|
|
112
|
+
private_class_method :project_either_defined
|
|
113
|
+
|
|
114
|
+
# The def walk answers `[node, owner]` or `[nil, nil]`, and `[nil, nil]` both when the chain holds no definer
|
|
115
|
+
# and when it was cut; the chain (memoised, so this is the one the walk read) tells the two apart.
|
|
116
|
+
def project_def_defined(class_name, method_name, kind, scope)
|
|
117
|
+
found = if kind == :singleton
|
|
118
|
+
scope.singleton_def_through_ancestors(class_name, method_name)
|
|
119
|
+
else
|
|
120
|
+
scope.user_def_through_ancestors(class_name, method_name)
|
|
121
|
+
end
|
|
122
|
+
return Trinary.yes if found.first
|
|
123
|
+
|
|
124
|
+
project_chain_truncated?(class_name, kind, scope) ? Trinary.maybe : Trinary.no
|
|
125
|
+
end
|
|
126
|
+
private_class_method :project_def_defined
|
|
127
|
+
|
|
128
|
+
# The discovery walk answers `true` both for a definer and for a cut chain, so a `true` on a cut chain is
|
|
129
|
+
# settled by searching the chain's entries for a definer, as the walk does.
|
|
130
|
+
#
|
|
131
|
+
# Issue #2968 — where the walk finds none, a class on the chain whose side is open (an open-names mark: a
|
|
132
|
+
# splatted `Struct.new(*FIELDS)`, `attr_accessor(*NAMES)`, a `define_method` under a computed name) answers
|
|
133
|
+
# `maybe`: it defines methods under names no table holds. Issue #2994 — so does an instance side whose chain mixes
|
|
134
|
+
# in a module no table names (`include(*MODS)`, `ResolutionChain#unnamed_mixin?`); the #618 top-level-`def` veto
|
|
135
|
+
# (`ExpressionTyper#instance_self_answers?`) reads the same mark, so a plugin that stands down here does not hand
|
|
136
|
+
# the call to a top-level `def`.
|
|
137
|
+
def project_discovered_defined(class_name, method_name, kind, scope)
|
|
138
|
+
unless scope.discovered_method_through_ancestors?(class_name, method_name, kind)
|
|
139
|
+
return open_side?(class_name, kind, scope) ? Trinary.maybe : Trinary.no
|
|
140
|
+
end
|
|
141
|
+
# A shortcut, not a decision: on an untruncated chain the search below finds the same definer.
|
|
142
|
+
return Trinary.yes unless project_chain_truncated?(class_name, kind, scope)
|
|
143
|
+
|
|
144
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
|
|
145
|
+
found = chain.search(scope) do |entry|
|
|
146
|
+
# A guard, not a decision: it mirrors `Scope#discovered_method_through_ancestors?`, whose search skips
|
|
147
|
+
# an external entry (no project name to look up). `discovered_method?(nil, ...)` is false anyway.
|
|
148
|
+
next false if entry.external?
|
|
149
|
+
|
|
150
|
+
scope.discovered_method?(entry.name, method_name, discovered_entry_kind(entry, kind))
|
|
151
|
+
end
|
|
152
|
+
found ? Trinary.yes : Trinary.maybe
|
|
153
|
+
end
|
|
154
|
+
private_class_method :project_discovered_defined
|
|
155
|
+
|
|
156
|
+
def open_side?(class_name, kind, scope)
|
|
157
|
+
return true if Scope::DiscoveryIndex.open_names_on_chain?(scope, class_name, kind)
|
|
158
|
+
|
|
159
|
+
kind == :instance && Scope::DiscoveryIndex.unnamed_mixin_on_chain?(scope, class_name)
|
|
160
|
+
end
|
|
161
|
+
private_class_method :open_side?
|
|
162
|
+
|
|
163
|
+
# A singleton-side entry reads the singleton table; a module on the singleton chain answers with its
|
|
164
|
+
# INSTANCE methods; on the instance chain the caller's kind stands (`Scope#kind_for`).
|
|
165
|
+
def discovered_entry_kind(entry, kind)
|
|
166
|
+
return :singleton if entry.side == :singleton
|
|
167
|
+
|
|
168
|
+
kind == :singleton ? :instance : kind
|
|
169
|
+
end
|
|
170
|
+
private_class_method :discovered_entry_kind
|
|
171
|
+
|
|
172
|
+
def project_chain_truncated?(class_name, kind, scope)
|
|
173
|
+
Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods).truncated?
|
|
174
|
+
end
|
|
175
|
+
private_class_method :project_chain_truncated?
|
|
176
|
+
end
|
|
177
|
+
end
|
data/lib/rigor/reflection.rb
CHANGED
|
@@ -511,3 +511,5 @@ end
|
|
|
511
511
|
# private candidate lookups, so the facade has to exist first.
|
|
512
512
|
require_relative "reflection/constant_ancestors"
|
|
513
513
|
require_relative "reflection/constant_path"
|
|
514
|
+
require_relative "reflection/project_method"
|
|
515
|
+
require_relative "reflection/declarations"
|
data/lib/rigor/rooted_glob.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Rigor
|
|
|
20
20
|
|
|
21
21
|
module_function
|
|
22
22
|
|
|
23
|
-
# @param root — a directory path
|
|
23
|
+
# @param root — a directory path, taken literally
|
|
24
24
|
# @param pattern — a `Dir.glob` pattern relative to `root`
|
|
25
25
|
# @return the matches spelled under `root`, in `Dir.glob` order; empty when `root` is not a directory
|
|
26
26
|
def glob(root, pattern)
|
|
@@ -32,5 +32,36 @@ module Rigor
|
|
|
32
32
|
def literal(name)
|
|
33
33
|
name.to_s.gsub(METACHARACTERS, "\\\\\\0")
|
|
34
34
|
end
|
|
35
|
+
|
|
36
|
+
# A configured file path (#2222), absolute or relative to the working directory, as the `[root, pattern]` pair
|
|
37
|
+
# that `glob(root, pattern)` reads as exactly that file. A relative path stays under the working directory, as
|
|
38
|
+
# it always did. An absolute one is split into its own directory and a literal basename: `File.join(".", "/abs")`
|
|
39
|
+
# reads `./abs`, so an absolute path joined under `"."` matched nothing.
|
|
40
|
+
#
|
|
41
|
+
# A `~`-prefixed path is expanded before the split (#2586): the readers apply `File.expand_path`, so `~/x.yml`
|
|
42
|
+
# reads `$HOME/x.yml` and the watch must be split from that spelling — `[".", "~/x.yml"]` would watch
|
|
43
|
+
# `./~/x.yml` and never fire. Only the `~` form is expanded; expanding every path would change how relative
|
|
44
|
+
# entries are watched.
|
|
45
|
+
#
|
|
46
|
+
# @param path — a file path, taken literally
|
|
47
|
+
# @return `[root, pattern]`: `"."` and the literal path when relative, else the directory and the literal basename
|
|
48
|
+
def split_file(path)
|
|
49
|
+
path = expand_home(path.to_s)
|
|
50
|
+
return [".", literal(path)] unless File.absolute_path?(path)
|
|
51
|
+
|
|
52
|
+
[File.dirname(path), literal(File.basename(path))]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# `File.expand_path`'s `~` handling, kept literal on failure: `~nouser/x` (or an unset HOME) names no home
|
|
56
|
+
# directory and raises `ArgumentError`; the readers fail on the same path, so the unexpanded spelling —
|
|
57
|
+
# watched under `"."` like any other relative entry — is the consistent answer.
|
|
58
|
+
def expand_home(path)
|
|
59
|
+
return path unless path.start_with?("~")
|
|
60
|
+
|
|
61
|
+
File.expand_path(path)
|
|
62
|
+
rescue ArgumentError
|
|
63
|
+
path
|
|
64
|
+
end
|
|
65
|
+
private_class_method :expand_home
|
|
35
66
|
end
|
|
36
67
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require_relative "typing_state"
|
|
5
|
+
require_relative "memo_audit"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
# Issues #2412 and #2399 — the run-end hook for per-thread state whose contents belong to one analysis run. An
|
|
9
|
+
# owner declares such a slot in {TypingState} with `run_scoped: true` where it names it, and each entry point that
|
|
10
|
+
# runs an analysis calls {.release} in its `ensure`, so a finished run, or one that raised, leaves none of its
|
|
11
|
+
# scopes, discovery tables or environment behind on the thread.
|
|
12
|
+
#
|
|
13
|
+
# A slot that holds its run until the next run overwrites it retains one run per thread. A slot whose value
|
|
14
|
+
# reaches its own key retains every run, as `LoopValueRecord`'s `ObjectSpace::WeakKeyMap` did: the map holds
|
|
15
|
+
# its values strongly, and a recorded scope reaches the parse tree that holds the key node. Releasing at run end
|
|
16
|
+
# bounds both. Inside a run nothing changes: every memo keeps its key and its hit rate, and a slot read outside
|
|
17
|
+
# a run (a language-server hover on the main thread) refills on its first miss.
|
|
18
|
+
#
|
|
19
|
+
# Only Tables, Memos and Resources are run-scoped. A Frame a `with_*` block installs and restores is already empty
|
|
20
|
+
# when the frame returns, and clearing it from inside a frame would break the frame.
|
|
21
|
+
#
|
|
22
|
+
# ADR-129 — run-scoped state is a `run_scoped: true` {TypingState} slot, and nothing else registers: this module
|
|
23
|
+
# keeps no key list of its own.
|
|
24
|
+
module RunScopedSlots
|
|
25
|
+
# Empties every run-scoped {TypingState} slot on the calling thread.
|
|
26
|
+
#
|
|
27
|
+
# ADR-129 WD5 (#2785) — with the memo audit on, a mismatch no file drained (one raised by typing outside the
|
|
28
|
+
# per-file loop: return summaries, return re-evaluation, sig-gen) would be released here unreported, so it is
|
|
29
|
+
# raised after the release, unless an exception is already leaving the run.
|
|
30
|
+
def self.release
|
|
31
|
+
undrained = Inference::MemoAudit.enabled? ? Inference::MemoAudit.drain : nil
|
|
32
|
+
TypingState.release_run_scoped
|
|
33
|
+
raise undrained.first if undrained && !undrained.empty? && $ERROR_INFO.nil?
|
|
34
|
+
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/rigor/runtime/jit.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Rigor
|
|
|
37
37
|
# *remaining* deadline rather than restarting the window; see its own
|
|
38
38
|
# comment for why that distinction is worth the bookkeeping.
|
|
39
39
|
module Jit
|
|
40
|
-
# Opt-out switch: `RIGOR_DISABLE_YJIT=1` disables
|
|
40
|
+
# Opt-out switch: `RIGOR_DISABLE_YJIT=1` disables every entry point.
|
|
41
41
|
DISABLE_ENV = "RIGOR_DISABLE_YJIT"
|
|
42
42
|
|
|
43
43
|
# Advanced / calibration override of the {enable_after} deadline, in
|