Class: Rigor::Environment::RbsLoader
- Inherits:
-
Object
- Object
- Rigor::Environment::RbsLoader
- Defined in:
- lib/rigor/environment/rbs_loader.rb,
sig/rigor/environment.rbs
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
Constant Summary collapse
- SYNTHETIC_NAMESPACE_BUFFER =
Buffer name stamped on the
moduledeclarations synthesized by synthesize_missing_namespaces. Re-read off the built env by #synthesized_namespaces so the analysis layer can surface an:infodiagnostic naming the project's malformed-RBS namespaces — robust across the marshalled env cache, since the sentinel rides along on each synthetic declaration's location. "(rigor: synthesized namespaces)"- SYNTHETIC_STUB_BUFFER =
Buffer name stamped on the stub
class/moduledeclarations synthesized by stub_missing_referenced_types for types the project's RBS references but no loaded signature declares. #synthesized_stub_types reads them back off the built env (so the answer survives the marshalled env cache), and #synthesized_type_names folds them together with the namespace stubs into the set MethodDispatcher resolves toDynamic[Top](no falsecall.undefined-method). "(rigor: synthesized stub types)"- QUARANTINE_WARN_LIMIT =
Cap on how many quarantined
signature_paths:files #warn_about_quarantined_signatures lists by name before collapsing the tail to "… and N more" — a broken generator can emit many, and a wall of parse errors buries the signal. 10- MAX_STUB_PASSES =
ADR-5 robustness, second tier. A project
signature_paths:RBS that references a type no loaded signature declares —def x: () -> DRb::DRbServerwhen thedrbRBS is not available, or a stale reference to its own removedTextbringer::EditorError— makesRBS::DefinitionBuilder#build_instanceraiseNoTypeFoundError, and (per RBS's all-or-nothing per-class build) that single unresolved reference takes down EVERY method on the class, not just the one signature. Observed on shugo/textbringer: oneDRb::DRbServerreference left the wholeTextbringer::Commandsmodule — including its 186-call-sitedefine_commandDSL — resolving asDynamic[Top].We synthesize an empty stub for each such referenced-but- undeclared type so the rest of the class builds. A leaf type is stubbed as
class, its enclosing namespaces asmodule. Stubbed types carry no methods, so a call against a value of a stubbed type would otherwise mis-firecall.undefined-method; MethodDispatcher consults #synthesized_type_names and resolves such calls toDynamic[Top]instead (the same no-false-positive contract as the dependency-source tier).Detection re-uses RBS's own builder (correct by construction): build every PROJECT class and read the missing name out of the raised error. Bounded to
signature_pathsclasses (stdlib / vendored RBS is well-formed) and to MAX_STUB_PASSES iterations — a fresh stub can expose a deeper reference the first build error hid, but empty stubs reference nothing, so the fixpoint converges quickly. 5- CORE_OVERLAY_SIGS_ROOT =
Rigor-owned core-overlay RBS (
data/core_overlay/). Reopens Ruby core classes to add methods upstreamruby/rbsomits but which every concrete value answers at runtime — loaded last so upstream always wins on conflict. Public so the cache descriptor can digest these files into the env-blob key. File.( "../../../data/core_overlay", __dir__ ).freeze
- GEM_OVERLAY_SIGS_ROOT =
Rigor-owned per-gem RBS overlays (
data/gem_overlay/<gem>/), ADR-72. Unlike the unconditionalcore_overlay, each gem's overlay is loaded ONLY when that gem is locked in the project's Gemfile.lock but ships no RBS of its own — Rigor::Environment.for_project decides eligibility and passes the already-filtered gem-name set here. One directory per gem name keeps the membership check a cheapFile.directory?. File.( "../../../data/gem_overlay", __dir__ ).freeze
Instance Attribute Summary collapse
-
#cache_store ⇒ Object?
readonly
Returns the value of attribute cache_store.
-
#libraries ⇒ Array[String]
readonly
Returns the value of attribute libraries.
-
#signature_paths ⇒ Array[String | _ToPath]
readonly
Returns the value of attribute signature_paths.
-
#virtual_rbs ⇒ Object
readonly
Returns the value of attribute virtual_rbs.
Class Method Summary collapse
-
.add_parsed_decls(env, buffer, directives, decls) ⇒ Object
Appends freshly-parsed declarations to an
RBS::Environmentacross the gemspec's supported RBS range (rbs >= 3.0, < 5.0). -
.add_project_signatures(env, signature_paths) ⇒ Object
Load the project's
signature_paths:RBS files intoenvONE FILE AT A TIME, quarantining any file that fails to parse rather than letting it collapse the whole env (thefrom_loaderbatch parse is all-or-nothing — see RbsLoader.build_env_for). -
.add_virtual_rbs(env, virtual_rbs) ⇒ Object
ADR-32 WD4 — merge synthesised-from-source RBS strings into the freshly-built environment.
-
.append_stub_declarations(base_env, missing) ⇒ Object
Adds empty stub declarations for the missing referenced types (and any enclosing namespace they need) to the pre-resolve env, tagged with SYNTHETIC_STUB_BUFFER.
-
.build_env_for(libraries:, signature_paths:, virtual_rbs: []) ⇒ Object
Builds an
RBS::Environmentfrom explicitlibrariesandsignature_paths. -
.collect_missing_namespaces(env) ⇒ Object
Returns the
::-stripped names of every enclosing namespace that some declaration references but no declaration defines, shallowest-first so the synthesized source declaresFoobeforeFoo::Bar. - .core_overlay_sig_paths ⇒ Object
-
.declared_type_names(env) ⇒ Object
The
::-stripped names of every class / module / class-alias declaration already present inenv, so the synthesis paths never re-declare (and thereby duplicate) a real declaration. - .default ⇒ RbsLoader
-
.gem_overlay_sig_paths(gem_names) ⇒ Array<Pathname>
The bundled overlay directory for each gem that ships one; empty when none match or the overlay root is absent.
-
.parse_signature_file(file) ⇒ Object
Parse one project
.rbsinto[buffer, directives, decls], or nil when it is unparseable / unreadable. -
.primary_decl_for(entry) ⇒ Object
Normalises a
class_declsentry's representative declaration across the gemspec's supported RBS range (rbs >= 3.0, < 5.0). -
.project_entry?(entry, project_files) ⇒ Boolean
True when a
class_declsentry was declared in one of the project's own signature files (by declaration location), so the sweep skips the bundled stdlib / vendored universe. -
.project_sig_files(signature_paths) ⇒ Object
The absolute paths of every
.rbsfile under the project'ssignature_paths:(NOT vendored / stdlib RBS — those are well-formed, so attempting to build them would only waste time). -
.quarantined_project_signatures(signature_paths) ⇒ Object
The project
signature_paths:files that FAIL to parse, as[absolute_path, first_error_line]pairs (sorted, deterministic). -
.reset_default! ⇒ void
Used by tests to discard the cached default loader; production code MUST NOT call this.
- .stub_missing_referenced_types(base_env, resolved, project_files) ⇒ Object
-
.synthesize_missing_namespaces(env) ⇒ Object
Robustness (ADR-5): a project whose RBS declares qualified names (
class Foo::Bar) without ever declaring the enclosing namespace (module Foo) is invalid by upstream RBS rules —RBS::DefinitionBuilder#build_instanceraisesNoTypeFoundError: Could not find ::Foo, which the loader's fail-soft rescue turns into a silent dispatch miss (every method on every such class degrades toDynamic[Top]). -
.unresolved_referenced_types(env, project_files) ⇒ Object
Builds every project class (instance + singleton side) and returns the
::-stripped names of the types whose absence raisedNoTypeFoundError. -
.vendored_gem_names ⇒ Array[String]
Gem names whose RBS ships under
data/vendored_gem_sigs/<gem>/. - .vendored_gem_sig_paths ⇒ Array[Pathname]
Instance Method Summary collapse
-
#class_decl_paths ⇒ Object
Returns a frozen
Hash<String, String>mapping each loaded class / module name (top-level prefixed) to the file path of its FIRST declaration's RBS source. -
#class_known?(name) ⇒ Boolean
Returns true when an RBS class or module declaration with the given name is loaded.
- #class_ordering(lhs, rhs) ⇒ ordering
-
#class_type_param_names(class_name) ⇒ Array[Symbol]
Slice 4 phase 2d.
-
#constant_names ⇒ Array<String>
Every RBS-declared constant name (top-level prefixed, e.g.,
"::Math::PI") currently loaded into the environment. -
#constant_type(name) ⇒ Type::t?
Slice A constant-value lookup.
-
#each_class_decl_annotation ⇒ Object
ADR-20 slice 2e — iterates over every
%a{...}annotation attached to a class- or module-level declaration in the loaded RBS environment, yielding(annotation_string, source_location)pairs. -
#each_class_decl_annotation_with_name ⇒ Object
Like #each_class_decl_annotation, but also yields the owning class / module's RBS name as the first block argument:
(class_name, annotation_string, location). -
#each_constant_decl ⇒ Object
Yields
(name, entry)for every RBS constant declaration currently loaded into the environment. -
#each_known_class_name {|name| ... } ⇒ Object
Yields every known class / module / alias name (top-level prefixed) currently loaded into the environment.
-
#expand_type_alias(rbs_alias) ⇒ RBS::Types::t?
The alias's aliased type one level out, with type arguments substituted for a generic alias (
string→::String | ::_ToStr;range[int?]→::Range[int?] | ::_Range[int?]), or nil for an unresolved name. -
#initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: []) ⇒ RbsLoader
constructor
A new instance of RbsLoader.
-
#instance_definition(class_name) ⇒ RBS::Definition?
Built on demand from the (possibly cache-loaded) env; the in-memory
@instance_definition_cachekeeps the per-process short-circuit. - #instance_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
-
#instance_method_names(class_name) ⇒ Array<Symbol>?
Every instance-method name on
class_name— own, inherited, and included — as resolved byRBS::DefinitionBuilder. -
#interface_definition(interface_name) ⇒ RBS::Definition?
The built definition for the RBS interface
interface_name(_RewindableStream), whose.methodsare the required members (including interface-ancestor members). -
#interface_method_names(interface_name) ⇒ Array<Symbol>?
Every method name required by the RBS interface
interface_name, or nil when it does not resolve. -
#prewarm ⇒ Object
ADR-15 Phase 4b.x — eagerly drives every cached producer (plus the eager definitions tables, computed from the cached env since ADR-54 WD1) so a subsequent worker Ractor can serve all of its RBS queries without ever calling
RBS::EnvironmentLoader.new. -
#rbs_cache_descriptor ⇒ Object
ADR-54 WD4 — the shared cache descriptor for every RBS-derived producer consulting this loader.
-
#rbs_module?(name) ⇒ Boolean
Returns true when the named RBS declaration is a Module (
RBS::AST::Declarations::Module) rather than a Class. -
#reflection ⇒ Object
ADR-15 Phase 2b — return the loader's read-only query surface as a frozen,
Ractor.shareable?Reflection value object. -
#singleton_definition(class_name) ⇒ RBS::Definition?
Built on demand from the env with a per-process memo; the same on-demand discipline as #instance_definition (ADR-54 WD1).
-
#singleton_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
The class method on
class_name. -
#synthesized_namespaces ⇒ Object
The enclosing namespaces RbsLoader.synthesize_missing_namespaces had to invent because the project's
signature_paths:RBS declared qualified names (class Foo::Bar) without ever declaringFoo. -
#synthesized_stub_types ⇒ Object
The referenced-but-undeclared types RbsLoader.stub_missing_referenced_types stubbed so the project classes that mention them could build (e.g. an unavailable
DRb::DRbServer, or a staleTextbringer::EditorError). -
#synthesized_type_names ⇒ Object
Every type name Rigor invented to make an otherwise-inert / unbuildable project signature set resolve — both the namespace stubs and the referenced-type stubs.
Constructor Details
#initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: []) ⇒ RbsLoader
Returns a new instance of RbsLoader.
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
# File 'lib/rigor/environment/rbs_loader.rb', line 453 def initialize(libraries: [], signature_paths: [], cache_store: nil, virtual_rbs: []) @libraries = libraries.map(&:to_s).freeze @signature_paths = signature_paths.map { |p| Pathname(p) }.freeze @cache_store = cache_store @virtual_rbs = virtual_rbs.map { |name, content| [name.to_s.dup.freeze, content.to_s.dup.freeze].freeze }.freeze # Per-loader memoization bucket. Held as a single mutable Hash so the loader instance itself can be # `.freeze`d (per ADR-15 reflection-facade contract) without losing the lazy-memo behaviour. Slot # names currently consulted: `:env`, `:env_loaded`, `:env_build_warned`, `:builder`, `:reflection`, # `:instance_definitions_table`, `:singleton_definitions_table`. Constructed via `Hash.new` (NOT a # `{ ... }` literal) so Rigor's `HashShape` narrowing doesn't infer a fixed key set from the initial # state and fold post-initial slot reads (e.g. `@state[:env_loaded]`) to a constant `nil`. @state = Hash.new # rubocop:disable Style/EmptyLiteral @instance_definition_cache = {} @singleton_definition_cache = {} @class_known_cache = {} @hierarchy = RbsHierarchy.new(self) end |
Instance Attribute Details
#cache_store ⇒ Object? (readonly)
Returns the value of attribute cache_store.
436 437 438 |
# File 'lib/rigor/environment/rbs_loader.rb', line 436 def cache_store @cache_store end |
#libraries ⇒ Array[String] (readonly)
Returns the value of attribute libraries.
436 437 438 |
# File 'lib/rigor/environment/rbs_loader.rb', line 436 def libraries @libraries end |
#signature_paths ⇒ Array[String | _ToPath] (readonly)
Returns the value of attribute signature_paths.
436 437 438 |
# File 'lib/rigor/environment/rbs_loader.rb', line 436 def signature_paths @signature_paths end |
#virtual_rbs ⇒ Object (readonly)
Returns the value of attribute virtual_rbs.
436 437 438 |
# File 'lib/rigor/environment/rbs_loader.rb', line 436 def virtual_rbs @virtual_rbs end |
Class Method Details
.add_parsed_decls(env, buffer, directives, decls) ⇒ Object
Appends freshly-parsed declarations to an RBS::Environment across the gemspec's supported RBS
range (rbs >= 3.0, < 5.0). RBS 4.x wraps the declarations in an RBS::Source::RBS and takes them
through env.add_source; RBS 3.x has neither RBS::Source nor add_source and instead registers
them with env.add_signature(buffer:, directives:, decls:) (a bare env << decl is NOT enough —
it skips the signatures table that resolve_type_names rebuilds from, so the synthesized
declarations silently vanish on resolve). Without this guard the synthesis paths
(synthesize_missing_namespaces, append_stub_declarations, add_virtual_rbs) crashed under RBS
3.x with uninitialized constant RBS::Source.
277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/rigor/environment/rbs_loader.rb', line 277 def add_parsed_decls(env, buffer, directives, decls) decls ||= [] directives ||= [] if env.respond_to?(:add_source) env.add_source(::RBS::Source::RBS.new(buffer, directives, decls)) elsif env.respond_to?(:add_signature) env.add_signature(buffer: buffer, directives: directives, decls: decls) else decls.each { |decl| env << decl } end end |
.add_project_signatures(env, signature_paths) ⇒ Object
Load the project's signature_paths: RBS files into env ONE FILE AT A TIME, quarantining any file
that fails to parse rather than letting it collapse the whole env (the from_loader batch parse is
all-or-nothing — see build_env_for). A quarantined file's declarations are simply absent, so calls
into the types it would have declared read Dynamic[top]; the rest of the project's (and all
bundled) RBS still loads. The user is told which files were skipped via
#warn_about_quarantined_signatures, so this is a graceful degrade, not a silent one.
Buffer names are the file's absolute path (matching project_sig_files) so project_entry? — which
attributes a class_decls entry to the project by buffer name — still recognises these declarations.
Sorted for a deterministic add order (the env feeds the cache, ADR-54).
195 196 197 198 199 200 201 202 203 |
# File 'lib/rigor/environment/rbs_loader.rb', line 195 def add_project_signatures(env, signature_paths) project_sig_files(signature_paths).sort.each do |file| parsed = parse_signature_file(file) next if parsed.nil? # quarantined (unparseable) or unreadable — skip so the env survives buffer, directives, decls = parsed add_parsed_decls(env, buffer, directives, decls) end end |
.add_virtual_rbs(env, virtual_rbs) ⇒ Object
ADR-32 WD4 — merge synthesised-from-source RBS strings into the freshly-built environment. Each
entry is a [virtual_filename, rbs_source] pair. virtual_filename is purely for diagnostic
provenance (RBS parse errors cite it) — it is not a real file path. Per WD6 the synthesizer-emit
path is responsible for catching its own parse errors and returning nil rather than garbage; this
method assumes its input is parseable and only rescues RBS::ParsingError as a fail-soft.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/rigor/environment/rbs_loader.rb', line 349 def add_virtual_rbs(env, virtual_rbs) return if virtual_rbs.nil? || virtual_rbs.empty? virtual_rbs.each do |filename, content| next if content.nil? || content.empty? buffer = ::RBS::Buffer.new(name: filename.to_s, content: content.to_s) _, directives, decls = ::RBS::Parser.parse_signature(buffer) add_parsed_decls(env, buffer, directives, decls) rescue ::RBS::BaseError # WD6 fail-soft: a single broken virtual RBS contribution does not pull the whole env down. The # plugin layer records a `source-rbs-synthesis-failed` info diagnostic in slice 2; here we just # skip the entry. end end |
.append_stub_declarations(base_env, missing) ⇒ Object
Adds empty stub declarations for the missing referenced types (and any enclosing namespace they
need) to the pre-resolve env, tagged with SYNTHETIC_STUB_BUFFER. A name that is a prefix of
another name is declared module (it is a namespace); a leaf is declared class (referenced types
appear in instance position far more often than as mixins).
Names already declared in base_env are skipped — exactly the declared.include? guard
collect_missing_namespaces applies. Without it, stubbing a nested reference (Foo::Bar::Baz)
re-emits its enclosing prefix (Foo::Bar) as a module, and when that prefix is already a class
in the project's own sig/ the kind mismatch makes resolve_type_names raise
RBS::DuplicatedDeclarationError, collapsing the WHOLE env to nil (every type-of query then
degrades to Dynamic[Top]). One malformed .rbs must not disproportionately blind the analysis: a
subclass sig that references an inherited nested type (class GitAdapter; def x: () -> GitAdapter::Revision) was the real-world trigger — see the 2026-07-04 redmine onboarding note.
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/rigor/environment/rbs_loader.rb', line 313 def append_stub_declarations(base_env, missing) declared = declared_type_names(base_env) names = missing.to_set missing.each do |name| parts = name.split("::") (1...parts.length).each { |i| names << parts[0, i].join("::") } end names = names.reject { |name| declared.include?(name) }.to_set return if names.empty? source = names.sort_by { |n| n.count(":") }.map do |name| keyword = names.any? { |other| other != name && other.start_with?("#{name}::") } ? "module" : "class" "#{keyword} #{name}\nend\n" end.join buffer = ::RBS::Buffer.new(name: SYNTHETIC_STUB_BUFFER, content: source) _, directives, decls = ::RBS::Parser.parse_signature(buffer) add_parsed_decls(base_env, buffer, directives, decls) rescue ::RBS::BaseError nil end |
.build_env_for(libraries:, signature_paths:, virtual_rbs: []) ⇒ Object
Builds an RBS::Environment from explicit libraries and signature_paths. Stateless surface so
the v0.0.9 Cache::RbsEnvironment producer can build an env on cache miss without holding a loader
instance, and the instance-side #build_env delegates here so the implementation stays
single-rooted.
Vendored gem stubs (data/vendored_gem_sigs/<gem>/) are loaded on top of signature_paths so the
per-gem RBS bundled with Rigor itself is in scope for every analysis run. The gem stubs are
intentionally read-only and appended LAST so user-supplied signature_paths win on name conflicts.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rigor/environment/rbs_loader.rb', line 66 def build_env_for(libraries:, signature_paths:, virtual_rbs: []) rbs_loader = RBS::EnvironmentLoader.new libraries.each do |library| next unless rbs_loader.has_library?(library: library, version: nil) rbs_loader.add(library: library, version: nil) end # Project `signature_paths:` are loaded per-file by {.add_project_signatures} AFTER `from_loader`, # NOT added to the loader here: `RBS::Environment.from_loader` parses every added file all-or-nothing, # so one unparseable user `.rbs` raises `RBS::ParsingError` and collapses the WHOLE env to nil (every # type-of query then degrades to `Dynamic[top]` — the "sig looks harmful" failure of the 2026-07-06 # mastodon coverage note). Per-file loading quarantines the broken file instead. Vendored / core-overlay # sigs are Rigor-shipped and trusted, so they stay on the loader's fast batch path. vendored_gem_sig_paths.each do |path| rbs_loader.add(path: path) if path.directory? end # Rigor-owned core overlay — loaded LAST so an upstream declaration always wins on conflict; these # reopenings only fill genuine holes (e.g. `Numeric#to_f`/`to_i`/`to_r`, which upstream RBS # declares on the concrete subclasses but not on the abstract `Numeric` that Rigor's # arithmetic-chain widening produces). .each do |path| rbs_loader.add(path: path) if path.directory? end env = RBS::Environment.from_loader(rbs_loader) add_project_signatures(env, signature_paths) add_virtual_rbs(env, virtual_rbs) synthesize_missing_namespaces(env) resolved = env.resolve_type_names stub_missing_referenced_types(env, resolved, project_sig_files(signature_paths)) end |
.collect_missing_namespaces(env) ⇒ Object
Returns the ::-stripped names of every enclosing namespace that some declaration references but
no declaration defines, shallowest-first so the synthesized source declares Foo before
Foo::Bar.
159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/rigor/environment/rbs_loader.rb', line 159 def collect_missing_namespaces(env) declared = env.class_decls.keys.to_set missing = {} env.class_decls.each_key do |type_name| path = type_name.namespace.path path.each_index do |i| prefix = path[0..i] full = ::RBS::TypeName.parse("::#{prefix.join('::')}") missing[prefix.join("::")] = prefix.length unless declared.include?(full) end end missing.sort_by { |_name, depth| depth }.map(&:first) end |
.core_overlay_sig_paths ⇒ Object
385 386 387 388 389 |
# File 'lib/rigor/environment/rbs_loader.rb', line 385 def return [] unless File.directory?(CORE_OVERLAY_SIGS_ROOT) [Pathname(CORE_OVERLAY_SIGS_ROOT)] end |
.declared_type_names(env) ⇒ Object
The ::-stripped names of every class / module / class-alias declaration already present in
env, so the synthesis paths never re-declare (and thereby duplicate) a real declaration.
336 337 338 339 340 341 342 |
# File 'lib/rigor/environment/rbs_loader.rb', line 336 def declared_type_names(env) names = env.class_decls.keys.map { |n| n.to_s.sub(/\A::/, "") } if env.respond_to?(:class_alias_decls) names.concat(env.class_alias_decls.keys.map { |n| n.to_s.sub(/\A::/, "") }) end names.to_set end |
.default ⇒ RbsLoader
47 48 49 |
# File 'lib/rigor/environment/rbs_loader.rb', line 47 def default @default ||= new.freeze end |
.gem_overlay_sig_paths(gem_names) ⇒ Array<Pathname>
Returns the bundled overlay directory for each gem that ships one; empty when none match or the overlay root is absent.
405 406 407 408 409 410 411 412 |
# File 'lib/rigor/environment/rbs_loader.rb', line 405 def (gem_names) return [] unless File.directory?(GEM_OVERLAY_SIGS_ROOT) gem_names.filter_map do |name| dir = File.join(GEM_OVERLAY_SIGS_ROOT, name.to_s) Pathname(dir) if File.directory?(dir) end end |
.parse_signature_file(file) ⇒ Object
Parse one project .rbs into [buffer, directives, decls], or nil when it is unparseable /
unreadable. Mirrors RBS::EnvironmentLoader#each_signature's per-file parse so the decls register
identically to the loader's batch path.
208 209 210 211 212 213 214 |
# File 'lib/rigor/environment/rbs_loader.rb', line 208 def parse_signature_file(file) buffer = ::RBS::Buffer.new(name: file, content: File.read(file, encoding: "UTF-8")) _buffer, directives, decls = ::RBS::Parser.parse_signature(buffer) [buffer, directives, decls] rescue ::RBS::ParsingError, Errno::ENOENT, Errno::EISDIR, Errno::EACCES nil end |
.primary_decl_for(entry) ⇒ Object
Normalises a class_decls entry's representative declaration across the gemspec's supported RBS
range (rbs >= 3.0, < 5.0). RBS 4.x exposes it as entry.primary_decl (the AST declaration
directly); RBS 3.x exposes entry.primary (a wrapper whose #decl is the AST declaration).
Returns the AST declaration, or nil when neither accessor is present. Without this guard,
class_decl_paths crashed under RBS 3.x with undefined method 'primary_decl'.
260 261 262 263 264 265 266 267 |
# File 'lib/rigor/environment/rbs_loader.rb', line 260 def primary_decl_for(entry) if entry.respond_to?(:primary_decl) entry.primary_decl elsif entry.respond_to?(:primary) primary = entry.primary primary.respond_to?(:decl) ? primary.decl : primary end end |
.project_entry?(entry, project_files) ⇒ Boolean
True when a class_decls entry was declared in one of the project's own signature files (by
declaration location), so the sweep skips the bundled stdlib / vendored universe.
291 292 293 294 295 296 297 298 |
# File 'lib/rigor/environment/rbs_loader.rb', line 291 def project_entry?(entry, project_files) decl = primary_decl_for(entry) location = decl&.location buffer_name = location&.buffer&.name return false unless buffer_name project_files.include?(File.(buffer_name.to_s)) end |
.project_sig_files(signature_paths) ⇒ Object
The absolute paths of every .rbs file under the project's signature_paths: (NOT vendored /
stdlib RBS — those are well-formed, so attempting to build them would only waste time). Used to
scope the referenced-type build sweep.
176 177 178 179 180 181 182 183 |
# File 'lib/rigor/environment/rbs_loader.rb', line 176 def project_sig_files(signature_paths) signature_paths.flat_map do |path| path = Pathname(path) unless path.is_a?(Pathname) next [] unless path.directory? Dir.glob(path.join("**", "*.rbs")).map { |p| File.(p) } end.to_set end |
.quarantined_project_signatures(signature_paths) ⇒ Object
The project signature_paths: files that FAIL to parse, as [absolute_path, first_error_line] pairs
(sorted, deterministic). Detection is independent of add_project_signatures so the warning fires
even on a cache hit (where the env was already built with the file quarantined). Cheap: it only
re-parses the user's own (usually small) sig/ set, and returns empty immediately when there is no
signature_paths:.
221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/rigor/environment/rbs_loader.rb', line 221 def quarantined_project_signatures(signature_paths) project_sig_files(signature_paths).sort.filter_map do |file| buffer = ::RBS::Buffer.new(name: file, content: File.read(file, encoding: "UTF-8")) ::RBS::Parser.parse_signature(buffer) nil rescue ::RBS::ParsingError => e [file, e..to_s.lines.first.to_s.strip] rescue Errno::ENOENT, Errno::EISDIR, Errno::EACCES nil end end |
.reset_default! ⇒ void
This method returns an undefined value.
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.
54 55 56 |
# File 'lib/rigor/environment/rbs_loader.rb', line 54 def reset_default! @default = nil end |
.stub_missing_referenced_types(base_env, resolved, project_files) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/rigor/environment/rbs_loader.rb', line 120 def stub_missing_referenced_types(base_env, resolved, project_files) return resolved if project_files.empty? MAX_STUB_PASSES.times do missing = unresolved_referenced_types(resolved, project_files) break if missing.empty? append_stub_declarations(base_env, missing) resolved = base_env.resolve_type_names end resolved end |
.synthesize_missing_namespaces(env) ⇒ Object
Robustness (ADR-5): a project whose RBS declares qualified names (class Foo::Bar) without ever
declaring the enclosing namespace (module Foo) is invalid by upstream RBS rules —
RBS::DefinitionBuilder#build_instance raises NoTypeFoundError: Could not find ::Foo, which the
loader's fail-soft rescue turns into a silent dispatch miss (every method on every such class
degrades to Dynamic[Top]). This is a common authoring mistake (e.g. shugo/textbringer ships a
sig/ that rbs validate itself rejects). Rather than let an otherwise-usable signature set
contribute nothing, synthesize an empty module declaration for each undeclared enclosing
namespace so the definitions build. We only ever add names that are absent — a genuinely-declared
namespace (module or class, here or in a loaded gem) is left untouched.
142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/rigor/environment/rbs_loader.rb', line 142 def synthesize_missing_namespaces(env) missing = collect_missing_namespaces(env) return if missing.empty? source = missing.map { |name| "module #{name}\nend\n" }.join buffer = ::RBS::Buffer.new(name: SYNTHETIC_NAMESPACE_BUFFER, content: source) _, directives, decls = ::RBS::Parser.parse_signature(buffer) add_parsed_decls(env, buffer, directives, decls) rescue ::RBS::BaseError # Fail-soft: synthesis is an opportunistic uplift, never a hard requirement. A parse failure here # just leaves the env as it was (dispatch misses on the affected classes). nil end |
.unresolved_referenced_types(env, project_files) ⇒ Object
Builds every project class (instance + singleton side) and returns the ::-stripped names of the
types whose absence raised NoTypeFoundError. Only the FIRST missing reference per class surfaces
per build, which is why the caller loops.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/rigor/environment/rbs_loader.rb', line 236 def unresolved_referenced_types(env, project_files) builder = ::RBS::DefinitionBuilder.new(env: env) missing = [] env.class_decls.each do |type_name, entry| next unless project_entry?(entry, project_files) %i[build_instance build_singleton].each do |build| builder.public_send(build, type_name) rescue ::RBS::NoTypeFoundError => e name = e.[/Could not find (\S+)/, 1] missing << name.sub(/\A::/, "") if name rescue ::RBS::BaseError # Other build failures (duplicate decl, mixin cycle, ...) are not ours to repair here — leave # them fail-soft. end end missing.uniq end |
.vendored_gem_names ⇒ Array[String]
Gem names whose RBS ships under data/vendored_gem_sigs/<gem>/. The directory walk is the source
of truth (the README.md sibling is not a gem and is excluded). Callers building the RBS env use
this set to drop the matching rbs collection install directory before it double-declares against
the vendored copy — the same hazard DEFAULT_LIBRARIES creates for stdlib-extracted gems. See
RbsCollectionDiscovery's skip_gem_names:.
427 428 429 430 431 432 433 |
# File 'lib/rigor/environment/rbs_loader.rb', line 427 def vendored_gem_names return [] unless File.directory?(VENDORED_GEM_SIGS_ROOT) Dir.children(VENDORED_GEM_SIGS_ROOT).reject do |child| File.file?(File.join(VENDORED_GEM_SIGS_ROOT, child)) end end |
.vendored_gem_sig_paths ⇒ Array[Pathname]
414 415 416 417 418 419 420 |
# File 'lib/rigor/environment/rbs_loader.rb', line 414 def vendored_gem_sig_paths return [] unless File.directory?(VENDORED_GEM_SIGS_ROOT) Dir.children(VENDORED_GEM_SIGS_ROOT).map do |gem_dir| Pathname(File.join(VENDORED_GEM_SIGS_ROOT, gem_dir)) end end |
Instance Method Details
#class_decl_paths ⇒ Object
Returns a frozen Hash<String, String> mapping each loaded class / module name (top-level prefixed)
to the file path of its FIRST declaration's RBS source. Used by Analysis::RunStats to
attribute the type universe between "project sig/" (paths under the configured signature_paths)
and "bundled" (everything else — RBS core, stdlib libraries, gem-bundled RBS). Each value is a frozen
String so the whole result is Ractor.shareable? — the Phase 4b worker pool ships a snapshot back
to the coordinator on the first :prepare message.
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/rigor/environment/rbs_loader.rb', line 600 def class_decl_paths return {}.freeze if env.nil? result = {} env.class_decls.each do |rbs_name, entry| decl = self.class.primary_decl_for(entry) next if decl.nil? location = decl.location next if location.nil? buffer = location.buffer name = buffer.respond_to?(:name) ? buffer.name : nil next if name.nil? result[rbs_name.to_s.dup.freeze] = name.to_s.dup.freeze end result.freeze rescue ::RBS::BaseError {}.freeze end |
#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).
When cache_store is set, the loader fetches the entire set of known class / module / alias names
once (per process) through Cache::RbsKnownClassNames.fetch and answers class_known? from the
in-memory Set. Cold runs pay a single env walk and persist the result; warm runs (and a separate
loader sharing the same Store) skip the env walk entirely.
506 507 508 509 510 511 512 513 514 515 |
# File 'lib/rigor/environment/rbs_loader.rb', line 506 def class_known?(name) key = name.to_s return @class_known_cache[key] if @class_known_cache.key?(key) @class_known_cache[key] = if cache_store cached_class_known(name) else compute_class_known(name) end end |
#class_ordering(lhs, rhs) ⇒ ordering
747 748 749 |
# File 'lib/rigor/environment/rbs_loader.rb', line 747 def class_ordering(lhs, rhs) @hierarchy.class_ordering(lhs, rhs) end |
#class_type_param_names(class_name) ⇒ Array[Symbol]
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).
When cache_store is set, the loader fetches the entire type-parameter-name table once (per
process) through Cache::RbsClassTypeParamNames.fetch and answers point lookups from it. Cold runs
build the table once and persist it; warm runs (and a separate loader sharing the same Store) skip
the env walk entirely.
735 736 737 738 739 740 741 742 743 744 745 |
# File 'lib/rigor/environment/rbs_loader.rb', line 735 def class_type_param_names(class_name) if cache_store key = class_name.to_s.delete_prefix("::") return type_param_names_table.fetch(key, []).dup end definition = instance_definition(class_name) return [] unless definition definition.type_params.dup end |
#constant_names ⇒ Array<String>
Returns every RBS-declared constant name (top-level prefixed, e.g., "::Math::PI")
currently loaded into the environment. Used by the cache producer that materialises the
constant-type table; ordinary callers should keep using #constant_type for point lookups.
754 755 756 757 758 759 760 |
# File 'lib/rigor/environment/rbs_loader.rb', line 754 def constant_names return [] if env.nil? env.constant_decls.keys.map(&:to_s) rescue ::RBS::BaseError [] end |
#constant_type(name) ⇒ Type::t?
Slice A constant-value lookup. Returns the translated Rigor::Type for a non-class constant
declaration (BUCKETS: Array[Symbol], 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.
When cache_store is set, the loader fetches the entire translated constant table once (per
process) through Cache::RbsConstantTable.fetch and answers point lookups from it. Cold runs pay
the translation cost up-front and write the result to disk; warm runs skip the translation entirely
and pay only a Marshal.load of the table.
786 787 788 789 790 791 792 793 794 795 796 797 |
# File 'lib/rigor/environment/rbs_loader.rb', line 786 def constant_type(name) rbs_name = parse_type_name(name) return nil unless rbs_name if cache_store constant_type_table[rbs_name.to_s] else translate_constant_decl(rbs_name) end rescue ::RBS::BaseError nil end |
#each_class_decl_annotation ⇒ Object
ADR-20 slice 2e — iterates over every %a{...} annotation attached to a class- or module-level
declaration in the loaded RBS environment, yielding (annotation_string, source_location) pairs.
Used by Inference::HktRegistry.scan_rbs_loader to find rigor:v1:hkt_register /
rigor:v1:hkt_define directives in user-authored overlays and merge them into the per-Environment
HKT registry. Yields nothing when the env failed to build (fail-soft, same shape as
#each_known_class_name).
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
# File 'lib/rigor/environment/rbs_loader.rb', line 558 def each_class_decl_annotation return enum_for(:each_class_decl_annotation) unless block_given? return if env.nil? env.class_decls.each_value do |entry| entry.each_decl do |decl| next unless decl.respond_to?(:annotations) decl.annotations.each { |a| yield a.string, a.location } end end rescue ::RBS::BaseError, ::Ractor::IsolationError # fail-soft: matches each_known_class_name's policy. Ractor::IsolationError surfaces when the scan # is invoked from a non-main Ractor pool worker before ADR-15's full deep-freeze migration completes # — the worker falls back to the base (builtins-only) registry rather than crashing. end |
#each_class_decl_annotation_with_name ⇒ Object
Like #each_class_decl_annotation, but also yields the owning class / module's RBS name as the
first block argument: (class_name, annotation_string, location). Used by
RbsExtended::ConformanceChecker to resolve a rigor:v1:conforms-to directive back to the
class it annotates. Same fail-soft policy as the un-named variant.
579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/rigor/environment/rbs_loader.rb', line 579 def each_class_decl_annotation_with_name return enum_for(:each_class_decl_annotation_with_name) unless block_given? return if env.nil? env.class_decls.each do |rbs_name, entry| entry.each_decl do |decl| next unless decl.respond_to?(:annotations) decl.annotations.each { |a| yield rbs_name.to_s, a.string, a.location } end end rescue ::RBS::BaseError, ::Ractor::IsolationError # fail-soft: see #each_class_decl_annotation. end |
#each_constant_decl ⇒ Object
Yields (name, entry) for every RBS constant declaration currently loaded into the environment. The
cache producer uses this to materialise the constant-type table without going back through
#constant_type (which would recurse back into the cache when cache_store is set).
765 766 767 768 769 770 771 772 773 774 |
# File 'lib/rigor/environment/rbs_loader.rb', line 765 def each_constant_decl return enum_for(:each_constant_decl) unless block_given? return if env.nil? env.constant_decls.each do |rbs_name, entry| yield rbs_name.to_s, entry end rescue ::RBS::BaseError # fail-soft: a broken RBS environment yields no entries. end |
#each_known_class_name {|name| ... } ⇒ Object
Yields every known class / module / alias name (top-level prefixed) currently loaded into the environment. The cache producer that materialises the known-name set uses this so it never recurses back through #class_known?.
540 541 542 543 544 545 546 547 548 549 550 |
# File 'lib/rigor/environment/rbs_loader.rb', line 540 def each_known_class_name return enum_for(:each_known_class_name) unless block_given? return if env.nil? env.class_decls.each_key { |rbs_name| yield rbs_name.to_s } env.class_alias_decls.each_key { |rbs_name| yield rbs_name.to_s } rescue ::RBS::BaseError # fail-soft: a broken RBS environment yields no names. Analyzer-internal errors (NameError, # NoMethodError, LoadError) are NOT swallowed — those are bugs and must surface so they don't hide # silently the way the v0.0.9 cache `Cache::Descriptor` regression did. end |
#expand_type_alias(rbs_alias) ⇒ RBS::Types::t?
Returns the alias's aliased type one level out, with type arguments substituted
for a generic alias (string → ::String | ::_ToStr; range[int?] → ::Range[int?] | ::_Range[int?]), or nil for an unresolved name. Lets a caller see through the alias that
Inference::RbsTypeTranslator otherwise degrades to untyped, which is why an interface/alias
parameter does not reject nil. expand_alias2 handles the (rarer) generic case — a range[T]
param previously fell back to "admits", which suppressed e.g. MatchData#[](nil).
685 686 687 688 689 690 691 692 693 694 695 |
# File 'lib/rigor/environment/rbs_loader.rb', line 685 def (rbs_alias) return nil if env.nil? name = rbs_alias.name name = name.absolute! unless name.absolute? return nil unless env.type_alias_decls.key?(name) builder.(name, rbs_alias.args) rescue ::RBS::BaseError, StandardError nil end |
#instance_definition(class_name) ⇒ RBS::Definition?
Built on demand from the (possibly cache-loaded) env; the in-memory @instance_definition_cache
keeps the per-process short-circuit. ADR-54 WD1 retired the definitions disk blob: given a cached
env, Marshal.load-ing every definition was measurably slower (and allocation-heavier) than
rebuilding the ones a run actually touches.
630 631 632 633 634 635 |
# File 'lib/rigor/environment/rbs_loader.rb', line 630 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?
638 639 640 641 642 643 |
# File 'lib/rigor/environment/rbs_loader.rb', line 638 def instance_method(class_name:, method_name:) definition = instance_definition(class_name) return nil unless definition definition.methods[method_name.to_sym] end |
#instance_method_names(class_name) ⇒ Array<Symbol>?
Returns every instance-method name on class_name — own, inherited, and
included — as resolved by RBS::DefinitionBuilder. Returns nil (NOT []) when the class
definition cannot be built so callers can tell "no methods" apart from "unknown class". Used by the
rigor:v1:conforms-to presence check (RbsExtended::ConformanceChecker).
649 650 651 652 653 654 |
# File 'lib/rigor/environment/rbs_loader.rb', line 649 def instance_method_names(class_name) definition = instance_definition(class_name) return nil unless definition definition.methods.keys end |
#interface_definition(interface_name) ⇒ RBS::Definition?
Returns the built definition for the RBS interface interface_name
(_RewindableStream), whose .methods are the required members (including interface-ancestor
members). Returns nil when the name does not resolve to a loaded interface (a typo, or the
defining library / sig set is not on the load path). Fail-soft on RBS build errors.
660 661 662 663 664 665 666 667 668 669 |
# File 'lib/rigor/environment/rbs_loader.rb', line 660 def interface_definition(interface_name) rbs_name = parse_type_name(interface_name) return nil unless rbs_name return nil if env.nil? return nil unless env.interface_decls.key?(rbs_name) builder.build_interface(rbs_name) rescue ::RBS::BaseError nil end |
#interface_method_names(interface_name) ⇒ Array<Symbol>?
Returns every method name required by the RBS interface interface_name, or nil
when it does not resolve. Thin accessor over #interface_definition for the presence check.
673 674 675 |
# File 'lib/rigor/environment/rbs_loader.rb', line 673 def interface_method_names(interface_name) interface_definition(interface_name)&.methods&.keys end |
#prewarm ⇒ Object
ADR-15 Phase 4b.x — eagerly drives every cached producer (plus the eager definitions tables, computed
from the cached env since ADR-54 WD1) so a subsequent worker Ractor can serve all of its RBS queries
without ever calling RBS::EnvironmentLoader.new. The loader path that calls
EnvironmentLoader.new transitively reads a chain of non-Ractor.shareable? module constants
(RBS::EnvironmentLoader::DEFAULT_CORE_ROOT, RBS::Repository::DEFAULT_STDLIB_ROOT,
Gem::Requirement::DefaultRequirement, …) and trips Ractor::IsolationError. Pre-warming on the
main Ractor — env blob loaded, derived tables built — keeps workers off that chain
(RBS::DefinitionBuilder over an already-built env does not touch it).
No-op when cache_store is nil — without a Store the worker has no choice but to build env via the
loader, so the caller MUST ensure pool mode runs with caching enabled. Returns self so the call
chains cleanly from the Runner pre-spawn hook.
811 812 813 814 815 816 817 818 819 820 821 822 |
# File 'lib/rigor/environment/rbs_loader.rb', line 811 def prewarm return self if cache_store.nil? env known_class_names_set constant_type_table type_param_names_table ancestor_names_table instance_definitions_table singleton_definitions_table self end |
#rbs_cache_descriptor ⇒ Object
ADR-54 WD4 — the shared cache descriptor for every RBS-derived producer consulting this loader.
Building it digests every .rbs file under signature_paths + the vendored gem sigs, and the
result is identical across producers, so one build is memoised per loader (on @state, alongside
:env — the env itself is loader-lifetime-memoised, so this adds no new staleness class).
828 829 830 831 832 833 |
# File 'lib/rigor/environment/rbs_loader.rb', line 828 def rbs_cache_descriptor @state[:rbs_cache_descriptor] ||= begin require_relative "../cache/rbs_descriptor" Cache::RbsDescriptor.build(self) end end |
#rbs_module?(name) ⇒ Boolean
Returns true when the named RBS declaration is a Module (RBS::AST::Declarations::Module) rather
than a Class. The user_class_fallback_receiver tier consults this to route
Nominal[M].some_kernel_method (where M is a module mixin like PP::ObjectMixin) through the
Nominal[Object] fallback, because every concrete includer of M sees Kernel / Object instance
methods as part of its own ancestor chain.
Returns false for classes, for unknown names, and when the RBS environment failed to build (fail-soft).
525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/rigor/environment/rbs_loader.rb', line 525 def rbs_module?(name) return false if env.nil? rbs_name = parse_type_name(name) return false if rbs_name.nil? entry = env.class_decls[rbs_name] entry.is_a?(::RBS::Environment::ModuleEntry) rescue ::RBS::BaseError false end |
#reflection ⇒ Object
ADR-15 Phase 2b — return the loader's read-only query surface as a frozen, Ractor.shareable?
Rigor::Environment::Reflection value object. Built lazily on first access; the loader memoises so repeated calls
return the same instance.
The Reflection consumes the loader's already-warmed cache producers (or, when no cache_store is
set, eagerly walks the env). Once constructed, the Reflection carries the derived tables
independently and never re-consults the loader — making it safe to share across Ractors while the
loader stays per- process / per-Ractor for write-path operations.
843 844 845 846 847 848 849 850 851 852 853 854 855 |
# File 'lib/rigor/environment/rbs_loader.rb', line 843 def reflection @state[:reflection] ||= begin require_relative "reflection" Environment::Reflection.new( known_class_names: known_class_names_set, instance_definitions: instance_definitions_table, singleton_definitions: singleton_definitions_table, type_param_names: type_param_names_table, constant_types: constant_type_table, ancestor_names: ancestor_names_table ) end end |
#singleton_definition(class_name) ⇒ RBS::Definition?
Built on demand from the env with a per-process memo; the same on-demand discipline as #instance_definition (ADR-54 WD1).
704 705 706 707 708 709 |
# File 'lib/rigor/environment/rbs_loader.rb', line 704 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.
715 716 717 718 719 720 |
# File 'lib/rigor/environment/rbs_loader.rb', line 715 def singleton_method(class_name:, method_name:) definition = singleton_definition(class_name) return nil unless definition definition.methods[method_name.to_sym] end |
#synthesized_namespaces ⇒ Object
The enclosing namespaces synthesize_missing_namespaces had to invent because the project's
signature_paths: RBS declared qualified names (class Foo::Bar) without ever declaring Foo.
Recovered by scanning the built env for class/module entries whose every declaration originated from
the synthetic buffer, so the answer survives the marshalled-env cache (where no build-time collector
would). Returns ::-stripped names, shallowest-first. Empty for a well-formed sig set (the common
case) and whenever the env failed to build.
477 478 479 |
# File 'lib/rigor/environment/rbs_loader.rb', line 477 def synthesized_namespaces names_synthesized_in(SYNTHETIC_NAMESPACE_BUFFER) end |
#synthesized_stub_types ⇒ Object
The referenced-but-undeclared types stub_missing_referenced_types stubbed so the project classes
that mention them could build (e.g. an unavailable DRb::DRbServer, or a stale
Textbringer::EditorError). Recovered off the built env like #synthesized_namespaces, so it
survives the marshalled-env cache.
485 486 487 |
# File 'lib/rigor/environment/rbs_loader.rb', line 485 def synthesized_stub_types names_synthesized_in(SYNTHETIC_STUB_BUFFER) end |
#synthesized_type_names ⇒ Object
Every type name Rigor invented to make an otherwise-inert / unbuildable project signature set resolve
— both the namespace stubs and the referenced-type stubs. MethodDispatcher resolves a call whose
receiver is one of these (and that no real signature answered) to Dynamic[Top], so the empty stub
never mis-fires call.undefined-method. Memoised; empty (and cheap) for the common well-formed sig
set.
494 495 496 |
# File 'lib/rigor/environment/rbs_loader.rb', line 494 def synthesized_type_names @state[:synthesized_type_names] ||= (synthesized_namespaces + synthesized_stub_types).to_set end |