rigortype 0.1.16 → 0.1.18
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/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +18 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +67 -0
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +100 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +226 -0
- data/lib/rigor/analysis/check_rules.rb +180 -73
- data/lib/rigor/analysis/dependency_recorder.rb +122 -0
- data/lib/rigor/analysis/diagnostic.rb +18 -0
- data/lib/rigor/analysis/incremental.rb +162 -0
- data/lib/rigor/analysis/incremental_session.rb +337 -0
- data/lib/rigor/analysis/rule_catalog.rb +48 -0
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +580 -0
- data/lib/rigor/analysis/runner/pool_coordinator.rb +569 -0
- data/lib/rigor/analysis/runner/project_pre_passes.rb +318 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +46 -0
- data/lib/rigor/analysis/runner.rb +477 -1110
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +121 -0
- data/lib/rigor/analysis/worker_session.rb +47 -8
- data/lib/rigor/builtins/static_return_refinements.rb +7 -1
- data/lib/rigor/cache/descriptor.rb +50 -49
- data/lib/rigor/cache/incremental_snapshot.rb +153 -0
- data/lib/rigor/cache/rbs_cache_producer.rb +34 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +2 -8
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -8
- data/lib/rigor/cache/rbs_constant_table.rb +2 -8
- data/lib/rigor/cache/rbs_environment.rb +2 -8
- data/lib/rigor/cache/rbs_known_class_names.rb +2 -8
- data/lib/rigor/cache/store.rb +145 -14
- data/lib/rigor/cli/annotate_command.rb +2 -7
- data/lib/rigor/cli/baseline_command.rb +2 -7
- data/lib/rigor/cli/check_command.rb +705 -0
- data/lib/rigor/cli/ci_detector.rb +94 -0
- data/lib/rigor/cli/command.rb +47 -0
- data/lib/rigor/cli/coverage_command.rb +3 -23
- data/lib/rigor/cli/coverage_renderer.rb +3 -8
- data/lib/rigor/cli/diagnostic_formats.rb +345 -0
- data/lib/rigor/cli/diff_command.rb +3 -7
- data/lib/rigor/cli/explain_command.rb +2 -7
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mcp_command.rb +3 -7
- data/lib/rigor/cli/options.rb +57 -0
- data/lib/rigor/cli/plugin_command.rb +3 -7
- data/lib/rigor/cli/plugins_command.rb +2 -7
- data/lib/rigor/cli/prism_colorizer.rb +10 -3
- data/lib/rigor/cli/renderable.rb +26 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -7
- data/lib/rigor/cli/skill_command.rb +3 -7
- data/lib/rigor/cli/trace_command.rb +143 -0
- data/lib/rigor/cli/trace_renderer.rb +310 -0
- data/lib/rigor/cli/triage_command.rb +2 -7
- data/lib/rigor/cli/type_of_command.rb +5 -38
- data/lib/rigor/cli/type_of_renderer.rb +4 -9
- data/lib/rigor/cli/type_scan_command.rb +3 -23
- data/lib/rigor/cli/type_scan_renderer.rb +4 -9
- data/lib/rigor/cli.rb +15 -532
- data/lib/rigor/configuration/dependencies.rb +18 -1
- data/lib/rigor/configuration/severity_profile.rb +22 -3
- data/lib/rigor/configuration.rb +16 -3
- data/lib/rigor/environment/rbs_loader.rb +129 -71
- data/lib/rigor/environment.rb +1 -1
- data/lib/rigor/inference/acceptance.rb +10 -0
- data/lib/rigor/inference/block_parameter_binder.rb +1 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/complex_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/date_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/encoding_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/hash_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/method_catalog.rb +15 -0
- data/lib/rigor/inference/builtins/numeric_catalog.rb +21 -93
- data/lib/rigor/inference/builtins/pathname_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/proc_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/range_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/rational_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/re_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/set_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/string_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/struct_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/time_catalog.rb +2 -5
- data/lib/rigor/inference/expression_typer.rb +149 -63
- data/lib/rigor/inference/flow_tracer.rb +180 -0
- data/lib/rigor/inference/macro_block_self_type.rb +10 -11
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +5 -1
- data/lib/rigor/inference/method_dispatcher/call_context.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +11 -10
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +12 -6
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +246 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +12 -7
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +33 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +23 -13
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +120 -9
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +12 -12
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +49 -0
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher.rb +185 -84
- data/lib/rigor/inference/narrowing.rb +262 -5
- data/lib/rigor/inference/scope_indexer.rb +208 -21
- data/lib/rigor/inference/statement_evaluator.rb +110 -48
- data/lib/rigor/language_server/buffer_resolution.rb +33 -0
- data/lib/rigor/language_server/completion_provider.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +4 -4
- data/lib/rigor/language_server/folding_range_provider.rb +4 -4
- data/lib/rigor/language_server/hover_provider.rb +4 -4
- data/lib/rigor/language_server/selection_range_provider.rb +4 -4
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/plugin/additional_initializer.rb +61 -38
- data/lib/rigor/plugin/base.rb +302 -45
- data/lib/rigor/plugin/node_rule_walk.rb +147 -0
- data/lib/rigor/plugin/registry.rb +281 -15
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/rbs_extended/conformance_checker.rb +293 -0
- data/lib/rigor/rbs_extended.rb +39 -0
- data/lib/rigor/scope/discovery_index.rb +58 -0
- data/lib/rigor/scope.rb +150 -167
- data/lib/rigor/sig_gen/observation_collector.rb +6 -6
- data/lib/rigor/source/literals.rb +14 -0
- data/lib/rigor/type/acceptance_router.rb +19 -0
- data/lib/rigor/type/accepts_result.rb +3 -10
- data/lib/rigor/type/app.rb +3 -7
- data/lib/rigor/type/bot.rb +2 -3
- data/lib/rigor/type/bound_method.rb +5 -12
- data/lib/rigor/type/combinator.rb +22 -0
- data/lib/rigor/type/constant.rb +2 -3
- data/lib/rigor/type/data_class.rb +80 -0
- data/lib/rigor/type/data_instance.rb +100 -0
- data/lib/rigor/type/difference.rb +5 -10
- data/lib/rigor/type/dynamic.rb +5 -10
- data/lib/rigor/type/hash_shape.rb +5 -15
- data/lib/rigor/type/integer_range.rb +5 -10
- data/lib/rigor/type/intersection.rb +5 -10
- data/lib/rigor/type/nominal.rb +5 -10
- data/lib/rigor/type/refined.rb +5 -10
- data/lib/rigor/type/singleton.rb +5 -10
- data/lib/rigor/type/top.rb +2 -3
- data/lib/rigor/type/tuple.rb +5 -10
- data/lib/rigor/type/union.rb +5 -10
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/value_semantics.rb +77 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +2 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +70 -32
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +3 -3
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +15 -21
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +12 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +12 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +35 -18
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +8 -29
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +17 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +83 -36
- data/sig/rigor/cache.rbs +19 -0
- data/sig/rigor/environment.rbs +0 -2
- data/sig/rigor/inference.rbs +27 -0
- data/sig/rigor/plugin/base.rbs +1 -2
- data/sig/rigor/rbs_extended.rbs +2 -0
- data/sig/rigor/scope.rbs +42 -25
- data/sig/rigor/source.rbs +1 -0
- data/sig/rigor/type.rbs +58 -1
- data/sig/rigor.rbs +6 -1
- data/skills/rigor-ci-setup/SKILL.md +319 -0
- metadata +36 -2
- data/lib/rigor/cache/rbs_instance_definitions.rb +0 -79
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "uri"
|
|
4
4
|
require_relative "../../type"
|
|
5
|
+
require_relative "singleton_folding"
|
|
5
6
|
|
|
6
7
|
module Rigor
|
|
7
8
|
module Inference
|
|
@@ -40,24 +41,23 @@ module Rigor
|
|
|
40
41
|
module_function
|
|
41
42
|
|
|
42
43
|
# @return [Rigor::Type, nil] folded result, or nil to defer.
|
|
43
|
-
def try_dispatch(
|
|
44
|
-
|
|
44
|
+
def try_dispatch(context)
|
|
45
|
+
receiver = context.receiver
|
|
46
|
+
method_name = context.method_name
|
|
47
|
+
args = context.args
|
|
48
|
+
return nil unless SingletonFolding.receiver?(receiver, "URI")
|
|
45
49
|
return nil unless URI_COMPONENT_METHODS.include?(method_name)
|
|
46
50
|
|
|
47
51
|
fold_uri_call(method_name, args)
|
|
48
52
|
end
|
|
49
53
|
|
|
50
|
-
def dispatch_target?(receiver)
|
|
51
|
-
receiver.is_a?(Type::Singleton) && receiver.class_name == "URI"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
54
|
def fold_uri_call(method_name, args)
|
|
55
55
|
return nil unless args.size == 1
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
return nil
|
|
57
|
+
str = SingletonFolding.constant_string(args.first)
|
|
58
|
+
return nil if str.nil?
|
|
59
59
|
|
|
60
|
-
Type::Combinator.constant_of(URI.public_send(method_name,
|
|
60
|
+
Type::Combinator.constant_of(URI.public_send(method_name, str))
|
|
61
61
|
rescue StandardError
|
|
62
62
|
nil
|
|
63
63
|
end
|
|
@@ -6,9 +6,12 @@ require_relative "../flow_contribution"
|
|
|
6
6
|
require_relative "../flow_contribution/merger"
|
|
7
7
|
require_relative "../builtins/hkt_builtins"
|
|
8
8
|
require_relative "../builtins/static_return_refinements"
|
|
9
|
+
require_relative "flow_tracer"
|
|
10
|
+
require_relative "method_dispatcher/call_context"
|
|
9
11
|
require_relative "method_dispatcher/constant_folding"
|
|
10
12
|
require_relative "method_dispatcher/literal_string_folding"
|
|
11
13
|
require_relative "method_dispatcher/shape_dispatch"
|
|
14
|
+
require_relative "method_dispatcher/data_folding"
|
|
12
15
|
require_relative "method_dispatcher/rbs_dispatch"
|
|
13
16
|
require_relative "method_dispatcher/iterator_dispatch"
|
|
14
17
|
require_relative "method_dispatcher/block_folding"
|
|
@@ -71,19 +74,45 @@ module Rigor
|
|
|
71
74
|
# @param environment [Rigor::Environment, nil] required for
|
|
72
75
|
# RBS-backed dispatch; when nil only constant folding can fire.
|
|
73
76
|
# @return [Rigor::Type, nil] inferred result type, or `nil` for "no rule".
|
|
74
|
-
def dispatch(receiver_type:, method_name:, arg_types:,
|
|
77
|
+
def dispatch(receiver_type:, method_name:, arg_types:,
|
|
75
78
|
block_type: nil, environment: nil,
|
|
76
79
|
call_node: nil, scope: nil)
|
|
80
|
+
result = resolve(
|
|
81
|
+
receiver_type: receiver_type, method_name: method_name, arg_types: arg_types,
|
|
82
|
+
block_type: block_type, environment: environment,
|
|
83
|
+
call_node: call_node, scope: scope
|
|
84
|
+
)
|
|
85
|
+
# `rigor trace` — record the dispatch outcome (resolved type, or
|
|
86
|
+
# the fail-soft `nil` the caller widens to `Dynamic[Top]`).
|
|
87
|
+
if FlowTracer.active?
|
|
88
|
+
FlowTracer.dispatch(
|
|
89
|
+
receiver: receiver_type, method_name: method_name, args: arg_types,
|
|
90
|
+
result: result, location: call_node&.location
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
result
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def resolve(receiver_type:, method_name:, arg_types:, # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
97
|
+
block_type: nil, environment: nil,
|
|
98
|
+
call_node: nil, scope: nil)
|
|
77
99
|
return nil if receiver_type.nil?
|
|
78
100
|
|
|
79
|
-
|
|
101
|
+
# Build the call context once and thread it — unchanged —
|
|
102
|
+
# through every tier (`_DispatchTier#try_dispatch`). The
|
|
103
|
+
# dispatcher's own private fallback tiers still read the
|
|
104
|
+
# positional locals below; only the tier modules consume the
|
|
105
|
+
# context object.
|
|
106
|
+
context = CallContext.build(
|
|
80
107
|
receiver: receiver_type, method_name: method_name, args: arg_types,
|
|
81
108
|
block_type: block_type, environment: environment,
|
|
82
109
|
call_node: call_node, scope: scope
|
|
83
110
|
)
|
|
111
|
+
|
|
112
|
+
bound_method_result = MethodFolding.try_backward(context)
|
|
84
113
|
return bound_method_result if bound_method_result
|
|
85
114
|
|
|
86
|
-
precise = dispatch_precise_tiers(
|
|
115
|
+
precise = dispatch_precise_tiers(context)
|
|
87
116
|
return precise if precise
|
|
88
117
|
|
|
89
118
|
# v0.1.1 Track 2 slice 7 — plugin return-type contribution
|
|
@@ -123,10 +152,7 @@ module Rigor
|
|
|
123
152
|
static_refinement = try_static_refinement(receiver_type, method_name, arg_types)
|
|
124
153
|
return static_refinement if static_refinement
|
|
125
154
|
|
|
126
|
-
rbs_result = RbsDispatch.try_dispatch(
|
|
127
|
-
receiver: receiver_type, method_name: method_name, args: arg_types,
|
|
128
|
-
environment: environment, block_type: block_type, scope: scope
|
|
129
|
-
)
|
|
155
|
+
rbs_result = RbsDispatch.try_dispatch(context)
|
|
130
156
|
if rbs_result
|
|
131
157
|
record_boundary_cross_if_applicable(receiver_type, method_name, rbs_result, environment)
|
|
132
158
|
return rbs_result
|
|
@@ -211,7 +237,7 @@ module Rigor
|
|
|
211
237
|
# introspection (`attr_reader`, `private`, ...) on
|
|
212
238
|
# user classes without requiring the user to author
|
|
213
239
|
# their own RBS.
|
|
214
|
-
try_user_class_fallback(receiver_type,
|
|
240
|
+
try_user_class_fallback(receiver_type, environment, call_node, context)
|
|
215
241
|
end
|
|
216
242
|
|
|
217
243
|
# v0.1.3 — discovered-method dispatch tier. `scope` carries
|
|
@@ -292,12 +318,12 @@ module Rigor
|
|
|
292
318
|
end
|
|
293
319
|
|
|
294
320
|
# ADR-2 § "Flow Contribution Bundle" / v0.1.1 Track 2
|
|
295
|
-
# slice 7
|
|
296
|
-
#
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
321
|
+
# slice 7; ADR-52 WD3 — consults each loaded plugin's gated
|
|
322
|
+
# `dynamic_return` rules, wraps the contributed types as
|
|
323
|
+
# `FlowContribution` bundles, merges them through
|
|
324
|
+
# `FlowContribution::Merger`, and returns the merged
|
|
325
|
+
# `return_type` slot (or nil when no plugin contributed a
|
|
326
|
+
# return type).
|
|
301
327
|
#
|
|
302
328
|
# Plugins whose hook raises have their contribution
|
|
303
329
|
# silently dropped for this call so the dispatch chain
|
|
@@ -467,8 +493,10 @@ module Rigor
|
|
|
467
493
|
|
|
468
494
|
module_type = Type::Combinator.nominal_of(module_name)
|
|
469
495
|
RbsDispatch.try_dispatch(
|
|
470
|
-
|
|
471
|
-
|
|
496
|
+
CallContext.build(
|
|
497
|
+
receiver: module_type, method_name: method_name, args: arg_types,
|
|
498
|
+
environment: environment, block_type: block_type
|
|
499
|
+
)
|
|
472
500
|
)
|
|
473
501
|
end
|
|
474
502
|
|
|
@@ -635,23 +663,15 @@ module Rigor
|
|
|
635
663
|
Type::Combinator.untyped
|
|
636
664
|
end
|
|
637
665
|
|
|
666
|
+
# ADR-52 WD1 — the per-dispatch plugins × owns_receivers ×
|
|
667
|
+
# `class_ordering` walk moved into the compiled contribution
|
|
668
|
+
# table: the union is built once per registry (almost always
|
|
669
|
+
# empty → O(1) false) and per-class verdicts memoise per run.
|
|
638
670
|
def plugin_owns_receiver?(class_name, environment)
|
|
639
671
|
registry = environment&.plugin_registry
|
|
640
672
|
return false if registry.nil? || registry.empty?
|
|
641
673
|
|
|
642
|
-
registry.
|
|
643
|
-
owns = plugin.manifest.owns_receivers # rigor:disable undefined-method
|
|
644
|
-
owns.any? { |owner| receiver_matches_owner?(class_name, owner, environment) }
|
|
645
|
-
end
|
|
646
|
-
end
|
|
647
|
-
|
|
648
|
-
def receiver_matches_owner?(class_name, owner, environment)
|
|
649
|
-
return true if class_name == owner
|
|
650
|
-
|
|
651
|
-
ordering = environment.class_ordering(class_name, owner)
|
|
652
|
-
%i[equal subclass].include?(ordering)
|
|
653
|
-
rescue StandardError
|
|
654
|
-
false
|
|
674
|
+
registry.contribution_index.owns_receiver?(class_name, environment)
|
|
655
675
|
end
|
|
656
676
|
|
|
657
677
|
def dep_source_class_name(receiver_type)
|
|
@@ -660,22 +680,66 @@ module Rigor
|
|
|
660
680
|
end
|
|
661
681
|
end
|
|
662
682
|
|
|
663
|
-
# ADR-37 slice 2 — gathers each plugin's return-type
|
|
664
|
-
# from
|
|
665
|
-
#
|
|
666
|
-
# `flow_contribution_for` escape valve
|
|
667
|
-
#
|
|
683
|
+
# ADR-37 slice 2 / ADR-52 WD3 — gathers each plugin's return-type
|
|
684
|
+
# contribution from the gated `dynamic_return` DSL, wrapped as a
|
|
685
|
+
# return-only `FlowContribution` for the shared merger. (The legacy
|
|
686
|
+
# ungated `flow_contribution_for` escape valve was deleted once its
|
|
687
|
+
# five users migrated.)
|
|
688
|
+
EMPTY_CONTRIBUTIONS = [].freeze
|
|
689
|
+
private_constant :EMPTY_CONTRIBUTIONS
|
|
690
|
+
|
|
691
|
+
# Collects every plugin's flow / dynamic-return contribution for one
|
|
692
|
+
# call site. Two prunings keep this off the hot path on plugin-heavy
|
|
693
|
+
# projects (it was the #1 allocation site and a top CPU cost):
|
|
694
|
+
#
|
|
695
|
+
# 1. Only the plugins that *structurally* implement a per-call path
|
|
696
|
+
# are visited — `registry.contribution_index.for_method_dispatch`
|
|
697
|
+
# is the registry-ordered subset declaring a `dynamic_return`.
|
|
698
|
+
# Iterating the subset in registry order, and gating each path by
|
|
699
|
+
# membership, yields the exact same contributions in the same
|
|
700
|
+
# order as visiting every plugin would (a skipped plugin's call
|
|
701
|
+
# returns nil/[] anyway). The receiver-class ancestry match still
|
|
702
|
+
# happens per dispatch inside `dynamic_return_type`.
|
|
703
|
+
# 2. Contributions accumulate lazily — allocate only when one
|
|
704
|
+
# actually appears, and share a frozen empty array otherwise. The
|
|
705
|
+
# caller treats the result as read-only (`.empty?` / `Merger.merge`).
|
|
706
|
+
# 3. ADR-52 WD1 — method-name gates compiled at registry build. The
|
|
707
|
+
# global gate makes the common "no plugin cares about this call"
|
|
708
|
+
# case a single Set probe; the per-plugin gate skips a plugin
|
|
709
|
+
# whose `dynamic_return` rules are all `methods:`-gated on other
|
|
710
|
+
# names. A pruned consultation could only have returned nil, so
|
|
711
|
+
# contribution order and content are unchanged.
|
|
668
712
|
def collect_plugin_contributions(registry, call_node, scope, receiver_type)
|
|
669
|
-
registry.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
713
|
+
index = registry.contribution_index
|
|
714
|
+
relevant = index.for_method_dispatch
|
|
715
|
+
return EMPTY_CONTRIBUTIONS if relevant.empty?
|
|
716
|
+
|
|
717
|
+
# `call_node` is not always a CallNode — the `&:symbol` block
|
|
718
|
+
# path dispatches with the `Prism::BlockArgumentNode` itself
|
|
719
|
+
# (`ExpressionTyper#symbol_block_return_type`). A bare `.name`
|
|
720
|
+
# here raised, and the raise was silently absorbed by
|
|
721
|
+
# `block_return_type_for`'s rescue, nil-ing the block type and
|
|
722
|
+
# flipping `select(&:p)`-style calls onto their no-block
|
|
723
|
+
# Enumerator overloads (caught by the GitLab corpus gate).
|
|
724
|
+
name = call_node.respond_to?(:name) ? call_node.name : nil
|
|
725
|
+
return EMPTY_CONTRIBUTIONS unless index.dispatch_candidate?(name)
|
|
726
|
+
|
|
727
|
+
collect_gated_contributions(index, relevant, name, call_node, scope, receiver_type)
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# The post-gate walk, in registry order — the same order the
|
|
731
|
+
# ungated walk used.
|
|
732
|
+
def collect_gated_contributions(index, relevant, name, call_node, scope, receiver_type)
|
|
733
|
+
result = nil
|
|
734
|
+
relevant.each do |plugin|
|
|
735
|
+
next unless index.dynamic_candidate_for?(plugin, name)
|
|
736
|
+
|
|
673
737
|
dynamic = plugin.dynamic_return_type(call_node: call_node, scope: scope, receiver_type: receiver_type)
|
|
674
|
-
|
|
675
|
-
contributions
|
|
738
|
+
(result ||= []) << FlowContribution.new(return_type: dynamic) if dynamic
|
|
676
739
|
rescue StandardError
|
|
677
|
-
|
|
740
|
+
next
|
|
678
741
|
end
|
|
742
|
+
result || EMPTY_CONTRIBUTIONS
|
|
679
743
|
end
|
|
680
744
|
|
|
681
745
|
# Runs the precision tiers (constant fold, shape dispatch,
|
|
@@ -690,37 +754,80 @@ module Rigor
|
|
|
690
754
|
# its rules apply only to block-taking calls, so the cheaper
|
|
691
755
|
# arity-based fold tiers above it filter out the common
|
|
692
756
|
# cases first. When `block_type` is nil the tier is a no-op.
|
|
693
|
-
|
|
694
|
-
|
|
757
|
+
# The precise-tier folders, consulted in order via the uniform
|
|
758
|
+
# `_DispatchTier` interface (`try_dispatch(CallContext) -> Type?`).
|
|
759
|
+
# Order is significant: ConstantFolding's exact-value folds win
|
|
760
|
+
# first, the eight stdlib singleton folders sit in the middle (each
|
|
761
|
+
# gates on a distinct `Singleton` receiver, so their relative order
|
|
762
|
+
# is immaterial), and BlockFolding runs last because its rules only
|
|
763
|
+
# apply to block-taking calls — the cheaper arity folds above it
|
|
764
|
+
# filter the common cases first. Adding a precise tier is a
|
|
765
|
+
# one-line append here rather than another link in a hand-written
|
|
766
|
+
# `||` ladder.
|
|
767
|
+
PRECISE_TIERS_HEAD = Ractor.make_shareable([
|
|
768
|
+
ConstantFolding, LiteralStringFolding, ShapeDispatch
|
|
769
|
+
].freeze)
|
|
770
|
+
private_constant :PRECISE_TIERS_HEAD
|
|
771
|
+
|
|
772
|
+
# ADR-53 re-review follow-up (gate-by-held-key applied to the
|
|
773
|
+
# built-in tiers): the eight stdlib singleton folders are mutually
|
|
774
|
+
# exclusive — each fires only on `Singleton[<its class>]`, the
|
|
775
|
+
# first check in every `try_dispatch` — so at most one can match a
|
|
776
|
+
# given receiver and their relative trial order was never
|
|
777
|
+
# observable. Compiling them into a class-name table turns eight
|
|
778
|
+
# no-op trials per call into one Hash read, skipped entirely when
|
|
779
|
+
# the receiver is not a `Singleton` (the overwhelmingly common
|
|
780
|
+
# case). The table sits where the eight sat in the old flat list:
|
|
781
|
+
# after ShapeDispatch, before KernelDispatch.
|
|
782
|
+
STDLIB_SINGLETON_FOLDERS = Ractor.make_shareable({
|
|
783
|
+
"File" => FileFolding,
|
|
784
|
+
"Shellwords" => ShellwordsFolding,
|
|
785
|
+
"Math" => MathFolding,
|
|
786
|
+
"Time" => TimeFolding,
|
|
787
|
+
"Regexp" => RegexpFolding,
|
|
788
|
+
"CGI" => CGIFolding,
|
|
789
|
+
"URI" => URIFolding,
|
|
790
|
+
"Set" => SetFolding
|
|
791
|
+
}.freeze)
|
|
792
|
+
private_constant :STDLIB_SINGLETON_FOLDERS
|
|
793
|
+
|
|
794
|
+
PRECISE_TIERS_TAIL = Ractor.make_shareable([
|
|
795
|
+
KernelDispatch, MethodFolding, BlockFolding
|
|
796
|
+
].freeze)
|
|
797
|
+
private_constant :PRECISE_TIERS_TAIL
|
|
798
|
+
|
|
799
|
+
def dispatch_precise_tiers(context)
|
|
800
|
+
# ADR-48 — Data value folding runs ahead of meta-introspection:
|
|
801
|
+
# `meta_new` intercepts every `Singleton[*].new` (returning
|
|
802
|
+
# `Nominal`), which would mask a `Data` class's precise instance.
|
|
803
|
+
# The tier only fires on Data receivers (a `Data.define`, a
|
|
804
|
+
# `DataClass`/`DataInstance`, or a `Singleton` with a recorded
|
|
805
|
+
# member layout), so it never shadows meta's Array/Set/Range lifts.
|
|
806
|
+
data_result = DataFolding.try_dispatch(context)
|
|
807
|
+
return data_result if data_result
|
|
808
|
+
|
|
809
|
+
meta_result = try_meta_introspection(context.receiver, context.method_name, context.args)
|
|
695
810
|
return meta_result if meta_result
|
|
696
811
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
812
|
+
PRECISE_TIERS_HEAD.each do |tier|
|
|
813
|
+
result = tier.try_dispatch(context)
|
|
814
|
+
return result if result
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
receiver = context.receiver
|
|
818
|
+
if receiver.is_a?(Type::Singleton) && (folder = STDLIB_SINGLETON_FOLDERS[receiver.class_name])
|
|
819
|
+
result = folder.try_dispatch(context)
|
|
820
|
+
return result if result
|
|
821
|
+
end
|
|
707
822
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
FileFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
714
|
-
ShellwordsFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
715
|
-
MathFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
716
|
-
TimeFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
717
|
-
RegexpFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
718
|
-
CGIFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
719
|
-
URIFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types) ||
|
|
720
|
-
SetFolding.try_dispatch(receiver: receiver_type, method_name: method_name, args: arg_types)
|
|
823
|
+
PRECISE_TIERS_TAIL.each do |tier|
|
|
824
|
+
result = tier.try_dispatch(context)
|
|
825
|
+
return result if result
|
|
826
|
+
end
|
|
827
|
+
nil
|
|
721
828
|
end
|
|
722
829
|
|
|
723
|
-
def try_user_class_fallback(receiver_type,
|
|
830
|
+
def try_user_class_fallback(receiver_type, environment, call_node, context)
|
|
724
831
|
return nil if environment.nil?
|
|
725
832
|
|
|
726
833
|
fallback_receiver = user_class_fallback_receiver(receiver_type, environment)
|
|
@@ -745,13 +852,12 @@ module Rigor
|
|
|
745
852
|
# self / `self.`-receiver calls (`puts`, `raise`, `require`)
|
|
746
853
|
# keep resolving — those are the fallback's intended targets.
|
|
747
854
|
RbsDispatch.try_dispatch(
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
public_only: explicit_non_self_receiver?(call_node)
|
|
855
|
+
context.with(
|
|
856
|
+
receiver: fallback_receiver,
|
|
857
|
+
self_type_override: receiver_type,
|
|
858
|
+
public_only: explicit_non_self_receiver?(call_node),
|
|
859
|
+
call_node: nil, scope: nil
|
|
860
|
+
)
|
|
755
861
|
)
|
|
756
862
|
end
|
|
757
863
|
|
|
@@ -1093,19 +1199,14 @@ module Rigor
|
|
|
1093
1199
|
def expected_block_param_types(receiver_type:, method_name:, arg_types:, environment: nil)
|
|
1094
1200
|
return [] if receiver_type.nil?
|
|
1095
1201
|
|
|
1096
|
-
|
|
1097
|
-
receiver: receiver_type,
|
|
1098
|
-
|
|
1099
|
-
args: arg_types
|
|
1202
|
+
context = CallContext.build(
|
|
1203
|
+
receiver: receiver_type, method_name: method_name,
|
|
1204
|
+
args: arg_types, environment: environment
|
|
1100
1205
|
)
|
|
1206
|
+
iterator_result = IteratorDispatch.block_param_types(context)
|
|
1101
1207
|
return iterator_result if iterator_result
|
|
1102
1208
|
|
|
1103
|
-
RbsDispatch.block_param_types(
|
|
1104
|
-
receiver: receiver_type,
|
|
1105
|
-
method_name: method_name,
|
|
1106
|
-
args: arg_types,
|
|
1107
|
-
environment: environment
|
|
1108
|
-
)
|
|
1209
|
+
RbsDispatch.block_param_types(context)
|
|
1109
1210
|
end
|
|
1110
1211
|
end
|
|
1111
1212
|
end
|