rigortype 0.3.9 → 0.4.1
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 +5 -4
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
- data/docs/handbook/03-narrowing.md +95 -10
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/07-rbs-and-extended.md +16 -11
- data/docs/handbook/08-understanding-errors.md +3 -2
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/install.md +87 -15
- data/docs/manual/02-cli-reference.md +65 -4
- data/docs/manual/03-configuration.md +12 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/08-skills.md +4 -3
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/12-caching.md +43 -4
- data/docs/manual/14-rails-quickstart.md +19 -11
- data/docs/manual/16-rbs-extended-annotations.md +40 -7
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +243 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +110 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +1 -1
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +427 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
- data/lib/rigor/analysis/check_rules.rb +638 -136
- data/lib/rigor/analysis/dependency_recorder.rb +222 -17
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
- data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
- data/lib/rigor/analysis/incremental_session.rb +309 -53
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -1
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +46 -18
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +168 -12
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +109 -17
- data/lib/rigor/analysis/runner/project_pre_passes.rb +44 -17
- data/lib/rigor/analysis/runner.rb +603 -54
- data/lib/rigor/analysis/shadow_harness.rb +27 -0
- data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +93 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +409 -0
- data/lib/rigor/analysis/worker_session.rb +57 -13
- data/lib/rigor/bleeding_edge.rb +0 -2
- data/lib/rigor/builtins/hkt_builtins.rb +1 -0
- data/lib/rigor/builtins/imported_refinements.rb +4 -0
- data/lib/rigor/builtins/regex_refinement.rb +17 -9
- data/lib/rigor/builtins/static_return_refinements.rb +2 -0
- data/lib/rigor/cache/descriptor.rb +45 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/file_digest.rb +28 -0
- data/lib/rigor/cache/incremental_snapshot.rb +102 -8
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_descriptor.rb +37 -7
- data/lib/rigor/cache/store.rb +63 -4
- data/lib/rigor/cache/value_digest.rb +325 -0
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/check_command.rb +83 -13
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/effects_report.rb +2 -2
- data/lib/rigor/cli/plugin_command.rb +3 -3
- data/lib/rigor/cli/prism_colorizer.rb +0 -1
- data/lib/rigor/cli/sig_gen_command.rb +210 -29
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +0 -1
- data/lib/rigor/cli/type_of_command.rb +19 -5
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/unused_command.rb +3 -3
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/config_audit.rb +3 -2
- data/lib/rigor/config_path.rb +29 -0
- data/lib/rigor/configuration/severity_profile.rb +24 -3
- data/lib/rigor/configuration.rb +71 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +11 -2
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +9 -2
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/effect_table.rb +11 -3
- data/lib/rigor/effects/envelope_check.rb +1 -1
- data/lib/rigor/effects/envelope_index.rb +15 -0
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/identity.rb +16 -0
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +21 -0
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +49 -37
- data/lib/rigor/effects/propagator.rb +295 -16
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +121 -72
- data/lib/rigor/effects/signature_sources.rb +1 -1
- data/lib/rigor/effects/snapshot.rb +2 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +385 -30
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
- data/lib/rigor/environment/lockfile_resolver.rb +27 -1
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
- data/lib/rigor/environment/rbs_loader.rb +400 -134
- data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
- data/lib/rigor/environment.rb +104 -96
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +139 -9
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +88 -26
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +2 -1
- data/lib/rigor/inference/budget_trace.rb +2 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +393 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/declaration_walk/context.rb +195 -0
- data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
- data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
- data/lib/rigor/inference/declaration_walk.rb +7 -0
- data/lib/rigor/inference/def_node_resolver.rb +3 -2
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/definer_resolution.rb +266 -0
- data/lib/rigor/inference/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1665 -538
- data/lib/rigor/inference/external_ancestor_resolution.rb +264 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +488 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/index_write_widening.rb +16 -3
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +179 -17
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +901 -38
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +214 -13
- data/lib/rigor/inference/method_parameter_binder.rb +8 -3
- data/lib/rigor/inference/module_function_state.rb +213 -0
- data/lib/rigor/inference/multi_target_binder.rb +345 -52
- data/lib/rigor/inference/mutation_rejoin.rb +4 -1
- data/lib/rigor/inference/mutation_widening.rb +70 -48
- data/lib/rigor/inference/narrowing.rb +674 -124
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +152 -9
- data/lib/rigor/inference/parameter_inference_collector.rb +13 -11
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +7 -2
- data/lib/rigor/inference/project_patched_scanner.rb +7 -3
- data/lib/rigor/inference/receiver_alias.rb +90 -1
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +15 -11
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
- data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
- data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
- data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
- data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
- data/lib/rigor/inference/scope_indexer.rb +5913 -701
- data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
- data/lib/rigor/inference/statement_evaluator.rb +3249 -497
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +44 -7
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
- data/lib/rigor/language_server/hover_renderer.rb +3 -3
- data/lib/rigor/language_server/project_context.rb +5 -3
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +196 -13
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +1 -1
- data/lib/rigor/plugin/effect_attribution.rb +58 -4
- data/lib/rigor/plugin/io_boundary.rb +82 -4
- data/lib/rigor/plugin/isolation.rb +1 -1
- data/lib/rigor/plugin/loader.rb +2 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/manifest.rb +71 -10
- data/lib/rigor/plugin/registry.rb +35 -1
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +20 -9
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +4 -5
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +146 -0
- data/lib/rigor/reflection/constant_path.rb +41 -13
- data/lib/rigor/reflection.rb +73 -107
- data/lib/rigor/scope/discovery_index.rb +351 -2
- data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
- data/lib/rigor/scope/resolution_chain.rb +884 -0
- data/lib/rigor/scope.rb +981 -198
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +580 -47
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +124 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +166 -35
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +1 -1
- data/lib/rigor/source/node_walker.rb +0 -3
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +112 -12
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/hash_shape.rb +1 -1
- data/lib/rigor/type/refined.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +10 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +14 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +6 -4
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +50 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +7 -6
- data/sig/rigor/scope.rbs +177 -20
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +6 -0
- data/sig/rigor.rbs +38 -20
- data/skills/rigor-ask/SKILL.md +8 -5
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +46 -18
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +40 -10
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +4 -6
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
- data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +132 -1
|
@@ -15,6 +15,7 @@ module Rigor
|
|
|
15
15
|
:class_ivars,
|
|
16
16
|
:class_cvars,
|
|
17
17
|
:program_globals,
|
|
18
|
+
:program_global_seeds,
|
|
18
19
|
:discovered_classes,
|
|
19
20
|
:in_source_constants,
|
|
20
21
|
:discovered_methods,
|
|
@@ -24,12 +25,20 @@ module Rigor
|
|
|
24
25
|
:discovered_def_sources,
|
|
25
26
|
:discovered_singleton_def_sources,
|
|
26
27
|
:discovered_method_visibilities,
|
|
28
|
+
:discovered_parameter_envelopes,
|
|
27
29
|
:discovered_superclasses,
|
|
30
|
+
:discovered_deferred_ranges,
|
|
31
|
+
:discovered_refinements,
|
|
32
|
+
:discovered_global_write_census,
|
|
28
33
|
:discovered_header_nestings,
|
|
29
34
|
:discovered_includes,
|
|
35
|
+
:discovered_prepends,
|
|
30
36
|
:discovered_extends,
|
|
37
|
+
:unpositioned_mixins,
|
|
31
38
|
:discovered_class_sources,
|
|
32
39
|
:constant_sources,
|
|
40
|
+
:constant_writers,
|
|
41
|
+
:constant_shadowers,
|
|
33
42
|
:published_constant_names,
|
|
34
43
|
:local_constant_names,
|
|
35
44
|
:published_constant_alias_names,
|
|
@@ -37,7 +46,17 @@ module Rigor
|
|
|
37
46
|
:data_member_layouts,
|
|
38
47
|
:struct_member_layouts,
|
|
39
48
|
:param_inferred_types,
|
|
40
|
-
:run_generation
|
|
49
|
+
:run_generation,
|
|
50
|
+
:patched_line_readers,
|
|
51
|
+
:clears_last_status,
|
|
52
|
+
:defines_case_equality,
|
|
53
|
+
:implicit_self_evidence,
|
|
54
|
+
:possible_discovered_methods,
|
|
55
|
+
:possible_discovered_deferred_ranges,
|
|
56
|
+
:contested_discovered_def_nodes,
|
|
57
|
+
:contested_discovered_singleton_def_nodes,
|
|
58
|
+
:contested_discovered_method_visibilities,
|
|
59
|
+
:contested_discovered_parameter_envelopes
|
|
41
60
|
)
|
|
42
61
|
|
|
43
62
|
class DiscoveryIndex
|
|
@@ -46,6 +65,114 @@ module Rigor
|
|
|
46
65
|
EMPTY_NAME_SET = Set.new.freeze
|
|
47
66
|
private_constant :EMPTY_NODE_TABLE, :EMPTY_TABLE, :EMPTY_NAME_SET
|
|
48
67
|
|
|
68
|
+
# ADR-119 WD1 — each member that may one day admit `possible` or `contested` facts, and the sibling that
|
|
69
|
+
# holds them. Every copy path reads this one declaration, so a member never travels without its sibling:
|
|
70
|
+
# {#with} raises on a half pair and {.compact_pairs} drops a pair only when BOTH halves are empty. No
|
|
71
|
+
# producer fills a sibling yet (the lane-1 carriage), so every sibling is empty and each member keeps its
|
|
72
|
+
# meaning, the union of its certain and possible facts.
|
|
73
|
+
#
|
|
74
|
+
# - `possible_*` has its member's shape and is a subset of it (a `discovered_methods` pair is a
|
|
75
|
+
# `{class => {name => kind}}` table).
|
|
76
|
+
# - `contested_*` is a Set of key paths into its member, one per entry whose value depends on a possible
|
|
77
|
+
# fact: `[class, method]`, or `[class, [kind, method]]` for an envelope.
|
|
78
|
+
#
|
|
79
|
+
# The members without a sibling here (`discovered_def_sources`, `discovered_singleton_def_sources`) are site
|
|
80
|
+
# tables (dependency edges, positions, fingerprints): no read takes a definer, visibility, arity or type from
|
|
81
|
+
# them, and certainty does not move a site. The reads whose answer depends on the site are
|
|
82
|
+
# `Scope#same_file_top_level_def?` (file identity, independent of certainty), `#singleton_def_shadows_call?`
|
|
83
|
+
# and `#instance_def_shadows_call?` (a conditional def's site answers "shadows"). Where "shadows" narrows an
|
|
84
|
+
# answer, the caller pairs it with {#possible_method?}: existence is the method table, certainty is the possible
|
|
85
|
+
# table alone (ADR-119 C1d-c).
|
|
86
|
+
SIBLINGS = {
|
|
87
|
+
discovered_methods: :possible_discovered_methods,
|
|
88
|
+
discovered_deferred_ranges: :possible_discovered_deferred_ranges,
|
|
89
|
+
discovered_def_nodes: :contested_discovered_def_nodes,
|
|
90
|
+
discovered_singleton_def_nodes: :contested_discovered_singleton_def_nodes,
|
|
91
|
+
discovered_method_visibilities: :contested_discovered_method_visibilities,
|
|
92
|
+
discovered_parameter_envelopes: :contested_discovered_parameter_envelopes
|
|
93
|
+
}.freeze
|
|
94
|
+
|
|
95
|
+
# Either half of a pair to the other.
|
|
96
|
+
PARTNERS = SIBLINGS.flat_map { |member, sibling| [[member, sibling], [sibling, member]] }.to_h.freeze
|
|
97
|
+
private_constant :PARTNERS
|
|
98
|
+
|
|
99
|
+
# [#1507](https://github.com/rigortype/rigor/issues/1507) — the kind of fact each member holds, which decides
|
|
100
|
+
# what a reader may conclude from an entry and which gate covers the member (ADR-119 WD4, proposed). Each
|
|
101
|
+
# reason describes the table as the code builds it today. `spec/rigor/declaration_facts/member_classes_spec.rb`
|
|
102
|
+
# fails on a member that is unclassified or classified twice, and checks each class's shape on an index built
|
|
103
|
+
# from a fixture project; it pins the few misfilings those checks accept.
|
|
104
|
+
#
|
|
105
|
+
# - `:set_valued` — names, edges, files or rows whose presence is the fact. A reader asks whether an entry is
|
|
106
|
+
# there.
|
|
107
|
+
# - `:single_valued` — one value per key, which readers use; disagreeing contributions fold by a fixed rule.
|
|
108
|
+
# - `:typed` — the values are types, which already carry uncertainty in the type lattice.
|
|
109
|
+
# - `:syntactic` — read off the parse of the analysed file alone, which is their reference.
|
|
110
|
+
# - `:run_state` — the run's own state, not a fact about the program.
|
|
111
|
+
# - `:sibling` — the `possible_*` / `contested_*` half of a pair in {SIBLINGS}, which travels with its member.
|
|
112
|
+
MEMBER_CLASSES = {
|
|
113
|
+
set_valued: {
|
|
114
|
+
discovered_classes: "the class and module names the project declares; the value is the name's singleton",
|
|
115
|
+
discovered_methods: "which names each class answers, and on which side",
|
|
116
|
+
discovered_refinements: "the methods a refine block adds, keyed by the refined class",
|
|
117
|
+
discovered_global_write_census: "the names any file aliases, defines or mixes in at the top level",
|
|
118
|
+
discovered_includes: "the modules each class includes, prepends among them",
|
|
119
|
+
discovered_prepends: "the modules each class prepends",
|
|
120
|
+
discovered_extends: "the modules each singleton mixes in; the module itself for module_function",
|
|
121
|
+
unpositioned_mixins: "per side, the mixin edges each class records whose order is not a fact, or \"*\"",
|
|
122
|
+
discovered_class_sources: "the files that declare each class, reopenings included",
|
|
123
|
+
discovered_deferred_ranges: "each file's def and block ranges; def rows add owner and module_function side",
|
|
124
|
+
constant_sources: "the files that write each constant",
|
|
125
|
+
constant_writers: "the constant names some write binds, by last segment",
|
|
126
|
+
constant_shadowers: "the constant names a non-memo write assigns, by last segment",
|
|
127
|
+
published_constant_names: "the last segments of the constants the project publishes",
|
|
128
|
+
local_constant_names: "the last segments of the constants the analysed file assigns",
|
|
129
|
+
published_constant_alias_names: "the file's constants copied straight from a published one",
|
|
130
|
+
published_constant_ivars: "the ivars each class seeds from a published constant"
|
|
131
|
+
}.freeze,
|
|
132
|
+
single_valued: {
|
|
133
|
+
discovered_def_nodes: "the def node each instance method resolves to",
|
|
134
|
+
discovered_singleton_def_nodes: "the def node each singleton method resolves to",
|
|
135
|
+
discovered_def_sources: "the first-seen path:line of each instance method, which readers use",
|
|
136
|
+
discovered_singleton_def_sources: "the first-seen path:line of each singleton method, which readers use",
|
|
137
|
+
discovered_method_visibilities: "each method's visibility",
|
|
138
|
+
discovered_parameter_envelopes: "each method's parameter shape, opaque when contributions disagree",
|
|
139
|
+
discovered_superclasses: "each class's superclass as written",
|
|
140
|
+
discovered_header_nestings: "the cref each ancestor name resolves in, an alternatives list when ambiguous",
|
|
141
|
+
data_member_layouts: "each Data class's member layout",
|
|
142
|
+
struct_member_layouts: "each Struct class's member layout"
|
|
143
|
+
}.freeze,
|
|
144
|
+
typed: {
|
|
145
|
+
declared_types: "the type each declaration node binds",
|
|
146
|
+
class_ivars: "the joined type of each ivar's writes, per class",
|
|
147
|
+
class_cvars: "the joined type of each cvar's writes, per class",
|
|
148
|
+
program_globals: "the joined type of each global's writes",
|
|
149
|
+
program_global_seeds: "a declared global's signature type joined with its writes",
|
|
150
|
+
in_source_constants: "the type each constant's write binds",
|
|
151
|
+
param_inferred_types: "the union of each parameter's call-site argument types"
|
|
152
|
+
}.freeze,
|
|
153
|
+
syntactic: {
|
|
154
|
+
discovered_def_nestings: "the Module.nesting each def is written in, keyed by node identity",
|
|
155
|
+
patched_line_readers: "the gets / readline names the file defines through the define_method family",
|
|
156
|
+
clears_last_status: "whether the analysed file holds a call that may set $? to nil",
|
|
157
|
+
defines_case_equality: "whether the analysed file holds a define_method naming ===",
|
|
158
|
+
implicit_self_evidence: "where the file's implicit-self readers sit; built lazily from its tree, never seeded"
|
|
159
|
+
}.freeze,
|
|
160
|
+
run_state: {
|
|
161
|
+
run_generation: "the identity token of the current run"
|
|
162
|
+
}.freeze,
|
|
163
|
+
sibling: {
|
|
164
|
+
possible_discovered_methods: "the names and sides of discovered_methods that only a possible definer gives",
|
|
165
|
+
possible_discovered_deferred_ranges: "the rows of discovered_deferred_ranges that only a possible fact gives",
|
|
166
|
+
contested_discovered_def_nodes: "the key paths of discovered_def_nodes that a possible definer may change",
|
|
167
|
+
contested_discovered_singleton_def_nodes:
|
|
168
|
+
"the key paths of discovered_singleton_def_nodes that a possible definer may change",
|
|
169
|
+
contested_discovered_method_visibilities:
|
|
170
|
+
"the key paths of discovered_method_visibilities that a possible fact may change",
|
|
171
|
+
contested_discovered_parameter_envelopes:
|
|
172
|
+
"the key paths of discovered_parameter_envelopes that a possible fact may change"
|
|
173
|
+
}.freeze
|
|
174
|
+
}.freeze
|
|
175
|
+
|
|
49
176
|
# The third value a `discovered_methods` entry can hold, beside `:instance` and `:singleton`. One name may
|
|
50
177
|
# legitimately be defined on both sides of the same class (`def helper` plus a `class << self` twin), and the
|
|
51
178
|
# table is keyed by name alone — so before this existed the second `def` overwrote the first's kind and
|
|
@@ -60,6 +187,55 @@ module Rigor
|
|
|
60
187
|
# as written, and no ancestor name can be nil.
|
|
61
188
|
UNKEYED_HEADER_NESTING = nil
|
|
62
189
|
|
|
190
|
+
# Issue #986 — a bucket entry is normally ONE chain (`["Wrap"]`, innermost first). When the
|
|
191
|
+
# compact-header rename pass lands two declarations of one class on a single key and both wrote the
|
|
192
|
+
# same raw ancestor name in DIFFERENT crefs, the entry is instead the LIST of those chains —
|
|
193
|
+
# `[["Wrap"], []]` — because no union of them is Ruby's answer for either site. `Scope` resolves each
|
|
194
|
+
# alternative and declines outright when they name two different project classes: which one wins
|
|
195
|
+
# depends on the runtime load order of the two `include`s, which static analysis cannot know, so
|
|
196
|
+
# picking either is unsound. A chain's entries are Strings and an alternatives list's are Arrays, so
|
|
197
|
+
# the two shapes cannot be confused.
|
|
198
|
+
def self.ambiguous_header_nesting?(entries)
|
|
199
|
+
entries.first.is_a?(Array)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Issue #992 — the two class-wide keys a `discovered_parameter_envelopes` bucket can carry beside its
|
|
203
|
+
# `[kind, method_name]` entries. Their PRESENCE is the fact; the value is always
|
|
204
|
+
# `Source::ParameterEnvelope::OPAQUE`, so a bucket folds under the one join every other entry does.
|
|
205
|
+
# Symbols, because every per-method key is an Array and neither can collide with the other.
|
|
206
|
+
#
|
|
207
|
+
# The class marks below and {ENVELOPE_PROJECT_WIDE} are presence facts read only to decline a read; they are
|
|
208
|
+
# never a contested path. Contested envelope paths are always `[class, [kind, name]]`.
|
|
209
|
+
#
|
|
210
|
+
# - {ENVELOPE_MODULE_MARK}: the name is declared with `module` (or `Const = Module.new do … end`) somewhere
|
|
211
|
+
# in the project. An instance of it is an instance of an unknown includer.
|
|
212
|
+
# - {ENVELOPE_DYNAMIC_MARK}: the class body rewrites its method table in a way no literal argument names
|
|
213
|
+
# (`class_eval`, a computed `define_method`, `send`, a non-constant mixin), or a constant-receiver form
|
|
214
|
+
# of those names it from outside.
|
|
215
|
+
# - {ENVELOPE_OBJECT_EXTENDED_MARK}: some method body passes the module to `extend`, so an object of any
|
|
216
|
+
# class may carry its instance methods ahead of that class's own.
|
|
217
|
+
# - {ENVELOPE_REFINED_MARK}: a `refine` block refines the class, which redefines its methods wherever a `using`
|
|
218
|
+
# is in effect. Issue #1367 splits it from {ENVELOPE_DYNAMIC_MARK}, which it used to share: an implicit
|
|
219
|
+
# conversion (`to_str`, `to_int`) ignores a refinement, so the `global.*` rules must tell the two apart.
|
|
220
|
+
# Every reader that asks whether a surface is rewritten reads both ({.rewritten_surface?}).
|
|
221
|
+
ENVELOPE_MODULE_MARK = :"<module>"
|
|
222
|
+
ENVELOPE_DYNAMIC_MARK = :"<dynamic>"
|
|
223
|
+
ENVELOPE_OBJECT_EXTENDED_MARK = :"<object-extended>"
|
|
224
|
+
ENVELOPE_REFINED_MARK = :"<refined>"
|
|
225
|
+
|
|
226
|
+
# Whether an envelope bucket says the project rewrites the class's method table beyond its literal names: a
|
|
227
|
+
# dynamic rewrite, or a refinement.
|
|
228
|
+
def self.rewritten_surface?(bucket)
|
|
229
|
+
bucket.key?(ENVELOPE_DYNAMIC_MARK) || bucket.key?(ENVELOPE_REFINED_MARK)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Issue #992 — the class key a WHOLE-PROJECT discovery pass adds to `discovered_parameter_envelopes`
|
|
233
|
+
# (`ScopeIndexer#finalize_def_index`), with an empty bucket. A single file's walk alone — `run_source`,
|
|
234
|
+
# the LSP `prebuilt:` scope — sees one `def` and cannot see the reopening, subclass or `class_eval` in
|
|
235
|
+
# another file that would make it opaque, so `call.wrong-arity` reads an envelope only when this key is
|
|
236
|
+
# present. Not a constant character, so no class can be named it.
|
|
237
|
+
ENVELOPE_PROJECT_WIDE = "<project-wide>"
|
|
238
|
+
|
|
63
239
|
# The shared all-empty index `Scope.empty` (and every scope that never sees a seeding pass) points at — one
|
|
64
240
|
# allocation per process.
|
|
65
241
|
EMPTY = new(
|
|
@@ -67,6 +243,12 @@ module Rigor
|
|
|
67
243
|
class_ivars: EMPTY_TABLE,
|
|
68
244
|
class_cvars: EMPTY_TABLE,
|
|
69
245
|
program_globals: EMPTY_TABLE,
|
|
246
|
+
# Issue #1362 — for each global in `program_globals` that Ruby's own signatures declare (`$VERBOSE: bool?`,
|
|
247
|
+
# `$stdout: IO`), its declared type joined with its writes in the file: what every method body and the top
|
|
248
|
+
# level start from, under the ADR-58 `:global` mark. Any other global starts from its `program_globals` entry,
|
|
249
|
+
# which keeps the writes alone for the consumers that compare against them (the mark's gates, and `$;` for
|
|
250
|
+
# `split`). Filled by `Inference::ScopeIndexer.index` from the file's own tree only.
|
|
251
|
+
program_global_seeds: EMPTY_TABLE,
|
|
70
252
|
discovered_classes: EMPTY_TABLE,
|
|
71
253
|
in_source_constants: EMPTY_TABLE,
|
|
72
254
|
discovered_methods: EMPTY_TABLE,
|
|
@@ -87,7 +269,37 @@ module Rigor
|
|
|
87
269
|
discovered_def_sources: EMPTY_TABLE,
|
|
88
270
|
discovered_singleton_def_sources: EMPTY_TABLE,
|
|
89
271
|
discovered_method_visibilities: EMPTY_TABLE,
|
|
272
|
+
# Issue #992 — `{qualified class name => {[kind, method_name] => Source::ParameterEnvelope}}`, the
|
|
273
|
+
# parameter envelope of every method the declaration walk records, plus {ENVELOPE_MODULE_MARK} /
|
|
274
|
+
# {ENVELOPE_DYNAMIC_MARK}. Written beside `discovered_methods` by one recorder, so an `alias` / `attr_*`
|
|
275
|
+
# / `define_method` name is always present here as `OPAQUE`, and folded across files and reopenings
|
|
276
|
+
# with `Source::ParameterEnvelope.merge`: one value per name, a real envelope only while every
|
|
277
|
+
# contribution agrees. Read by `call.wrong-arity` for a method no signature declares.
|
|
278
|
+
discovered_parameter_envelopes: EMPTY_TABLE,
|
|
90
279
|
discovered_superclasses: EMPTY_TABLE,
|
|
280
|
+
# Issue #1097 — `{file path => [[start_offset, end_offset, name, kind, owner], ...]}`, every
|
|
281
|
+
# `def` / block / lambda body range in the file. `Scope#*_def_shadows_call?` reads it to tell
|
|
282
|
+
# an eager class-body call (orderable by offset against a same-name, same-owner def entry)
|
|
283
|
+
# from a deferred one (contained in any range — it runs at invocation time, when every
|
|
284
|
+
# class-body def exists). Def entries carry the method name, the `:instance` / `:singleton` /
|
|
285
|
+
# `:both` (module_function) kind, and the qualified owner; block / lambda rows and defs nested
|
|
286
|
+
# inside another deferred range carry nils and answer only the containment half. Plain data,
|
|
287
|
+
# so the ADR-85 seed bundle round-trips it unchanged.
|
|
288
|
+
discovered_deferred_ranges: EMPTY_TABLE,
|
|
289
|
+
# Issue #1120 — `{refined class name => {method name => [refining module names]}}`, the instance
|
|
290
|
+
# methods a `refine X do … end` block defines. They are not X's methods everywhere: Ruby activates them
|
|
291
|
+
# only lexically after a `using` of the refining module, so `call.undefined-method` reads this table
|
|
292
|
+
# together with the call site's file ({Analysis::CheckRules::LexicalMethodSites}) and never through
|
|
293
|
+
# `discovered_methods`. A refinement a `Module.new { … }` block defines has no nameable module; it is
|
|
294
|
+
# keyed by the name the walk gives the block's owner. Plain data, so the ADR-85 seed bundle
|
|
295
|
+
# round-trips it unchanged.
|
|
296
|
+
discovered_refinements: EMPTY_TABLE,
|
|
297
|
+
# Issue #1367 — the project's `Inference::GlobalWriteCensus`: the globals any `alias` names, the
|
|
298
|
+
# `write` / `to_str` / `to_int` / hatch names any file defines (inside a refinement or not), and the
|
|
299
|
+
# modules a top-level mixin names, over every file and inside method bodies too. The `global.*` write rules
|
|
300
|
+
# read it. The project pre-pass seeds the whole project's census and `Inference::ScopeIndexer.index` adds the
|
|
301
|
+
# analysed file's own. Plain data, so the ADR-85 seed bundle round-trips it unchanged.
|
|
302
|
+
discovered_global_write_census: EMPTY_NAME_SET,
|
|
91
303
|
# Issue #682 — `{qualified class name => Module.nesting where its declaration HEADER is written}`,
|
|
92
304
|
# innermost first and EXCLUDING the declaration's own entry. Read by `Scope#ancestor_name_candidates`,
|
|
93
305
|
# which resolves a superclass / include name in that cref instead of peeling the subclass's qualified
|
|
@@ -102,18 +314,54 @@ module Rigor
|
|
|
102
314
|
# a name no site recorded under its own key.
|
|
103
315
|
discovered_header_nestings: EMPTY_TABLE,
|
|
104
316
|
discovered_includes: EMPTY_TABLE,
|
|
317
|
+
# Issue #1123 — `{qualified class or module name => [module names it `prepend`s, as written]}`,
|
|
318
|
+
# stored in instance-ancestor SEARCH order (nearest prepend first), the `discovered_extends`
|
|
319
|
+
# convention. The instance-side twin `discovered_includes` holds the same names — prepends
|
|
320
|
+
# included — in that order too since #1173: prepends ahead of includes, each nearest-first. The
|
|
321
|
+
# kind still cannot be told apart there, so `Scope#user_def_through_ancestors` reads the wedge
|
|
322
|
+
# from here. An absent entry means "prepends nothing", which is the un-preprended behaviour
|
|
323
|
+
# every scope had before it existed.
|
|
324
|
+
discovered_prepends: EMPTY_TABLE,
|
|
105
325
|
# Issue #898 — the singleton-side twin of `discovered_includes`: `{qualified class or module name =>
|
|
106
326
|
# [module names it `extend`s, as written]}`, built by the same `ScopeIndexer` walk that #526 already
|
|
107
327
|
# ran to fold an extended module's instance defs onto the extending class's singleton. #526 consumed
|
|
108
328
|
# the table inside the indexer and threw it away; `Narrowing` needs it to survive onto the scope,
|
|
109
329
|
# because `Singleton[C]`'s ancestry is exactly what `extend` writes and nothing else records it.
|
|
110
330
|
discovered_extends: EMPTY_TABLE,
|
|
331
|
+
# `{qualified class or module name => {include: [names], extend: [names]}}` — the mixin edges of the three
|
|
332
|
+
# tables above whose ORDER is not a fact, per side (`:include` is the instance side, `include` and
|
|
333
|
+
# `prepend`; `:extend` the singleton side). An edge is listed when it is written anywhere but as a direct
|
|
334
|
+
# statement of the class's own body (or its `class << self` body) in a declaration that is itself a
|
|
335
|
+
# direct statement — inside a `def`, a block, a conditional, or a conditional or deferred declaration —
|
|
336
|
+
# or in the receiver form, and when it shares an argument list with a name the walk could not record
|
|
337
|
+
# (`Inference::ScopeIndexer::MixinAccumulator`). A mixin call the walk cannot record at all (`include
|
|
338
|
+
# helper`, `include(*MODS)`, `send(:include, M)`, `self.include M`, `C.include(M)`,
|
|
339
|
+
# `singleton_class.include M`, a call on a `self.included(base)`-style hook's parameter, an `*_eval` block on
|
|
340
|
+
# one or on an opaque receiver whose body mixes a module in) lists the name
|
|
341
|
+
# `"*"` on the side it reshapes: that side of the owner has no known order. An edge written both ways
|
|
342
|
+
# is listed. Folded by union. A reader that depends on the order of a class's ancestors MUST decline
|
|
343
|
+
# when the owner it asks about, or any ancestor it walks through, lists anything on the side it reads;
|
|
344
|
+
# the three tables themselves are unchanged.
|
|
345
|
+
unpositioned_mixins: EMPTY_TABLE,
|
|
346
|
+
# The files that declare each class, reopenings included. Seeded on every run: the ADR-46 recording
|
|
347
|
+
# accessors read it for their class edges, and an ancestor-order reader reads whether a class is
|
|
348
|
+
# declared in more than one file, where the order of its mixins across the files is load order.
|
|
111
349
|
discovered_class_sources: EMPTY_TABLE,
|
|
112
350
|
# Issue #644 — `{qualified constant name => Set[declaring file]}`, the write attribution behind the
|
|
113
351
|
# cross-file value-constant table. Read only by `Scope#record_constant_dependency` during ADR-46
|
|
114
352
|
# dependency recording, so the runner seeds it only on a recording run; every other run leaves it
|
|
115
353
|
# empty and the edge costs one nil check.
|
|
116
354
|
constant_sources: EMPTY_TABLE,
|
|
355
|
+
# Issue #617 — the censused names that bind (any write other than a memo `||=`, or memos of the
|
|
356
|
+
# segment in two files), grouped by LAST SEGMENT, wildcard keys (`*::LIMIT`) under their segment.
|
|
357
|
+
# `Scope#bound_constant_names` reads it for a constant compound write whose plain read resolves to
|
|
358
|
+
# nothing: such a name is bound, just not to a value the analyzer carries. Seeded on every run, because
|
|
359
|
+
# the question is a typing one rather than a recording one.
|
|
360
|
+
constant_writers: EMPTY_TABLE,
|
|
361
|
+
# Issue #1290 — the censused names some write other than a memo `||=` assigns, grouped by LAST SEGMENT.
|
|
362
|
+
# `Scope#shadowing_constant_names` reads it for the lexical ladder: a candidate no source types but this
|
|
363
|
+
# table holds is where Ruby's lookup stops. Seeded on every run beside `constant_writers`.
|
|
364
|
+
constant_shadowers: EMPTY_TABLE,
|
|
117
365
|
# Issue #644 — the two halves of `Scope#published_constant?`, the question
|
|
118
366
|
# {Analysis::CheckRules::PublishedConstantGuard} asks. `published_constant_names` is the LAST
|
|
119
367
|
# SEGMENTS of the project-wide published table (run-wide, seeded by the runner);
|
|
@@ -147,8 +395,109 @@ module Rigor
|
|
|
147
395
|
# cross a run boundary (LSP / ADR-62 warm-loop re-runs land in a fresh bucket). Nil on scopes that
|
|
148
396
|
# never see the runner seed (single-file probes, `run_source` before the seed applies): the memo
|
|
149
397
|
# falls back to today's per-file `discovered_def_nodes` identity for those.
|
|
150
|
-
run_generation: nil
|
|
398
|
+
run_generation: nil,
|
|
399
|
+
# Issue #1359 — the `gets` / `readline` names this file patches in through the `define_method` family
|
|
400
|
+
# (`$stdin.define_singleton_method(:gets) { … }`, `IO.define_method(:gets)`, `alias_method :gets, :x`, or a
|
|
401
|
+
# computed name, which may be either), which `Inference::LastLine.reads_line?` declines on, as it does on a
|
|
402
|
+
# name `BlockCallTiming.project_defines_anywhere?` finds. Filled by `Inference::ScopeIndexer.index` from the
|
|
403
|
+
# file's own tree only.
|
|
404
|
+
patched_line_readers: EMPTY_NAME_SET,
|
|
405
|
+
# Issue #1360 — true when this file holds a call that may set `$?` to nil (`Inference::LastStatus.clears?`: a
|
|
406
|
+
# `wait`-family call with a flags argument, or `waitall`), which may run between any subprocess and a read of
|
|
407
|
+
# `$?`, so `Inference::LastStatus.after` binds it nowhere in the file. Filled by `Inference::ScopeIndexer.index`
|
|
408
|
+
# from the file's own tree only.
|
|
409
|
+
clears_last_status: false,
|
|
410
|
+
# Issue #1360 — true when this file holds a `define_method` or `define_singleton_method` call whose literal
|
|
411
|
+
# name is `===` (`Inference::ErrorInfo.defines_case_equality?`), which may give a class the singleton `===`
|
|
412
|
+
# `rescue` matches with; the class it lands on is not recorded, so no rescued class binds `$!` in the file.
|
|
413
|
+
# Filled by `Inference::ScopeIndexer.index` from the file's own tree only.
|
|
414
|
+
defines_case_equality: false,
|
|
415
|
+
# Issue #1415 — the file's evidence about the `self` its implicit-self `gets` / `readline` readers run with
|
|
416
|
+
# (`Inference::LastLine::SelfEvidence`, read on the first ask), which `Inference::LastLine.reads_line?` reads
|
|
417
|
+
# them by. Filled by `Inference::ScopeIndexer.index` from the file's own tree only; nil, where no file was
|
|
418
|
+
# indexed, declines every such reader.
|
|
419
|
+
implicit_self_evidence: nil,
|
|
420
|
+
# ADR-119 WD1 — the siblings of the members {SIBLINGS} pairs, empty until a producer admits a possible fact.
|
|
421
|
+
possible_discovered_methods: EMPTY_TABLE,
|
|
422
|
+
possible_discovered_deferred_ranges: EMPTY_TABLE,
|
|
423
|
+
contested_discovered_def_nodes: EMPTY_NAME_SET,
|
|
424
|
+
contested_discovered_singleton_def_nodes: EMPTY_NAME_SET,
|
|
425
|
+
contested_discovered_method_visibilities: EMPTY_NAME_SET,
|
|
426
|
+
contested_discovered_parameter_envelopes: EMPTY_NAME_SET
|
|
151
427
|
)
|
|
428
|
+
|
|
429
|
+
# The siblings of an all-empty index, `{sibling name => empty value}`, frozen: what a def-index with no
|
|
430
|
+
# possible fact carries, and a Runner's initial state. Copy it (`dup`) before mutating.
|
|
431
|
+
EMPTY_SIBLINGS = SIBLINGS.each_value.to_h { |sibling| [sibling, EMPTY.public_send(sibling)] }.freeze
|
|
432
|
+
|
|
433
|
+
# The all-empty value of a paired member or sibling.
|
|
434
|
+
def self.empty_value(name)
|
|
435
|
+
EMPTY.public_send(name)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def self.empty_siblings = EMPTY_SIBLINGS
|
|
439
|
+
|
|
440
|
+
# ADR-119 WD1 — `tables` (a `with(**)` argument Hash) with every pair either complete or absent: a pair whose
|
|
441
|
+
# halves are both missing, nil or empty is dropped, any other pair carries BOTH halves (the half that was
|
|
442
|
+
# absent or nil becomes its empty value). Dropping members one at a time would strand a non-empty sibling,
|
|
443
|
+
# and passing a half would make {#with} raise. Entries outside {SIBLINGS} pass through untouched.
|
|
444
|
+
def self.compact_pairs(tables)
|
|
445
|
+
tables.each_with_object({}) do |(name, table), out|
|
|
446
|
+
partner = PARTNERS[name]
|
|
447
|
+
next out[name] = table if partner.nil?
|
|
448
|
+
next if out.key?(name)
|
|
449
|
+
|
|
450
|
+
other = tables[partner]
|
|
451
|
+
next if blank_table?(table) && blank_table?(other)
|
|
452
|
+
|
|
453
|
+
out[name] = table.nil? ? empty_value(name) : table
|
|
454
|
+
out[partner] = other.nil? ? empty_value(partner) : other
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Whether `name` is a member or sibling of {SIBLINGS}.
|
|
459
|
+
def self.paired?(name)
|
|
460
|
+
PARTNERS.key?(name)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def self.blank_table?(table)
|
|
464
|
+
table.nil? || table.empty?
|
|
465
|
+
end
|
|
466
|
+
private_class_method :blank_table?
|
|
467
|
+
|
|
468
|
+
# The six siblings as `{sibling name => table}`, for a caller that rebuilds the paired members and carries
|
|
469
|
+
# their siblings over unchanged.
|
|
470
|
+
def sibling_tables
|
|
471
|
+
SIBLINGS.each_value.to_h { |sibling| [sibling, public_send(sibling)] }
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# ADR-119 WD1/WD2 — whether `class_name` answers `method_name` on `kind` only through a `possible` fact
|
|
475
|
+
# (`possible_discovered_methods`, empty until a producer admits one). Internal to the candidate-set read.
|
|
476
|
+
def possible_method?(class_name, method_name, kind)
|
|
477
|
+
recorded = possible_discovered_methods[class_name.to_s]&.[](method_name.to_sym)
|
|
478
|
+
recorded == kind || (!recorded.nil? && recorded == METHOD_KIND_BOTH)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# ADR-119 WD1 — whether `path` (`[class, name]`, or `[class, [kind, name]]` for an envelope) is a contested
|
|
482
|
+
# entry of `member`, one of the single-valued members {SIBLINGS} pairs. Internal to the candidate-set read.
|
|
483
|
+
def contested?(member, path)
|
|
484
|
+
public_send(SIBLINGS.fetch(member)).include?(path)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Data's `with`, except that a paired member and its sibling are accepted only TOGETHER (ADR-119 WD1): a
|
|
488
|
+
# caller that rebuilt one without the other would leave the sibling describing the old table, so it
|
|
489
|
+
# raises instead. A call with no change returns the receiver.
|
|
490
|
+
def with(**changes)
|
|
491
|
+
return self if changes.empty?
|
|
492
|
+
|
|
493
|
+
changes.each_key do |name|
|
|
494
|
+
partner = PARTNERS[name]
|
|
495
|
+
next if partner.nil? || changes.key?(partner)
|
|
496
|
+
|
|
497
|
+
raise ArgumentError, "DiscoveryIndex#with: #{name} and #{partner} change together; #{partner} is missing"
|
|
498
|
+
end
|
|
499
|
+
super
|
|
500
|
+
end
|
|
152
501
|
end
|
|
153
502
|
end
|
|
154
503
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
class Scope
|
|
5
|
+
class ResolutionChain
|
|
6
|
+
# ADR-119 WD2 — per-name relevance, the narrowing of an unsettled chain (#1591). A {Mark} says a node's mixin
|
|
7
|
+
# edges have no known order; the chain still answers a question about ONE method name when the module the
|
|
8
|
+
# mark names cannot answer that name, because with no fork every entry reached the chain by a single route,
|
|
9
|
+
# so a module of unknown presence or position adds or removes only its own closure's entries and moves no
|
|
10
|
+
# other entry's first occurrence. A closure that cannot answer the name therefore cannot move its first
|
|
11
|
+
# definer. {ResolutionChain#settle} asks it under `unknown_for:`; nothing else does.
|
|
12
|
+
#
|
|
13
|
+
# - A named entry `Q` (not `"*"`) is discharged for `n` when every entry of `Q`'s own instance chain (its
|
|
14
|
+
# closure) is clean: (i) a project entry, or an external one RBS knows whose declaration lacks `n`
|
|
15
|
+
# (the test `SourceArity#external_mixin_lacks_method?` applies); (ii) no project entry carries the dynamic
|
|
16
|
+
# mark, lists `"*"` on the instance side or the mark's, or records `method_missing`; (iii) no project
|
|
17
|
+
# entry records `n` in `discovered_methods` (either kind) or in `discovered_method_visibilities`.
|
|
18
|
+
# - A multi-file mark names no entry: it is discharged when at most one of the node's edges' closures is
|
|
19
|
+
# not clean. A mark that is both listed and multi-file must pass both rules.
|
|
20
|
+
# - An ambiguous spelling (`:arity` only: several project modules a compact-header collision left as the
|
|
21
|
+
# spelling's meaning, #986) is discharged when every one of them is clean; any candidate that fails fails it.
|
|
22
|
+
# - `"*"` and a truncated closure are never discharged.
|
|
23
|
+
#
|
|
24
|
+
# The verdict and the edges it read are memoised per `[mark, name]` in the flavor's bucket and the edges are
|
|
25
|
+
# replayed on every call while a dependency recording is active (ADR-46): per tested project entry its class
|
|
26
|
+
# edge and the negative method edge on `Owner#name`, per tested external the negative class edge on the last
|
|
27
|
+
# segment of its spelling, so a new file declaring the module re-checks the consumer.
|
|
28
|
+
module Relevance
|
|
29
|
+
WILDCARD = "*"
|
|
30
|
+
|
|
31
|
+
# The singleton hooks Ruby calls while a module enters (or is asked to enter) another's ancestry. A module
|
|
32
|
+
# that defines one can add methods to the includer, extender or prepender with nothing the tables record
|
|
33
|
+
# on the module (`def self.included(base) = base.attr_reader(:foo)`), so no closure holding one can be
|
|
34
|
+
# said not to answer a name.
|
|
35
|
+
HOOKS = %i[included extended prepended inherited append_features extend_object prepend_features].freeze
|
|
36
|
+
|
|
37
|
+
module_function
|
|
38
|
+
|
|
39
|
+
def discharged?(scope, chain, mark, name)
|
|
40
|
+
name = name.to_sym
|
|
41
|
+
memo = ResolutionChain.relevance_memo(scope, chain.flavor)
|
|
42
|
+
verdict, edges = memo[[mark, name]] ||= compute(scope, chain.flavor, mark, name)
|
|
43
|
+
replay(scope, edges) if Analysis::DependencyRecorder.active?
|
|
44
|
+
verdict
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def compute(scope, flavor, mark, name)
|
|
48
|
+
context = Context.new(scope, flavor, mark, name)
|
|
49
|
+
verdict = !mark.listed.include?(WILDCARD) && listed_discharged?(context) && multi_file_discharged?(context)
|
|
50
|
+
[verdict, context.edges.uniq.freeze].freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def listed_discharged?(context)
|
|
54
|
+
mark = context.mark
|
|
55
|
+
mark.listed.all? { |raw| context.closure_clean?(raw) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def multi_file_discharged?(context)
|
|
59
|
+
mark = context.mark
|
|
60
|
+
return true unless mark.multi_file
|
|
61
|
+
|
|
62
|
+
edges = context.edge_names
|
|
63
|
+
edges.count { |raw| !context.closure_clean?(raw) } <= 1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def replay(scope, edges)
|
|
67
|
+
edges.each do |kind, value|
|
|
68
|
+
case kind
|
|
69
|
+
when :class then ResolutionChain.record_class(scope, value)
|
|
70
|
+
when :method then Analysis::DependencyRecorder.read_missing(:method, value)
|
|
71
|
+
when :external then Analysis::DependencyRecorder.read_missing(:class, value)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# True only for an ancestor the project does not declare that RBS knows and whose declaration lacks `name`
|
|
77
|
+
# (`SourceArity#external_mixin_lacks_method?`); an unknown one, or one that has it, may answer.
|
|
78
|
+
def external_lacks?(scope, candidates, name)
|
|
79
|
+
known = candidates.find { |candidate| Rigor::Reflection.rbs_class_known?(candidate, scope: scope) }
|
|
80
|
+
return false if known.nil?
|
|
81
|
+
|
|
82
|
+
Rigor::Reflection.instance_method_definition(known, name, scope: scope).nil?
|
|
83
|
+
rescue StandardError
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# One verdict's working state: the scope, the resolver, and the edges the tests read.
|
|
88
|
+
class Context
|
|
89
|
+
attr_reader :mark, :edges
|
|
90
|
+
|
|
91
|
+
def initialize(scope, flavor, mark, name)
|
|
92
|
+
@scope = scope
|
|
93
|
+
@flavor = flavor
|
|
94
|
+
@mark = mark
|
|
95
|
+
@name = name
|
|
96
|
+
@resolver = ResolutionChain.resolver_for(scope, flavor)
|
|
97
|
+
@edges = []
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The raw names of the node's own edges on the side the mark covers.
|
|
101
|
+
def edge_names
|
|
102
|
+
discovery = @scope.discovery
|
|
103
|
+
table = @mark.kind == :extend ? discovery.discovered_extends : discovery.discovered_includes
|
|
104
|
+
table[@mark.node] || EMPTY_NAMES
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Whether the closure of the module `raw` names from the marked node cannot answer the name.
|
|
108
|
+
def closure_clean?(raw)
|
|
109
|
+
resolved = @resolver.resolve(@mark.node, raw)
|
|
110
|
+
case resolved
|
|
111
|
+
when String then project_closure_clean?(resolved)
|
|
112
|
+
when Array then resolved.all? { |name| project_closure_clean?(name) }
|
|
113
|
+
else external_clean?(@resolver.candidates(@mark.node, raw), raw)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def project_closure_clean?(module_name)
|
|
120
|
+
chain = ResolutionChain.for(@scope, module_name, :instance, @flavor)
|
|
121
|
+
return false if chain.truncated?
|
|
122
|
+
|
|
123
|
+
chain.entries.all? do |entry|
|
|
124
|
+
entry.external? ? external_clean?(entry.candidates, entry.raw) : project_clean?(entry)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def project_clean?(entry)
|
|
129
|
+
owner = entry.name
|
|
130
|
+
@edges << [:class, owner] << [:method, "#{owner}##{@name}"]
|
|
131
|
+
return false if Scope::DiscoveryIndex.rewritten_surface?(@scope.parameter_envelopes_of(owner))
|
|
132
|
+
return false if wildcard_listed?(owner)
|
|
133
|
+
return false if @scope.discovered_method?(owner, :method_missing, :instance)
|
|
134
|
+
return false if HOOKS.any? { |hook| @scope.discovered_method?(owner, hook, :singleton) }
|
|
135
|
+
|
|
136
|
+
!records_name?(owner)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def wildcard_listed?(owner)
|
|
140
|
+
sides = @scope.discovery.unpositioned_mixins[owner]
|
|
141
|
+
return false if sides.nil?
|
|
142
|
+
|
|
143
|
+
[:include, @mark.kind].uniq.any? { |side| sides[side]&.include?(WILDCARD) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def records_name?(owner)
|
|
147
|
+
@scope.discovered_method?(owner, @name, :instance) || @scope.discovered_method?(owner, @name, :singleton) ||
|
|
148
|
+
!@scope.discovered_method_visibility(owner, @name).nil?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# An ancestor the project does not declare: only a module RBS knows, whose declaration lacks the name,
|
|
152
|
+
# is evidence it does not define it (`SourceArity#external_mixin_lacks_method?`).
|
|
153
|
+
def external_clean?(candidates, raw)
|
|
154
|
+
@edges << [:external, raw.to_s.split("::").last]
|
|
155
|
+
Relevance.external_lacks?(@scope, candidates, @name)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
private_constant :Context
|
|
159
|
+
|
|
160
|
+
EMPTY_NAMES = [].freeze
|
|
161
|
+
private_constant :EMPTY_NAMES
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|