rigortype 0.1.3 → 0.1.5
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 +154 -33
- data/lib/rigor/analysis/check_rules.rb +10 -18
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +75 -0
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +47 -21
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +32 -3
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference.rb +1 -0
- data/lib/rigor/analysis/diagnostic.rb +0 -2
- data/lib/rigor/analysis/fact_store.rb +26 -6
- data/lib/rigor/analysis/result.rb +11 -3
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +193 -0
- data/lib/rigor/analysis/runner.rb +498 -12
- data/lib/rigor/analysis/worker_session.rb +327 -0
- data/lib/rigor/builtins/imported_refinements.rb +364 -55
- data/lib/rigor/builtins/regex_refinement.rb +17 -12
- data/lib/rigor/cache/descriptor.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +3 -1
- data/lib/rigor/cache/store.rb +39 -6
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +173 -0
- data/lib/rigor/cli/type_of_command.rb +1 -1
- data/lib/rigor/cli/type_scan_renderer.rb +1 -1
- data/lib/rigor/cli/type_scan_report.rb +2 -2
- data/lib/rigor/cli.rb +61 -3
- data/lib/rigor/configuration/dependencies.rb +2 -2
- data/lib/rigor/configuration.rb +131 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
- data/lib/rigor/environment/class_registry.rb +12 -3
- data/lib/rigor/environment/lockfile_resolver.rb +125 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +126 -0
- data/lib/rigor/environment/rbs_coverage_report.rb +112 -0
- data/lib/rigor/environment/rbs_loader.rb +194 -6
- data/lib/rigor/environment/reflection.rb +152 -0
- data/lib/rigor/environment.rb +109 -6
- data/lib/rigor/flow_contribution/conflict.rb +2 -2
- data/lib/rigor/flow_contribution/element.rb +1 -1
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/flow_contribution/merge_result.rb +1 -1
- data/lib/rigor/flow_contribution/merger.rb +3 -3
- data/lib/rigor/flow_contribution.rb +2 -2
- data/lib/rigor/inference/acceptance.rb +35 -1
- data/lib/rigor/inference/block_parameter_binder.rb +0 -2
- data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
- data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
- data/lib/rigor/inference/coverage_scanner.rb +1 -1
- data/lib/rigor/inference/expression_typer.rb +77 -11
- data/lib/rigor/inference/fallback.rb +1 -1
- data/lib/rigor/inference/macro_block_self_type.rb +96 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +3 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -41
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +135 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +7 -12
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +27 -11
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -44
- data/lib/rigor/inference/method_dispatcher.rb +274 -5
- data/lib/rigor/inference/method_parameter_binder.rb +22 -14
- data/lib/rigor/inference/narrowing.rb +129 -12
- data/lib/rigor/inference/rbs_type_translator.rb +0 -2
- data/lib/rigor/inference/scope_indexer.rb +14 -9
- data/lib/rigor/inference/statement_evaluator.rb +7 -7
- data/lib/rigor/inference/synthetic_method.rb +86 -0
- data/lib/rigor/inference/synthetic_method_index.rb +82 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +521 -0
- data/lib/rigor/plugin/blueprint.rb +60 -0
- data/lib/rigor/plugin/io_boundary.rb +0 -2
- data/lib/rigor/plugin/loader.rb +5 -3
- data/lib/rigor/plugin/macro/block_as_method.rb +131 -0
- data/lib/rigor/plugin/macro/external_file.rb +143 -0
- data/lib/rigor/plugin/macro/heredoc_template.rb +201 -0
- data/lib/rigor/plugin/macro/trait_registry.rb +198 -0
- data/lib/rigor/plugin/macro.rb +31 -0
- data/lib/rigor/plugin/manifest.rb +102 -10
- data/lib/rigor/plugin/registry.rb +43 -2
- data/lib/rigor/plugin/services.rb +1 -1
- data/lib/rigor/plugin/type_node_resolver.rb +52 -0
- data/lib/rigor/plugin.rb +2 -0
- data/lib/rigor/rbs_extended/reporter.rb +91 -0
- data/lib/rigor/rbs_extended.rb +131 -32
- data/lib/rigor/scope.rb +25 -8
- data/lib/rigor/sig_gen/classification.rb +36 -0
- data/lib/rigor/sig_gen/generator.rb +1048 -0
- data/lib/rigor/sig_gen/layout_index.rb +108 -0
- data/lib/rigor/sig_gen/method_candidate.rb +62 -0
- data/lib/rigor/sig_gen/observation_collector.rb +391 -0
- data/lib/rigor/sig_gen/observed_call.rb +62 -0
- data/lib/rigor/sig_gen/path_mapper.rb +116 -0
- data/lib/rigor/sig_gen/renderer.rb +157 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +92 -0
- data/lib/rigor/sig_gen/write_result.rb +48 -0
- data/lib/rigor/sig_gen/writer.rb +530 -0
- data/lib/rigor/sig_gen.rb +25 -0
- data/lib/rigor/trinary.rb +15 -11
- data/lib/rigor/type/bot.rb +6 -3
- data/lib/rigor/type/bound_method.rb +79 -0
- data/lib/rigor/type/combinator.rb +207 -3
- data/lib/rigor/type/constant.rb +13 -0
- data/lib/rigor/type/hash_shape.rb +0 -2
- data/lib/rigor/type/integer_range.rb +7 -7
- data/lib/rigor/type/refined.rb +18 -12
- data/lib/rigor/type/top.rb +4 -3
- data/lib/rigor/type/union.rb +20 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +68 -0
- data/lib/rigor/type_node/identifier.rb +38 -0
- data/lib/rigor/type_node/indexed_access.rb +41 -0
- data/lib/rigor/type_node/integer_literal.rb +29 -0
- data/lib/rigor/type_node/name_scope.rb +52 -0
- data/lib/rigor/type_node/resolver_chain.rb +56 -0
- data/lib/rigor/type_node/string_literal.rb +32 -0
- data/lib/rigor/type_node/symbol_literal.rb +28 -0
- data/lib/rigor/type_node/union.rb +42 -0
- data/lib/rigor/type_node.rb +29 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +2 -0
- data/sig/rigor/analysis/check_rules/always_truthy_condition_collector.rbs +10 -0
- data/sig/rigor/analysis/check_rules/dead_assignment_collector.rbs +10 -0
- data/sig/rigor/analysis/dependency_source_inference/gem_resolver.rbs +25 -0
- data/sig/rigor/analysis/dependency_source_inference/index.rbs +9 -0
- data/sig/rigor/cli/diff_command.rbs +4 -0
- data/sig/rigor/cli/explain_command.rbs +4 -0
- data/sig/rigor/cli/sig_gen_command.rbs +4 -0
- data/sig/rigor/cli/type_scan_command.rbs +3 -0
- data/sig/rigor/environment.rbs +8 -2
- data/sig/rigor/inference/builtins/method_catalog.rbs +4 -0
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +3 -0
- data/sig/rigor/inference/builtins.rbs +2 -0
- data/sig/rigor/plugin/access_denied_error.rbs +3 -0
- data/sig/rigor/plugin/base.rbs +6 -0
- data/sig/rigor/plugin/blueprint.rbs +7 -0
- data/sig/rigor/plugin/fact_store.rbs +11 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/load_error.rbs +6 -0
- data/sig/rigor/plugin/loader.rbs +20 -0
- data/sig/rigor/plugin/manifest.rbs +9 -0
- data/sig/rigor/plugin/registry.rbs +16 -0
- data/sig/rigor/plugin/services.rbs +3 -0
- data/sig/rigor/plugin/trust_policy.rbs +4 -0
- data/sig/rigor/plugin/type_node_resolver.rbs +3 -0
- data/sig/rigor/plugin.rbs +8 -0
- data/sig/rigor/scope.rbs +4 -2
- data/sig/rigor/type.rbs +28 -6
- data/sig/rigor.rbs +35 -2
- metadata +90 -1
|
@@ -55,7 +55,7 @@ module Rigor
|
|
|
55
55
|
# In every conflict case the result keeps the higher-tier value
|
|
56
56
|
# for that slot, records a {Conflict} with both provenances, and
|
|
57
57
|
# continues processing the remaining slots / contributions.
|
|
58
|
-
module Merger
|
|
58
|
+
module Merger
|
|
59
59
|
AUTHORITY_TIERS = {
|
|
60
60
|
builtin: 0,
|
|
61
61
|
rbs_extended: 1,
|
|
@@ -112,7 +112,7 @@ module Rigor
|
|
|
112
112
|
fold_role_conformance(state, contribution)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
def fold_return_type(state, contribution, tier)
|
|
115
|
+
def fold_return_type(state, contribution, tier)
|
|
116
116
|
incoming = contribution.return_type
|
|
117
117
|
return if incoming.nil?
|
|
118
118
|
|
|
@@ -202,7 +202,7 @@ module Rigor
|
|
|
202
202
|
end
|
|
203
203
|
end
|
|
204
204
|
|
|
205
|
-
def build_conflict(target:, edge:, kind:, reason:, provenances:, message:)
|
|
205
|
+
def build_conflict(target:, edge:, kind:, reason:, provenances:, message:)
|
|
206
206
|
Conflict.new(target: target, edge: edge, kind: kind, reason: reason,
|
|
207
207
|
provenances: provenances, message: message)
|
|
208
208
|
end
|
|
@@ -32,7 +32,7 @@ module Rigor
|
|
|
32
32
|
# `descriptor` is the {Rigor::Cache::Descriptor} this
|
|
33
33
|
# contribution attaches to (or `nil` when the contribution does
|
|
34
34
|
# not need its own cache slice).
|
|
35
|
-
Provenance
|
|
35
|
+
class Provenance < Data.define(:source_family, :plugin_id, :node, :descriptor)
|
|
36
36
|
def self.builtin
|
|
37
37
|
new(source_family: :builtin, plugin_id: nil, node: nil, descriptor: nil)
|
|
38
38
|
end
|
|
@@ -122,7 +122,7 @@ module Rigor
|
|
|
122
122
|
# | role_conformance | normal | role | (per-role target) |
|
|
123
123
|
#
|
|
124
124
|
# @return [Array<Element>]
|
|
125
|
-
def to_element_list # rubocop:disable Metrics/AbcSize
|
|
125
|
+
def to_element_list # rubocop:disable Metrics/AbcSize
|
|
126
126
|
elements = []
|
|
127
127
|
elements << element_for(:return, :normal, :return_type, return_type) unless return_type.nil?
|
|
128
128
|
Array(truthy_facts).each { |fact| elements << element_for(fact_target(fact), :truthy, :truthy_fact, fact) }
|
|
@@ -327,10 +327,44 @@ module Rigor
|
|
|
327
327
|
)
|
|
328
328
|
return class_result if class_result.no?
|
|
329
329
|
|
|
330
|
-
|
|
330
|
+
# Parametrized-ancestor projection. When `actual <:= target`
|
|
331
|
+
# holds at the class level but the type-arg arities differ,
|
|
332
|
+
# the actual's parametrization has to be projected into the
|
|
333
|
+
# target's view before the element-wise covariance check.
|
|
334
|
+
# The canonical case is `Hash[K, V] <:= Enumerable[[K, V]]`:
|
|
335
|
+
# Hash carries two type_args, Enumerable carries one, and
|
|
336
|
+
# the inherited parametrization at the Enumerable boundary
|
|
337
|
+
# is `Tuple[K, V]`. RBS encodes this as
|
|
338
|
+
# `include Enumerable[[K, V]]` in `Hash`'s definition.
|
|
339
|
+
projected_other = project_to_target_arity(self_type, other_type) || other_type
|
|
340
|
+
args_result = accepts_nominal_args(self_type, projected_other, mode)
|
|
331
341
|
combine_results(class_result, args_result, mode)
|
|
332
342
|
end
|
|
333
343
|
|
|
344
|
+
# Returns `other_type` rewritten so its type_args have the
|
|
345
|
+
# same arity as `self_type.type_args`, or `nil` if no
|
|
346
|
+
# projection is known. Today only the Hash → Enumerable
|
|
347
|
+
# projection is hand-rolled; a general RBS-driven
|
|
348
|
+
# implementation that consults `definition.ancestors[i].args`
|
|
349
|
+
# for arbitrary subclass / module-include relations is the
|
|
350
|
+
# principled follow-up.
|
|
351
|
+
def project_to_target_arity(self_type, other_type)
|
|
352
|
+
return nil if self_type.type_args.size == other_type.type_args.size
|
|
353
|
+
return nil if self_type.type_args.empty? || other_type.type_args.empty?
|
|
354
|
+
|
|
355
|
+
if self_type.class_name == "Enumerable" &&
|
|
356
|
+
other_type.class_name == "Hash" &&
|
|
357
|
+
self_type.type_args.size == 1 &&
|
|
358
|
+
other_type.type_args.size == 2
|
|
359
|
+
return Type::Combinator.nominal_of(
|
|
360
|
+
"Hash",
|
|
361
|
+
type_args: [Type::Combinator.tuple_of(*other_type.type_args)]
|
|
362
|
+
)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
nil
|
|
366
|
+
end
|
|
367
|
+
|
|
334
368
|
def project_tuple_to_nominal(tuple)
|
|
335
369
|
if tuple.elements.empty?
|
|
336
370
|
Type::Combinator.nominal_of(Array)
|
|
@@ -45,7 +45,6 @@ module Rigor
|
|
|
45
45
|
# scope MUST NOT observe them and the binder leaves them unbound.
|
|
46
46
|
#
|
|
47
47
|
# See docs/internal-spec/inference-engine.md for the binding contract.
|
|
48
|
-
# rubocop:disable Metrics/ClassLength
|
|
49
48
|
class BlockParameterBinder
|
|
50
49
|
# @param expected_param_types [Array<Rigor::Type>] positional block
|
|
51
50
|
# parameter types in order. Indices the binder cannot fill from
|
|
@@ -208,6 +207,5 @@ module Rigor
|
|
|
208
207
|
@expected_param_types[index] || Type::Combinator.untyped
|
|
209
208
|
end
|
|
210
209
|
end
|
|
211
|
-
# rubocop:enable Metrics/ClassLength
|
|
212
210
|
end
|
|
213
211
|
end
|
|
@@ -30,7 +30,16 @@ module Rigor
|
|
|
30
30
|
def initialize(path:, mutating_selectors: {})
|
|
31
31
|
@path = path
|
|
32
32
|
@mutating_selectors = mutating_selectors.transform_values(&:freeze).freeze
|
|
33
|
-
|
|
33
|
+
# ADR-15 Phase 4b.x — eager-load so the instance is
|
|
34
|
+
# safe to `Ractor.make_shareable`. Lazy init via
|
|
35
|
+
# `@catalog ||= load_catalog` would write to a
|
|
36
|
+
# potentially-frozen instance the first time a
|
|
37
|
+
# worker Ractor consults the catalog, raising
|
|
38
|
+
# `FrozenError`. The YAML parse is a once-per-process
|
|
39
|
+
# cost and the catalogs are constructed at module
|
|
40
|
+
# load time anyway, so eager init is free in
|
|
41
|
+
# practice.
|
|
42
|
+
@catalog = load_catalog
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
def safe_for_folding?(class_name, selector, kind: :instance)
|
|
@@ -52,7 +61,7 @@ module Rigor
|
|
|
52
61
|
end
|
|
53
62
|
|
|
54
63
|
def reset!
|
|
55
|
-
@catalog =
|
|
64
|
+
@catalog = load_catalog
|
|
56
65
|
end
|
|
57
66
|
|
|
58
67
|
private
|
|
@@ -72,9 +81,7 @@ module Rigor
|
|
|
72
81
|
per_class.include?(selector.to_sym) || per_class.include?(selector_str.to_sym)
|
|
73
82
|
end
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
@catalog ||= load_catalog
|
|
77
|
-
end
|
|
84
|
+
attr_reader :catalog
|
|
78
85
|
|
|
79
86
|
def load_catalog
|
|
80
87
|
return EMPTY_CATALOG unless File.exist?(@path)
|
|
@@ -68,15 +68,21 @@ module Rigor
|
|
|
68
68
|
# Used by tests to drop the cached catalog so a different
|
|
69
69
|
# path or content can be exercised. Production code MUST
|
|
70
70
|
# NOT call this during normal operation.
|
|
71
|
+
#
|
|
72
|
+
# ADR-15 Phase 4b.x — reset re-loads eagerly so the
|
|
73
|
+
# singleton-class `@catalog` ivar stays populated, and
|
|
74
|
+
# the loaded Hash is deep-shared via `Ractor.make_shareable`
|
|
75
|
+
# so a worker Ractor reading the ivar via `catalog.dig(...)`
|
|
76
|
+
# does not trip `Ractor::IsolationError`. Plain `.freeze`
|
|
77
|
+
# is insufficient: YAML parses to a nested Hash/Array/String
|
|
78
|
+
# graph and only the outer Hash would be frozen.
|
|
71
79
|
def reset!
|
|
72
|
-
@catalog =
|
|
80
|
+
@catalog = Ractor.make_shareable(load_catalog)
|
|
73
81
|
end
|
|
74
82
|
|
|
75
83
|
private
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
@catalog ||= load_catalog
|
|
79
|
-
end
|
|
85
|
+
attr_reader :catalog
|
|
80
86
|
|
|
81
87
|
def load_catalog
|
|
82
88
|
return EMPTY_CATALOG unless File.exist?(CATALOG_PATH)
|
|
@@ -87,6 +93,11 @@ module Rigor
|
|
|
87
93
|
EMPTY_CATALOG
|
|
88
94
|
end
|
|
89
95
|
end
|
|
96
|
+
|
|
97
|
+
# ADR-15 Phase 4b.x — eager-load on the main Ractor at
|
|
98
|
+
# module-load time so worker Ractors only READ the
|
|
99
|
+
# populated singleton-class `@catalog` ivar.
|
|
100
|
+
reset!
|
|
90
101
|
end
|
|
91
102
|
end
|
|
92
103
|
end
|
|
@@ -24,7 +24,7 @@ module Rigor
|
|
|
24
24
|
# hot inference path: it allocates a tracer per visited node and discards
|
|
25
25
|
# the inferred type values.
|
|
26
26
|
class CoverageScanner
|
|
27
|
-
Result
|
|
27
|
+
class Result < Data.define(:visits, :unrecognized, :events)
|
|
28
28
|
# @return [Integer] sum of all visits across node classes.
|
|
29
29
|
def visited_count
|
|
30
30
|
visits.values.sum
|
|
@@ -6,6 +6,7 @@ require_relative "../type"
|
|
|
6
6
|
require_relative "../ast"
|
|
7
7
|
require_relative "block_parameter_binder"
|
|
8
8
|
require_relative "fallback"
|
|
9
|
+
require_relative "macro_block_self_type"
|
|
9
10
|
require_relative "method_dispatcher"
|
|
10
11
|
|
|
11
12
|
module Rigor
|
|
@@ -111,7 +112,21 @@ module Rigor
|
|
|
111
112
|
Prism::IndexOrWriteNode => :type_of_assignment_write,
|
|
112
113
|
Prism::IndexAndWriteNode => :type_of_assignment_write,
|
|
113
114
|
Prism::MultiWriteNode => :type_of_assignment_write,
|
|
115
|
+
# LHS-only target nodes (destructuring assignment, pattern matching,
|
|
116
|
+
# `for x in xs`, block parameter `|a, (b, c)|`). They have no value
|
|
117
|
+
# to extract — the type-of pass acknowledges the node class so the
|
|
118
|
+
# coverage scanner stops flagging it; binding the inner names back
|
|
119
|
+
# into the scope is the StatementEvaluator / MultiTargetBinder /
|
|
120
|
+
# BlockParameterBinder side's concern.
|
|
114
121
|
Prism::LocalVariableTargetNode => :type_of_non_value,
|
|
122
|
+
Prism::MultiTargetNode => :type_of_non_value,
|
|
123
|
+
Prism::InstanceVariableTargetNode => :type_of_non_value,
|
|
124
|
+
Prism::ClassVariableTargetNode => :type_of_non_value,
|
|
125
|
+
Prism::GlobalVariableTargetNode => :type_of_non_value,
|
|
126
|
+
Prism::ConstantTargetNode => :type_of_non_value,
|
|
127
|
+
Prism::ConstantPathTargetNode => :type_of_non_value,
|
|
128
|
+
Prism::CallTargetNode => :type_of_non_value,
|
|
129
|
+
Prism::IndexTargetNode => :type_of_non_value,
|
|
115
130
|
# Hashes and interpolation
|
|
116
131
|
Prism::HashNode => :type_of_hash,
|
|
117
132
|
Prism::KeywordHashNode => :type_of_hash,
|
|
@@ -931,7 +946,6 @@ module Rigor
|
|
|
931
946
|
# for the CallNode itself (the inner type_of calls already record
|
|
932
947
|
# their own fallbacks for unrecognised receivers/args, so the tracer
|
|
933
948
|
# captures both the immediate dispatch miss and the deeper cause).
|
|
934
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
935
949
|
def call_type_for(node)
|
|
936
950
|
receiver = call_receiver_type_for(node)
|
|
937
951
|
arg_types = call_arg_types(node)
|
|
@@ -1004,7 +1018,6 @@ module Rigor
|
|
|
1004
1018
|
|
|
1005
1019
|
fallback_for(node, family: :prism)
|
|
1006
1020
|
end
|
|
1007
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
1008
1021
|
|
|
1009
1022
|
# v0.0.2 #5 — re-types the body of a user-defined
|
|
1010
1023
|
# instance method with the call site's argument types
|
|
@@ -1160,9 +1173,20 @@ module Rigor
|
|
|
1160
1173
|
# when typing the body raises (defensive against malformed
|
|
1161
1174
|
# subtrees); the dispatcher then runs in its no-block-aware
|
|
1162
1175
|
# path.
|
|
1176
|
+
#
|
|
1177
|
+
# ADR-14 gap-#3 (d): a `Prism::BlockArgumentNode` carrying
|
|
1178
|
+
# `&:symbol` (the Symbol#to_proc shorthand) is treated as
|
|
1179
|
+
# a block. The block's return type is computed by
|
|
1180
|
+
# dispatching `:symbol` on the expected block param type
|
|
1181
|
+
# (per `Symbol#to_proc`'s `{ |x| x.symbol }` semantics).
|
|
1182
|
+
# A precise inner dispatch produces the right return; any
|
|
1183
|
+
# failure step falls back to `Dynamic[Top]` so the
|
|
1184
|
+
# dispatcher still SEES a block — selecting the block-
|
|
1185
|
+
# bearing overload of e.g. `Hash#transform_values` over
|
|
1186
|
+
# the no-block overload that returns `Enumerator`.
|
|
1163
1187
|
def block_return_type_for(call_node, receiver_type, arg_types)
|
|
1164
|
-
|
|
1165
|
-
return nil
|
|
1188
|
+
block_arg = call_node.block
|
|
1189
|
+
return nil if block_arg.nil?
|
|
1166
1190
|
return nil if receiver_type.nil?
|
|
1167
1191
|
|
|
1168
1192
|
expected = MethodDispatcher.expected_block_param_types(
|
|
@@ -1171,13 +1195,59 @@ module Rigor
|
|
|
1171
1195
|
arg_types: arg_types,
|
|
1172
1196
|
environment: scope.environment
|
|
1173
1197
|
)
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1198
|
+
# ADR-16 Tier A: when a registered plugin's `block_as_methods`
|
|
1199
|
+
# entry matches `(receiver_type, call_node.name)`, narrow the
|
|
1200
|
+
# block body's `self_type` to the receiver class's instance
|
|
1201
|
+
# type. The narrowing is `nil` for unmatched calls, leaving
|
|
1202
|
+
# the existing scope contract unchanged.
|
|
1203
|
+
narrowed_self = MacroBlockSelfType.narrow_self_type_for(
|
|
1204
|
+
scope: scope, call_node: call_node, receiver_type: receiver_type
|
|
1205
|
+
)
|
|
1206
|
+
block_return_for(block_arg, expected, narrowed_self_type: narrowed_self)
|
|
1177
1207
|
rescue StandardError
|
|
1178
1208
|
nil
|
|
1179
1209
|
end
|
|
1180
1210
|
|
|
1211
|
+
def block_return_for(block_arg, expected, narrowed_self_type: nil)
|
|
1212
|
+
case block_arg
|
|
1213
|
+
when Prism::BlockNode
|
|
1214
|
+
bindings = BlockParameterBinder.new(expected_param_types: expected).bind(block_arg)
|
|
1215
|
+
block_scope = bindings.reduce(scope) { |acc, (name, type)| acc.with_local(name, type) }
|
|
1216
|
+
block_scope = block_scope.with_self_type(narrowed_self_type) if narrowed_self_type
|
|
1217
|
+
type_block_body(block_arg, block_scope)
|
|
1218
|
+
when Prism::BlockArgumentNode
|
|
1219
|
+
symbol_block_return_type(block_arg, expected)
|
|
1220
|
+
end
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
# `&:symbol` desugars to a one-arg Proc that dispatches
|
|
1224
|
+
# `symbol` against its argument. When the param type is
|
|
1225
|
+
# known and the resulting inner dispatch is precise,
|
|
1226
|
+
# this returns the precise carrier; otherwise it
|
|
1227
|
+
# returns `Dynamic[Top]` (still non-nil) so the outer
|
|
1228
|
+
# dispatcher selects the block-bearing overload.
|
|
1229
|
+
# `&proc_local` / `&method(:foo)` and friends — anything
|
|
1230
|
+
# not a bare SymbolNode — still resolve to
|
|
1231
|
+
# `Dynamic[Top]` for the same block-presence signal.
|
|
1232
|
+
def symbol_block_return_type(block_arg, expected_param_types)
|
|
1233
|
+
expression = block_arg.expression
|
|
1234
|
+
return dynamic_top unless expression.is_a?(Prism::SymbolNode)
|
|
1235
|
+
|
|
1236
|
+
param_type = expected_param_types&.first
|
|
1237
|
+
return dynamic_top if param_type.nil?
|
|
1238
|
+
|
|
1239
|
+
result = MethodDispatcher.dispatch(
|
|
1240
|
+
receiver_type: param_type,
|
|
1241
|
+
method_name: expression.unescaped.to_sym,
|
|
1242
|
+
arg_types: [],
|
|
1243
|
+
block_type: nil,
|
|
1244
|
+
environment: scope.environment,
|
|
1245
|
+
call_node: block_arg,
|
|
1246
|
+
scope: scope
|
|
1247
|
+
)
|
|
1248
|
+
result || dynamic_top
|
|
1249
|
+
end
|
|
1250
|
+
|
|
1181
1251
|
def type_block_body(block_node, block_scope)
|
|
1182
1252
|
body = block_node.body
|
|
1183
1253
|
return Type::Combinator.constant_of(nil) if body.nil?
|
|
@@ -1213,7 +1283,6 @@ module Rigor
|
|
|
1213
1283
|
PER_ELEMENT_RANGE_LIMIT = 8
|
|
1214
1284
|
private_constant :PER_ELEMENT_RANGE_LIMIT
|
|
1215
1285
|
|
|
1216
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1217
1286
|
def try_per_element_block_fold(call_node, receiver_type)
|
|
1218
1287
|
return nil unless PER_ELEMENT_TUPLE_METHODS.include?(call_node.name)
|
|
1219
1288
|
return nil if find_family_with_args?(call_node)
|
|
@@ -1231,7 +1300,6 @@ module Rigor
|
|
|
1231
1300
|
|
|
1232
1301
|
assemble_per_element_result(call_node.name, per_position, element_types)
|
|
1233
1302
|
end
|
|
1234
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1235
1303
|
|
|
1236
1304
|
# Returns the per-position element types for a finite,
|
|
1237
1305
|
# statically-known receiver shape — or nil when the
|
|
@@ -1254,7 +1322,6 @@ module Rigor
|
|
|
1254
1322
|
end
|
|
1255
1323
|
end
|
|
1256
1324
|
|
|
1257
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
1258
1325
|
def constant_range_elements(value)
|
|
1259
1326
|
return nil unless value.is_a?(Range)
|
|
1260
1327
|
return nil unless value.begin.is_a?(Integer) && value.end.is_a?(Integer)
|
|
@@ -1264,7 +1331,6 @@ module Rigor
|
|
|
1264
1331
|
|
|
1265
1332
|
value.to_a.map { |v| Type::Combinator.constant_of(v) }
|
|
1266
1333
|
end
|
|
1267
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
1268
1334
|
|
|
1269
1335
|
# `index(value)` and `find_index(value)` carry a positional
|
|
1270
1336
|
# argument and search by `==` rather than running the block.
|
|
@@ -20,7 +20,7 @@ module Rigor
|
|
|
20
20
|
# - inner_type: the Rigor::Type returned to the caller (currently
|
|
21
21
|
# always Dynamic[Top]; later slices may carry richer fallback
|
|
22
22
|
# types).
|
|
23
|
-
Fallback
|
|
23
|
+
class Fallback < Data.define(:node_class, :location, :family, :inner_type)
|
|
24
24
|
def initialize(node_class:, location:, family:, inner_type:)
|
|
25
25
|
raise ArgumentError, "node_class must be a Class, got #{node_class.class}" unless node_class.is_a?(Class)
|
|
26
26
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
# ADR-16 Tier A — engine hook. Consults every registered
|
|
8
|
+
# plugin manifest's `block_as_methods` entries to decide
|
|
9
|
+
# whether a block call site qualifies for `Scope#self_type`
|
|
10
|
+
# narrowing.
|
|
11
|
+
#
|
|
12
|
+
# The match contract for a class-level DSL like Sinatra's
|
|
13
|
+
# `class MyApp < Sinatra::Base; get '/foo' do ... end; end`:
|
|
14
|
+
#
|
|
15
|
+
# - the call's lexical receiver type is `Singleton[X]`
|
|
16
|
+
# (the implicit-self in a class body, or an explicit
|
|
17
|
+
# `MyApp.get(...)` call);
|
|
18
|
+
# - the underlying class `X` equals or inherits from the
|
|
19
|
+
# entry's `receiver_constraint`;
|
|
20
|
+
# - the call's method name is in the entry's `verbs`.
|
|
21
|
+
#
|
|
22
|
+
# On a match the helper returns the **instance** type of
|
|
23
|
+
# the receiver class (`Nominal[X]`) — the narrowed
|
|
24
|
+
# `self_type` for the block body, matching Sinatra's
|
|
25
|
+
# runtime semantics where `Sinatra::Base#generate_method`
|
|
26
|
+
# turns the block into an instance method of the user's
|
|
27
|
+
# app class.
|
|
28
|
+
#
|
|
29
|
+
# Slice 1b ships the floor only (per ADR-16 § WD13):
|
|
30
|
+
# bare-identifier method lookups inside the block resolve
|
|
31
|
+
# through the inference engine's normal `self_type`-driven
|
|
32
|
+
# path, so methods declared on `Sinatra::Base` (RBS or
|
|
33
|
+
# otherwise) become visible. Precision additions —
|
|
34
|
+
# parameter-typed block params, declared per-verb argument
|
|
35
|
+
# contracts — are ceiling concerns for later slices.
|
|
36
|
+
module MacroBlockSelfType
|
|
37
|
+
module_function
|
|
38
|
+
|
|
39
|
+
# @param scope [Rigor::Scope]
|
|
40
|
+
# @param call_node [Prism::CallNode]
|
|
41
|
+
# @param receiver_type [Rigor::Type, nil]
|
|
42
|
+
# @return [Rigor::Type, nil] the narrowed self-type, or
|
|
43
|
+
# `nil` when no registered entry matches the call shape.
|
|
44
|
+
def narrow_self_type_for(scope:, call_node:, receiver_type:)
|
|
45
|
+
return nil if receiver_type.nil?
|
|
46
|
+
|
|
47
|
+
environment = scope&.environment
|
|
48
|
+
registry = environment&.plugin_registry
|
|
49
|
+
return nil if registry.nil? || registry.empty?
|
|
50
|
+
|
|
51
|
+
receiver_class_name = singleton_receiver_class_name(receiver_type)
|
|
52
|
+
return nil if receiver_class_name.nil?
|
|
53
|
+
|
|
54
|
+
verb = call_node.name
|
|
55
|
+
registry.plugins.each do |plugin|
|
|
56
|
+
plugin.manifest.block_as_methods.each do |entry| # rigor:disable undefined-method
|
|
57
|
+
return instance_type_for(receiver_class_name, environment) if matches?(entry, verb, receiver_class_name,
|
|
58
|
+
environment)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Tier A's match contract is intentionally narrow:
|
|
65
|
+
# class-level DSL calls (receiver is `Singleton[X]`) only.
|
|
66
|
+
# Instance-receiver calls and DSL forms whose block body
|
|
67
|
+
# binds a different `self` (Concern's `included do`,
|
|
68
|
+
# `instance_eval { ... }`) are handled by later slices
|
|
69
|
+
# (Concern walker, Tier D, etc.) — not Tier A.
|
|
70
|
+
def singleton_receiver_class_name(receiver_type)
|
|
71
|
+
return nil unless receiver_type.is_a?(Type::Singleton)
|
|
72
|
+
|
|
73
|
+
receiver_type.class_name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def matches?(entry, verb, receiver_class_name, environment)
|
|
77
|
+
return false unless entry.verbs.include?(verb)
|
|
78
|
+
|
|
79
|
+
receiver_class_inherits_from?(receiver_class_name, entry.receiver_constraint, environment)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def receiver_class_inherits_from?(class_name, constraint, environment)
|
|
83
|
+
return true if class_name == constraint
|
|
84
|
+
|
|
85
|
+
ordering = environment.class_ordering(class_name, constraint)
|
|
86
|
+
%i[equal subclass].include?(ordering)
|
|
87
|
+
rescue StandardError
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def instance_type_for(class_name, environment)
|
|
92
|
+
environment.nominal_for_name(class_name) || Type::Nominal.new(class_name)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -37,7 +37,7 @@ module Rigor
|
|
|
37
37
|
# tuple — element-wise block re-evaluation against
|
|
38
38
|
# `Constant<Array>` receivers (the `map` / `filter_map` /
|
|
39
39
|
# `flat_map` precision tier) is reserved for a later slice.
|
|
40
|
-
module BlockFolding
|
|
40
|
+
module BlockFolding
|
|
41
41
|
module_function
|
|
42
42
|
|
|
43
43
|
FILTER_KEEP_ON_TRUTHY = Set[:select, :filter, :take_while].freeze
|
|
@@ -69,7 +69,6 @@ module Rigor
|
|
|
69
69
|
# the call's block. `nil` means "no block at the call site"
|
|
70
70
|
# and disqualifies every rule here.
|
|
71
71
|
# @return [Rigor::Type, nil]
|
|
72
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
73
72
|
def try_fold(receiver:, method_name:, args:, block_type:)
|
|
74
73
|
return nil if receiver.nil? || block_type.nil?
|
|
75
74
|
|
|
@@ -86,7 +85,6 @@ module Rigor
|
|
|
86
85
|
fold_count(receiver, truthiness, args)
|
|
87
86
|
end
|
|
88
87
|
end
|
|
89
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
90
88
|
|
|
91
89
|
def filter_method?(method_name)
|
|
92
90
|
FILTER_KEEP_ON_TRUTHY.include?(method_name) ||
|
|
@@ -154,7 +152,7 @@ module Rigor
|
|
|
154
152
|
end
|
|
155
153
|
|
|
156
154
|
# @return [:always_true, :always_false, :bool, nil]
|
|
157
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
155
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
158
156
|
def predicate_decision(method_name, truthiness, emptiness)
|
|
159
157
|
case method_name
|
|
160
158
|
when :all?
|
|
@@ -177,7 +175,7 @@ module Rigor
|
|
|
177
175
|
:bool
|
|
178
176
|
end
|
|
179
177
|
end
|
|
180
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
178
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
181
179
|
|
|
182
180
|
def bool_union
|
|
183
181
|
Type::Combinator.union(
|
|
@@ -149,7 +149,6 @@ module Rigor
|
|
|
149
149
|
# and runs the format. Symbol keys are kept as
|
|
150
150
|
# Symbols (matching Ruby's `%{key}` resolution).
|
|
151
151
|
# Anything else declines so the RBS tier widens.
|
|
152
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
153
152
|
def try_fold_string_format(receiver, method_name, args)
|
|
154
153
|
return nil unless method_name == :%
|
|
155
154
|
return nil unless args.size == 1
|
|
@@ -166,7 +165,6 @@ module Rigor
|
|
|
166
165
|
rescue StandardError
|
|
167
166
|
nil
|
|
168
167
|
end
|
|
169
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
170
168
|
|
|
171
169
|
def format_argument_value(arg)
|
|
172
170
|
case arg
|
|
@@ -275,7 +273,6 @@ module Rigor
|
|
|
275
273
|
[result]
|
|
276
274
|
end
|
|
277
275
|
|
|
278
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
279
276
|
def try_fold_unary_set(receiver_values, method_name)
|
|
280
277
|
range_lift = try_fold_range_constant_unary(receiver_values, method_name)
|
|
281
278
|
return range_lift if range_lift
|
|
@@ -299,8 +296,6 @@ module Rigor
|
|
|
299
296
|
end
|
|
300
297
|
build_constant_type(results, source: receiver_values)
|
|
301
298
|
end
|
|
302
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
303
|
-
|
|
304
299
|
# v0.0.7 — `Constant<Range>#to_a` and the no-arg
|
|
305
300
|
# `first` / `last` / `min` / `max` short-circuit through a
|
|
306
301
|
# Range-specific arm that catalog dispatch cannot reach:
|
|
@@ -353,7 +348,6 @@ module Rigor
|
|
|
353
348
|
Type::Combinator.constant_of(edge == :first ? values.first : values.last)
|
|
354
349
|
end
|
|
355
350
|
|
|
356
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
357
351
|
def try_fold_binary_set(receiver_values, method_name, arg_values)
|
|
358
352
|
string_lift = try_fold_string_array_binary(receiver_values, method_name, arg_values)
|
|
359
353
|
return string_lift if string_lift
|
|
@@ -369,8 +363,6 @@ module Rigor
|
|
|
369
363
|
end
|
|
370
364
|
build_constant_type(results, source: receiver_values + arg_values)
|
|
371
365
|
end
|
|
372
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
373
|
-
|
|
374
366
|
# v0.0.7 — `Constant<String>#chars` / `bytes` / `lines` /
|
|
375
367
|
# `split` (no-arg) return a Ruby Array of foldable
|
|
376
368
|
# scalars; `foldable_constant_value?` rejects Array
|
|
@@ -425,7 +417,6 @@ module Rigor
|
|
|
425
417
|
nil
|
|
426
418
|
end
|
|
427
419
|
|
|
428
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
429
420
|
def try_fold_pathname_binary(receiver_values, method_name, arg_values)
|
|
430
421
|
return nil unless PATHNAME_PURE_BINARY.include?(method_name)
|
|
431
422
|
return nil unless receiver_values.size == 1 && arg_values.size == 1
|
|
@@ -442,7 +433,6 @@ module Rigor
|
|
|
442
433
|
rescue StandardError
|
|
443
434
|
nil
|
|
444
435
|
end
|
|
445
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
446
436
|
|
|
447
437
|
def try_fold_string_array_unary(receiver_values, method_name)
|
|
448
438
|
return nil unless STRING_ARRAY_UNARY_METHODS.include?(method_name)
|
|
@@ -459,7 +449,6 @@ module Rigor
|
|
|
459
449
|
# `Constant<String>#split(arg)` / `#scan(arg)` — lift the
|
|
460
450
|
# Array result to a Tuple when both sides are statically
|
|
461
451
|
# known and the cardinality fits.
|
|
462
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
463
452
|
def try_fold_string_array_binary(receiver_values, method_name, arg_values)
|
|
464
453
|
return nil unless STRING_ARRAY_BINARY_METHODS.include?(method_name)
|
|
465
454
|
return nil unless receiver_values.size == 1 && arg_values.size == 1
|
|
@@ -473,7 +462,6 @@ module Rigor
|
|
|
473
462
|
rescue StandardError
|
|
474
463
|
nil
|
|
475
464
|
end
|
|
476
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
477
465
|
|
|
478
466
|
def lift_array_result(result)
|
|
479
467
|
return nil unless result.is_a?(Array)
|
|
@@ -1040,10 +1028,10 @@ module Rigor
|
|
|
1040
1028
|
# class's ancestor chain at lookup time; the catalog
|
|
1041
1029
|
# corresponds to the module-mode YAML at
|
|
1042
1030
|
# `data/builtins/ruby_core/<topic>.yml`.
|
|
1043
|
-
MODULE_CATALOGS = [
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1031
|
+
MODULE_CATALOGS = Ractor.make_shareable([
|
|
1032
|
+
[Comparable, Builtins::COMPARABLE_CATALOG, "Comparable"],
|
|
1033
|
+
[Enumerable, Builtins::ENUMERABLE_CATALOG, "Enumerable"]
|
|
1034
|
+
])
|
|
1047
1035
|
private_constant :MODULE_CATALOGS
|
|
1048
1036
|
|
|
1049
1037
|
# Returns the `(catalog, class_name)` pairs for every
|
|
@@ -1069,31 +1057,31 @@ module Rigor
|
|
|
1069
1057
|
# Otherwise a `DateTime` receiver would match the `Date`
|
|
1070
1058
|
# arm first and the catalog would consult the Date entry
|
|
1071
1059
|
# in `DATE_CATALOG` for the wrong class.
|
|
1072
|
-
CATALOG_BY_CLASS = [
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1060
|
+
CATALOG_BY_CLASS = Ractor.make_shareable([
|
|
1061
|
+
[Integer, [Builtins::NumericCatalog, "Integer"]],
|
|
1062
|
+
[Float, [Builtins::NumericCatalog, "Float"]],
|
|
1063
|
+
[String, [Builtins::STRING_CATALOG, "String"]],
|
|
1064
|
+
[Symbol, [Builtins::STRING_CATALOG, "Symbol"]],
|
|
1065
|
+
[Array, [Builtins::ARRAY_CATALOG, "Array"]],
|
|
1066
|
+
[Hash, [Builtins::HASH_CATALOG, "Hash"]],
|
|
1067
|
+
[Range, [Builtins::RANGE_CATALOG, "Range"]],
|
|
1068
|
+
[::Set, [Builtins::SET_CATALOG, "Set"]],
|
|
1069
|
+
[Time, [Builtins::TIME_CATALOG, "Time"]],
|
|
1070
|
+
[DateTime, [Builtins::DATE_CATALOG, "DateTime"]],
|
|
1071
|
+
[Date, [Builtins::DATE_CATALOG, "Date"]],
|
|
1072
|
+
[Rational, [Builtins::RATIONAL_CATALOG, "Rational"]],
|
|
1073
|
+
[Complex, [Builtins::COMPLEX_CATALOG, "Complex"]],
|
|
1074
|
+
[Pathname, [Builtins::PATHNAME_CATALOG, "Pathname"]],
|
|
1075
|
+
[Random, [Builtins::RANDOM_CATALOG, "Random"]],
|
|
1076
|
+
[Struct, [Builtins::STRUCT_CATALOG, "Struct"]],
|
|
1077
|
+
[Encoding, [Builtins::ENCODING_CATALOG, "Encoding"]],
|
|
1078
|
+
[Regexp, [Builtins::REGEXP_CATALOG, "Regexp"]],
|
|
1079
|
+
[MatchData, [Builtins::REGEXP_CATALOG, "MatchData"]],
|
|
1080
|
+
[Proc, [Builtins::PROC_CATALOG, "Proc"]],
|
|
1081
|
+
[Method, [Builtins::PROC_CATALOG, "Method"]],
|
|
1082
|
+
[UnboundMethod, [Builtins::PROC_CATALOG, "UnboundMethod"]],
|
|
1083
|
+
[Exception, [Builtins::EXCEPTION_CATALOG, "Exception"]]
|
|
1084
|
+
])
|
|
1097
1085
|
private_constant :CATALOG_BY_CLASS
|
|
1098
1086
|
|
|
1099
1087
|
# Returns `[catalog, class_name]` for receivers we have a
|