Class: Rigor::Environment

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/environment.rb,
lib/rigor/environment/rbs_loader.rb,
lib/rigor/environment/rbs_hierarchy.rb,
lib/rigor/environment/class_registry.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

Classes: ClassRegistry, RbsHierarchy, RbsLoader

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 uri logger date
  prism rbs
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(class_registry: ClassRegistry.default, rbs_loader: nil) ⇒ Environment

Returns a new instance of Environment.

Parameters:

  • class_registry (Rigor::Environment::ClassRegistry) (defaults to: ClassRegistry.default)
  • rbs_loader (Rigor::Environment::RbsLoader, nil) (defaults to: nil)

    when nil the environment is “RBS-blind”; useful in tests that want to assert how the engine behaves without RBS data. The default Environment wires the shared core loader, which is itself lazy: requesting an environment instance does NOT load RBS until a method or class query actually consults the loader.



51
52
53
54
55
# File 'lib/rigor/environment.rb', line 51

def initialize(class_registry: ClassRegistry.default, rbs_loader: nil)
  @class_registry = class_registry
  @rbs_loader = rbs_loader
  freeze
end

Instance Attribute Details

#class_registryObject (readonly)

Returns the value of attribute class_registry.



42
43
44
# File 'lib/rigor/environment.rb', line 42

def class_registry
  @class_registry
end

#rbs_loaderObject (readonly)

Returns the value of attribute rbs_loader.



42
43
44
# File 'lib/rigor/environment.rb', line 42

def rbs_loader
  @rbs_loader
end

Class Method Details

.defaultObject



58
59
60
# File 'lib/rigor/environment.rb', line 58

def default
  @default ||= new(rbs_loader: RbsLoader.default).freeze
end

.for_project(root: Dir.pwd, libraries: [], signature_paths: nil) ⇒ Rigor::Environment

Builds an Environment that consults the project’s local signatures and any opt-in stdlib libraries on top of RBS core.

Parameters:

  • root (String, Pathname) (defaults to: Dir.pwd)

    project root used to auto-detect the default signature path. Defaults to the current working directory.

  • libraries (Array<String, Symbol>) (defaults to: [])

    additional stdlib libraries to load on top of DEFAULT_LIBRARIES. The final list is the union of the two, de-duplicated while preserving order. Pass an empty array (the default) to load only the defaults.

  • signature_paths (Array<String, Pathname>, nil) (defaults to: nil)

    explicit list of ‘sig/`-style directories. When `nil` (the default), the canonical project layout `<root>/sig` is used if it exists, otherwise no signature path is loaded.

Returns:



78
79
80
81
82
83
# File 'lib/rigor/environment.rb', line 78

def for_project(root: Dir.pwd, libraries: [], signature_paths: nil)
  resolved_paths = signature_paths || default_signature_paths(root)
  merged_libraries = (DEFAULT_LIBRARIES + libraries.map(&:to_s)).uniq
  loader = RbsLoader.new(libraries: merged_libraries, signature_paths: resolved_paths)
  new(rbs_loader: loader)
end

Instance Method Details

#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.

Returns:

  • (Boolean)


141
142
143
144
145
# File 'lib/rigor/environment.rb', line 141

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.



152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rigor/environment.rb', line 152

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.



132
133
134
135
136
# File 'lib/rigor/environment.rb', line 132

def constant_for_name(name)
  return nil if rbs_loader.nil?

  rbs_loader.constant_type(name.to_s)
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.



101
102
103
104
105
106
# File 'lib/rigor/environment.rb', line 101

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

#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.



115
116
117
118
119
# File 'lib/rigor/environment.rb', line 115

def singleton_for_name(name)
  return nil unless class_known?(name)

  Type::Combinator.singleton_of(name.to_s)
end