rigortype 0.4.0 → 0.4.2
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 -3
- data/data/builtins/ruby_core/io.yml +1 -0
- data/data/builtins/ruby_core/proc.yml +4 -0
- data/data/builtins/ruby_core/random.yml +4 -0
- data/data/builtins/ruby_core/struct.yml +3 -0
- data/data/core_overlay/env.rbs +34 -0
- data/data/core_overlay/gc.rbs +50 -0
- data/data/core_overlay/hash.rbs +45 -0
- data/data/core_overlay/integer.rbs +31 -0
- data/data/core_overlay/io_buffer.rbs +27 -0
- data/data/core_overlay/kernel.rbs +40 -0
- data/data/core_overlay/match_data.rbs +28 -0
- data/data/core_overlay/module.rbs +51 -0
- data/data/core_overlay/object_space.rbs +29 -0
- data/data/core_overlay/proc.rbs +45 -0
- data/data/core_overlay/range.rbs +40 -0
- data/data/core_overlay/source_range.rbs +78 -0
- data/data/core_overlay/string.rbs +86 -0
- data/data/vendored_gem_sigs/prime/integer-extension.rbs +41 -0
- data/data/vendored_gem_sigs/prime/prime.rbs +372 -0
- data/docs/handbook/01-getting-started.md +3 -3
- data/docs/handbook/06-classes.md +3 -1
- data/docs/handbook/08-understanding-errors.md +3 -2
- data/docs/handbook/11-sig-gen.md +6 -0
- data/docs/handbook/appendix-liskov.md +5 -0
- data/docs/install.md +87 -15
- data/docs/manual/02-cli-reference.md +2 -2
- data/docs/manual/03-configuration.md +6 -1
- data/docs/manual/04-diagnostics.md +2 -1
- data/docs/manual/06-baseline.md +2 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/08-skills.md +4 -3
- data/docs/manual/12-caching.md +43 -4
- data/docs/manual/14-rails-quickstart.md +19 -11
- data/docs/manual/18-removing-dead-code.md +32 -2
- data/docs/manual/plugins/README.md +6 -1
- data/docs/manual/plugins/rigor-activerecord.md +21 -0
- data/docs/manual/plugins/rigor-alba.md +60 -0
- data/docs/manual/plugins/rigor-minitest.md +22 -4
- data/docs/manual/plugins/rigor-typelizer.md +71 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +27 -3
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +21 -92
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +275 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +4 -0
- data/lib/rigor/analysis/check_rules/source_arity.rb +184 -61
- data/lib/rigor/analysis/check_rules.rb +407 -144
- data/lib/rigor/analysis/dependency_recorder.rb +215 -18
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +8 -1
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +21 -0
- data/lib/rigor/analysis/dependency_source_inference/index.rb +36 -11
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +196 -27
- data/lib/rigor/analysis/erb_template_detector.rb +2 -1
- data/lib/rigor/analysis/incremental.rb +29 -9
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +291 -0
- data/lib/rigor/analysis/incremental_run_slot.rb +173 -0
- data/lib/rigor/analysis/incremental_session.rb +287 -50
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
- data/lib/rigor/analysis/reachability/graph.rb +123 -23
- data/lib/rigor/analysis/reachability/scan.rb +290 -36
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -1
- data/lib/rigor/analysis/rule_catalog.rb +43 -0
- data/lib/rigor/analysis/run_cache_key.rb +43 -9
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +88 -6
- data/lib/rigor/analysis/runner/pool_coordinator.rb +52 -13
- data/lib/rigor/analysis/runner/project_pre_passes.rb +21 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +371 -49
- 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_paths.rb +6 -4
- data/lib/rigor/analysis/template_units.rb +15 -5
- data/lib/rigor/analysis/worker_session.rb +11 -7
- data/lib/rigor/cache/descriptor.rb +40 -1
- data/lib/rigor/cache/file_digest.rb +28 -0
- data/lib/rigor/cache/incremental_snapshot.rb +92 -8
- data/lib/rigor/cache/store.rb +63 -3
- data/lib/rigor/cache/value_digest.rb +325 -0
- data/lib/rigor/cli/annotate_command.rb +5 -0
- data/lib/rigor/cli/baseline_command.rb +7 -18
- data/lib/rigor/cli/check_command.rb +85 -14
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/effects_report.rb +2 -2
- data/lib/rigor/cli/trace_command.rb +5 -0
- data/lib/rigor/cli/type_of_command.rb +5 -3
- data/lib/rigor/cli/unused_command.rb +16 -10
- data/lib/rigor/cli.rb +11 -3
- data/lib/rigor/config_audit.rb +3 -2
- data/lib/rigor/config_path.rb +29 -0
- data/lib/rigor/configuration/severity_profile.rb +11 -0
- data/lib/rigor/configuration.rb +52 -7
- data/lib/rigor/effects/plugin_facts.rb +6 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
- data/lib/rigor/environment/gated_signature_guard.rb +43 -0
- data/lib/rigor/environment/gated_signature_plan.rb +339 -0
- data/lib/rigor/environment/lockfile_resolver.rb +10 -1
- data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
- data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
- data/lib/rigor/environment/rbs_loader.rb +284 -15
- data/lib/rigor/environment/required_features.rb +206 -0
- data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
- data/lib/rigor/environment.rb +34 -73
- data/lib/rigor/inference/acceptance.rb +28 -3
- data/lib/rigor/inference/block_parameter_binder.rb +29 -13
- data/lib/rigor/inference/builtins/cruby_definers.rb +61 -0
- data/lib/rigor/inference/builtins/method_catalog.rb +3 -0
- data/lib/rigor/inference/captured_locals.rb +7 -1
- 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 +26 -3
- data/lib/rigor/inference/definer_resolution.rb +266 -0
- data/lib/rigor/inference/expression_typer.rb +351 -61
- data/lib/rigor/inference/external_ancestor_resolution.rb +13 -16
- data/lib/rigor/inference/global_write_census.rb +7 -4
- data/lib/rigor/inference/guard_rebinding.rb +54 -13
- data/lib/rigor/inference/in_effect_refinements/proc_literals.rb +83 -0
- data/lib/rigor/inference/in_effect_refinements/refine_defs.rb +54 -0
- data/lib/rigor/inference/in_effect_refinements/scope_reads.rb +282 -0
- data/lib/rigor/inference/in_effect_refinements/walk.rb +276 -0
- data/lib/rigor/inference/in_effect_refinements.rb +256 -0
- data/lib/rigor/inference/key_presence_guard/scan.rb +281 -0
- data/lib/rigor/inference/key_presence_guard.rb +303 -0
- data/lib/rigor/inference/macro_block_self_type.rb +73 -14
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/arithmetic_sequence_elements.rb +73 -0
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +68 -0
- data/lib/rigor/inference/method_dispatcher/integer_step_block_params.rb +81 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -0
- data/lib/rigor/inference/method_dispatcher/keyword_arguments.rb +267 -0
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -1
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +122 -105
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +305 -54
- data/lib/rigor/inference/method_dispatcher/splat_arity.rb +71 -0
- data/lib/rigor/inference/method_dispatcher.rb +44 -11
- data/lib/rigor/inference/module_function_state.rb +214 -0
- data/lib/rigor/inference/multi_target_binder.rb +21 -16
- data/lib/rigor/inference/narrowing.rb +166 -22
- data/lib/rigor/inference/object_mixins.rb +180 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +10 -9
- data/lib/rigor/inference/project_patched_methods.rb +10 -5
- data/lib/rigor/inference/project_patched_scanner.rb +71 -3
- data/lib/rigor/inference/refine_census.rb +239 -0
- data/lib/rigor/inference/refine_self.rb +317 -0
- data/lib/rigor/inference/refined_dispatch/own_method.rb +403 -0
- data/lib/rigor/inference/refined_dispatch.rb +272 -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 +143 -0
- data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
- data/lib/rigor/inference/scope_indexer.rb +2129 -354
- data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
- data/lib/rigor/inference/statement_evaluator.rb +149 -32
- data/lib/rigor/inference/string_mutation.rb +9 -3
- data/lib/rigor/inference/synthetic_method_scanner.rb +2 -1
- data/lib/rigor/inference/version_guard.rb +19 -10
- data/lib/rigor/language_server/project_context.rb +31 -0
- data/lib/rigor/language_server/server.rb +18 -0
- data/lib/rigor/plugin/base.rb +28 -8
- data/lib/rigor/plugin/inflector.rb +1 -1
- data/lib/rigor/plugin/io_boundary.rb +124 -7
- data/lib/rigor/plugin/isolation.rb +1 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +51 -8
- data/lib/rigor/plugin/trust_policy.rb +3 -3
- data/lib/rigor/project_environment.rb +29 -6
- data/lib/rigor/protection/discovery_seed.rb +18 -9
- data/lib/rigor/protection/mutation_cache.rb +4 -4
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/reflection/constant_ancestors.rb +211 -46
- data/lib/rigor/reflection/constant_path.rb +33 -7
- data/lib/rigor/reflection.rb +41 -10
- data/lib/rigor/scope/discovery_index.rb +258 -3
- data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
- data/lib/rigor/scope/resolution_chain.rb +1032 -0
- data/lib/rigor/scope.rb +480 -328
- data/lib/rigor/sig_gen/alias_index.rb +1 -1
- data/lib/rigor/sig_gen/classification.rb +4 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +2 -2
- data/lib/rigor/sig_gen/generator.rb +77 -21
- data/lib/rigor/sig_gen/method_candidate.rb +2 -2
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +12 -0
- data/lib/rigor/source/alias_names.rb +52 -0
- data/lib/rigor/source/literals.rb +3 -2
- data/lib/rigor/source/symbol_name.rb +20 -0
- data/lib/rigor/type/combinator.rb +59 -3
- data/lib/rigor/type/float_range.rb +4 -1
- data/lib/rigor/type/nominal.rb +6 -1
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-ac-library-rb/lib/rigor/plugin/ac_library_rb.rb +28 -0
- data/plugins/rigor-ac-library-rb/lib/rigor-ac-library-rb.rb +3 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/convolution.rbs +7 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/data_structures.rbs +160 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/graph.rbs +94 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/math.rbs +42 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/modint.rbs +71 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/object.rbs +37 -0
- data/plugins/rigor-ac-library-rb/sig/ac_library_rb/segtree.rbs +83 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +100 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +94 -10
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +43 -2
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +3 -1
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +149 -0
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +74 -0
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +104 -0
- data/plugins/rigor-alba/lib/rigor/plugin/alba.rb +85 -0
- data/plugins/rigor-alba/lib/rigor-alba.rb +3 -0
- data/plugins/rigor-alba/sig/alba.rbs +4 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +2 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +29 -12
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +13 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +130 -21
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +6 -4
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +4 -4
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +31 -0
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +102 -0
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +87 -0
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +76 -0
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +73 -0
- data/plugins/rigor-typelizer/lib/rigor-typelizer.rb +3 -0
- data/sig/rigor/analysis/dependency_source_inference/index.rbs +1 -1
- data/sig/rigor/environment.rbs +2 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/scope.rbs +69 -4
- data/sig/rigor/source.rbs +4 -0
- data/sig/rigor.rbs +3 -1
- data/skills/rigor-project-init/SKILL.md +37 -11
- data/skills/rigor-project-init/references/02-configure.md +7 -2
- metadata +90 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Inference
|
|
5
|
+
# ADR-119 WD2 — the candidate-set read over {Scope::ResolutionChain}, for a reader that can decline. It is
|
|
6
|
+
# internal (not a `Scope` method, so the pinned public surface is unchanged) and answers one of three
|
|
7
|
+
# things about a method name on a class:
|
|
8
|
+
#
|
|
9
|
+
# - {Known} — every candidate gives the same answer to the question asked;
|
|
10
|
+
# - {UNKNOWN} — the chain does not stand for this name (a mark not discharged, a fork, a retro world that
|
|
11
|
+
# answers otherwise), a candidate disagrees, the walk was cut by the budget, an external ancestor ahead of
|
|
12
|
+
# the candidate may answer first (one RBS does not know, or knows and declares the name in; one it knows
|
|
13
|
+
# and whose declaration lacks the name is skipped);
|
|
14
|
+
# - {ABSENT} — no project entry, no external entry and not the implicit `Object` can answer.
|
|
15
|
+
#
|
|
16
|
+
# On the singleton side (ADR-119 WD3, errata 2026-10-08) the chain is `K`'s singleton, its extends' instance
|
|
17
|
+
# chains, then the superclass's singleton chain; an external superclass entry stands for its whole tail and is
|
|
18
|
+
# tested against RBS's singleton side, an extended external module against its instance side, the implicit tail
|
|
19
|
+
# after a project superclass against `Object`'s singleton side. A standing chain is then read past
|
|
20
|
+
# {SingletonHookDecline}' positional decline: a hook's singleton-side edge is recorded on no includer, so the read
|
|
21
|
+
# declines where one could have landed ahead of its last candidate.
|
|
22
|
+
#
|
|
23
|
+
# THE CALL SITE IS A CONTRACT. A result is consumed only by an exhaustive `case/in` that is the call's own
|
|
24
|
+
# direct predicate, with one arm per answer and no `else` or `in _`, and is never stored, returned or
|
|
25
|
+
# truth-tested: {UNKNOWN} and {ABSENT} are both truthy, so any other use folds a decline into a firing arm.
|
|
26
|
+
# `spec/rigor/inference/definer_resolution_case_in_spec.rb` fails on a call site of any other shape.
|
|
27
|
+
#
|
|
28
|
+
# It migrates one question at a time: `SourceArity` (C1b) and the relationship lints (C1c) read the instance
|
|
29
|
+
# side; no production reader reads the singleton side yet (C2-a landed it for C2's typing sites).
|
|
30
|
+
module DefinerResolution
|
|
31
|
+
Known = Data.define(:answer, :owner)
|
|
32
|
+
UnknownResult = Data.define
|
|
33
|
+
AbsentResult = Data.define
|
|
34
|
+
UNKNOWN = UnknownResult.new
|
|
35
|
+
ABSENT = AbsentResult.new
|
|
36
|
+
|
|
37
|
+
# One candidate definer as a per-chain answer function reports it: the `answer` to the question, the
|
|
38
|
+
# `owner` entry's name, the `index` of that entry on the chain and its `side` (`:instance` or `:singleton`).
|
|
39
|
+
Hit = Data.define(:answer, :owner, :index, :side)
|
|
40
|
+
|
|
41
|
+
QUESTIONS = %i[definer visibility arity override own_side].freeze
|
|
42
|
+
NONE = :rigor_definer_resolution_none
|
|
43
|
+
STRAY = :rigor_definer_resolution_stray
|
|
44
|
+
private_constant :NONE, :STRAY
|
|
45
|
+
|
|
46
|
+
module_function
|
|
47
|
+
|
|
48
|
+
# `answer_in` is the question's per-chain answer function: called with a chain and a position, it returns
|
|
49
|
+
# the {Hit} of the first project entry at or after the position that defines `method_name`, or nil. `:definer`
|
|
50
|
+
# and `:visibility` have a default; every other question supplies its own. It is called once per candidate
|
|
51
|
+
# on the chain, and again on the retro world when the chain has one fork, where `from` is mapped to the
|
|
52
|
+
# entry it followed.
|
|
53
|
+
def resolve(scope, class_name, method_name, side, question:, from: 0, &answer_in)
|
|
54
|
+
raise ArgumentError, "unknown question #{question.inspect}" unless QUESTIONS.include?(question)
|
|
55
|
+
|
|
56
|
+
answer_in = override_answer(scope, &answer_in) if question == :override
|
|
57
|
+
answer_in ||= default_answer(scope, method_name, question)
|
|
58
|
+
flavor = question == :arity ? :arity : :methods
|
|
59
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s, side, flavor)
|
|
60
|
+
|
|
61
|
+
hits = candidates(scope, chain, method_name, from, answer_in)
|
|
62
|
+
return UNKNOWN if hits.nil?
|
|
63
|
+
|
|
64
|
+
verdict = chain.settle(scope, outcomes(hits), owner: hits.first&.owner, unknown_for: method_name) do |retro|
|
|
65
|
+
retro_from = retro_position(chain, retro, from)
|
|
66
|
+
outcomes(retro_from && candidates(scope, retro, method_name, retro_from, answer_in))
|
|
67
|
+
end
|
|
68
|
+
return UNKNOWN unless verdict == :chain
|
|
69
|
+
return UNKNOWN if side == :singleton && SingletonHookDecline.decline?(scope, chain, method_name, from, hits)
|
|
70
|
+
|
|
71
|
+
collapse(hits)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The `:override` question's answer function, built from the caller's block: that block is NOT a per-chain
|
|
75
|
+
# function but `|owner_name| value`, called on each project entry in order, and its first non-nil `value` is
|
|
76
|
+
# the entry's answer, as `[owner_name, value]` (the owner is part of the answer so that two worlds naming
|
|
77
|
+
# different parents disagree). It is the override lints' parent read: the nearest project ancestor after the
|
|
78
|
+
# class whose block answers.
|
|
79
|
+
def override_answer(scope)
|
|
80
|
+
lambda do |chain, from|
|
|
81
|
+
first_hit(chain, scope, from) do |entry|
|
|
82
|
+
value = yield(entry.name)
|
|
83
|
+
[entry.name, value] unless value.nil?
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# `from` is a position on `chain`; the same position on the retro world is just after the entry it followed.
|
|
89
|
+
# An entry the chain carries twice cannot be matched, which declines.
|
|
90
|
+
def retro_position(chain, retro, from)
|
|
91
|
+
return 0 if from.zero?
|
|
92
|
+
|
|
93
|
+
entry = chain.entries[from - 1]
|
|
94
|
+
return nil if entry.nil? || chain.entries.count(entry) > 1
|
|
95
|
+
|
|
96
|
+
found = retro.entries.index(entry)
|
|
97
|
+
found && (found + 1)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# The first definer and, while that definer is `possible`, the next; a nil joins the set when nothing
|
|
101
|
+
# follows (the absent candidate). A cut chain, and a chain on which an external ancestor ahead of the
|
|
102
|
+
# candidate (or, with no candidate, the implicit `Object`) may define the name, is a decline, answered by
|
|
103
|
+
# nil instead of a set.
|
|
104
|
+
def candidates(scope, chain, method_name, from, answer_in)
|
|
105
|
+
hits = []
|
|
106
|
+
position = from
|
|
107
|
+
loop do
|
|
108
|
+
hit = answer_in.call(chain, position)
|
|
109
|
+
hit = past_fold_copies(scope, chain, method_name, hit, answer_in) if chain.side == :singleton
|
|
110
|
+
return nil if hit.equal?(STRAY)
|
|
111
|
+
|
|
112
|
+
raise ArgumentError, "answer function went backwards" if hit && hit.index < position
|
|
113
|
+
return nil if hit.nil? && chain.truncated?
|
|
114
|
+
|
|
115
|
+
hits << hit
|
|
116
|
+
return decided(scope, chain, method_name, from, hits) if hit.nil? || !possible?(scope, hit, method_name)
|
|
117
|
+
|
|
118
|
+
position = hit.index + 1
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# A class entry answering through a copy the extends fold made is asked past
|
|
123
|
+
# (`SingletonHookDecline.copy_kind`), so the module that wrote the `def` answers at its own position; a copy
|
|
124
|
+
# from a module the chain does not hold on that level is {STRAY}, a decline.
|
|
125
|
+
def past_fold_copies(scope, chain, method_name, hit, answer_in)
|
|
126
|
+
while hit
|
|
127
|
+
case SingletonHookDecline.copy_kind(scope, chain, method_name, hit)
|
|
128
|
+
when :copy then hit = answer_in.call(chain, hit.index + 1)
|
|
129
|
+
when :stray then return STRAY
|
|
130
|
+
else return hit
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
hit
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def decided(scope, chain, method_name, from, hits)
|
|
137
|
+
external_may_answer?(scope, chain, method_name, from, hits.last) ? nil : hits
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# An external entry ahead of the last candidate that RBS does not know, or knows and declares the name in,
|
|
141
|
+
# may be the definer Ruby calls; with no candidate, so may the implicit `Object` (`Kernel`, for the object
|
|
142
|
+
# that every class is). Both decline. Each tested external files the negative class edge on its spelling.
|
|
143
|
+
def external_may_answer?(scope, chain, method_name, from, last)
|
|
144
|
+
stop = last.nil? ? chain.entries.size : last.index
|
|
145
|
+
recording = Analysis::DependencyRecorder.active?
|
|
146
|
+
(from...stop).any? do |index|
|
|
147
|
+
entry = chain.entries[index]
|
|
148
|
+
next false unless entry.external?
|
|
149
|
+
|
|
150
|
+
Analysis::DependencyRecorder.read_missing(:class, entry.raw.to_s.split("::").last) if recording
|
|
151
|
+
!external_lacks?(scope, entry.candidates, entry.side, method_name)
|
|
152
|
+
end || (last.nil? && implicit_object_answers?(scope, chain, method_name))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The implicit tail after a chain's last project superclass: `Object` (with `Kernel` and `BasicObject`) on the
|
|
156
|
+
# instance side, `#<Class:Object>` through `Class`, `Module` and `Kernel` on the singleton side.
|
|
157
|
+
def implicit_object_answers?(scope, chain, method_name)
|
|
158
|
+
!external_lacks?(scope, ["Object"], chain.side, method_name)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# True only for an ancestor RBS knows whose declaration lacks the name, on the side the entry stands for: an
|
|
162
|
+
# external superclass on a singleton chain (`:singleton`, its whole tail) is asked for a singleton method, an
|
|
163
|
+
# extended module (`:instance`) and every instance-side entry for an instance method.
|
|
164
|
+
def external_lacks?(scope, candidates, side, method_name)
|
|
165
|
+
return Scope::ResolutionChain::Relevance.external_lacks?(scope, candidates, method_name) if side == :instance
|
|
166
|
+
|
|
167
|
+
known = candidates.find { |candidate| Rigor::Reflection.rbs_class_known?(candidate, scope: scope) }
|
|
168
|
+
return false if known.nil?
|
|
169
|
+
|
|
170
|
+
Rigor::Reflection.singleton_method_definition(known, method_name, scope: scope).nil?
|
|
171
|
+
rescue StandardError
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# What a candidate set says, for {Scope::ResolutionChain#settle} to compare across worlds.
|
|
176
|
+
def outcomes(hits)
|
|
177
|
+
return NONE if hits.nil?
|
|
178
|
+
|
|
179
|
+
hits.map { |hit| hit.nil? ? NONE : hit.answer }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def collapse(hits)
|
|
183
|
+
first = hits.first
|
|
184
|
+
return ABSENT if hits.all?(&:nil?)
|
|
185
|
+
return UNKNOWN if hits.any?(&:nil?) || hits.any? { |hit| hit.answer != first.answer }
|
|
186
|
+
|
|
187
|
+
Known.new(first.answer, first.owner)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# A definer that rests on a fact the walk could not prove executes: the member's `possible` sibling, or a
|
|
191
|
+
# contested slot of one of the single-valued members.
|
|
192
|
+
def possible?(scope, hit, method_name)
|
|
193
|
+
discovery = scope.discovery
|
|
194
|
+
owner = hit.owner
|
|
195
|
+
kind = hit.side == :singleton ? :singleton : :instance
|
|
196
|
+
nodes = kind == :singleton ? :discovered_singleton_def_nodes : :discovered_def_nodes
|
|
197
|
+
discovery.possible_method?(owner, method_name, kind) ||
|
|
198
|
+
discovery.contested?(nodes, [owner, method_name.to_sym]) ||
|
|
199
|
+
discovery.contested?(:discovered_method_visibilities, [owner, method_name.to_sym]) ||
|
|
200
|
+
discovery.contested?(:discovered_parameter_envelopes, [owner, [kind, method_name.to_sym]])
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def default_answer(scope, method_name, question)
|
|
204
|
+
case question
|
|
205
|
+
when :definer then definer_answer(scope, method_name)
|
|
206
|
+
when :visibility then visibility_answer(scope, method_name)
|
|
207
|
+
else raise ArgumentError, "question #{question.inspect} needs an answer function"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# The first project entry with a `def` of the name, as `[def node, owner]`.
|
|
212
|
+
def definer_answer(scope, method_name)
|
|
213
|
+
lambda do |chain, from|
|
|
214
|
+
first_hit(chain, scope, from) do |entry|
|
|
215
|
+
node = if entry.side == :singleton
|
|
216
|
+
scope.singleton_def_for(entry.name,
|
|
217
|
+
method_name)
|
|
218
|
+
else
|
|
219
|
+
scope.user_def_for(entry.name,
|
|
220
|
+
method_name)
|
|
221
|
+
end
|
|
222
|
+
[node, entry.name] if node
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# The first project entry that records the name (a discovered method, an instance `def` the def table
|
|
228
|
+
# holds, or a visibility change such as `private :foo` recorded for it), with its visibility (`:public` where
|
|
229
|
+
# none was stated).
|
|
230
|
+
def visibility_answer(scope, method_name)
|
|
231
|
+
lambda do |chain, from|
|
|
232
|
+
first_hit(chain, scope, from) do |entry|
|
|
233
|
+
kind = entry.side == :singleton ? :singleton : :instance
|
|
234
|
+
next unless scope.discovered_method?(entry.name, method_name, kind) ||
|
|
235
|
+
(kind == :instance && (scope.user_def_for(entry.name, method_name) ||
|
|
236
|
+
scope.discovered_method_visibility(entry.name, method_name)))
|
|
237
|
+
|
|
238
|
+
scope.discovered_method_visibility(entry.name, method_name) || :public
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def first_hit(chain, scope, from)
|
|
244
|
+
index = from - 1
|
|
245
|
+
found = nil
|
|
246
|
+
chain.search(scope, from) do |entry|
|
|
247
|
+
index += 1
|
|
248
|
+
next if entry.external?
|
|
249
|
+
|
|
250
|
+
answer = yield(entry)
|
|
251
|
+
next if answer.nil?
|
|
252
|
+
|
|
253
|
+
found = Hit.new(answer, entry.name, index, entry.side)
|
|
254
|
+
end
|
|
255
|
+
found
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private_class_method :override_answer, :retro_position, :candidates, :past_fold_copies, :decided,
|
|
259
|
+
:external_may_answer?, :implicit_object_answers?, :external_lacks?,
|
|
260
|
+
:outcomes, :collapse, :possible?,
|
|
261
|
+
:default_answer, :definer_answer, :visibility_answer, :first_hit
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
require_relative "singleton_hook_decline"
|