rigortype 0.1.0 → 0.1.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -2
  3. data/data/builtins/ruby_core/array.yml +6 -6
  4. data/data/builtins/ruby_core/hash.yml +1 -1
  5. data/data/builtins/ruby_core/io.yml +3 -3
  6. data/data/builtins/ruby_core/numeric.yml +1 -1
  7. data/data/builtins/ruby_core/pathname.yml +100 -100
  8. data/data/builtins/ruby_core/proc.yml +1 -1
  9. data/data/builtins/ruby_core/time.yml +3 -3
  10. data/lib/rigor/analysis/runner.rb +88 -5
  11. data/lib/rigor/builtins/regex_refinement.rb +104 -0
  12. data/lib/rigor/cli/type_of_command.rb +3 -3
  13. data/lib/rigor/cli/type_scan_command.rb +4 -4
  14. data/lib/rigor/cli.rb +11 -4
  15. data/lib/rigor/configuration.rb +177 -10
  16. data/lib/rigor/environment.rb +12 -4
  17. data/lib/rigor/inference/expression_typer.rb +3 -1
  18. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +31 -0
  19. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +43 -2
  20. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +68 -2
  21. data/lib/rigor/inference/method_dispatcher.rb +50 -1
  22. data/lib/rigor/inference/narrowing.rb +150 -6
  23. data/lib/rigor/inference/scope_indexer.rb +49 -15
  24. data/lib/rigor/inference/statement_evaluator.rb +29 -0
  25. data/lib/rigor/plugin/base.rb +43 -0
  26. data/lib/rigor/plugin/fact_store.rb +92 -0
  27. data/lib/rigor/plugin/load_error.rb +14 -2
  28. data/lib/rigor/plugin/loader.rb +116 -0
  29. data/lib/rigor/plugin/manifest.rb +75 -6
  30. data/lib/rigor/plugin/services.rb +14 -2
  31. data/lib/rigor/plugin.rb +1 -0
  32. data/lib/rigor/trinary.rb +1 -1
  33. data/lib/rigor/type/integer_range.rb +6 -2
  34. data/lib/rigor/version.rb +1 -1
  35. data/sig/rigor/environment.rbs +3 -2
  36. data/sig/rigor.rbs +8 -2
  37. metadata +3 -1
@@ -111,6 +111,49 @@ module Rigor
111
111
  nil
112
112
  end
113
113
 
114
+ # ADR-2 § "Flow Contribution Bundle" / v0.1.1 Track 2
115
+ # slice 7 — per-call return-type contribution hook. When
116
+ # the inference engine dispatches a `Prism::CallNode` and
117
+ # neither the precision tiers nor RBS resolve a result,
118
+ # `MethodDispatcher` consults each loaded plugin via this
119
+ # hook ahead of `RbsDispatch`. Plugins that override the
120
+ # default return a {Rigor::FlowContribution} bundle whose
121
+ # `return_type` slot pins the call site's result type.
122
+ #
123
+ # Default returns nil — plugins that don't refine return
124
+ # types skip the override. Failures are isolated: a hook
125
+ # that raises gets its contribution dropped silently for
126
+ # this call so the rest of the dispatch chain continues.
127
+ def flow_contribution_for(call_node:, scope:) # rubocop:disable Lint/UnusedMethodArgument
128
+ nil
129
+ end
130
+
131
+ # ADR-9 slice 3 — per-run preparation hook. The runner
132
+ # invokes `#prepare(services)` on every loaded plugin once
133
+ # per `Analysis::Runner.run`, after `#init` has run on every
134
+ # plugin and before any `#diagnostics_for_file` call.
135
+ # Plugins use this hook to compute and publish facts other
136
+ # plugins consume:
137
+ #
138
+ # def prepare(services)
139
+ # services.fact_store.publish(
140
+ # plugin_id: manifest.id, name: :model_index, value: model_index
141
+ # )
142
+ # end
143
+ #
144
+ # Default no-op so plugins without facts to publish leave
145
+ # `#prepare` unimplemented. Failures isolate as
146
+ # `:plugin_loader runtime-error` diagnostics; a plugin that
147
+ # raises in `#prepare` has its facts considered un-published
148
+ # and downstream consumers see `nil` from `fact_store.read`.
149
+ #
150
+ # Slice 3 calls plugins in registration order. ADR-9 slice 5
151
+ # introduces topological ordering by `consumes:` so producers
152
+ # always run before consumers.
153
+ def prepare(services) # rubocop:disable Lint/UnusedMethodArgument
154
+ nil
155
+ end
156
+
114
157
  # ADR-7 § "Slice 5-A" — per-file diagnostic emission hook.
115
158
  # Override in plugin subclasses to return an array of
116
159
  # `Rigor::Analysis::Diagnostic` rows for the analysed file.
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ module Plugin
5
+ # Per-run cross-plugin fact store. ADR-9 § "Plugin::FactStore".
6
+ #
7
+ # A plugin publishes typed `(plugin_id, name) -> value` tuples
8
+ # in its `Plugin::Base#prepare(services)` hook (slice 3); other
9
+ # plugins read them in `#diagnostics_for_file` via
10
+ # `services.fact_store.read(plugin_id:, name:)`. The store is
11
+ # constructed fresh at the start of every `Analysis::Runner.run`
12
+ # and discarded at the end — caching the underlying expensive
13
+ # computation is the producer's job (`Plugin::Base.producer`);
14
+ # the FactStore just publishes a *reference* to that
15
+ # already-cached result.
16
+ #
17
+ # `(plugin_id, name)` is a unique key. A second `publish` with
18
+ # the same value is a no-op (`==` comparison); a second
19
+ # `publish` with a different value raises {Conflict}. Since
20
+ # `plugin_id` namespaces the key, a real conflict only happens
21
+ # when a single plugin publishes twice with differing values —
22
+ # the conflict signals a plugin-author bug, never a load-time
23
+ # interaction between unrelated plugins.
24
+ class FactStore
25
+ Fact = Data.define(:plugin_id, :name, :value)
26
+
27
+ class Conflict < StandardError
28
+ attr_reader :plugin_id, :name, :existing, :incoming
29
+
30
+ def initialize(plugin_id:, name:, existing:, incoming:)
31
+ @plugin_id = plugin_id
32
+ @name = name
33
+ @existing = existing
34
+ @incoming = incoming
35
+ super(
36
+ "fact store conflict: plugin #{plugin_id.inspect} published " \
37
+ "two different values for #{name.inspect} " \
38
+ "(existing: #{existing.inspect}, incoming: #{incoming.inspect})"
39
+ )
40
+ end
41
+ end
42
+
43
+ def initialize
44
+ @facts = {}
45
+ @mutex = Mutex.new
46
+ end
47
+
48
+ # Writes a `(plugin_id, name) -> value` triple. Idempotent if
49
+ # the same value is published twice (`==`); raises
50
+ # {Conflict} if the values differ.
51
+ #
52
+ # @param plugin_id [String] producing plugin's manifest id.
53
+ # @param name [Symbol, String] fact name (canonicalised to
54
+ # Symbol for lookup).
55
+ # @param value [Object] frozen-shape value object the
56
+ # producer chose to publish. The value is stored as-is.
57
+ def publish(plugin_id:, name:, value:)
58
+ plugin_id = plugin_id.to_s
59
+ name = name.to_sym
60
+ @mutex.synchronize do
61
+ existing = @facts[[plugin_id, name]]
62
+ if existing && existing.value != value
63
+ raise Conflict.new(plugin_id: plugin_id, name: name, existing: existing.value, incoming: value)
64
+ end
65
+
66
+ @facts[[plugin_id, name]] = Fact.new(plugin_id: plugin_id, name: name, value: value)
67
+ end
68
+ nil
69
+ end
70
+
71
+ # @return [Object, nil] the published value, or `nil` when no
72
+ # fact is registered. Reads do NOT establish a dependency —
73
+ # `manifest(consumes:)` (slice 4) is the dependency
74
+ # declaration mechanism.
75
+ def read(plugin_id:, name:)
76
+ fact = @mutex.synchronize { @facts[[plugin_id.to_s, name.to_sym]] }
77
+ fact&.value
78
+ end
79
+
80
+ # @return [Boolean] whether a fact is registered.
81
+ def published?(plugin_id:, name:)
82
+ @mutex.synchronize { @facts.key?([plugin_id.to_s, name.to_sym]) }
83
+ end
84
+
85
+ # @yield [Fact] every published fact in publication order.
86
+ def each_fact(&)
87
+ snapshot = @mutex.synchronize { @facts.values }
88
+ snapshot.each(&)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -11,12 +11,24 @@ module Rigor
11
11
  # to be isolated at the analyzer boundary; this class is the
12
12
  # carrier for that contract on the loading side.
13
13
  class LoadError < StandardError
14
- attr_reader :plugin_ref, :cause_class
14
+ attr_reader :plugin_ref, :cause_class, :reason
15
15
 
16
- def initialize(message, plugin_ref:, cause: nil)
16
+ # ADR-9 slice 5 introduces two new reason codes alongside the
17
+ # implicit "load failure" used for require / configuration /
18
+ # init failures:
19
+ #
20
+ # - `:missing-producer` — a non-optional `manifest(consumes:)`
21
+ # entry names a `(plugin_id, name)` no loaded plugin
22
+ # produces.
23
+ # - `:dependency-cycle` — the consumes graph forms a cycle.
24
+ #
25
+ # Older callers omit `reason:` and the field defaults to nil
26
+ # (the legacy "load failure" envelope).
27
+ def initialize(message, plugin_ref:, cause: nil, reason: nil)
17
28
  super(message)
18
29
  @plugin_ref = plugin_ref
19
30
  @cause_class = cause&.class
31
+ @reason = reason&.to_sym
20
32
  end
21
33
  end
22
34
  end
@@ -63,6 +63,15 @@ module Rigor
63
63
  end
64
64
  end
65
65
 
66
+ # ADR-9 slice 5 — topological sort by `manifest(consumes:)`
67
+ # so producers run before consumers, plus early
68
+ # `missing-producer` validation. Cycles surface as
69
+ # `dependency-cycle` LoadErrors. When validation fails, the
70
+ # offending plugin(s) drop from the returned plugins list
71
+ # and the LoadError surfaces alongside any earlier failure.
72
+ plugins, sort_errors = topo_sort_plugins(plugins)
73
+ load_errors.concat(sort_errors)
74
+
66
75
  Registry.new(plugins: plugins, load_errors: load_errors)
67
76
  end
68
77
 
@@ -186,6 +195,113 @@ module Rigor
186
195
  rescue StandardError
187
196
  plugin_class.to_s
188
197
  end
198
+
199
+ # ADR-9 slice 5 — topological sort of plugins by their
200
+ # `manifest(consumes:)` declarations. Returns `[sorted_plugins,
201
+ # load_errors]`. Determinism: when no dependency relation
202
+ # forces an order, plugins are visited alphabetically by
203
+ # manifest id. A non-optional consume of a `(plugin_id, name)`
204
+ # whose producer is missing emits a `:missing-producer`
205
+ # LoadError and drops the consumer; cycles emit a
206
+ # `:dependency-cycle` LoadError naming the offending chain.
207
+ def topo_sort_plugins(plugins)
208
+ # If no plugin opts into the cross-plugin API the loader's
209
+ # legacy configuration-order contract is preserved
210
+ # unchanged. Topo sort and missing-producer validation only
211
+ # run when at least one plugin declares `consumes:`.
212
+ return [plugins, []] unless plugins.any? { |p| p.manifest.consumes.any? }
213
+
214
+ index = plugins.to_h { |plugin| [plugin.manifest.id, plugin] }
215
+ errors = validate_missing_producers(plugins, index)
216
+ sortable = plugins.reject { |p| errors.any? { |e| e.plugin_ref == p.manifest.id } }
217
+ config_order = plugins.each_with_index.to_h { |plugin, i| [plugin.manifest.id, i] }
218
+
219
+ sort_in_topo_order(sortable, index, errors, config_order)
220
+ end
221
+
222
+ def validate_missing_producers(plugins, index)
223
+ errors = []
224
+ plugins.each do |plugin|
225
+ plugin.manifest.consumes.each do |consume|
226
+ next if consume.optional
227
+ next if index.key?(consume.plugin_id) && producer_provides?(index[consume.plugin_id], consume.name)
228
+
229
+ errors << LoadError.new(
230
+ "plugin #{plugin.manifest.id.inspect} consumes " \
231
+ "#{consume.plugin_id.inspect}/#{consume.name} but no loaded plugin " \
232
+ "with that id declares `produces: [#{consume.name.inspect}]`",
233
+ plugin_ref: plugin.manifest.id,
234
+ reason: :"missing-producer"
235
+ )
236
+ end
237
+ end
238
+ errors
239
+ end
240
+
241
+ def producer_provides?(producer, name)
242
+ producer.manifest.produces.include?(name)
243
+ end
244
+
245
+ # Kahn's algorithm with `Configuration#plugins`-order
246
+ # tie-break. Edges go from producer -> consumer (producer
247
+ # must visit first). When two plugins are simultaneously
248
+ # ready, the configuration-order index decides the visit
249
+ # order — preserves the v0.1.0 legacy contract for plugins
250
+ # without dependencies.
251
+ def sort_in_topo_order(plugins, index, errors, config_order)
252
+ in_degree, forward = build_consumes_graph(plugins, index, errors)
253
+ ordered, cycle_errors = kahn_walk(plugins, in_degree, forward, config_order)
254
+ [ordered, errors + cycle_errors]
255
+ end
256
+
257
+ def build_consumes_graph(plugins, index, errors)
258
+ in_degree = Hash.new(0)
259
+ forward = Hash.new { |h, k| h[k] = [] }
260
+ plugins.each do |consumer|
261
+ consumer.manifest.consumes.each do |consume|
262
+ next unless index.key?(consume.plugin_id)
263
+ next if errors.any? { |e| e.plugin_ref == consume.plugin_id }
264
+
265
+ forward[consume.plugin_id] << consumer.manifest.id
266
+ in_degree[consumer.manifest.id] += 1
267
+ end
268
+ end
269
+ [in_degree, forward]
270
+ end
271
+
272
+ def kahn_walk(plugins, in_degree, forward, config_order)
273
+ order = ->(plugin) { config_order.fetch(plugin.manifest.id, Float::INFINITY) }
274
+ ready = plugins.select { |p| in_degree[p.manifest.id].zero? }.sort_by(&order)
275
+ result = kahn_collect(plugins, in_degree, forward, ready, order)
276
+
277
+ return [result, []] if result.size == plugins.size
278
+
279
+ cycled = plugins - result
280
+ [result, [dependency_cycle_error(cycled)]]
281
+ end
282
+
283
+ def kahn_collect(plugins, in_degree, forward, ready, order)
284
+ result = []
285
+ until ready.empty?
286
+ plugin = ready.shift
287
+ result << plugin
288
+ forward[plugin.manifest.id].each do |consumer_id|
289
+ in_degree[consumer_id] -= 1
290
+ ready << plugins.find { |p| p.manifest.id == consumer_id } if in_degree[consumer_id].zero?
291
+ end
292
+ ready.sort_by!(&order)
293
+ end
294
+ result
295
+ end
296
+
297
+ def dependency_cycle_error(cycled)
298
+ ids = cycled.map { |p| p.manifest.id }.sort
299
+ LoadError.new(
300
+ "plugin dependency cycle through `manifest(consumes:)`: #{ids.inspect}",
301
+ plugin_ref: ids.first,
302
+ reason: :"dependency-cycle"
303
+ )
304
+ end
189
305
  end
190
306
  end
191
307
  end
@@ -11,7 +11,7 @@ module Rigor
11
11
  # The fields are pinned by ADR-2 § "Registration, Configuration,
12
12
  # and Caching"; the v0.1.0 plugin contract surface treats this
13
13
  # struct as the public manifest shape.
14
- class Manifest
14
+ class Manifest # rubocop:disable Metrics/ClassLength
15
15
  # Same regex {Rigor::Cache::Store::VALID_PRODUCER_ID} uses,
16
16
  # so plugin ids round-trip through cache producer ids and
17
17
  # `plugin.<id>.<rule>` diagnostic identifiers without escape.
@@ -23,23 +23,51 @@ module Rigor
23
23
  # the v0.1.0 protocol slices need them.
24
24
  VALID_VALUE_KINDS = %i[string boolean integer array hash any].freeze
25
25
 
26
- attr_reader :id, :version, :description, :protocols, :config_schema
26
+ # ADR-9 slice 4 declared cross-plugin fact dependencies.
27
+ # `produces:` lists the names this plugin publishes through
28
+ # its `#prepare(services)` hook. `consumes:` lists the
29
+ # `(plugin_id, name)` pairs this plugin reads from
30
+ # `services.fact_store`. The loader uses both for
31
+ # topological sort + missing-producer detection (slice 5);
32
+ # slice 4 carries the declarations on the manifest but the
33
+ # loader does not yet enforce them.
34
+ Consumption = Data.define(:plugin_id, :name, :optional) do
35
+ def initialize(plugin_id:, name:, optional: false)
36
+ super(plugin_id: plugin_id.to_s, name: name.to_sym, optional: optional ? true : false)
37
+ end
38
+ end
27
39
 
28
- def initialize(id:, version:, description: nil, protocols: [], config_schema: {})
40
+ attr_reader :id, :version, :description, :protocols, :config_schema, :produces, :consumes
41
+
42
+ def initialize( # rubocop:disable Metrics/ParameterLists
43
+ id:, version:,
44
+ description: nil, protocols: [], config_schema: {},
45
+ produces: [], consumes: []
46
+ )
29
47
  validate_id!(id)
30
48
  validate_version!(version)
31
49
  validate_protocols!(protocols)
32
50
  validate_config_schema!(config_schema)
51
+ validate_produces!(produces)
52
+
53
+ assign_fields(id, version, description, protocols, config_schema, produces, consumes)
54
+ freeze
55
+ end
56
+
57
+ private
33
58
 
59
+ def assign_fields(id, version, description, protocols, config_schema, produces, consumes) # rubocop:disable Metrics/ParameterLists
34
60
  @id = id.dup.freeze
35
61
  @version = version.dup.freeze
36
62
  @description = description.nil? ? nil : description.to_s.dup.freeze
37
63
  @protocols = protocols.map(&:to_sym).freeze
38
64
  @config_schema = config_schema.to_h { |k, v| [k.to_s.dup.freeze, v.to_sym] }.freeze
39
-
40
- freeze
65
+ @produces = produces.map(&:to_sym).freeze
66
+ @consumes = coerce_consumes(consumes)
41
67
  end
42
68
 
69
+ public
70
+
43
71
  # Validates the user-supplied plugin config block against this
44
72
  # manifest's `config_schema`. Returns an array of human-readable
45
73
  # error strings (empty when the config is valid). Slice 1 checks
@@ -69,7 +97,9 @@ module Rigor
69
97
  "version" => version,
70
98
  "description" => description,
71
99
  "protocols" => protocols.map(&:to_s),
72
- "config_schema" => config_schema.to_h { |k, v| [k, v.to_s] }
100
+ "config_schema" => config_schema.to_h { |k, v| [k, v.to_s] },
101
+ "produces" => produces.map(&:to_s),
102
+ "consumes" => consumes.map { |c| consumption_hash(c) }
73
103
  }
74
104
  end
75
105
 
@@ -129,6 +159,45 @@ module Rigor
129
159
  else false
130
160
  end
131
161
  end
162
+
163
+ def validate_produces!(produces)
164
+ return if produces.is_a?(Array) && produces.all? { |p| p.is_a?(Symbol) || p.is_a?(String) }
165
+
166
+ raise ArgumentError, "plugin manifest produces must be an Array of Symbol/String, got #{produces.inspect}"
167
+ end
168
+
169
+ def coerce_consumes(consumes)
170
+ unless consumes.is_a?(Array)
171
+ raise ArgumentError, "plugin manifest consumes must be an Array, got #{consumes.inspect}"
172
+ end
173
+
174
+ consumes.map { |entry| coerce_consumption(entry) }.freeze
175
+ end
176
+
177
+ def coerce_consumption(entry)
178
+ case entry
179
+ when Consumption then entry
180
+ when Hash then build_consumption_from_hash(entry)
181
+ else raise ArgumentError,
182
+ "plugin manifest consumes entry must be a Hash or Consumption, got #{entry.inspect}"
183
+ end
184
+ end
185
+
186
+ def consumption_hash(consumption)
187
+ { "plugin_id" => consumption.plugin_id, "name" => consumption.name.to_s, "optional" => consumption.optional }
188
+ end
189
+
190
+ def build_consumption_from_hash(entry)
191
+ plugin_id = entry[:plugin_id] || entry["plugin_id"]
192
+ name = entry[:name] || entry["name"]
193
+ optional = entry.key?(:optional) ? entry[:optional] : entry["optional"]
194
+ if plugin_id.nil? || name.nil?
195
+ raise ArgumentError,
196
+ "plugin manifest consumes entry missing plugin_id/name: #{entry.inspect}"
197
+ end
198
+
199
+ Consumption.new(plugin_id: plugin_id, name: name, optional: optional || false)
200
+ end
132
201
  end
133
202
  end
134
203
  end
@@ -31,15 +31,27 @@ module Rigor
31
31
  # raw `File.read` so reads stay within the trusted scope and
32
32
  # feed cache invalidation; ADR-2 § "Plugin Trust and I/O
33
33
  # Policy" documents the trust model the boundary enforces.
34
+ #
35
+ # ADR-9 slice 2 adds `fact_store`: the per-run cross-plugin
36
+ # `Plugin::FactStore`. Producer plugins publish their facts
37
+ # in `#prepare(services)` (slice 3); consumer plugins read in
38
+ # `#diagnostics_for_file` via `services.fact_store.read(...)`.
39
+ # A fresh `FactStore` instance is constructed per Services
40
+ # when none is supplied — the runner threads its own instance
41
+ # in once slice 3 wires `#prepare` invocation.
34
42
  class Services
35
- attr_reader :reflection, :type, :configuration, :cache_store, :trust_policy
43
+ attr_reader :reflection, :type, :configuration, :cache_store, :trust_policy, :fact_store
36
44
 
37
- def initialize(reflection:, type:, configuration:, cache_store: nil, trust_policy: nil)
45
+ def initialize( # rubocop:disable Metrics/ParameterLists
46
+ reflection:, type:, configuration:,
47
+ cache_store: nil, trust_policy: nil, fact_store: nil
48
+ )
38
49
  @reflection = reflection
39
50
  @type = type
40
51
  @configuration = configuration
41
52
  @cache_store = cache_store
42
53
  @trust_policy = trust_policy || default_trust_policy
54
+ @fact_store = fact_store || FactStore.new
43
55
  freeze
44
56
  end
45
57
 
data/lib/rigor/plugin.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "plugin/manifest"
4
4
  require_relative "plugin/access_denied_error"
5
5
  require_relative "plugin/trust_policy"
6
6
  require_relative "plugin/io_boundary"
7
+ require_relative "plugin/fact_store"
7
8
  require_relative "plugin/services"
8
9
  require_relative "plugin/base"
9
10
  require_relative "plugin/registry"
data/lib/rigor/trinary.rb CHANGED
@@ -58,7 +58,7 @@ module Rigor
58
58
  case value
59
59
  when :yes then self.class.no
60
60
  when :no then self.class.yes
61
- when :maybe then self.class.maybe
61
+ else self.class.maybe
62
62
  end
63
63
  end
64
64
 
@@ -66,12 +66,16 @@ module Rigor
66
66
  # `:neg_infinity` directly with an `Integer`.
67
67
  def lower
68
68
  m = min
69
- m.is_a?(Symbol) ? -Float::INFINITY : m
69
+ return m if m.is_a?(Integer)
70
+
71
+ -Float::INFINITY
70
72
  end
71
73
 
72
74
  def upper
73
75
  m = max
74
- m.is_a?(Symbol) ? Float::INFINITY : m
76
+ return m if m.is_a?(Integer)
77
+
78
+ Float::INFINITY
75
79
  end
76
80
 
77
81
  ALIAS_NAMES = {
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.0"
4
+ VERSION = "0.1.1"
5
5
  end
@@ -6,11 +6,12 @@ module Rigor
6
6
 
7
7
  attr_reader class_registry: ClassRegistry
8
8
  attr_reader rbs_loader: RbsLoader?
9
+ attr_reader plugin_registry: untyped?
9
10
 
10
11
  def self.default: () -> Environment
11
- def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?) -> Environment
12
+ def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?) -> Environment
12
13
 
13
- def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?) -> void
14
+ def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?) -> void
14
15
  def nominal_for_name: (String | Symbol name) -> Type::Nominal?
15
16
  def singleton_for_name: (String | Symbol name) -> Type::Singleton?
16
17
  def constant_for_name: (String | Symbol name) -> Type::t?
data/sig/rigor.rbs CHANGED
@@ -64,8 +64,14 @@ module Rigor
64
64
  class Runner
65
65
  RUBY_GLOB: String
66
66
  DEFAULT_CACHE_ROOT: String
67
- attr_reader cache_store: Rigor::Cache::Store?
68
- def initialize: (configuration: Configuration, ?explain: bool, ?cache_store: Rigor::Cache::Store?) -> void
67
+ # `Rigor::Cache::Store` itself is not yet sig-covered (the
68
+ # cache namespace is in `UNSIGNED_NAMESPACES` per
69
+ # `spec/rigor/public_api_drift_spec.rb`), so reference it as
70
+ # `untyped` until the full Cache::Store sig lands. Steep
71
+ # otherwise raises `RBS::UnknownTypeName` for the named type.
72
+ attr_reader cache_store: untyped
73
+ attr_reader plugin_registry: untyped
74
+ def initialize: (configuration: Configuration, ?explain: bool, ?cache_store: untyped, ?plugin_requirer: untyped) -> void
69
75
  def run: (?Array[String] paths) -> Result
70
76
  end
71
77
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -191,6 +191,7 @@ files:
191
191
  - lib/rigor/ast.rb
192
192
  - lib/rigor/ast/type_node.rb
193
193
  - lib/rigor/builtins/imported_refinements.rb
194
+ - lib/rigor/builtins/regex_refinement.rb
194
195
  - lib/rigor/cache/descriptor.rb
195
196
  - lib/rigor/cache/rbs_class_ancestor_table.rb
196
197
  - lib/rigor/cache/rbs_class_type_param_names.rb
@@ -265,6 +266,7 @@ files:
265
266
  - lib/rigor/plugin.rb
266
267
  - lib/rigor/plugin/access_denied_error.rb
267
268
  - lib/rigor/plugin/base.rb
269
+ - lib/rigor/plugin/fact_store.rb
268
270
  - lib/rigor/plugin/io_boundary.rb
269
271
  - lib/rigor/plugin/load_error.rb
270
272
  - lib/rigor/plugin/loader.rb