rigortype 0.1.4 → 0.1.6
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 +69 -56
- data/lib/rigor/analysis/buffer_binding.rb +36 -0
- data/lib/rigor/analysis/check_rules.rb +11 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +14 -1
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +105 -0
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +32 -12
- data/lib/rigor/analysis/fact_store.rb +15 -3
- data/lib/rigor/analysis/project_scan.rb +39 -0
- data/lib/rigor/analysis/result.rb +11 -3
- data/lib/rigor/analysis/run_stats.rb +193 -0
- data/lib/rigor/analysis/runner.rb +681 -19
- data/lib/rigor/analysis/worker_session.rb +339 -0
- data/lib/rigor/builtins/hkt_builtins.rb +342 -0
- data/lib/rigor/builtins/imported_refinements.rb +6 -2
- data/lib/rigor/builtins/regex_refinement.rb +17 -12
- data/lib/rigor/builtins/static_return_refinements.rb +120 -0
- data/lib/rigor/cache/rbs_descriptor.rb +3 -1
- data/lib/rigor/cache/store.rb +72 -9
- data/lib/rigor/cli/lsp_command.rb +129 -0
- data/lib/rigor/cli/type_of_command.rb +44 -5
- data/lib/rigor/cli.rb +122 -10
- data/lib/rigor/configuration.rb +168 -7
- data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
- data/lib/rigor/environment/class_registry.rb +12 -3
- data/lib/rigor/environment/hkt_registry_holder.rb +33 -0
- 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 +238 -7
- data/lib/rigor/environment/reflection.rb +152 -0
- data/lib/rigor/environment/reporters.rb +40 -0
- data/lib/rigor/environment.rb +179 -10
- data/lib/rigor/inference/acceptance.rb +83 -4
- data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
- data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
- data/lib/rigor/inference/expression_typer.rb +59 -2
- data/lib/rigor/inference/hkt_body.rb +171 -0
- data/lib/rigor/inference/hkt_body_parser.rb +363 -0
- data/lib/rigor/inference/hkt_reducer.rb +256 -0
- data/lib/rigor/inference/hkt_registry.rb +223 -0
- data/lib/rigor/inference/macro_block_self_type.rb +96 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -29
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +18 -1
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +126 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +87 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -40
- data/lib/rigor/inference/method_dispatcher.rb +282 -6
- data/lib/rigor/inference/method_parameter_binder.rb +21 -11
- data/lib/rigor/inference/narrowing.rb +127 -8
- data/lib/rigor/inference/project_patched_methods.rb +70 -0
- data/lib/rigor/inference/project_patched_scanner.rb +210 -0
- data/lib/rigor/inference/scope_indexer.rb +156 -12
- data/lib/rigor/inference/statement_evaluator.rb +106 -6
- 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 +599 -0
- data/lib/rigor/language_server/buffer_table.rb +63 -0
- data/lib/rigor/language_server/completion_provider.rb +438 -0
- data/lib/rigor/language_server/debouncer.rb +86 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +167 -0
- data/lib/rigor/language_server/document_symbol_provider.rb +142 -0
- data/lib/rigor/language_server/folding_range_provider.rb +75 -0
- data/lib/rigor/language_server/hover_provider.rb +74 -0
- data/lib/rigor/language_server/hover_renderer.rb +312 -0
- data/lib/rigor/language_server/loop.rb +71 -0
- data/lib/rigor/language_server/project_context.rb +145 -0
- data/lib/rigor/language_server/selection_range_provider.rb +93 -0
- data/lib/rigor/language_server/server.rb +384 -0
- data/lib/rigor/language_server/signature_help_provider.rb +249 -0
- data/lib/rigor/language_server/synchronized_writer.rb +28 -0
- data/lib/rigor/language_server/uri.rb +40 -0
- data/lib/rigor/language_server.rb +29 -0
- data/lib/rigor/plugin/base.rb +63 -0
- data/lib/rigor/plugin/blueprint.rb +60 -0
- data/lib/rigor/plugin/loader.rb +3 -1
- 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 +315 -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 +127 -9
- data/lib/rigor/plugin/registry.rb +51 -2
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/rbs_extended/hkt_directives.rb +326 -0
- data/lib/rigor/rbs_extended.rb +82 -2
- data/lib/rigor/sig_gen/generator.rb +12 -3
- data/lib/rigor/trinary.rb +15 -11
- data/lib/rigor/type/app.rb +107 -0
- data/lib/rigor/type/bot.rb +6 -3
- data/lib/rigor/type/combinator.rb +12 -1
- 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.rb +1 -0
- data/lib/rigor/type_node/generic.rb +7 -1
- data/lib/rigor/type_node/identifier.rb +9 -1
- data/lib/rigor/type_node/string_literal.rb +4 -1
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/environment.rbs +11 -4
- data/sig/rigor/inference.rbs +2 -0
- data/sig/rigor/plugin/blueprint.rbs +7 -0
- data/sig/rigor/plugin/manifest.rbs +1 -1
- data/sig/rigor/plugin/registry.rbs +14 -1
- data/sig/rigor.rbs +37 -2
- metadata +92 -1
data/lib/rigor/rbs_extended.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative "type"
|
|
|
4
4
|
require_relative "builtins/imported_refinements"
|
|
5
5
|
require_relative "flow_contribution"
|
|
6
6
|
require_relative "rbs_extended/reporter"
|
|
7
|
+
require_relative "rbs_extended/hkt_directives"
|
|
7
8
|
|
|
8
9
|
module Rigor
|
|
9
10
|
# Slice 7 phase 15 — first-preview reader for the
|
|
@@ -385,13 +386,15 @@ module Rigor
|
|
|
385
386
|
|
|
386
387
|
name_scope = environment&.name_scope
|
|
387
388
|
reporter = environment&.rbs_extended_reporter
|
|
389
|
+
hkt_registry = environment&.hkt_registry
|
|
388
390
|
|
|
389
391
|
annotations.each do |annotation|
|
|
390
392
|
type = parse_return_type_override(
|
|
391
393
|
annotation.string,
|
|
392
394
|
name_scope: name_scope,
|
|
393
395
|
reporter: reporter,
|
|
394
|
-
source_location: annotation.location
|
|
396
|
+
source_location: annotation.location,
|
|
397
|
+
hkt_registry: hkt_registry
|
|
395
398
|
)
|
|
396
399
|
return type if type
|
|
397
400
|
end
|
|
@@ -417,10 +420,40 @@ module Rigor
|
|
|
417
420
|
/x
|
|
418
421
|
private_constant :RETURN_DIRECTIVE_PATTERN
|
|
419
422
|
|
|
420
|
-
|
|
423
|
+
# ADR-20 slice 2d — recognises `App[<uri>, <ClassName>, ...]`
|
|
424
|
+
# syntax in a `rigor:v1:return:` payload before falling
|
|
425
|
+
# through to the refinement-name parser. The match captures
|
|
426
|
+
# the namespaced URI (`json::value`) plus a comma-separated
|
|
427
|
+
# list of bare class names (`String`, `Symbol`, `Integer`).
|
|
428
|
+
# Slice 2d keeps the arg vocabulary intentionally narrow;
|
|
429
|
+
# parameterised forms (`Array[T]`, `Hash[K, V]`), unions,
|
|
430
|
+
# and refinements inside `App[...]` wait for a follow-up
|
|
431
|
+
# slice's expression parser.
|
|
432
|
+
APP_PAYLOAD_PATTERN = /
|
|
433
|
+
\A
|
|
434
|
+
App\[
|
|
435
|
+
\s*
|
|
436
|
+
(?<uri>[a-z_][a-z0-9_]*(?:::[a-z_][a-z0-9_]*)+)
|
|
437
|
+
\s*,\s*
|
|
438
|
+
(?<args>[^\[\]]+?)
|
|
439
|
+
\s*\]
|
|
440
|
+
\z
|
|
441
|
+
/x
|
|
442
|
+
private_constant :APP_PAYLOAD_PATTERN
|
|
443
|
+
|
|
444
|
+
def parse_return_type_override(string, name_scope: nil, reporter: nil, source_location: nil, hkt_registry: nil)
|
|
421
445
|
match = RETURN_DIRECTIVE_PATTERN.match(string)
|
|
422
446
|
return nil if match.nil?
|
|
423
447
|
|
|
448
|
+
app_type = parse_app_payload(
|
|
449
|
+
match[:payload],
|
|
450
|
+
name_scope: name_scope,
|
|
451
|
+
reporter: reporter,
|
|
452
|
+
source_location: source_location,
|
|
453
|
+
hkt_registry: hkt_registry
|
|
454
|
+
)
|
|
455
|
+
return app_type if app_type
|
|
456
|
+
|
|
424
457
|
type = Builtins::ImportedRefinements.parse(
|
|
425
458
|
match[:payload],
|
|
426
459
|
name_scope: name_scope,
|
|
@@ -431,6 +464,53 @@ module Rigor
|
|
|
431
464
|
type
|
|
432
465
|
end
|
|
433
466
|
|
|
467
|
+
# ADR-20 slice 2d. Parses `App[<uri>, <ClassName>, ...]`
|
|
468
|
+
# syntax into a `Rigor::Type::App`. When `hkt_registry` is
|
|
469
|
+
# supplied and the URI is registered with a body_tree, the
|
|
470
|
+
# `App` is reduced eagerly via {Inference::HktRegistry#reduce}
|
|
471
|
+
# so call sites observe the unfolded form (e.g.
|
|
472
|
+
# `Union[nil, true, false, ..., Array[App[json::value,
|
|
473
|
+
# String]], Hash[String, App[json::value, String]]]`)
|
|
474
|
+
# rather than the opaque carrier. When the registry is
|
|
475
|
+
# absent or the URI is unregistered, the carrier with its
|
|
476
|
+
# registry-supplied bound (or `untyped` as a last-resort
|
|
477
|
+
# fallback) is returned as-is.
|
|
478
|
+
def parse_app_payload(payload, name_scope: nil, reporter: nil, source_location: nil, hkt_registry: nil)
|
|
479
|
+
match = APP_PAYLOAD_PATTERN.match(payload)
|
|
480
|
+
return nil if match.nil?
|
|
481
|
+
|
|
482
|
+
uri = match[:uri].to_sym
|
|
483
|
+
arg_classes = match[:args].split(",").map(&:strip)
|
|
484
|
+
args = arg_classes.map { |name| resolve_app_arg(name, name_scope: name_scope) }
|
|
485
|
+
|
|
486
|
+
if args.any?(&:nil?)
|
|
487
|
+
record_unresolved(reporter, "App payload `#{payload}`: unresolved arg class name", source_location)
|
|
488
|
+
return nil
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
registration = hkt_registry&.registration(uri)
|
|
492
|
+
bound = registration&.bound || Type::Combinator.untyped
|
|
493
|
+
app = Type::App.new(uri, args, bound: bound)
|
|
494
|
+
|
|
495
|
+
return app if hkt_registry.nil? || !hkt_registry.defined?(uri)
|
|
496
|
+
|
|
497
|
+
reduced = hkt_registry.reduce(app)
|
|
498
|
+
reduced || app
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def resolve_app_arg(class_name, name_scope: nil)
|
|
502
|
+
return nil unless /\A(?:::)?(?:[A-Z]\w*)(?:::[A-Z]\w*)*\z/.match?(class_name)
|
|
503
|
+
|
|
504
|
+
normalized = class_name.sub(/\A::/, "")
|
|
505
|
+
return Type::Nominal.new(normalized) if name_scope.nil?
|
|
506
|
+
|
|
507
|
+
if name_scope.respond_to?(:nominal_for_name)
|
|
508
|
+
resolved = name_scope.nominal_for_name(normalized)
|
|
509
|
+
return resolved if resolved
|
|
510
|
+
end
|
|
511
|
+
Type::Nominal.new(normalized)
|
|
512
|
+
end
|
|
513
|
+
|
|
434
514
|
# Returned for `rigor:v1:param: <name> <refinement>`. The
|
|
435
515
|
# parameter name is a Ruby identifier (Symbol); the type
|
|
436
516
|
# is any `Rigor::Type` the refinement parser resolves
|
|
@@ -52,6 +52,15 @@ module Rigor
|
|
|
52
52
|
@paths = paths
|
|
53
53
|
@observations = normalize_observations(observations)
|
|
54
54
|
@include_private = include_private
|
|
55
|
+
# Per-file scratch state. `analyse_file` resets each
|
|
56
|
+
# one to a fresh container for every file walked so
|
|
57
|
+
# candidates from one file don't leak into another;
|
|
58
|
+
# initialising empty here gives downstream consumers
|
|
59
|
+
# (`build_candidate`, `method_def_prefix`) a never-nil
|
|
60
|
+
# invariant without per-call-site defensive guards.
|
|
61
|
+
@namespace_kinds = {}
|
|
62
|
+
@module_function_methods = Set.new
|
|
63
|
+
@class_shells = Set.new
|
|
55
64
|
end
|
|
56
65
|
|
|
57
66
|
# Lifts legacy plain-`Array[Type]` observation entries
|
|
@@ -270,8 +279,8 @@ module Rigor
|
|
|
270
279
|
# `Const = Data.define(...)` declarations.
|
|
271
280
|
def build_candidate(**)
|
|
272
281
|
MethodCandidate.new(
|
|
273
|
-
namespace_kinds: @namespace_kinds
|
|
274
|
-
class_shells:
|
|
282
|
+
namespace_kinds: @namespace_kinds,
|
|
283
|
+
class_shells: @class_shells.to_a,
|
|
275
284
|
**
|
|
276
285
|
)
|
|
277
286
|
end
|
|
@@ -282,7 +291,7 @@ module Rigor
|
|
|
282
291
|
# dispatch at runtime), or "def " (plain instance).
|
|
283
292
|
def method_def_prefix(class_name, method_name, kind)
|
|
284
293
|
return "def self." if kind == :singleton
|
|
285
|
-
return "def self?." if @module_function_methods
|
|
294
|
+
return "def self?." if @module_function_methods.include?([class_name, method_name])
|
|
286
295
|
|
|
287
296
|
"def "
|
|
288
297
|
end
|
data/lib/rigor/trinary.rb
CHANGED
|
@@ -10,18 +10,16 @@ module Rigor
|
|
|
10
10
|
class Trinary
|
|
11
11
|
VALUES = %i[yes no maybe].freeze
|
|
12
12
|
|
|
13
|
+
# ADR-15 Phase 4b.x — eager singleton instances so the
|
|
14
|
+
# class-level `@yes` / `@no` / `@maybe` ivars are populated
|
|
15
|
+
# on the main Ractor at load time. Workers READ the ivars
|
|
16
|
+
# via `Trinary.yes` etc. without performing the `||=`
|
|
17
|
+
# write that non-main Ractors are forbidden from doing.
|
|
18
|
+
# The actual `@yes = new(:yes).freeze` allocation happens
|
|
19
|
+
# at the bottom of the file, AFTER `def initialize` is
|
|
20
|
+
# defined.
|
|
13
21
|
class << self
|
|
14
|
-
|
|
15
|
-
@yes ||= new(:yes).freeze
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def no
|
|
19
|
-
@no ||= new(:no).freeze
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def maybe
|
|
23
|
-
@maybe ||= new(:maybe).freeze
|
|
24
|
-
end
|
|
22
|
+
attr_reader :yes, :no, :maybe
|
|
25
23
|
|
|
26
24
|
def from_symbol(symbol)
|
|
27
25
|
case symbol
|
|
@@ -104,5 +102,11 @@ module Rigor
|
|
|
104
102
|
|
|
105
103
|
raise TypeError, "expected Rigor::Trinary, got #{other.class}"
|
|
106
104
|
end
|
|
105
|
+
|
|
106
|
+
# ADR-15 Phase 4b.x eager singletons (see `class << self`
|
|
107
|
+
# above). Allocated after `initialize` is defined.
|
|
108
|
+
@yes = new(:yes).freeze
|
|
109
|
+
@no = new(:no).freeze
|
|
110
|
+
@maybe = new(:maybe).freeze
|
|
107
111
|
end
|
|
108
112
|
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../trinary"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Type
|
|
7
|
+
# A defunctionalised higher-kinded type application — the abstract
|
|
8
|
+
# "apply type-constructor `uri` to argument list `args`" carrier.
|
|
9
|
+
#
|
|
10
|
+
# `uri` is a namespaced `Symbol` of the form `:author::name`
|
|
11
|
+
# (e.g. `:"json::value"`, `:"dry_monads::result"`); the `::`
|
|
12
|
+
# namespace separator is mandatory per ADR-20 WD1 to prevent
|
|
13
|
+
# cross-plugin tag collisions.
|
|
14
|
+
#
|
|
15
|
+
# `args` is an ordered, frozen `Array` of `Rigor::Type` values
|
|
16
|
+
# carrying the application's argument list. At least one argument
|
|
17
|
+
# is required; arity-zero "HKT" forms are not modelled by this
|
|
18
|
+
# carrier (use a plain type alias instead).
|
|
19
|
+
#
|
|
20
|
+
# `bound` is a `Rigor::Type` representing the value Rigor MUST
|
|
21
|
+
# erase to when this `App` cannot be reduced — registered at
|
|
22
|
+
# `%a{rigor:v1:hkt_register}` time, defaulting to
|
|
23
|
+
# `Rigor::Type::Dynamic[Rigor::Type::Top]` per ADR-20 D5 / WD2. It
|
|
24
|
+
# also drives the lattice probes (`top` / `bot` / `dynamic`) and
|
|
25
|
+
# the acceptance fallback while no reduction is wired (Slice 1).
|
|
26
|
+
#
|
|
27
|
+
# Slice 1 ships the carrier as **opaque**: every operation
|
|
28
|
+
# delegates to `bound` since no reduction surface exists yet. Slice
|
|
29
|
+
# 2 introduces the conditional / indexed-access evaluator that
|
|
30
|
+
# reduces `App` to its registered body before delegating; the
|
|
31
|
+
# carrier shape stays identical.
|
|
32
|
+
#
|
|
33
|
+
# Display form per ADR-20 OQ5: bare RBS-style `uri[arg1, arg2]`,
|
|
34
|
+
# not the wrapped `App[uri, [arg1, arg2]]` faithful form. Two
|
|
35
|
+
# `App` values are structurally equal iff their `uri`, `args`,
|
|
36
|
+
# AND `bound` match.
|
|
37
|
+
#
|
|
38
|
+
# See docs/adr/20-lightweight-hkt.md.
|
|
39
|
+
class App
|
|
40
|
+
URI_SEPARATOR = "::"
|
|
41
|
+
|
|
42
|
+
attr_reader :uri, :args, :bound
|
|
43
|
+
|
|
44
|
+
def initialize(uri, args, bound:)
|
|
45
|
+
raise ArgumentError, "uri must be a Symbol, got #{uri.class}" unless uri.is_a?(Symbol)
|
|
46
|
+
unless uri.to_s.include?(URI_SEPARATOR)
|
|
47
|
+
raise ArgumentError,
|
|
48
|
+
"uri must be namespaced as `:author#{URI_SEPARATOR}name` per ADR-20 WD1, got #{uri.inspect}"
|
|
49
|
+
end
|
|
50
|
+
raise ArgumentError, "args must be an Array, got #{args.class}" unless args.is_a?(Array)
|
|
51
|
+
raise ArgumentError, "args must be non-empty (use a plain type alias for arity-0 forms)" if args.empty?
|
|
52
|
+
raise ArgumentError, "bound must be a Rigor type, got #{bound.class}" if bound.nil?
|
|
53
|
+
|
|
54
|
+
@uri = uri
|
|
55
|
+
@args = args.dup.freeze
|
|
56
|
+
@bound = bound
|
|
57
|
+
freeze
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def describe(verbosity = :short)
|
|
61
|
+
rendered = args.map { |t| t.describe(verbosity) }.join(", ")
|
|
62
|
+
"#{uri}[#{rendered}]"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def erase_to_rbs
|
|
66
|
+
bound.erase_to_rbs
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def top
|
|
70
|
+
bound.top
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def bot
|
|
74
|
+
bound.bot
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def dynamic
|
|
78
|
+
bound.dynamic
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def accepts(other, mode: :gradual)
|
|
82
|
+
Inference::Acceptance.accepts(bound, other, mode: mode)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def ==(other)
|
|
86
|
+
other.is_a?(App) && uri == other.uri && args == other.args && bound == other.bound
|
|
87
|
+
end
|
|
88
|
+
alias eql? ==
|
|
89
|
+
|
|
90
|
+
def hash
|
|
91
|
+
[App, uri, args, bound].hash
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def inspect
|
|
95
|
+
"#<Rigor::Type::App #{describe(:short)} (bound=#{bound.describe(:short)})>"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# ADR-20 Slice 2a — reduce this application against a
|
|
99
|
+
# registry. Returns the reducer's output (`bound` when
|
|
100
|
+
# reduction is blocked or fuel exhausts).
|
|
101
|
+
def reduce(registry, fuel: nil)
|
|
102
|
+
fuel ||= Inference::HktReducer::DEFAULT_FUEL
|
|
103
|
+
registry.reduce(self, fuel: fuel)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/rigor/type/bot.rb
CHANGED
|
@@ -8,10 +8,13 @@ module Rigor
|
|
|
8
8
|
# expressions that cannot terminate normally. See
|
|
9
9
|
# docs/type-specification/special-types.md.
|
|
10
10
|
class Bot
|
|
11
|
+
# ADR-15 Phase 4b.x — eager singleton so workers READ
|
|
12
|
+
# `@instance` without performing the lazy `||=` write
|
|
13
|
+
# that non-main Ractors are forbidden from doing.
|
|
14
|
+
@instance = new.freeze
|
|
15
|
+
|
|
11
16
|
class << self
|
|
12
|
-
|
|
13
|
-
@instance ||= new.freeze
|
|
14
|
-
end
|
|
17
|
+
attr_reader :instance
|
|
15
18
|
|
|
16
19
|
private :new
|
|
17
20
|
end
|
|
@@ -36,8 +36,14 @@ module Rigor
|
|
|
36
36
|
Bot.instance
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# ADR-15 Phase 4b.x — read the eagerly-allocated
|
|
40
|
+
# `@untyped` ivar instead of `||=`. The singleton-class
|
|
41
|
+
# `@untyped = Dynamic.new(top)` initializer runs at module
|
|
42
|
+
# body (below) on the main Ractor at load time. Workers
|
|
43
|
+
# READ the populated ivar without performing the lazy
|
|
44
|
+
# write that non-main Ractors are forbidden from doing.
|
|
39
45
|
def untyped
|
|
40
|
-
@untyped
|
|
46
|
+
@untyped
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
# Wraps the static facet in a Dynamic[T] carrier. Idempotent on the
|
|
@@ -809,6 +815,11 @@ module Rigor
|
|
|
809
815
|
members.sort_by { |m| m.describe(:short) }
|
|
810
816
|
end
|
|
811
817
|
end
|
|
818
|
+
|
|
819
|
+
# ADR-15 Phase 4b.x — eager-allocate the singleton
|
|
820
|
+
# `Dynamic[Top]` carrier on the main Ractor at load time.
|
|
821
|
+
# The `untyped` reader above just returns this ivar.
|
|
822
|
+
@untyped = Dynamic.new(Top.instance)
|
|
812
823
|
end
|
|
813
824
|
end
|
|
814
825
|
end
|
|
@@ -78,13 +78,13 @@ module Rigor
|
|
|
78
78
|
Float::INFINITY
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
ALIAS_NAMES = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
ALIAS_NAMES = Ractor.make_shareable({
|
|
82
|
+
[NEG_INFINITY, POS_INFINITY] => "int",
|
|
83
|
+
[1, POS_INFINITY] => "positive-int",
|
|
84
|
+
[0, POS_INFINITY] => "non-negative-int",
|
|
85
|
+
[NEG_INFINITY, -1] => "negative-int",
|
|
86
|
+
[NEG_INFINITY, 0] => "non-positive-int"
|
|
87
|
+
})
|
|
88
88
|
|
|
89
89
|
def describe(_verbosity = :short)
|
|
90
90
|
ALIAS_NAMES[[min, max]] || generic_description
|
data/lib/rigor/type/refined.rb
CHANGED
|
@@ -165,18 +165,24 @@ module Rigor
|
|
|
165
165
|
# kebab-case canonical name. Registered shapes print
|
|
166
166
|
# through `describe`; unregistered combinations fall back
|
|
167
167
|
# to the operator form.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
168
|
+
#
|
|
169
|
+
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
|
|
170
|
+
# because the keys are nested two-element Arrays. Plain
|
|
171
|
+
# `.freeze` would leave the inner arrays mutable, so a
|
|
172
|
+
# worker Ractor reading `CANONICAL_NAMES[[base, predicate]]`
|
|
173
|
+
# would trip `Ractor::IsolationError`.
|
|
174
|
+
CANONICAL_NAMES = Ractor.make_shareable({
|
|
175
|
+
["String", :lowercase] => "lowercase-string",
|
|
176
|
+
["String", :not_lowercase] => "non-lowercase-string",
|
|
177
|
+
["String", :uppercase] => "uppercase-string",
|
|
178
|
+
["String", :not_uppercase] => "non-uppercase-string",
|
|
179
|
+
["String", :numeric] => "numeric-string",
|
|
180
|
+
["String", :not_numeric] => "non-numeric-string",
|
|
181
|
+
["String", :decimal_int] => "decimal-int-string",
|
|
182
|
+
["String", :octal_int] => "octal-int-string",
|
|
183
|
+
["String", :hex_int] => "hex-int-string",
|
|
184
|
+
["String", :literal_string] => "literal-string"
|
|
185
|
+
})
|
|
180
186
|
private_constant :CANONICAL_NAMES
|
|
181
187
|
|
|
182
188
|
# Bidirectional `predicate_id ↔ complement_predicate_id`
|
data/lib/rigor/type/top.rb
CHANGED
|
@@ -7,10 +7,11 @@ module Rigor
|
|
|
7
7
|
# The top of the value lattice: contains every value, including untyped
|
|
8
8
|
# boundaries. See docs/type-specification/special-types.md.
|
|
9
9
|
class Top
|
|
10
|
+
# ADR-15 Phase 4b.x — eager singleton (see Bot.rb).
|
|
11
|
+
@instance = new.freeze
|
|
12
|
+
|
|
10
13
|
class << self
|
|
11
|
-
|
|
12
|
-
@instance ||= new.freeze
|
|
13
|
-
end
|
|
14
|
+
attr_reader :instance
|
|
14
15
|
|
|
15
16
|
private :new
|
|
16
17
|
end
|
data/lib/rigor/type.rb
CHANGED
|
@@ -23,6 +23,7 @@ require_relative "type/union"
|
|
|
23
23
|
require_relative "type/difference"
|
|
24
24
|
require_relative "type/refined"
|
|
25
25
|
require_relative "type/intersection"
|
|
26
|
+
require_relative "type/app"
|
|
26
27
|
require_relative "type/bound_method"
|
|
27
28
|
require_relative "type/accepts_result"
|
|
28
29
|
require_relative "type/combinator"
|
|
@@ -38,7 +38,13 @@ module Rigor
|
|
|
38
38
|
"TypeNode::IntegerLiteral, got #{args.inspect}"
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
# Freeze the String head + Array args so the Data
|
|
42
|
+
# object is `Ractor.shareable?`. Each `a` is already a
|
|
43
|
+
# shareable TypeNode value object (checked above), so
|
|
44
|
+
# freezing the wrapping Array is sufficient.
|
|
45
|
+
frozen_head = head.frozen? ? head : head.dup.freeze
|
|
46
|
+
frozen_args = args.frozen? ? args : args.dup.freeze
|
|
47
|
+
super(head: frozen_head, args: frozen_args)
|
|
42
48
|
end
|
|
43
49
|
|
|
44
50
|
private
|
|
@@ -23,7 +23,15 @@ module Rigor
|
|
|
23
23
|
"got #{name.inspect}"
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# Freeze the String field so the resulting Data object
|
|
27
|
+
# is `Ractor.shareable?` regardless of whether the
|
|
28
|
+
# caller passed a `# frozen_string_literal: true`
|
|
29
|
+
# constant or a dynamically built String. The same
|
|
30
|
+
# discipline applies to every other TypeNode value
|
|
31
|
+
# object — they live in the parser's hot path and are
|
|
32
|
+
# the natural carriers to flow through future Ractor
|
|
33
|
+
# boundaries (see CURRENT_WORK Open Items #8).
|
|
34
|
+
super(name: name.frozen? ? name : name.dup.freeze)
|
|
27
35
|
end
|
|
28
36
|
end
|
|
29
37
|
end
|
|
@@ -22,7 +22,10 @@ module Rigor
|
|
|
22
22
|
"got #{value.inspect}"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
# Freeze the String field so the Data object is
|
|
26
|
+
# `Ractor.shareable?` regardless of caller frozen-
|
|
27
|
+
# string-literal state.
|
|
28
|
+
super(value: value.frozen? ? value : value.dup.freeze)
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
end
|
data/lib/rigor/version.rb
CHANGED
data/sig/rigor/environment.rbs
CHANGED
|
@@ -8,19 +8,25 @@ module Rigor
|
|
|
8
8
|
attr_reader rbs_loader: RbsLoader?
|
|
9
9
|
attr_reader plugin_registry: untyped?
|
|
10
10
|
attr_reader dependency_source_index: untyped?
|
|
11
|
-
attr_reader
|
|
12
|
-
attr_reader boundary_cross_reporter: untyped?
|
|
11
|
+
attr_reader reporters: untyped
|
|
13
12
|
attr_reader name_scope: untyped?
|
|
13
|
+
attr_reader synthetic_method_index: untyped?
|
|
14
|
+
attr_reader project_patched_methods: untyped?
|
|
15
|
+
attr_reader hkt_registry: untyped?
|
|
14
16
|
|
|
15
17
|
def self.default: () -> Environment
|
|
16
|
-
def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?) -> Environment
|
|
18
|
+
def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?bundler_bundle_path: String?, ?bundler_auto_detect: bool, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> Environment
|
|
17
19
|
|
|
18
|
-
def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?) -> void
|
|
20
|
+
def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> void
|
|
21
|
+
def rbs_extended_reporter: () -> untyped?
|
|
22
|
+
def boundary_cross_reporter: () -> untyped?
|
|
23
|
+
def attach_reporters!: (rbs_extended_reporter: untyped?, boundary_cross_reporter: untyped?) -> nil
|
|
19
24
|
def nominal_for_name: (String | Symbol name) -> Type::Nominal?
|
|
20
25
|
def singleton_for_name: (String | Symbol name) -> Type::Singleton?
|
|
21
26
|
def constant_for_name: (String | Symbol name) -> Type::t?
|
|
22
27
|
def class_known?: (String | Symbol name) -> bool
|
|
23
28
|
def class_ordering: (String | Symbol lhs, String | Symbol rhs) -> ordering
|
|
29
|
+
def reflection: () -> untyped?
|
|
24
30
|
|
|
25
31
|
class ClassRegistry
|
|
26
32
|
def self.default: () -> ClassRegistry
|
|
@@ -42,6 +48,7 @@ module Rigor
|
|
|
42
48
|
def self.default: () -> RbsLoader
|
|
43
49
|
def self.reset_default!: () -> void
|
|
44
50
|
def self.build_env_for: (libraries: Array[String], signature_paths: Array[String | _ToPath]) -> untyped
|
|
51
|
+
def self.vendored_gem_sig_paths: () -> Array[Pathname]
|
|
45
52
|
|
|
46
53
|
def initialize: (?libraries: Array[String], ?signature_paths: Array[String | _ToPath], ?cache_store: untyped?) -> void
|
|
47
54
|
def class_known?: (String | Symbol name) -> bool
|
data/sig/rigor/inference.rbs
CHANGED
|
@@ -134,6 +134,8 @@ module Rigor
|
|
|
134
134
|
def self?.index: (untyped root, default_scope: Scope) -> Hash[untyped, Scope]
|
|
135
135
|
def self?.build_declaration_overrides: (untyped root) -> Hash[untyped, Type::t]
|
|
136
136
|
def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] identity_table, Hash[String, Type::t] discovered) -> void
|
|
137
|
+
def self?.discovered_classes_for_paths: (Array[String] paths, ?buffer: untyped) -> Hash[String, Type::t]
|
|
138
|
+
def self?.collect_class_decls: (untyped node, Array[String] qualified_prefix, Hash[String, Type::t] accumulator) -> void
|
|
137
139
|
def self?.qualified_name_for: (untyped constant_path_node) -> String?
|
|
138
140
|
def self?.render_constant_path: (untyped node) -> String
|
|
139
141
|
def self?.propagate: (untyped node, Hash[untyped, Scope] table, Scope parent_scope) -> void
|
|
@@ -3,7 +3,7 @@ class Rigor::Plugin::Manifest::Consumption
|
|
|
3
3
|
end
|
|
4
4
|
|
|
5
5
|
class Rigor::Plugin::Manifest
|
|
6
|
-
def initialize: (id: untyped, version: untyped, ?description: untyped, ?protocols: untyped, ?config_schema: untyped, ?produces: untyped, ?consumes: untyped, ?owns_receivers: untyped, ?type_node_resolvers: untyped) -> void
|
|
6
|
+
def initialize: (id: untyped, version: untyped, ?description: untyped, ?protocols: untyped, ?config_schema: untyped, ?produces: untyped, ?consumes: untyped, ?owns_receivers: untyped, ?type_node_resolvers: untyped, ?block_as_methods: untyped, ?heredoc_templates: untyped, ?trait_registries: untyped, ?external_files: untyped) -> void
|
|
7
7
|
def validate_config: (untyped) -> Array[String]
|
|
8
8
|
def to_h: () -> Hash[String, untyped]
|
|
9
9
|
end
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
class Rigor::Plugin::Registry
|
|
2
|
-
|
|
2
|
+
attr_reader plugins: untyped
|
|
3
|
+
attr_reader load_errors: untyped
|
|
4
|
+
attr_reader blueprints: untyped
|
|
5
|
+
|
|
6
|
+
EMPTY: Rigor::Plugin::Registry
|
|
7
|
+
|
|
8
|
+
def self.materialize: (blueprints: untyped, services: untyped) -> Rigor::Plugin::Registry
|
|
9
|
+
|
|
10
|
+
def initialize: (?plugins: untyped, ?load_errors: untyped, ?blueprints: untyped) -> void
|
|
11
|
+
def find: (String | Symbol id) -> untyped
|
|
12
|
+
def ids: () -> Array[String]
|
|
13
|
+
def empty?: () -> bool
|
|
14
|
+
def any_load_errors?: () -> bool
|
|
15
|
+
def type_node_resolvers: () -> Array[untyped]
|
|
3
16
|
end
|
data/sig/rigor.rbs
CHANGED
|
@@ -54,13 +54,46 @@ module Rigor
|
|
|
54
54
|
|
|
55
55
|
class Result
|
|
56
56
|
attr_reader diagnostics: Array[Diagnostic]
|
|
57
|
+
attr_reader stats: RunStats?
|
|
57
58
|
|
|
58
|
-
def initialize: (?diagnostics: Array[Diagnostic]) -> void
|
|
59
|
+
def initialize: (?diagnostics: Array[Diagnostic], ?stats: RunStats?) -> void
|
|
59
60
|
def success?: () -> bool
|
|
60
61
|
def error_count: () -> Integer
|
|
61
62
|
def to_h: () -> Hash[String, untyped]
|
|
62
63
|
end
|
|
63
64
|
|
|
65
|
+
class RunStats
|
|
66
|
+
attr_reader wall_seconds: Float
|
|
67
|
+
attr_reader peak_rss_bytes: Integer?
|
|
68
|
+
attr_reader target_files: Integer
|
|
69
|
+
attr_reader rbs_classes_total: Integer
|
|
70
|
+
attr_reader rbs_classes_project_sig: Integer
|
|
71
|
+
attr_reader rbs_classes_bundled: Integer
|
|
72
|
+
attr_reader gem_walk_classes: Integer
|
|
73
|
+
attr_reader gem_walk_gems: Integer
|
|
74
|
+
attr_reader rbs_attribution_available: bool
|
|
75
|
+
|
|
76
|
+
CACHED_SENTINEL: String
|
|
77
|
+
|
|
78
|
+
def self.peak_rss_bytes: () -> Integer?
|
|
79
|
+
def self.partition_classes: (class_decl_paths: Hash[String, String], signature_paths: Array[untyped]) -> [Integer, Integer]
|
|
80
|
+
def self.attribution_available?: (class_decl_paths: Hash[String, String]) -> bool
|
|
81
|
+
|
|
82
|
+
def initialize: (
|
|
83
|
+
wall_seconds: Float,
|
|
84
|
+
peak_rss_bytes: Integer?,
|
|
85
|
+
target_files: Integer,
|
|
86
|
+
rbs_classes_total: Integer,
|
|
87
|
+
rbs_classes_project_sig: Integer,
|
|
88
|
+
rbs_classes_bundled: Integer,
|
|
89
|
+
gem_walk_classes: Integer,
|
|
90
|
+
gem_walk_gems: Integer,
|
|
91
|
+
?rbs_attribution_available: bool
|
|
92
|
+
) -> void
|
|
93
|
+
def format: (untyped out, ?prefix: String) -> void
|
|
94
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
95
|
+
end
|
|
96
|
+
|
|
64
97
|
class Runner
|
|
65
98
|
RUBY_GLOB: String
|
|
66
99
|
DEFAULT_CACHE_ROOT: String
|
|
@@ -71,8 +104,10 @@ module Rigor
|
|
|
71
104
|
# otherwise raises `RBS::UnknownTypeName` for the named type.
|
|
72
105
|
attr_reader cache_store: untyped
|
|
73
106
|
attr_reader plugin_registry: untyped
|
|
74
|
-
|
|
107
|
+
attr_reader buffer: untyped
|
|
108
|
+
def initialize: (configuration: Configuration, ?explain: bool, ?cache_store: untyped, ?plugin_requirer: untyped, ?workers: Integer, ?collect_stats: bool, ?buffer: untyped, ?prebuilt: untyped, ?environment: untyped) -> void
|
|
75
109
|
def run: (?Array[String] paths) -> Result
|
|
110
|
+
def prepare_project_scan: (?paths: Array[String]) -> untyped
|
|
76
111
|
end
|
|
77
112
|
end
|
|
78
113
|
end
|