Class: Rigor::Environment::RbsLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/environment/rbs_loader.rb

Overview

Loads RBS class declarations and method definitions from disk and exposes them to the inference engine in a small, stable surface.

Slice 4 phase 1 only enabled the RBS core signatures shipped with the ‘rbs` gem (`Object`, `Integer`, `String`, `Array`, …). Phase 2a adds opt-in stdlib library loading (`pathname`, `json`, `tempfile`, …) and arbitrary-directory signature loading (typically the project’s local ‘sig/` tree). Both are off by default on `RbsLoader.default` so the core-only fast path stays cheap; project-aware loading is opted into through for_project or by constructing a custom loader.

The default instance is shared across the process: building the core RBS environment costs hundreds of milliseconds and the data is read-only. The shared instance is frozen, but holds a mutable state hash for lazy memoization of the heavy ‘RBS::Environment` and `RBS::DefinitionBuilder` – the user-visible API stays purely functional.

See docs/internal-spec/inference-engine.md for the binding contract. rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(libraries: [], signature_paths: []) ⇒ RbsLoader

Returns a new instance of RbsLoader.

Parameters:

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

    stdlib library names to load on top of core (e.g., ‘[“pathname”, “json”]`). Empty by default. Each entry MUST correspond to a directory under the `rbs` gem’s ‘stdlib/` tree; unknown names are silently dropped on environment build (the underlying `RBS::EnvironmentLoader` raises and we fail-soft).

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

    additional directories of ‘.rbs` files to load (typically the project’s ‘sig/` tree). Non-existent or non-directory paths are filtered out at build time so the loader stays robust to fixtures and bare repositories.



60
61
62
63
64
65
66
67
68
# File 'lib/rigor/environment/rbs_loader.rb', line 60

def initialize(libraries: [], signature_paths: [])
  @libraries = libraries.map(&:to_s).freeze
  @signature_paths = signature_paths.map { |p| Pathname(p) }.freeze
  @state = { env: nil, builder: nil }
  @instance_definition_cache = {}
  @singleton_definition_cache = {}
  @class_known_cache = {}
  @hierarchy = RbsHierarchy.new(self)
end

Instance Attribute Details

#librariesObject (readonly)

Returns the value of attribute libraries.



47
48
49
# File 'lib/rigor/environment/rbs_loader.rb', line 47

def libraries
  @libraries
end

#signature_pathsObject (readonly)

Returns the value of attribute signature_paths.



47
48
49
# File 'lib/rigor/environment/rbs_loader.rb', line 47

def signature_paths
  @signature_paths
end

Class Method Details

.defaultObject



34
35
36
# File 'lib/rigor/environment/rbs_loader.rb', line 34

def default
  @default ||= new.freeze
end

.reset_default!Object

Used by tests to discard the cached default loader; production code MUST NOT call this. The shared loader holds a several-MB RBS::Environment, so dropping it during a normal run wastes the cost of rebuilding it.



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

def reset_default!
  @default = nil
end

Instance Method Details

#class_known?(name) ⇒ Boolean

Returns true when an RBS class or module declaration with the given name is loaded. Accepts unprefixed or top-level-prefixed names (“Integer” or “::Integer”). Memoized per-name (positive and negative results both cache).

Returns:

  • (Boolean)


74
75
76
77
78
79
# File 'lib/rigor/environment/rbs_loader.rb', line 74

def class_known?(name)
  key = name.to_s
  return @class_known_cache[key] if @class_known_cache.key?(key)

  @class_known_cache[key] = compute_class_known(name)
end

#class_ordering(lhs, rhs) ⇒ Object



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

def class_ordering(lhs, rhs)
  @hierarchy.class_ordering(lhs, rhs)
end

#class_type_param_names(class_name) ⇒ Object

Slice 4 phase 2d. Returns the class’s declared type-parameter names as Symbols (e.g., ‘[:Elem]` for `Array`, `[:K, :V]` for `Hash`). Used by the dispatcher to build the substitution map from receiver `type_args` into the method’s return type. The instance definition is the canonical source because singleton methods (e.g., ‘Array.new`) parameterize over the same `Elem` as instance methods.

Returns an empty array for non-generic classes and for unknown names (the loader stays fail-soft). NOTE: in the ‘rbs` gem, `RBS::Definition#type_params` returns `Array<Symbol>` directly, not the AST `TypeParam` object (those live on the AST level).



136
137
138
139
140
141
# File 'lib/rigor/environment/rbs_loader.rb', line 136

def class_type_param_names(class_name)
  definition = instance_definition(class_name)
  return [] unless definition

  definition.type_params.dup
end

#constant_type(name) ⇒ Object

Slice A constant-value lookup. Returns the translated ‘Rigor::Type` for a non-class constant declaration (`BUCKETS: Array`, `DEFAULT_PATH: String`, …) or `nil` when no constant entry exists for `name` in the loaded RBS environment. Callers MUST treat the return value as authoritative when present and as “unknown” when nil; the loader does NOT consult the class declarations here — class objects are still resolved through #class_known? and `Environment#singleton_for_name`.



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rigor/environment/rbs_loader.rb', line 156

def constant_type(name)
  rbs_name = parse_type_name(name)
  return nil unless rbs_name

  entry = env.constant_decls[rbs_name]
  return nil unless entry

  translated = Inference::RbsTypeTranslator.translate(entry.decl.type)
  translated unless translated.is_a?(Type::Bot)
rescue StandardError
  nil
end

#instance_definition(class_name) ⇒ RBS::Definition?

Returns the resolved instance definition for ‘class_name`, or nil when the class is unknown or its definition cannot be built (RBS may raise on broken hierarchies; we fail-soft and return nil so the caller can fall back).

Returns:

  • (RBS::Definition, nil)

    the resolved instance definition for ‘class_name`, or nil when the class is unknown or its definition cannot be built (RBS may raise on broken hierarchies; we fail-soft and return nil so the caller can fall back).



85
86
87
88
89
90
# File 'lib/rigor/environment/rbs_loader.rb', line 85

def instance_definition(class_name)
  key = class_name.to_s
  return @instance_definition_cache[key] if @instance_definition_cache.key?(key)

  @instance_definition_cache[key] = build_instance_definition(class_name)
end

#instance_method(class_name:, method_name:) ⇒ RBS::Definition::Method?

Returns:

  • (RBS::Definition::Method, nil)


93
94
95
96
97
98
# File 'lib/rigor/environment/rbs_loader.rb', line 93

def instance_method(class_name:, method_name:)
  definition = instance_definition(class_name)
  return nil unless definition

  definition.methods[method_name.to_sym]
end

#singleton_definition(class_name) ⇒ RBS::Definition?

The resolved singleton (class object) definition for ‘class_name`. The methods on this definition are the *class methods* of `class_name`, including those inherited from `Class` and `Module` for class types. Returns nil for unknown names and on RBS build errors (fail-soft).

Returns:

  • (RBS::Definition, nil)

    the resolved singleton (class object) definition for ‘class_name`. The methods on this definition are the *class methods* of `class_name`, including those inherited from `Class` and `Module` for class types. Returns nil for unknown names and on RBS build errors (fail-soft).



105
106
107
108
109
110
# File 'lib/rigor/environment/rbs_loader.rb', line 105

def singleton_definition(class_name)
  key = class_name.to_s
  return @singleton_definition_cache[key] if @singleton_definition_cache.key?(key)

  @singleton_definition_cache[key] = build_singleton_definition(class_name)
end

#singleton_method(class_name:, method_name:) ⇒ RBS::Definition::Method?

Returns the class method on ‘class_name`. For example, `singleton_method(class_name: “Integer”, method_name: :sqrt)` returns the definition for `Integer.sqrt`, while `singleton_method(class_name: “Foo”, method_name: :new)` returns Class#new for any class type.

Returns:

  • (RBS::Definition::Method, nil)

    the class method on ‘class_name`. For example, `singleton_method(class_name: “Integer”, method_name: :sqrt)` returns the definition for `Integer.sqrt`, while `singleton_method(class_name: “Foo”, method_name: :new)` returns Class#new for any class type.



117
118
119
120
121
122
# File 'lib/rigor/environment/rbs_loader.rb', line 117

def singleton_method(class_name:, method_name:)
  definition = singleton_definition(class_name)
  return nil unless definition

  definition.methods[method_name.to_sym]
end