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.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -56
  3. data/lib/rigor/analysis/buffer_binding.rb +36 -0
  4. data/lib/rigor/analysis/check_rules.rb +11 -1
  5. data/lib/rigor/analysis/dependency_source_inference/index.rb +14 -1
  6. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +105 -0
  7. data/lib/rigor/analysis/dependency_source_inference/walker.rb +32 -12
  8. data/lib/rigor/analysis/fact_store.rb +15 -3
  9. data/lib/rigor/analysis/project_scan.rb +39 -0
  10. data/lib/rigor/analysis/result.rb +11 -3
  11. data/lib/rigor/analysis/run_stats.rb +193 -0
  12. data/lib/rigor/analysis/runner.rb +681 -19
  13. data/lib/rigor/analysis/worker_session.rb +339 -0
  14. data/lib/rigor/builtins/hkt_builtins.rb +342 -0
  15. data/lib/rigor/builtins/imported_refinements.rb +6 -2
  16. data/lib/rigor/builtins/regex_refinement.rb +17 -12
  17. data/lib/rigor/builtins/static_return_refinements.rb +120 -0
  18. data/lib/rigor/cache/rbs_descriptor.rb +3 -1
  19. data/lib/rigor/cache/store.rb +72 -9
  20. data/lib/rigor/cli/lsp_command.rb +129 -0
  21. data/lib/rigor/cli/type_of_command.rb +44 -5
  22. data/lib/rigor/cli.rb +122 -10
  23. data/lib/rigor/configuration.rb +168 -7
  24. data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
  25. data/lib/rigor/environment/class_registry.rb +12 -3
  26. data/lib/rigor/environment/hkt_registry_holder.rb +33 -0
  27. data/lib/rigor/environment/lockfile_resolver.rb +125 -0
  28. data/lib/rigor/environment/rbs_collection_discovery.rb +126 -0
  29. data/lib/rigor/environment/rbs_coverage_report.rb +112 -0
  30. data/lib/rigor/environment/rbs_loader.rb +238 -7
  31. data/lib/rigor/environment/reflection.rb +152 -0
  32. data/lib/rigor/environment/reporters.rb +40 -0
  33. data/lib/rigor/environment.rb +179 -10
  34. data/lib/rigor/inference/acceptance.rb +83 -4
  35. data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
  36. data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
  37. data/lib/rigor/inference/expression_typer.rb +59 -2
  38. data/lib/rigor/inference/hkt_body.rb +171 -0
  39. data/lib/rigor/inference/hkt_body_parser.rb +363 -0
  40. data/lib/rigor/inference/hkt_reducer.rb +256 -0
  41. data/lib/rigor/inference/hkt_registry.rb +223 -0
  42. data/lib/rigor/inference/macro_block_self_type.rb +96 -0
  43. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -29
  44. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
  45. data/lib/rigor/inference/method_dispatcher/method_folding.rb +18 -1
  46. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +126 -31
  47. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +87 -0
  48. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -40
  49. data/lib/rigor/inference/method_dispatcher.rb +282 -6
  50. data/lib/rigor/inference/method_parameter_binder.rb +21 -11
  51. data/lib/rigor/inference/narrowing.rb +127 -8
  52. data/lib/rigor/inference/project_patched_methods.rb +70 -0
  53. data/lib/rigor/inference/project_patched_scanner.rb +210 -0
  54. data/lib/rigor/inference/scope_indexer.rb +156 -12
  55. data/lib/rigor/inference/statement_evaluator.rb +106 -6
  56. data/lib/rigor/inference/synthetic_method.rb +86 -0
  57. data/lib/rigor/inference/synthetic_method_index.rb +82 -0
  58. data/lib/rigor/inference/synthetic_method_scanner.rb +599 -0
  59. data/lib/rigor/language_server/buffer_table.rb +63 -0
  60. data/lib/rigor/language_server/completion_provider.rb +438 -0
  61. data/lib/rigor/language_server/debouncer.rb +86 -0
  62. data/lib/rigor/language_server/diagnostic_publisher.rb +167 -0
  63. data/lib/rigor/language_server/document_symbol_provider.rb +142 -0
  64. data/lib/rigor/language_server/folding_range_provider.rb +75 -0
  65. data/lib/rigor/language_server/hover_provider.rb +74 -0
  66. data/lib/rigor/language_server/hover_renderer.rb +312 -0
  67. data/lib/rigor/language_server/loop.rb +71 -0
  68. data/lib/rigor/language_server/project_context.rb +145 -0
  69. data/lib/rigor/language_server/selection_range_provider.rb +93 -0
  70. data/lib/rigor/language_server/server.rb +384 -0
  71. data/lib/rigor/language_server/signature_help_provider.rb +249 -0
  72. data/lib/rigor/language_server/synchronized_writer.rb +28 -0
  73. data/lib/rigor/language_server/uri.rb +40 -0
  74. data/lib/rigor/language_server.rb +29 -0
  75. data/lib/rigor/plugin/base.rb +63 -0
  76. data/lib/rigor/plugin/blueprint.rb +60 -0
  77. data/lib/rigor/plugin/loader.rb +3 -1
  78. data/lib/rigor/plugin/macro/block_as_method.rb +131 -0
  79. data/lib/rigor/plugin/macro/external_file.rb +143 -0
  80. data/lib/rigor/plugin/macro/heredoc_template.rb +315 -0
  81. data/lib/rigor/plugin/macro/trait_registry.rb +198 -0
  82. data/lib/rigor/plugin/macro.rb +31 -0
  83. data/lib/rigor/plugin/manifest.rb +127 -9
  84. data/lib/rigor/plugin/registry.rb +51 -2
  85. data/lib/rigor/plugin.rb +1 -0
  86. data/lib/rigor/rbs_extended/hkt_directives.rb +326 -0
  87. data/lib/rigor/rbs_extended.rb +82 -2
  88. data/lib/rigor/sig_gen/generator.rb +12 -3
  89. data/lib/rigor/trinary.rb +15 -11
  90. data/lib/rigor/type/app.rb +107 -0
  91. data/lib/rigor/type/bot.rb +6 -3
  92. data/lib/rigor/type/combinator.rb +12 -1
  93. data/lib/rigor/type/integer_range.rb +7 -7
  94. data/lib/rigor/type/refined.rb +18 -12
  95. data/lib/rigor/type/top.rb +4 -3
  96. data/lib/rigor/type.rb +1 -0
  97. data/lib/rigor/type_node/generic.rb +7 -1
  98. data/lib/rigor/type_node/identifier.rb +9 -1
  99. data/lib/rigor/type_node/string_literal.rb +4 -1
  100. data/lib/rigor/version.rb +1 -1
  101. data/sig/rigor/environment.rbs +11 -4
  102. data/sig/rigor/inference.rbs +2 -0
  103. data/sig/rigor/plugin/blueprint.rbs +7 -0
  104. data/sig/rigor/plugin/manifest.rbs +1 -1
  105. data/sig/rigor/plugin/registry.rbs +14 -1
  106. data/sig/rigor.rbs +37 -2
  107. metadata +92 -1
@@ -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
- def parse_return_type_override(string, name_scope: nil, reporter: nil, source_location: nil)
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: (@class_shells || Set.new).to_a,
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&.include?([class_name, method_name])
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
- def yes
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
@@ -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
- def instance
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 ||= Dynamic.new(top)
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
- [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
- }.freeze
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
@@ -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
- CANONICAL_NAMES = {
169
- ["String", :lowercase] => "lowercase-string",
170
- ["String", :not_lowercase] => "non-lowercase-string",
171
- ["String", :uppercase] => "uppercase-string",
172
- ["String", :not_uppercase] => "non-uppercase-string",
173
- ["String", :numeric] => "numeric-string",
174
- ["String", :not_numeric] => "non-numeric-string",
175
- ["String", :decimal_int] => "decimal-int-string",
176
- ["String", :octal_int] => "octal-int-string",
177
- ["String", :hex_int] => "hex-int-string",
178
- ["String", :literal_string] => "literal-string"
179
- }.freeze
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`
@@ -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
- def instance
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
- super(head: head, args: args.freeze)
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
- super
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
- super
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.6"
5
5
  end
@@ -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 rbs_extended_reporter: untyped?
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
@@ -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
@@ -0,0 +1,7 @@
1
+ class Rigor::Plugin::Blueprint
2
+ attr_reader klass_name: String
3
+ attr_reader config: Hash[untyped, untyped]
4
+
5
+ def initialize: (klass_name: String | Module, ?config: Hash[untyped, untyped]) -> void
6
+ def materialize: (services: untyped) -> Rigor::Plugin::Base
7
+ end
@@ -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
- def initialize: (?plugins: untyped, ?load_errors: untyped) -> void
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
- def initialize: (configuration: Configuration, ?explain: bool, ?cache_store: untyped, ?plugin_requirer: untyped) -> void
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