Class: Rigor::Environment
- Inherits:
-
Object
- Object
- Rigor::Environment
- Defined in:
- lib/rigor/environment.rb,
lib/rigor/environment/reporters.rb,
lib/rigor/environment/rbs_loader.rb,
lib/rigor/environment/reflection.rb,
lib/rigor/environment/rbs_hierarchy.rb,
lib/rigor/environment/class_registry.rb,
lib/rigor/environment/lockfile_resolver.rb,
lib/rigor/environment/hkt_registry_holder.rb,
lib/rigor/environment/rbs_coverage_report.rb,
lib/rigor/environment/bundle_sig_discovery.rb,
lib/rigor/environment/rbs_collection_discovery.rb
Overview
The engine’s view of the type universe outside the current scope. Slice 1 only exposed the class registry; Slice 4 adds the RBS loader, which threads through ExpressionTyper and MethodDispatcher to type constant references and method calls that the literal-typer and constant-folding tiers cannot answer.
See docs/internal-spec/inference-engine.md for the binding contract.
Defined Under Namespace
Modules: BundleSigDiscovery, LockfileResolver, RbsCollectionDiscovery, RbsCoverageReport Classes: ClassRegistry, HktRegistryHolder, RbsHierarchy, RbsLoader, Reflection, Reporters
Constant Summary collapse
- DEFAULT_LIBRARIES =
Slice A stdlib expansion. Stdlib libraries that ‘Environment.for_project` loads on top of RBS core unless the caller passes an explicit `libraries:` array. Each entry MUST be a stdlib library name accepted by `RBS::EnvironmentLoader#has_library?`; unknown libraries MUST fail-soft (`RbsLoader#build_env` already filters through `has_library?`). The default set covers the common stdlib surface a Ruby program is likely to import (`pathname`, `optparse`, `json`, `yaml`, `fileutils`, `tempfile`, `uri`, `logger`, `date`) plus the analyzer- adjacent gems shipping their own RBS in this bundle (`prism`, `rbs`). On hosts where one of these libraries is not installed, the loader silently drops it.
Callers MAY add to the default by passing ‘libraries: %w[csv …]`; the explicit list is appended to `DEFAULT_LIBRARIES` and de-duplicated. Callers that need a strictly RBS-core view MUST construct an `RbsLoader` directly instead of going through `for_project`.
%w[ pathname optparse json yaml fileutils tempfile tmpdir stringio forwardable digest securerandom uri logger date pp delegate observable abbrev find tsort singleton shellwords benchmark base64 did_you_mean monitor mutex_m timeout open3 erb etc ipaddr bigdecimal bigdecimal-math prettyprint random-formatter time open-uri resolv csv pstore objspace io-console cgi cgi-escape strscan prism rbs ].freeze
Instance Attribute Summary collapse
-
#class_registry ⇒ Object
readonly
Returns the value of attribute class_registry.
-
#dependency_source_index ⇒ Object
readonly
Returns the value of attribute dependency_source_index.
-
#name_scope ⇒ Object
readonly
Returns the value of attribute name_scope.
-
#plugin_registry ⇒ Object
readonly
Returns the value of attribute plugin_registry.
-
#project_patched_methods ⇒ Object
readonly
Returns the value of attribute project_patched_methods.
-
#rbs_loader ⇒ Object
readonly
Returns the value of attribute rbs_loader.
-
#reporters ⇒ Object
readonly
Returns the value of attribute reporters.
-
#synthetic_method_index ⇒ Object
readonly
Returns the value of attribute synthetic_method_index.
Class Method Summary collapse
- .default ⇒ Object
-
.for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil, plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, bundler_bundle_path: nil, bundler_auto_detect: false, bundler_lockfile: nil, rbs_collection_lockfile: nil, rbs_collection_auto_detect: false, synthetic_method_index: nil, project_patched_methods: nil) ⇒ Rigor::Environment
Builds an Environment that consults the project’s local signatures and any opt-in stdlib libraries on top of RBS core.
Instance Method Summary collapse
-
#attach_reporters!(rbs_extended_reporter:, boundary_cross_reporter:) ⇒ Object
Replaces the env’s per-run reporter slots.
- #boundary_cross_reporter ⇒ Object
-
#class_known?(name) ⇒ Boolean
Returns true when the constant name is known to either the static registry or the RBS loader.
-
#class_ordering(lhs, rhs) ⇒ Object
Compares two class/module names using analyzer-owned class data.
-
#constant_for_name(name) ⇒ Object
Slice A constant-value lookup.
-
#hkt_registry ⇒ Object
ADR-20 slices 2e + 6 — lazy HKT registry getter.
-
#initialize(class_registry: ClassRegistry.default, rbs_loader: nil, plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, synthetic_method_index: nil, project_patched_methods: nil, hkt_registry: nil) ⇒ Environment
constructor
A new instance of Environment.
-
#nominal_for_name(name) ⇒ Object
Resolves a constant name to a Rigor::Type::Nominal (the instance type carrier).
-
#rbs_extended_reporter ⇒ Object
Backwards-compatible reporter accessors — every existing consumer (rbs_extended, method_dispatcher) calls these.
-
#rbs_module?(name) ⇒ Boolean
Returns true when the RBS environment carries the named declaration as a Module (not a Class).
-
#reflection ⇒ Object
ADR-15 Phase 2b — returns the loader’s read-only, ‘Ractor.shareable?` query surface as a frozen Reflection.
-
#singleton_for_name(name) ⇒ Object
Resolves a constant name to a Rigor::Type::Singleton (the *class object* carrier).
Constructor Details
#initialize(class_registry: ClassRegistry.default, rbs_loader: nil, plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, synthetic_method_index: nil, project_patched_methods: nil, hkt_registry: nil) ⇒ Environment
Returns a new instance of Environment.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/rigor/environment.rb', line 87 def initialize(class_registry: ClassRegistry.default, rbs_loader: nil, # rubocop:disable Metrics/ParameterLists plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, synthetic_method_index: nil, project_patched_methods: nil, hkt_registry: nil) @class_registry = class_registry @rbs_loader = rbs_loader @plugin_registry = plugin_registry @dependency_source_index = dependency_source_index # ADR-pending — reporters live in a mutable container so # long-lived integrations (LSP `ProjectContext`) can swap # them per `Runner.run` without rebuilding the env. The # existing `#rbs_extended_reporter` / `#boundary_cross_reporter` # accessors below preserve the public lookup shape. @reporters = Reporters.new( rbs_extended: rbs_extended_reporter, boundary_cross: boundary_cross_reporter ) @synthetic_method_index = synthetic_method_index || Inference::SyntheticMethodIndex::EMPTY @project_patched_methods = project_patched_methods || Inference::ProjectPatchedMethods::EMPTY # ADR-20 slice 2c + 2e — the per-env HKT registry # consulted by the reducer when resolving `Type::App` # carriers. Defaults to {Inference::HktRegistry::EMPTY}; # the {.default} / {.for_project} class methods seed it # with the bundled builtins (`json::value`, …) plus any # `%a{rigor:v1:hkt_register / hkt_define}` annotations # the RBS loader exposes. The hkt_registry getter # (defined below) MEMOIZES the result of merging the # base with the RBS scan so the scan is paid at most # once per Environment lifetime — and only when first # consulted, leaving fast paths like `rigor check # --cache-stats --no-stats` from doing the RBS env # build at all. @hkt_registry_base = hkt_registry || Inference::HktRegistry::EMPTY @hkt_registry_holder = HktRegistryHolder.new @name_scope = build_name_scope freeze end |
Instance Attribute Details
#class_registry ⇒ Object (readonly)
Returns the value of attribute class_registry.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def class_registry @class_registry end |
#dependency_source_index ⇒ Object (readonly)
Returns the value of attribute dependency_source_index.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def dependency_source_index @dependency_source_index end |
#name_scope ⇒ Object (readonly)
Returns the value of attribute name_scope.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def name_scope @name_scope end |
#plugin_registry ⇒ Object (readonly)
Returns the value of attribute plugin_registry.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def plugin_registry @plugin_registry end |
#project_patched_methods ⇒ Object (readonly)
Returns the value of attribute project_patched_methods.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def project_patched_methods @project_patched_methods end |
#rbs_loader ⇒ Object (readonly)
Returns the value of attribute rbs_loader.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def rbs_loader @rbs_loader end |
#reporters ⇒ Object (readonly)
Returns the value of attribute reporters.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def reporters @reporters end |
#synthetic_method_index ⇒ Object (readonly)
Returns the value of attribute synthetic_method_index.
64 65 66 |
# File 'lib/rigor/environment.rb', line 64 def synthetic_method_index @synthetic_method_index end |
Class Method Details
.default ⇒ Object
181 182 183 184 185 186 |
# File 'lib/rigor/environment.rb', line 181 def default @default ||= new( rbs_loader: RbsLoader.default, hkt_registry: Builtins::HktBuiltins.registry ).freeze end |
.for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil, plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, bundler_bundle_path: nil, bundler_auto_detect: false, bundler_lockfile: nil, rbs_collection_lockfile: nil, rbs_collection_auto_detect: false, synthetic_method_index: nil, project_patched_methods: nil) ⇒ Rigor::Environment
Builds an Environment that consults the project’s local signatures and any opt-in stdlib libraries on top of RBS core.
rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/rigor/environment.rb', line 210 def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil, plugin_registry: nil, dependency_source_index: nil, rbs_extended_reporter: nil, boundary_cross_reporter: nil, bundler_bundle_path: nil, bundler_auto_detect: false, bundler_lockfile: nil, rbs_collection_lockfile: nil, rbs_collection_auto_detect: false, synthetic_method_index: nil, project_patched_methods: nil) resolved_paths = signature_paths || default_signature_paths(root) # O4 MVP — append per-gem `sig/` directories discovered # under the target project's bundler install root. Empty # array when neither an explicit path nor auto-detection # finds a bundle. Order: user `signature_paths:` win first # (semantic precedence inside `RbsLoader.build_env_for`); # gem-shipped sigs append last so user overrides stay # authoritative. # # O4 Layer 3 — when a Gemfile.lock is available (explicit # `bundler_lockfile:` or auto-detected next to the project # root), use the locked gem set to filter the discovered # `sig/` directories. Stale gems in the bundle install # tree (out-of-band installs, version drift after a # `bundle update`) are silently dropped so only gems the # project actually declares contribute RBS. locked = LockfileResolver.locked_gems( lockfile_path: bundler_lockfile, project_root: root, auto_detect: bundler_auto_detect ) gem_sig_paths = BundleSigDiscovery.discover( bundle_path: bundler_bundle_path, project_root: root, auto_detect: bundler_auto_detect, locked_gems: locked.empty? ? nil : locked ).map(&:to_s) # O4 Layer 3 slice 2 — when `rbs collection install` # has been run for the target project, parse the # resulting `rbs_collection.lock.yaml` and feed each # gem's `<collection_path>/<name>/<version>/` directory # into `signature_paths:`. Stdlib-typed entries are # skipped (already covered by `DEFAULT_LIBRARIES`). collection_paths = RbsCollectionDiscovery.discover( lockfile_path: rbs_collection_lockfile, project_root: root, auto_detect: rbs_collection_auto_detect ).map(&:to_s) loader_signature_paths = resolved_paths + gem_sig_paths + collection_paths merged_libraries = (DEFAULT_LIBRARIES + libraries.map(&:to_s)).uniq loader = RbsLoader.new( libraries: merged_libraries, signature_paths: loader_signature_paths, cache_store: cache_store ) # ADR-20 slice 2c + 2e — seed hkt_registry with the # bundled builtins. The Environment's `#hkt_registry` # getter then LAZILY merges in the RBS env scan on # first call so fast paths that don't consult HKT # (e.g. `rigor check --cache-stats --no-stats`) don't # pay the eager env-build cost up front. URI # collisions let the user-authored overlay win over # the bundled builtin (last-write-wins per ADR-20 # OQ3 tentative). new( rbs_loader: loader, plugin_registry: plugin_registry, dependency_source_index: dependency_source_index, rbs_extended_reporter: rbs_extended_reporter, boundary_cross_reporter: boundary_cross_reporter, synthetic_method_index: synthetic_method_index, project_patched_methods: project_patched_methods, hkt_registry: Builtins::HktBuiltins.registry ) end |
Instance Method Details
#attach_reporters!(rbs_extended_reporter:, boundary_cross_reporter:) ⇒ Object
Replaces the env’s per-run reporter slots. Intended for long-lived integrations (LSP ‘ProjectContext`) that share one Environment instance across many `Runner.run` calls: each call attaches its own fresh reporter pair so per-call diagnostic events stay scoped to that call rather than accumulating across publishes.
Single-threaded use only. Concurrent publishes against one Environment must serialise — the LSP ‘Server` debouncer + synchronized writer already enforces this for the editor path. The Ractor pool path builds a per-worker Environment and does not reach this surface.
174 175 176 177 178 |
# File 'lib/rigor/environment.rb', line 174 def attach_reporters!(rbs_extended_reporter:, boundary_cross_reporter:) @reporters.rbs_extended = rbs_extended_reporter @reporters.boundary_cross = boundary_cross_reporter nil end |
#boundary_cross_reporter ⇒ Object
158 159 160 |
# File 'lib/rigor/environment.rb', line 158 def boundary_cross_reporter @reporters.boundary_cross end |
#class_known?(name) ⇒ Boolean
Returns true when the constant name is known to either the static registry or the RBS loader. Useful for callers that only need a presence check without materialising a type carrier.
340 341 342 343 344 |
# File 'lib/rigor/environment.rb', line 340 def class_known?(name) return true if class_registry.nominal_for_name(name) class_known_in_rbs?(name) end |
#class_ordering(lhs, rhs) ⇒ Object
Compares two class/module names using analyzer-owned class data. Returns ‘:equal`, `:subclass`, `:superclass`, `:disjoint`, or `:unknown`. The static registry handles built-ins cheaply; the RBS loader handles project/stdlib classes without relying on host Ruby constants being loaded.
375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/rigor/environment.rb', line 375 def class_ordering(lhs, rhs) lhs = normalize_class_name(lhs) rhs = normalize_class_name(rhs) return :equal if lhs == rhs registry_result = class_registry.class_ordering(lhs, rhs) return registry_result unless registry_result == :unknown return :unknown unless rbs_loader rbs_loader.class_ordering(lhs, rhs) end |
#constant_for_name(name) ⇒ Object
Slice A constant-value lookup. Returns the translated ‘Rigor::Type` for an RBS-declared non-class constant (`Rigor::Analysis::FactStore::BUCKETS: Array`, `Rigor::Configuration::DEFAULT_PATH: String`, …) or `nil` when no RBS constant declaration covers `name`. This is the value-bearing counterpart of #singleton_for_name, which only resolves names that name a class or module. Callers that need to type a `Prism::ConstantReadNode`/ `Prism::ConstantPathNode` MUST consult #singleton_for_name first and fall through to this query when the constant is not a class.
331 332 333 334 335 |
# File 'lib/rigor/environment.rb', line 331 def constant_for_name(name) return nil if rbs_loader.nil? rbs_loader.constant_type(name.to_s) end |
#hkt_registry ⇒ Object
ADR-20 slices 2e + 6 — lazy HKT registry getter. Merge order on first call: builtins (base) ← plugin manifest aggregation ← RBS env scan. Last-write-wins on URI collisions so user-authored ‘.rbs` overlays beat plugin entries, which beat the bundled JSON_VALUE. Memoised; single-threaded use only (under the Ractor pool path each worker has its own Environment so cross-worker mutation is impossible; the LSP single-publish-at-a-time invariant serialises here).
135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/rigor/environment.rb', line 135 def hkt_registry @hkt_registry_holder.fetch do = if @plugin_registry.respond_to?(:hkt_overlay_registry) @hkt_registry_base.merge(@plugin_registry.) else @hkt_registry_base end Inference::HktRegistry.scan_rbs_loader( @rbs_loader, base: , reporter: rbs_extended_reporter ) end end |
#nominal_for_name(name) ⇒ Object
Resolves a constant name to a Rigor::Type::Nominal (the instance type carrier). Consults the static class registry first (cheap, hardcoded), then falls back to the RBS loader. Returns nil when the name is unknown to both.
NOTE: This is the construction helper for “an instance of class ‘Foo`”. For “the class object `Foo` itself” (the value of the constant), use #singleton_for_name instead.
300 301 302 303 304 305 |
# File 'lib/rigor/environment.rb', line 300 def nominal_for_name(name) registered = class_registry.nominal_for_name(name) return registered if registered class_known_in_rbs?(name) ? Type::Combinator.nominal_of(name.to_s) : nil end |
#rbs_extended_reporter ⇒ Object
Backwards-compatible reporter accessors — every existing consumer (rbs_extended, method_dispatcher) calls these. The frozen ‘@reporters` container is mutable for slot reassignment via #attach_reporters! below.
154 155 156 |
# File 'lib/rigor/environment.rb', line 154 def rbs_extended_reporter @reporters.rbs_extended end |
#rbs_module?(name) ⇒ Boolean
Returns true when the RBS environment carries the named declaration as a Module (not a Class). Used by the ‘user_class_fallback_receiver` tier to detect a module-mixin receiver (e.g. `PP::ObjectMixin`) so the dispatcher can route unresolved method calls through the `Nominal` fallback — every concrete includer of M honours Kernel / Object instance methods through its own ancestor chain.
364 365 366 367 368 |
# File 'lib/rigor/environment.rb', line 364 def rbs_module?(name) return false unless rbs_loader rbs_loader.rbs_module?(name) end |
#reflection ⇒ Object
ADR-15 Phase 2b — returns the loader’s read-only, ‘Ractor.shareable?` query surface as a frozen Reflection. Built lazily on first access; subsequent calls return the same instance. Returns `nil` when the environment carries no RBS loader (test-only `Environment.new` without `rbs_loader:`).
353 354 355 |
# File 'lib/rigor/environment.rb', line 353 def reflection @rbs_loader&.reflection end |
#singleton_for_name(name) ⇒ Object
Resolves a constant name to a Rigor::Type::Singleton (the *class object* carrier). The expression ‘Foo` evaluates to the class object, whose RBS type is `singleton(Foo)` – this method is the corresponding Rigor construction helper.
The lookup uses the same registry/RBS chain as #nominal_for_name so a class is either known to both queries or to neither.
314 315 316 317 318 |
# File 'lib/rigor/environment.rb', line 314 def singleton_for_name(name) return nil unless class_known?(name) Type::Combinator.singleton_of(name.to_s) end |