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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Type
|
|
@@ -79,17 +80,9 @@ module Rigor
|
|
|
79
80
|
self.class.new(trinary, mode: mode, reasons: reasons + [reason])
|
|
80
81
|
end
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
other.is_a?(AcceptsResult) &&
|
|
84
|
-
trinary == other.trinary &&
|
|
85
|
-
mode == other.mode &&
|
|
86
|
-
reasons == other.reasons
|
|
87
|
-
end
|
|
88
|
-
alias eql? ==
|
|
83
|
+
include Rigor::ValueSemantics
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
[AcceptsResult, trinary, mode, reasons].hash
|
|
92
|
-
end
|
|
85
|
+
value_fields :trinary, :mode, :reasons
|
|
93
86
|
|
|
94
87
|
def inspect
|
|
95
88
|
"#<Rigor::Type::AcceptsResult #{trinary.inspect} mode=#{mode}>"
|
data/lib/rigor/type/app.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Type
|
|
@@ -82,14 +83,9 @@ module Rigor
|
|
|
82
83
|
Inference::Acceptance.accepts(bound, other, mode: mode)
|
|
83
84
|
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
other.is_a?(App) && uri == other.uri && args == other.args && bound == other.bound
|
|
87
|
-
end
|
|
88
|
-
alias eql? ==
|
|
86
|
+
include Rigor::ValueSemantics
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
[App, uri, args, bound].hash
|
|
92
|
-
end
|
|
88
|
+
value_fields :uri, :args, :bound
|
|
93
89
|
|
|
94
90
|
def inspect
|
|
95
91
|
"#<Rigor::Type::App #{describe(:short)} (bound=#{bound.describe(:short)})>"
|
data/lib/rigor/type/bot.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "acceptance_router"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Type
|
|
@@ -39,9 +40,7 @@ module Rigor
|
|
|
39
40
|
Trinary.no
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
44
|
-
end
|
|
43
|
+
include Rigor::Type::AcceptanceRouter
|
|
45
44
|
|
|
46
45
|
def ==(other)
|
|
47
46
|
other.is_a?(Bot)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -56,20 +58,11 @@ module Rigor
|
|
|
56
58
|
Trinary.no
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
61
|
-
end
|
|
61
|
+
include Rigor::Type::AcceptanceRouter
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
other.is_a?(BoundMethod) &&
|
|
65
|
-
receiver_type == other.receiver_type &&
|
|
66
|
-
method_name == other.method_name
|
|
67
|
-
end
|
|
68
|
-
alias eql? ==
|
|
63
|
+
include Rigor::ValueSemantics
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
[BoundMethod, receiver_type, method_name].hash
|
|
72
|
-
end
|
|
65
|
+
value_fields :receiver_type, :method_name
|
|
73
66
|
|
|
74
67
|
def inspect
|
|
75
68
|
"#<Rigor::Type::BoundMethod #{describe(:short)}>"
|
|
@@ -9,12 +9,15 @@ require_relative "constant"
|
|
|
9
9
|
require_relative "integer_range"
|
|
10
10
|
require_relative "tuple"
|
|
11
11
|
require_relative "hash_shape"
|
|
12
|
+
require_relative "data_class"
|
|
13
|
+
require_relative "data_instance"
|
|
12
14
|
require_relative "union"
|
|
13
15
|
require_relative "difference"
|
|
14
16
|
require_relative "refined"
|
|
15
17
|
require_relative "intersection"
|
|
16
18
|
require_relative "bound_method"
|
|
17
19
|
require_relative "../inference/budget_trace"
|
|
20
|
+
require_relative "../inference/flow_tracer"
|
|
18
21
|
|
|
19
22
|
module Rigor
|
|
20
23
|
module Type
|
|
@@ -358,6 +361,21 @@ module Rigor
|
|
|
358
361
|
HashShape.new(pairs, **options)
|
|
359
362
|
end
|
|
360
363
|
|
|
364
|
+
# ADR-48 — the class object produced by `Data.define(:x, :y)`.
|
|
365
|
+
# `members` is the ordered Symbol member-name list; `class_name`
|
|
366
|
+
# tags the class when known (the named-subclass form) and is nil
|
|
367
|
+
# for the anonymous `Data.define(...)` result.
|
|
368
|
+
def data_class_of(members:, class_name: nil)
|
|
369
|
+
DataClass.new(members, class_name)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# ADR-48 — a `Data.define` value instance. `members` is the ordered
|
|
373
|
+
# member-name -> value-type map; `class_name` tags the instance's
|
|
374
|
+
# class when known.
|
|
375
|
+
def data_instance_of(members:, class_name: nil)
|
|
376
|
+
DataInstance.new(members, class_name)
|
|
377
|
+
end
|
|
378
|
+
|
|
361
379
|
# Normalized union. Flattens nested Unions, deduplicates structurally
|
|
362
380
|
# equal members, drops Bot, and collapses 0/1-member results.
|
|
363
381
|
def union(*types)
|
|
@@ -365,6 +383,10 @@ module Rigor
|
|
|
365
383
|
if Inference::BudgetTrace.enabled? && result.is_a?(Union)
|
|
366
384
|
Inference::BudgetTrace.observe(Inference::BudgetTrace::UNION_ARITY, result.members.size)
|
|
367
385
|
end
|
|
386
|
+
# `rigor trace` — degenerate merges (`1 | 1 → 1`, Dynamic
|
|
387
|
+
# absorption) are recorded too; the collapse itself is the
|
|
388
|
+
# teachable moment.
|
|
389
|
+
Inference::FlowTracer.union(types, result) if Inference::FlowTracer.active? && types.size >= 2
|
|
368
390
|
result
|
|
369
391
|
end
|
|
370
392
|
|
data/lib/rigor/type/constant.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
4
|
require_relative "../trinary"
|
|
5
|
+
require_relative "acceptance_router"
|
|
5
6
|
|
|
6
7
|
module Rigor
|
|
7
8
|
module Type
|
|
@@ -111,9 +112,7 @@ module Rigor
|
|
|
111
112
|
Trinary.no
|
|
112
113
|
end
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
116
|
-
end
|
|
115
|
+
include Rigor::Type::AcceptanceRouter
|
|
117
116
|
|
|
118
117
|
def ==(other)
|
|
119
118
|
other.is_a?(Constant) && value.class == other.value.class && value == other.value
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Type
|
|
9
|
+
# The class object produced by `Data.define(:x, :y)` (ADR-48). Models
|
|
10
|
+
# the *class*, not an instance — the value bound to `Point` in
|
|
11
|
+
# `Point = Data.define(:x, :y)` or the anonymous superclass in
|
|
12
|
+
# `class Point < Data.define(:x, :y)`. Parameterised by the ordered
|
|
13
|
+
# member-name list so that `Point.new(...)` can materialise a precise
|
|
14
|
+
# {DataInstance}.
|
|
15
|
+
#
|
|
16
|
+
# `class_name` carries the binding name when known (the named-subclass
|
|
17
|
+
# form, ADR-48 slice 3) and is `nil` for the anonymous result of a bare
|
|
18
|
+
# `Data.define(...)` before it is assigned to a constant. It tags the
|
|
19
|
+
# instances `.new` produces; it does not change the carrier's folding
|
|
20
|
+
# behaviour.
|
|
21
|
+
#
|
|
22
|
+
# Equality and hashing are structural over the member list and the
|
|
23
|
+
# class name. Two distinct `Data.define(:x)` results are equal *as
|
|
24
|
+
# types* — they describe the same shape; the engine distinguishes the
|
|
25
|
+
# constants they are bound to by the binding, not by the carrier.
|
|
26
|
+
#
|
|
27
|
+
# See docs/adr/48-data-struct-value-folding.md.
|
|
28
|
+
class DataClass
|
|
29
|
+
attr_reader :members, :class_name
|
|
30
|
+
|
|
31
|
+
# @param members [Array<Symbol>] ordered member names.
|
|
32
|
+
# @param class_name [String, nil] the bound class name, or nil for
|
|
33
|
+
# the anonymous `Data.define(...)` result.
|
|
34
|
+
def initialize(members, class_name = nil)
|
|
35
|
+
unless members.is_a?(Array) && members.all?(Symbol)
|
|
36
|
+
raise ArgumentError, "members must be an Array of Symbols, got #{members.inspect}"
|
|
37
|
+
end
|
|
38
|
+
unless class_name.nil? || (class_name.is_a?(String) && !class_name.empty?)
|
|
39
|
+
raise ArgumentError, "class_name must be a non-empty String or nil, got #{class_name.inspect}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@members = members.dup.freeze
|
|
43
|
+
@class_name = class_name&.freeze
|
|
44
|
+
freeze
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def describe(_verbosity = :short)
|
|
48
|
+
return "singleton(#{class_name})" if class_name
|
|
49
|
+
|
|
50
|
+
"Data.define(#{members.map(&:inspect).join(', ')})"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def erase_to_rbs
|
|
54
|
+
"singleton(#{class_name || 'Data'})"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def top
|
|
58
|
+
Trinary.no
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def bot
|
|
62
|
+
Trinary.no
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def dynamic
|
|
66
|
+
Trinary.no
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
include Rigor::Type::AcceptanceRouter
|
|
70
|
+
|
|
71
|
+
include Rigor::ValueSemantics
|
|
72
|
+
|
|
73
|
+
value_fields :members, :class_name
|
|
74
|
+
|
|
75
|
+
def inspect
|
|
76
|
+
"#<Rigor::Type::DataClass #{describe(:short)}>"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Type
|
|
9
|
+
# A `Data.define` value instance (ADR-48) — `Point.new(1, 2)`. Models a
|
|
10
|
+
# closed, total, class-tagged member map (member name -> value type).
|
|
11
|
+
# HashShape-shaped, but nominal: a `DataInstance` tagged `Point` is a
|
|
12
|
+
# different type from one tagged `Line` even with identical members,
|
|
13
|
+
# and it erases to its class nominal rather than an RBS record.
|
|
14
|
+
#
|
|
15
|
+
# `Data` instances are frozen, so the member map is sound for the
|
|
16
|
+
# instance's whole lifetime — the reason `Data` is the first target
|
|
17
|
+
# (a `Struct` instance can be mutated through a setter or `[]=`, which
|
|
18
|
+
# would invalidate the map; that follow-up is deferred — see ADR-48).
|
|
19
|
+
#
|
|
20
|
+
# Member reads fold to the member's type (`Point.new(1, 2).x` ->
|
|
21
|
+
# `Constant[1]`); `[]`, `to_h`, `deconstruct`, `deconstruct_keys`,
|
|
22
|
+
# `members`, and `with` project precisely. Methods this carrier does
|
|
23
|
+
# not fold project to the `Data` nominal (or the tagged class) through
|
|
24
|
+
# {RbsDispatch}'s `receiver_descriptor`, so non-member calls resolve
|
|
25
|
+
# without mis-firing undefined-method.
|
|
26
|
+
#
|
|
27
|
+
# Equality and hashing are structural over the (member -> type) map and
|
|
28
|
+
# the class name.
|
|
29
|
+
#
|
|
30
|
+
# See docs/adr/48-data-struct-value-folding.md.
|
|
31
|
+
class DataInstance
|
|
32
|
+
attr_reader :members, :class_name
|
|
33
|
+
|
|
34
|
+
# @param members [Hash{Symbol => Rigor::Type}] ordered member -> type
|
|
35
|
+
# map. Every declared member is present (Data instances are total).
|
|
36
|
+
# @param class_name [String, nil] the tagging class name, or nil for
|
|
37
|
+
# an instance of an anonymous `Data.define(...)` class.
|
|
38
|
+
def initialize(members, class_name = nil)
|
|
39
|
+
unless members.is_a?(Hash) && members.each_key.all?(Symbol)
|
|
40
|
+
raise ArgumentError, "members must be a Hash with Symbol keys, got #{members.inspect}"
|
|
41
|
+
end
|
|
42
|
+
unless class_name.nil? || (class_name.is_a?(String) && !class_name.empty?)
|
|
43
|
+
raise ArgumentError, "class_name must be a non-empty String or nil, got #{class_name.inspect}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@members = members.dup.freeze
|
|
47
|
+
@class_name = class_name&.freeze
|
|
48
|
+
freeze
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Array<Symbol>] ordered member names.
|
|
52
|
+
def member_names
|
|
53
|
+
members.keys
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [Rigor::Type, nil] the member's value type, or nil when the
|
|
57
|
+
# name is not a declared member.
|
|
58
|
+
def member_type(name)
|
|
59
|
+
members[name]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def describe(verbosity = :short)
|
|
63
|
+
rendered = members.map { |name, type| "#{name}: #{type.describe(verbosity)}" }
|
|
64
|
+
"#{class_name || 'Data'}(#{rendered.join(', ')})"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Erases to the tagging class nominal (conservative: the structural
|
|
68
|
+
# members are not RBS-expressible as a class instance). The
|
|
69
|
+
# anonymous case erases to the `Data` supertype.
|
|
70
|
+
def erase_to_rbs
|
|
71
|
+
name = class_name
|
|
72
|
+
return "Data" if name.nil?
|
|
73
|
+
|
|
74
|
+
name
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def top
|
|
78
|
+
Trinary.no
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def bot
|
|
82
|
+
Trinary.no
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def dynamic
|
|
86
|
+
Trinary.no
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
include Rigor::Type::AcceptanceRouter
|
|
90
|
+
|
|
91
|
+
include Rigor::ValueSemantics
|
|
92
|
+
|
|
93
|
+
value_fields :members, :class_name
|
|
94
|
+
|
|
95
|
+
def inspect
|
|
96
|
+
"#<Rigor::Type::DataInstance #{describe(:short)}>"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -67,18 +69,11 @@ module Rigor
|
|
|
67
69
|
base.respond_to?(:dynamic) ? base.dynamic : Trinary.no
|
|
68
70
|
end
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
72
|
-
end
|
|
72
|
+
include Rigor::Type::AcceptanceRouter
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
other.is_a?(Difference) && base == other.base && removed == other.removed
|
|
76
|
-
end
|
|
77
|
-
alias eql? ==
|
|
74
|
+
include Rigor::ValueSemantics
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
[Difference, base, removed].hash
|
|
81
|
-
end
|
|
76
|
+
value_fields :base, :removed
|
|
82
77
|
|
|
83
78
|
def inspect
|
|
84
79
|
"#<Rigor::Type::Difference #{describe(:short)}>"
|
data/lib/rigor/type/dynamic.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -39,18 +41,11 @@ module Rigor
|
|
|
39
41
|
Trinary.yes
|
|
40
42
|
end
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
44
|
-
end
|
|
44
|
+
include Rigor::Type::AcceptanceRouter
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
other.is_a?(Dynamic) && static_facet == other.static_facet
|
|
48
|
-
end
|
|
49
|
-
alias eql? ==
|
|
46
|
+
include Rigor::ValueSemantics
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
[Dynamic, static_facet].hash
|
|
53
|
-
end
|
|
48
|
+
value_fields :static_facet
|
|
54
49
|
|
|
55
50
|
def inspect
|
|
56
51
|
"#<Rigor::Type::Dynamic #{describe(:short)}>"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -106,23 +108,11 @@ module Rigor
|
|
|
106
108
|
Trinary.no
|
|
107
109
|
end
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
111
|
-
end
|
|
111
|
+
include Rigor::Type::AcceptanceRouter
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
other.is_a?(HashShape) &&
|
|
115
|
-
pairs == other.pairs &&
|
|
116
|
-
required_keys == other.required_keys &&
|
|
117
|
-
optional_keys == other.optional_keys &&
|
|
118
|
-
read_only_keys == other.read_only_keys &&
|
|
119
|
-
extra_keys == other.extra_keys
|
|
120
|
-
end
|
|
121
|
-
alias eql? ==
|
|
113
|
+
include Rigor::ValueSemantics
|
|
122
114
|
|
|
123
|
-
|
|
124
|
-
[HashShape, pairs, required_keys, optional_keys, read_only_keys, extra_keys].hash
|
|
125
|
-
end
|
|
115
|
+
value_fields :pairs, :required_keys, :optional_keys, :read_only_keys, :extra_keys
|
|
126
116
|
|
|
127
117
|
def inspect
|
|
128
118
|
"#<Rigor::Type::HashShape #{describe(:short)}>"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -113,18 +115,11 @@ module Rigor
|
|
|
113
115
|
Trinary.no
|
|
114
116
|
end
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
118
|
-
end
|
|
118
|
+
include Rigor::Type::AcceptanceRouter
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
other.is_a?(IntegerRange) && min == other.min && max == other.max
|
|
122
|
-
end
|
|
123
|
-
alias eql? ==
|
|
120
|
+
include Rigor::ValueSemantics
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
[IntegerRange, min, max].hash
|
|
127
|
-
end
|
|
122
|
+
value_fields :min, :max
|
|
128
123
|
|
|
129
124
|
def inspect
|
|
130
125
|
"#<Rigor::Type::IntegerRange #{describe(:short)}>"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -72,18 +74,11 @@ module Rigor
|
|
|
72
74
|
Trinary.no
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
77
|
-
end
|
|
77
|
+
include Rigor::Type::AcceptanceRouter
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
other.is_a?(Intersection) && members == other.members
|
|
81
|
-
end
|
|
82
|
-
alias eql? ==
|
|
79
|
+
include Rigor::ValueSemantics
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
[Intersection, members].hash
|
|
86
|
-
end
|
|
81
|
+
value_fields :members
|
|
87
82
|
|
|
88
83
|
def inspect
|
|
89
84
|
"#<Rigor::Type::Intersection #{describe(:short)}>"
|
data/lib/rigor/type/nominal.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -62,18 +64,11 @@ module Rigor
|
|
|
62
64
|
Trinary.no
|
|
63
65
|
end
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
67
|
-
end
|
|
67
|
+
include Rigor::Type::AcceptanceRouter
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
other.is_a?(Nominal) && class_name == other.class_name && type_args == other.type_args
|
|
71
|
-
end
|
|
72
|
-
alias eql? ==
|
|
69
|
+
include Rigor::ValueSemantics
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
[Nominal, class_name, type_args].hash
|
|
76
|
-
end
|
|
71
|
+
value_fields :class_name, :type_args
|
|
77
72
|
|
|
78
73
|
def inspect
|
|
79
74
|
"#<Rigor::Type::Nominal #{describe(:short)}>"
|
data/lib/rigor/type/refined.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -69,18 +71,11 @@ module Rigor
|
|
|
69
71
|
base.respond_to?(:dynamic) ? base.dynamic : Trinary.no
|
|
70
72
|
end
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
74
|
-
end
|
|
74
|
+
include Rigor::Type::AcceptanceRouter
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
other.is_a?(Refined) && base == other.base && predicate_id == other.predicate_id
|
|
78
|
-
end
|
|
79
|
-
alias eql? ==
|
|
76
|
+
include Rigor::ValueSemantics
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
[Refined, base, predicate_id].hash
|
|
83
|
-
end
|
|
78
|
+
value_fields :base, :predicate_id
|
|
84
79
|
|
|
85
80
|
def inspect
|
|
86
81
|
"#<Rigor::Type::Refined #{describe(:short)}>"
|
data/lib/rigor/type/singleton.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -44,18 +46,11 @@ module Rigor
|
|
|
44
46
|
Trinary.no
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
49
|
-
end
|
|
49
|
+
include Rigor::Type::AcceptanceRouter
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
other.is_a?(Singleton) && class_name == other.class_name
|
|
53
|
-
end
|
|
54
|
-
alias eql? ==
|
|
51
|
+
include Rigor::ValueSemantics
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
[Singleton, class_name].hash
|
|
58
|
-
end
|
|
53
|
+
value_fields :class_name
|
|
59
54
|
|
|
60
55
|
def inspect
|
|
61
56
|
"#<Rigor::Type::Singleton #{class_name}>"
|
data/lib/rigor/type/top.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "acceptance_router"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
6
7
|
module Type
|
|
@@ -36,9 +37,7 @@ module Rigor
|
|
|
36
37
|
Trinary.no
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
41
|
-
end
|
|
40
|
+
include Rigor::Type::AcceptanceRouter
|
|
42
41
|
|
|
43
42
|
def ==(other)
|
|
44
43
|
other.is_a?(Top)
|
data/lib/rigor/type/tuple.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../trinary"
|
|
4
|
+
require_relative "../value_semantics"
|
|
5
|
+
require_relative "acceptance_router"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
6
8
|
module Type
|
|
@@ -63,18 +65,11 @@ module Rigor
|
|
|
63
65
|
Trinary.no
|
|
64
66
|
end
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
Inference::Acceptance.accepts(self, other, mode: mode)
|
|
68
|
-
end
|
|
68
|
+
include Rigor::Type::AcceptanceRouter
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
other.is_a?(Tuple) && elements == other.elements
|
|
72
|
-
end
|
|
73
|
-
alias eql? ==
|
|
70
|
+
include Rigor::ValueSemantics
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
[Tuple, elements].hash
|
|
77
|
-
end
|
|
72
|
+
value_fields :elements
|
|
78
73
|
|
|
79
74
|
def inspect
|
|
80
75
|
"#<Rigor::Type::Tuple #{describe(:short)}>"
|