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
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../analysis/dependency_recorder"
|
|
4
|
+
require_relative "../../typing_state"
|
|
5
|
+
require_relative "../../memo_audit"
|
|
6
|
+
require_relative "../../type"
|
|
7
|
+
require_relative "../census_foreign_block"
|
|
8
|
+
require_relative "../ivar_writer_census"
|
|
9
|
+
|
|
10
|
+
module Rigor
|
|
11
|
+
module Inference
|
|
12
|
+
module ScopeIndexer
|
|
13
|
+
# Issues #2079 and #2170 — the instance-side ivar writes the class-ivar census of one file does not walk, joined
|
|
14
|
+
# into the ivars that census already holds for a class `K`:
|
|
15
|
+
#
|
|
16
|
+
# - the writes another file's reopening of `K` makes;
|
|
17
|
+
# - the writes of every project module and superclass on `K`'s instance resolution chain (`include`,
|
|
18
|
+
# `prepend`, transitively, and `<`; for a module `K`, the modules it includes or prepends), from this file's
|
|
19
|
+
# own census of that ancestor where it declares one (`writes`, taken before the read-before-write `nil` is
|
|
20
|
+
# added, so an ancestor's `initialize` exempts nothing and its read-before-write adds nothing, and only for an
|
|
21
|
+
# ivar an instance body of the ancestor writes in this file) and from every other file's entries for it.
|
|
22
|
+
#
|
|
23
|
+
# Another file's write comes from its `[:own_write_type, owner, ivar, value, path]` census entry
|
|
24
|
+
# ({IvarWriterCensus}): a literal joins as its `Constant`, anything else as `Dynamic[top]`. An ivar the census
|
|
25
|
+
# has no entry for gains none, so a read of an ivar only another file writes still reads `Dynamic[top]`. The
|
|
26
|
+
# join only widens, and every joined type is in the seed, so a `nil` it brings is declaration-sourced (ADR-58
|
|
27
|
+
# WD1). A file the scope names no path for (a census built outside the project pre-pass, `rigor sig-gen`'s
|
|
28
|
+
# unseeded scopes) joins nothing.
|
|
29
|
+
#
|
|
30
|
+
# ADR-46: each ivar read files `own_write_type:<owner>#<ivar>` for `K` and each chain owner, which the
|
|
31
|
+
# incremental session fires when a file's entries for the pair appear, vanish or change value; and the chain
|
|
32
|
+
# files its class edges ({Scope::ResolutionChain#record}), so an `include` added or removed re-checks the reader.
|
|
33
|
+
module IvarContributions
|
|
34
|
+
# ADR-129 WD1 — a run-scoped Memo: the table is a pure function of the census it is keyed on by identity.
|
|
35
|
+
TABLE = TypingState.memo(
|
|
36
|
+
:ivar_contributions_table,
|
|
37
|
+
run_scoped: true, audit: "ivar contributions table",
|
|
38
|
+
reason: "the contribution table of one census; keyed on the census's identity"
|
|
39
|
+
)
|
|
40
|
+
EMPTY = {}.freeze
|
|
41
|
+
NONE = [].freeze
|
|
42
|
+
BINDINGS = :bindings
|
|
43
|
+
CANDIDATE_KEYS = :candidate_keys
|
|
44
|
+
ANCESTOR_ROOTS = CensusForeignBlock::ANCESTOR_ROOTS.to_a.freeze
|
|
45
|
+
# The one implicit root a class whose superclass chain stops at `BasicObject` keeps.
|
|
46
|
+
BASIC_ROOTS = %w[BasicObject].freeze
|
|
47
|
+
SEPARATOR = CensusForeignBlock::CANDIDATE_SEPARATOR
|
|
48
|
+
OBJECT_MARK = CensusForeignBlock::OBJECT_MARK
|
|
49
|
+
private_constant :TABLE, :EMPTY, :NONE, :BINDINGS, :CANDIDATE_KEYS, :ANCESTOR_ROOTS, :BASIC_ROOTS, :SEPARATOR,
|
|
50
|
+
:OBJECT_MARK
|
|
51
|
+
|
|
52
|
+
module_function
|
|
53
|
+
|
|
54
|
+
def join!(accumulator, writes, scope)
|
|
55
|
+
path = scope.source_path
|
|
56
|
+
return if path.nil?
|
|
57
|
+
|
|
58
|
+
path = canonical(path)
|
|
59
|
+
|
|
60
|
+
table = table(scope.discovered_global_write_census)
|
|
61
|
+
resolution = nil
|
|
62
|
+
accumulator.each do |class_name, entries|
|
|
63
|
+
next unless class_name.is_a?(String) && !entries.empty?
|
|
64
|
+
|
|
65
|
+
resolution ||= memoized_resolution(scope, table)
|
|
66
|
+
owners = chain_owners(scope, class_name, resolution)
|
|
67
|
+
any = any_ivar_types(owners, table)
|
|
68
|
+
entries.each_key do |ivar|
|
|
69
|
+
joined = contributed(owners, class_name, ivar, table, writes, path)
|
|
70
|
+
joined = joined ? Type::Combinator.union(joined, any) : any if any
|
|
71
|
+
entries[ivar] = Type::Combinator.union(entries[ivar], joined) if joined
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# `K` and the project classes and modules on its instance chain, in chain order, with their class edges filed.
|
|
77
|
+
# A module's chain holds the modules it includes or prepends (#2487): each is on the chain of every class that
|
|
78
|
+
# includes the module, so its writes land on every object a read in the module's own methods sees. `extend`
|
|
79
|
+
# is no instance chain entry, for a module as for a class.
|
|
80
|
+
#
|
|
81
|
+
# Issue #2983 — the chain is read under the `:constants` flavor, so a project class or module with no method of
|
|
82
|
+
# its own (`class Anc; end`, whose writer a `class_eval` installs) is an owner too. {ANCESTOR_ROOTS}
|
|
83
|
+
# (`Object`, `BasicObject`, `Kernel`) follow, as every instance has them, except that a class whose superclass
|
|
84
|
+
# chain stops at `BasicObject` keeps `BasicObject` alone; then each owner's alias and instance constants
|
|
85
|
+
# (`[:binds_class, …]` entries), and every candidate key that resolves to an owner ({.resolution}), under which
|
|
86
|
+
# a `class_eval` rooted at that constant files its code.
|
|
87
|
+
def chain_owners(scope, class_name, resolution)
|
|
88
|
+
ancestors, reaches_object = ancestor_names(scope, class_name)
|
|
89
|
+
names = [class_name, *ancestors, *(reaches_object ? ANCESTOR_ROOTS : BASIC_ROOTS)].uniq
|
|
90
|
+
aliases, bound = resolution
|
|
91
|
+
bound_names = names.flat_map do |name|
|
|
92
|
+
segment = name.split("::").last
|
|
93
|
+
Analysis::DependencyRecorder.read_name(:binds_class, segment) if Analysis::DependencyRecorder.active?
|
|
94
|
+
bound[name] || NONE
|
|
95
|
+
end
|
|
96
|
+
(names + bound_names).uniq.flat_map { |owner| [owner, *(aliases[owner] || NONE)] }.uniq
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The project ancestors on `class_name`'s instance chain, and whether the chain reaches `Object`: false when a
|
|
100
|
+
# superclass edge names `BasicObject`, which has neither `Object` nor `Kernel` among its ancestors.
|
|
101
|
+
def ancestor_names(scope, class_name)
|
|
102
|
+
chain = Scope::ResolutionChain.for(scope, class_name, :instance, :constants)
|
|
103
|
+
# A guard: the class body's own `include` / `<` resolution already files these edges; kept so the read
|
|
104
|
+
# states its own dependency.
|
|
105
|
+
chain.record(scope) if Analysis::DependencyRecorder.active?
|
|
106
|
+
names = chain.entries.filter_map do |entry|
|
|
107
|
+
# An external entry has no name, so `filter_map` drops it. The side check is a guard: an instance chain
|
|
108
|
+
# holds only instance-side entries.
|
|
109
|
+
entry.name if entry.side == :instance && entry.name != class_name
|
|
110
|
+
end
|
|
111
|
+
[names, chain.entries.none? { |entry| basic_object_edge?(entry) }]
|
|
112
|
+
rescue StandardError
|
|
113
|
+
[NONE, true]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def basic_object_edge?(entry)
|
|
117
|
+
entry.external? && entry.superclass_edge &&
|
|
118
|
+
(entry.raw.to_s.delete_prefix("::") == "BasicObject" || entry.candidates.include?("BasicObject"))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Review round 1 of #3001 — the candidate keys ({CensusForeignBlock.candidate_key}) resolved against the
|
|
122
|
+
# project: each to its first candidate the project declares as a class or module, or binds as a constant, else
|
|
123
|
+
# to its last (top-level) spelling, as Ruby's lexical lookup finds the nearest. Answers `[aliases, bound]`:
|
|
124
|
+
# `{ resolved owner => [candidate key, ...] }` over the census's owners, and `{ resolved class => [bound
|
|
125
|
+
# constant, ...] }` over its `[:binds_class, …]` entries.
|
|
126
|
+
#
|
|
127
|
+
# Review round 2: a candidate a signature declares (`sig/`, a gem's RBS) counts as existing too, so a nearer
|
|
128
|
+
# `N::Anc` only RBS knows wins over a top-level project `Anc` and credits no project class; and a key carrying
|
|
129
|
+
# {OBJECT_MARK} joins only when it resolves to an instance binding.
|
|
130
|
+
def resolution(scope, table)
|
|
131
|
+
bindings = table[BINDINGS] || NONE
|
|
132
|
+
constants = bindings.to_set { |_, name, _| name }
|
|
133
|
+
instances = bindings.filter_map { |_, name, kind| name if kind == :instance }.to_set
|
|
134
|
+
bound = {}
|
|
135
|
+
bindings.each { |key, name, _| (bound[resolve(scope, key, constants)] ||= []) << name }
|
|
136
|
+
aliases = {}
|
|
137
|
+
(table[CANDIDATE_KEYS] || NONE).each do |key|
|
|
138
|
+
resolved = resolve(scope, key.delete_prefix(OBJECT_MARK), constants)
|
|
139
|
+
next if key.start_with?(OBJECT_MARK) && (!instances.include?(resolved) || module?(scope, resolved))
|
|
140
|
+
|
|
141
|
+
(aliases[resolved] ||= []) << key
|
|
142
|
+
end
|
|
143
|
+
[aliases, bound]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def resolve(scope, key, constants)
|
|
147
|
+
candidates = key.split(SEPARATOR)
|
|
148
|
+
candidates.find do |name|
|
|
149
|
+
constants.include?(name) || scope.discovered_classes.key?(name) ||
|
|
150
|
+
scope.discovered_superclasses.key?(name) || scope.discovered_includes.key?(name) || declared?(scope, name)
|
|
151
|
+
end || candidates.last
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Review round 3 of #3001 — {.resolution} for `table`, computed once per run: it reads only the census and the
|
|
155
|
+
# project-wide declaration tables and signatures, which no file changes within a run. The value rides the
|
|
156
|
+
# {TABLE} slot beside the table it was computed from, keyed on the discovery index it read.
|
|
157
|
+
def memoized_resolution(scope, table)
|
|
158
|
+
slot = TypingState.current[TABLE]
|
|
159
|
+
return resolution(scope, table) unless slot && slot[1].equal?(table)
|
|
160
|
+
|
|
161
|
+
discovery = scope.discovery
|
|
162
|
+
cached = slot[2]
|
|
163
|
+
return cached[1] if cached && cached[0].equal?(discovery)
|
|
164
|
+
|
|
165
|
+
value = resolution(scope, table)
|
|
166
|
+
slot[2] = [discovery, value]
|
|
167
|
+
value
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Review round 3 of #3001 — whether `name` is a class or module the project or a signature declares, whose
|
|
171
|
+
# `.class` / `.singleton_class` no instance of it runs, whatever a constructor binding says. A guard beside
|
|
172
|
+
# {CensusForeignBlock.binding_entry}'s meta-class check, which records no such binding to begin with.
|
|
173
|
+
def module?(scope, name)
|
|
174
|
+
scope.discovered_classes.key?(name) || scope.discovered_superclasses.key?(name) ||
|
|
175
|
+
scope.discovered_includes.key?(name) || scope.environment&.rbs_loader&.class_known?(name) || false
|
|
176
|
+
rescue StandardError
|
|
177
|
+
true
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Whether a loaded signature declares `name` as a class, module or constant.
|
|
181
|
+
def declared?(scope, name)
|
|
182
|
+
loader = scope.environment&.rbs_loader
|
|
183
|
+
return false if loader.nil?
|
|
184
|
+
|
|
185
|
+
loader.class_known?(name) || !loader.constant_declaration(name).nil?
|
|
186
|
+
rescue StandardError
|
|
187
|
+
false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Issue #2983 — what the owners' string `class_eval`s contribute to every ivar (`[:own_write_type, owner, :*,
|
|
191
|
+
# :dynamic]`), from every file, this one included: the per-file census reads no String either.
|
|
192
|
+
def any_ivar_types(owners, table)
|
|
193
|
+
types = []
|
|
194
|
+
owners.each do |owner|
|
|
195
|
+
# A guard: a String `class_eval` also gives its module the discovery index's dynamic mark, whose edge
|
|
196
|
+
# re-checks the reader when one comes or goes; kept so the read states its own dependency.
|
|
197
|
+
if Analysis::DependencyRecorder.active?
|
|
198
|
+
Analysis::DependencyRecorder.read_name(:own_write_type, "#{owner}##{IvarWriterCensus::ANY_IVAR}")
|
|
199
|
+
end
|
|
200
|
+
add_entry_types(types, table.dig(owner, IvarWriterCensus::ANY_IVAR) || EMPTY, nil)
|
|
201
|
+
end
|
|
202
|
+
types.empty? ? nil : Type::Combinator.union(*types)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# The union of what the owners contribute to `ivar`, or nil for nothing.
|
|
206
|
+
def contributed(owners, class_name, ivar, table, writes, path)
|
|
207
|
+
types = []
|
|
208
|
+
owners.each do |owner|
|
|
209
|
+
if Analysis::DependencyRecorder.active?
|
|
210
|
+
Analysis::DependencyRecorder.read_name(:own_write_type, "#{owner}##{ivar}")
|
|
211
|
+
end
|
|
212
|
+
by_path = table.dig(owner, ivar) || EMPTY
|
|
213
|
+
# This file's census of an ancestor counts only an ivar an instance body of it writes here. `writes` leaves
|
|
214
|
+
# out an ivar a `def self.` of the ancestor also writes (the census files that write under it, #1958, and it
|
|
215
|
+
# runs on the module object); this file's census entries for it join instead, as in another file (#2931).
|
|
216
|
+
own = writes.dig(owner, ivar) if owner != class_name && by_path.key?(path)
|
|
217
|
+
types << own if own
|
|
218
|
+
add_entry_types(types, by_path, own || owner == class_name ? path : nil)
|
|
219
|
+
end
|
|
220
|
+
types.empty? ? nil : Type::Combinator.union(*types)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Each census entry's value type, except those of `skipped_path` (the file whose census already holds them).
|
|
224
|
+
def add_entry_types(types, by_path, skipped_path)
|
|
225
|
+
by_path.each do |value_path, values|
|
|
226
|
+
next if value_path == skipped_path
|
|
227
|
+
|
|
228
|
+
values.each { |value| types << value_type(value) }
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# A String literal joins as `String`, not its `Constant`: a write elsewhere cannot see an in-place mutation of
|
|
233
|
+
# the String (`@s << "abc"`) in this file, in another or in an ancestor, so the value would be pinned too
|
|
234
|
+
# narrowly. Every other literal kind is immutable and joins as its `Constant`.
|
|
235
|
+
def value_type(value)
|
|
236
|
+
return Type::Combinator.untyped unless value.is_a?(Array)
|
|
237
|
+
|
|
238
|
+
literal = value[1]
|
|
239
|
+
literal.is_a?(String) ? Type::Combinator.nominal_of("String") : Type::Combinator.constant_of(literal)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# `path` resolved through symlinks, so a file the pre-pass tagged under one spelling (`/tmp/…`) is the one
|
|
243
|
+
# analysed under another (`/private/tmp/…`); the expanded spelling when it cannot be resolved.
|
|
244
|
+
def canonical(path)
|
|
245
|
+
File.realpath(path)
|
|
246
|
+
rescue SystemCallError
|
|
247
|
+
File.expand_path(path)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# `{ owner => { ivar => { path => [value, ...] } } }` over `census`'s path-tagged `:own_write_type` entries,
|
|
251
|
+
# built once per census and kept in one run-scoped slot keyed by its identity.
|
|
252
|
+
def table(census)
|
|
253
|
+
return build_table(census) if MemoAudit.enabled? && MemoAudit.suspended?
|
|
254
|
+
|
|
255
|
+
state = TypingState.current
|
|
256
|
+
slot = state[TABLE]
|
|
257
|
+
if slot && slot[0].equal?(census)
|
|
258
|
+
return MemoAudit.check("ivar contributions table", slot[1], census.size) { build_table(census) }
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
table = MemoAudit.key_audit("ivar contributions table") { build_table(census) }
|
|
262
|
+
state[TABLE] = [census, table]
|
|
263
|
+
table
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Issue #2983 — `table[BINDINGS]` lists `[class candidate key, constant name, kind]` over the census's
|
|
267
|
+
# `[:binds_class, …]` entries, and `table[CANDIDATE_KEYS]` the owners that are candidate keys; no class is named
|
|
268
|
+
# by a Symbol.
|
|
269
|
+
def build_table(census)
|
|
270
|
+
table = {}
|
|
271
|
+
canonical_paths = {}
|
|
272
|
+
census.each do |entry|
|
|
273
|
+
next (table[BINDINGS] ||= []) << entry.drop(1) if entry[0] == :binds_class
|
|
274
|
+
next unless entry.size == 5 && entry[0] == :own_write_type
|
|
275
|
+
|
|
276
|
+
by_path = (owner_slot(table, entry[1])[entry[2]] ||= {})
|
|
277
|
+
(by_path[canonical_paths[entry[4]] ||= canonical(entry[4])] ||= []) << entry[3]
|
|
278
|
+
end
|
|
279
|
+
table
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def owner_slot(table, owner)
|
|
283
|
+
table[owner] ||= begin
|
|
284
|
+
(table[CANDIDATE_KEYS] ||= []) << owner if owner.include?(SEPARATOR) || owner.start_with?(OBJECT_MARK)
|
|
285
|
+
{}
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
private_class_method :chain_owners, :ancestor_names, :basic_object_edge?, :resolution, :resolve, :declared?,
|
|
290
|
+
:memoized_resolution, :module?, :any_ivar_types, :contributed, :add_entry_types,
|
|
291
|
+
:value_type, :canonical, :table, :build_table, :owner_slot
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
@@ -25,6 +25,18 @@ module Rigor
|
|
|
25
25
|
# leaves the edges the tables did record looking positioned. So it taints the whole owner side with the
|
|
26
26
|
# {WILDCARD} name instead: an owner side that lists {WILDCARD} has no known order at all.
|
|
27
27
|
#
|
|
28
|
+
# Issue #2991 — {WILDCARD} says only that the ORDER is unknown: `include M; prepend M`, an edge recorded where its
|
|
29
|
+
# position is not a fact, and an opaque eval whose block mixes nothing into its own `self` all set it while every
|
|
30
|
+
# module is recorded. Where the walk recorded no module under any name, the instance side also lists
|
|
31
|
+
# {UNNAMED}, always beside {WILDCARD}, because its SET of modules is unknown too. That covers an instance-side
|
|
32
|
+
# mixin call it could not record whose argument is not a constant (`include helper`, `include(*MODS)`; a
|
|
33
|
+
# `Module.new { … }` argument keeps its #2975 reading), a name beside one it cannot name (`include M, h`), and
|
|
34
|
+
# an opaque eval whose block mixes into its own `self`, and a hook's `base.include M`. `self.include M` and a
|
|
35
|
+
# receiverless `send(:include, M)` are recorded as a bare `include M` is (#2995). Issue #2994 — the singleton side
|
|
36
|
+
# lists {UNNAMED} the same way for an `extend` (or a `class << self` `include` / `prepend`) it could not record
|
|
37
|
+
# whose arguments include no constant (`extend(*EMODS)`, `extend helper`), and for a name beside one it cannot
|
|
38
|
+
# name (`extend M, h`).
|
|
39
|
+
#
|
|
28
40
|
# The value is `{owner => {include: [names], extend: [names]}}`. `:include` is the instance side
|
|
29
41
|
# (`include` and `prepend`), `:extend` the singleton side (`extend`, and `include` / `prepend` inside
|
|
30
42
|
# `class << self`), so an unknown `extend` never taints a class's instance ancestry.
|
|
@@ -33,6 +45,11 @@ module Rigor
|
|
|
33
45
|
class MixinAccumulator < Hash
|
|
34
46
|
# The name an owner side lists when a mixin call the walk could not record was written on it.
|
|
35
47
|
WILDCARD = "*"
|
|
48
|
+
# The name an owner side also lists when such a call mixes in a module the walk cannot name (#2991).
|
|
49
|
+
UNNAMED = "?"
|
|
50
|
+
UNNAMED_LIST = [WILDCARD, UNNAMED].freeze
|
|
51
|
+
WILDCARD_LIST = [WILDCARD].freeze
|
|
52
|
+
private_constant :UNNAMED_LIST, :WILDCARD_LIST
|
|
36
53
|
|
|
37
54
|
# What {#unpositioned} answers when nothing was noted: one shared frozen table, so the common file
|
|
38
55
|
# allocates none.
|
|
@@ -47,7 +64,11 @@ module Rigor
|
|
|
47
64
|
@unpositioned = nil
|
|
48
65
|
@hook_params = nil
|
|
49
66
|
@body_blocks = nil
|
|
67
|
+
@program_body = nil
|
|
50
68
|
@program = nil
|
|
69
|
+
@class_bodies = nil
|
|
70
|
+
@class_body_calls = nil
|
|
71
|
+
@class_bodies_read = 0
|
|
51
72
|
end
|
|
52
73
|
|
|
53
74
|
# Marks the file's own top-level statements: a receiverless `include` among them is `main.include`,
|
|
@@ -55,11 +76,38 @@ module Rigor
|
|
|
55
76
|
def program_body(body)
|
|
56
77
|
return unless body.is_a?(Prism::StatementsNode)
|
|
57
78
|
|
|
58
|
-
@
|
|
79
|
+
@program_body = body
|
|
59
80
|
end
|
|
60
81
|
|
|
82
|
+
# Whether `node` is a call whose `self` is `main`'s as far as the walk can tell: a top-level statement, or
|
|
83
|
+
# a call reached from one through control flow alone (issue #3007), so a receiverless `include` there is
|
|
84
|
+
# `main.include`. The set is built on the first question, which only a top-level mixin call asks.
|
|
61
85
|
def program_statement?(node)
|
|
62
|
-
|
|
86
|
+
return false if @program_body.nil?
|
|
87
|
+
|
|
88
|
+
@program ||= MainSelfCalls.collect(@program_body)
|
|
89
|
+
@program.include?(node)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Issue #3006 — registers a class or module body (or its `class << self` body), whose calls
|
|
93
|
+
# {#class_body_scope} answers for.
|
|
94
|
+
def class_body(body)
|
|
95
|
+
(@class_bodies ||= []) << body if body
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# The node whose locals a call written in a registered class body reads — the body itself, or the `def self.`
|
|
99
|
+
# it sits in — or nil when the call is not one whose `self` is that body's class as far as {MainSelfCalls}
|
|
100
|
+
# can tell (an instance `def`, a block that may rebind `self`, a nested declaration). Built on the first
|
|
101
|
+
# question, which only a computed-name `send` asks, and extended with the bodies registered since.
|
|
102
|
+
def class_body_scope(node)
|
|
103
|
+
return nil if @class_bodies.nil?
|
|
104
|
+
|
|
105
|
+
@class_body_calls ||= {}.compare_by_identity
|
|
106
|
+
while @class_bodies_read < @class_bodies.size
|
|
107
|
+
@class_body_calls.merge!(MainSelfCalls.collect(@class_bodies[@class_bodies_read], instance_defs: false))
|
|
108
|
+
@class_bodies_read += 1
|
|
109
|
+
end
|
|
110
|
+
@class_body_calls[node]
|
|
63
111
|
end
|
|
64
112
|
|
|
65
113
|
# Marks the statements of a body whose own position is a fact — the calls and declarations in them
|
|
@@ -110,17 +158,18 @@ module Rigor
|
|
|
110
158
|
end
|
|
111
159
|
|
|
112
160
|
# Notes the edges `node` recorded for `owner` on `side`. `complete` is false when an argument named
|
|
113
|
-
# nothing the walk could record, which leaves the recorded names' neighbours unknown.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
161
|
+
# nothing the walk could record, which leaves the recorded names' neighbours unknown. `positioned` says a
|
|
162
|
+
# receiver-form call runs where it is written, as a receiverless one does (`singleton_class.prepend P`, #2998).
|
|
163
|
+
def note(node, owner, side, targets, complete:, positioned: false)
|
|
164
|
+
return taint(owner, side, unnamed: true) unless complete
|
|
165
|
+
return if (positioned || node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)) && direct?(node)
|
|
117
166
|
|
|
118
167
|
add(owner, side, targets)
|
|
119
168
|
end
|
|
120
169
|
|
|
121
|
-
# Names `owner`'s `side` as having no known order.
|
|
122
|
-
def taint(owner, side)
|
|
123
|
-
add(owner, side,
|
|
170
|
+
# Names `owner`'s `side` as having no known order; `unnamed` also as holding a module no name spells.
|
|
171
|
+
def taint(owner, side, unnamed: false)
|
|
172
|
+
add(owner, side, unnamed ? UNNAMED_LIST : WILDCARD_LIST)
|
|
124
173
|
end
|
|
125
174
|
|
|
126
175
|
# `{owner => {side => [names]}}`, frozen, each list de-duplicated.
|
|
@@ -138,6 +187,84 @@ module Rigor
|
|
|
138
187
|
((@unpositioned ||= {})[owner] ||= {})[side] = ((@unpositioned[owner][side] || []) + names)
|
|
139
188
|
end
|
|
140
189
|
end
|
|
190
|
+
|
|
191
|
+
# Issue #3007 — the calls whose `self` is `main` as far as the mixin walk can tell: the file's top-level
|
|
192
|
+
# statements, and the calls reached from them through control flow (`if`, `unless`, `case`, `begin`, `rescue`, a
|
|
193
|
+
# loop, `&&` / `||`) or a value position (an assignment's value, a call's receiver or argument, an array literal),
|
|
194
|
+
# or through the body of a top-level `def` or `def self.`, whose `include` the walk reads as ADR-119 WD7 reads one
|
|
195
|
+
# in a method body: a member of the set with no position. A block is entered only when it is a core iterator's
|
|
196
|
+
# that keeps `self` ({SELF_KEEPING_ITERATORS}) on a literal receiver, or a receiverless `loop` / `tap` / `then`.
|
|
197
|
+
# Any other block or lambda is not: its `self` may not be `main` (a `describe do … end` or `class_exec` block
|
|
198
|
+
# rebinds it), and neither is a declaration, `class << self` or a `def` on another receiver, whose `self` is not
|
|
199
|
+
# `main`. Recording more here can only silence: these edges feed no typing (`Inference::ObjectMixins`).
|
|
200
|
+
module MainSelfCalls
|
|
201
|
+
CONTROL_FLOW = [
|
|
202
|
+
Prism::StatementsNode, Prism::BeginNode, Prism::RescueNode, Prism::ElseNode, Prism::EnsureNode,
|
|
203
|
+
Prism::IfNode, Prism::UnlessNode, Prism::CaseNode, Prism::CaseMatchNode, Prism::WhenNode, Prism::InNode,
|
|
204
|
+
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::ParenthesesNode, Prism::AndNode, Prism::OrNode,
|
|
205
|
+
Prism::RescueModifierNode,
|
|
206
|
+
Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::GlobalVariableWriteNode,
|
|
207
|
+
Prism::ConstantWriteNode, Prism::MultiWriteNode, Prism::ArgumentsNode, Prism::ArrayNode
|
|
208
|
+
].freeze
|
|
209
|
+
# Core iterators whose block runs with the caller's `self`, entered on a literal receiver.
|
|
210
|
+
SELF_KEEPING_ITERATORS = %i[each each_with_index each_with_object map times upto downto tap then loop].freeze
|
|
211
|
+
# The iterators entered without a receiver: `Kernel#loop`, and `tap` / `then` on `main` itself.
|
|
212
|
+
RECEIVERLESS_ITERATORS = %i[tap then loop].freeze
|
|
213
|
+
LITERAL_RECEIVERS = [Prism::ArrayNode, Prism::RangeNode, Prism::IntegerNode].freeze
|
|
214
|
+
private_constant :CONTROL_FLOW, :SELF_KEEPING_ITERATORS, :RECEIVERLESS_ITERATORS, :LITERAL_RECEIVERS
|
|
215
|
+
|
|
216
|
+
module_function
|
|
217
|
+
|
|
218
|
+
# `{call => scope}`: each call and the node whose locals it reads, `body` or the `def` it sits in. Issue #3006 —
|
|
219
|
+
# a class body's calls are collected the same way with `instance_defs: false`: an instance `def`'s `self` is
|
|
220
|
+
# an instance, not the class, while a `def self.`'s is the class, and every block is entered.
|
|
221
|
+
def collect(body, instance_defs: true)
|
|
222
|
+
calls = {}.compare_by_identity
|
|
223
|
+
visit(body, calls, body, instance_defs)
|
|
224
|
+
calls
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def visit(node, calls, scope, instance_defs)
|
|
228
|
+
case node
|
|
229
|
+
when Prism::CallNode
|
|
230
|
+
calls[node] = scope
|
|
231
|
+
visit(node.receiver, calls, scope, instance_defs)
|
|
232
|
+
visit(node.arguments, calls, scope, instance_defs)
|
|
233
|
+
entered = instance_defs ? self_keeping_block?(node) : any_block?(node)
|
|
234
|
+
visit(node.block.body, calls, scope, instance_defs) if entered
|
|
235
|
+
when Prism::DefNode
|
|
236
|
+
visit(node.body, calls, node, instance_defs) if (instance_defs && node.receiver.nil?) ||
|
|
237
|
+
node.receiver.is_a?(Prism::SelfNode)
|
|
238
|
+
when *CONTROL_FLOW
|
|
239
|
+
node.compact_child_nodes.each { |child| visit(child, calls, scope, instance_defs) }
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
private_class_method :visit
|
|
243
|
+
|
|
244
|
+
# Issue #3006 — a class body's calls include those in any block written there (`KINDS.each { … }`, a concern's
|
|
245
|
+
# `included do … end`): a computed `send` in one may mix into the class, or into what the block's `self` is.
|
|
246
|
+
def any_block?(call)
|
|
247
|
+
call.block.is_a?(Prism::BlockNode)
|
|
248
|
+
end
|
|
249
|
+
private_class_method :any_block?
|
|
250
|
+
|
|
251
|
+
def self_keeping_block?(call)
|
|
252
|
+
return false unless call.block.is_a?(Prism::BlockNode) && SELF_KEEPING_ITERATORS.include?(call.name)
|
|
253
|
+
return RECEIVERLESS_ITERATORS.include?(call.name) if call.receiver.nil?
|
|
254
|
+
|
|
255
|
+
LITERAL_RECEIVERS.any? { |kind| literal(call.receiver).is_a?(kind) }
|
|
256
|
+
end
|
|
257
|
+
private_class_method :self_keeping_block?
|
|
258
|
+
|
|
259
|
+
# `(1..2)` is a range literal in parentheses.
|
|
260
|
+
def literal(receiver)
|
|
261
|
+
return receiver unless receiver.is_a?(Prism::ParenthesesNode) && receiver.body.is_a?(Prism::StatementsNode)
|
|
262
|
+
|
|
263
|
+
body = receiver.body.body
|
|
264
|
+
body.size == 1 ? body.first : receiver
|
|
265
|
+
end
|
|
266
|
+
private_class_method :literal
|
|
267
|
+
end
|
|
141
268
|
end
|
|
142
269
|
end
|
|
143
270
|
end
|