Module: Rigor::Reflection
- Defined in:
- lib/rigor/reflection.rb,
sig/rigor/reflection.rbs
Overview
Read-side facade over Rigor's three reflection sources:
Rigor::Environment::ClassRegistry— RubyClass/Moduleobjects (Integer, Float, Set, Pathname, …) registered at boot. Static; never changes during arigor checkrun.Rigor::Environment::RbsLoader— RBS-side declarations (instance / singleton methods, class hierarchy, constants). Loaded on demand from the project'ssig/directory + the bundled stdlib RBS.Rigor::Scopediscovered facts — source-side discoveries produced byRigor::Inference::ScopeIndexer(user-defined classes / modules, in-source constants, discovered method nodes, class ivar / cvar declarations).
This module is the stable read shape the plugin API is designed against (ADR-2,
docs/adr/2-extension-api.md).
The facade is read-only and additive. Existing call sites that read directly from
Rigor::Scope or Rigor::Environment::RbsLoader continue to work unchanged; they
migrate to the facade at their own pace. The facade performs no caching beyond what the
underlying sources already provide.
Public surface (v0.0.7 first pass)
- Reflection.class_known? — does any source recognise this class / module name?
- Reflection.class_ordering —
:equal/:subclass/:superclass/:disjoint/:unknownordering between two class names. - Reflection.nominal_for_name —
Rigor::Type::Nominalfor the class name, joining registry + RBS lookups. - Reflection.singleton_for_name —
Rigor::Type::Singletonfor the class name's class object. - Reflection.constant_type_for — type of a constant (joins in-source constants and RBS-side constants).
- Reflection.instance_method_definition / Reflection.singleton_method_definition — RBS-side
RBS::Definition::Methodfor the method, ornilwhen the method is not declared in RBS. Source-side discovered methods are exposed through Reflection.discovered_method? below until the unifiedMethodDefinitioncarrier ships. - Reflection.discovered_class? / Reflection.discovered_method? — has the ScopeIndexer pass recorded the class / method as user-defined in the analyzed sources?
The provenance side of the API (which source family contributed each fact) is explicitly out of scope for the v0.0.7 first pass; v0.1.0's plugin API added it as a separate concern.
Class Method Summary collapse
- .class_known?(class_name, scope: Scope.empty) ⇒ Boolean
-
.class_ordering(lhs, rhs, scope: Scope.empty) ⇒ Symbol
One of
:equal,:subclass,:superclass,:disjoint,:unknown. -
.class_type_param_names(class_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS-declared type parameter names for the class (e.g.
[:A]forArray[A]), or[]when the class is non-generic / not in RBS. -
.constant_type_for(constant_name, scope: Scope.empty) ⇒ Object
Returns the type of the named constant.
-
.discovered_class?(class_name, scope: Scope.empty) ⇒ Boolean
True when the analyzed source contains a class / module declaration for the given name.
-
.discovered_method?(class_name, method_name, kind: :instance, scope: Scope.empty) ⇒ Boolean
True when the ScopeIndexer recorded a
deffor the given method on the given class with the matching kind. -
.instance_definition(class_name, scope: nil, environment: nil) ⇒ Object
Returns the full RBS instance-side class definition (
RBS::Definition), used by callers that walk the method table or member list. -
.instance_method_definition(class_name, method_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS
RBS::Definition::Methodfor the instance method, or nil when the class or method is not in RBS. -
.nominal_for_name(class_name, scope: Scope.empty) ⇒ Object
Returns the
Rigor::Type::Nominalfor the class name, or nil when no source knows the class. -
.rbs_class_known?(class_name, scope: nil, environment: nil) ⇒ Boolean
RBS-only variant of Reflection.class_known?.
-
.resolve_constant_type(name, scope: Scope.empty) ⇒ Object
Resolves a constant reference to its type through Ruby's lexical constant lookup: the most-qualified candidate first (the enclosing class path joined to
name), then progressively less-qualified, then the barename. -
.singleton_definition(class_name, scope: nil, environment: nil) ⇒ Object
Returns the full RBS singleton-side class definition.
-
.singleton_for_name(class_name, scope: Scope.empty) ⇒ Object
Returns the
Rigor::Type::Singletonfor the class name's class object, or nil when no source knows the class. -
.singleton_method_definition(class_name, method_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS
RBS::Definition::Methodfor the singleton (class-side) method, or nil.
Instance Method Summary collapse
- #self?.class_known? ⇒ Boolean
- #self?.class_ordering ⇒ Symbol
- #self?.class_type_param_names ⇒ Array[Symbol]
- #self?.constant_type_for ⇒ Type::t?
- #self?.discovered_class? ⇒ Boolean
- #self?.discovered_method? ⇒ Boolean
- #self?.instance_definition ⇒ Object
- #self?.instance_method_definition ⇒ Object
- #self?.nominal_for_name ⇒ Type::Nominal?
- #self?.rbs_class_known? ⇒ Boolean
- #self?.resolve_constant_type ⇒ Type::t?
- #self?.singleton_definition ⇒ Object
- #self?.singleton_for_name ⇒ Type::Singleton?
- #self?.singleton_method_definition ⇒ Object
Class Method Details
.class_known?(class_name, scope: Scope.empty) ⇒ Boolean
56 57 58 59 60 |
# File 'lib/rigor/reflection.rb', line 56 def class_known?(class_name, scope: Scope.empty) return true if scope.discovered_classes.key?(class_name.to_s) scope.environment.class_known?(class_name) end |
.class_ordering(lhs, rhs, scope: Scope.empty) ⇒ Symbol
Returns one of :equal, :subclass, :superclass,
:disjoint, :unknown.
81 82 83 |
# File 'lib/rigor/reflection.rb', line 81 def class_ordering(lhs, rhs, scope: Scope.empty) scope.environment.class_ordering(lhs, rhs) end |
.class_type_param_names(class_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS-declared type parameter names for the class (e.g. [:A] for
Array[A]), or [] when the class is non-generic / not in RBS. Used by the dispatcher
when binding generic method types to a concrete receiver.
315 316 317 318 319 320 |
# File 'lib/rigor/reflection.rb', line 315 def class_type_param_names(class_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return [] if loader.nil? loader.class_type_param_names(class_name.to_s) end |
.constant_type_for(constant_name, scope: Scope.empty) ⇒ Object
Returns the type of the named constant. Joins in-source constants (recorded by
ScopeIndexer) and RBS-side constants. In-source wins on collision because the user's
source is the authoritative declaration.
This is the flat lookup keyed on the literal constant_name. Callers that need Ruby's
lexical constant resolution (walking the enclosing class path, and folding in registry
classes / source-discovered classes as Singleton / class types) use
resolve_constant_type instead.
105 106 107 108 109 110 111 |
# File 'lib/rigor/reflection.rb', line 105 def constant_type_for(constant_name, scope: Scope.empty) key = constant_name.to_s in_source = scope.in_source_constants[key] return in_source if in_source scope.environment.constant_for_name(constant_name) end |
.discovered_class?(class_name, scope: Scope.empty) ⇒ Boolean
Returns true when the analyzed source contains a class / module declaration for the given name. Does NOT consult the RBS loader (use class_known? for the union).
336 337 338 |
# File 'lib/rigor/reflection.rb', line 336 def discovered_class?(class_name, scope: Scope.empty) scope.discovered_classes.key?(class_name.to_s) end |
.discovered_method?(class_name, method_name, kind: :instance, scope: Scope.empty) ⇒ Boolean
Returns true when the ScopeIndexer recorded a def for the given method on
the given class with the matching kind.
343 344 345 |
# File 'lib/rigor/reflection.rb', line 343 def discovered_method?(class_name, method_name, kind: :instance, scope: Scope.empty) scope.discovered_method?(class_name, method_name, kind) end |
.instance_definition(class_name, scope: nil, environment: nil) ⇒ Object
Returns the full RBS instance-side class definition (RBS::Definition), used by
callers that walk the method table or member list. Returns nil when the class is not
in RBS or when the loader cannot build a definition (e.g. constant aliases, malformed
signatures).
293 294 295 296 297 298 299 300 |
# File 'lib/rigor/reflection.rb', line 293 def instance_definition(class_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return nil if loader.nil? loader.instance_definition(class_name.to_s) rescue ::RBS::BaseError nil end |
.instance_method_definition(class_name, method_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS RBS::Definition::Method for the instance method, or nil when the
class or method is not in RBS. The source-side discovered-method facts are reachable
through discovered_method?; a future slice will unify the two under a
MethodDefinition carrier.
273 274 275 276 277 278 |
# File 'lib/rigor/reflection.rb', line 273 def instance_method_definition(class_name, method_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return nil if loader.nil? loader.instance_method(class_name: class_name.to_s, method_name: method_name.to_sym) end |
.nominal_for_name(class_name, scope: Scope.empty) ⇒ Object
Returns the Rigor::Type::Nominal for the class name, or nil when no source knows the
class.
87 88 89 |
# File 'lib/rigor/reflection.rb', line 87 def nominal_for_name(class_name, scope: Scope.empty) scope.environment.nominal_for_name(class_name) end |
.rbs_class_known?(class_name, scope: nil, environment: nil) ⇒ Boolean
RBS-only variant of class_known?. Use when the caller needs to know specifically
whether RBS has a definition for the class, independent of any source-discovered class Foo; end declarations. The diagnostic-rule code paths that walk RBS method tables to
decide whether to flag a missing method use this variant; otherwise the
source-discovered class would suppress the rule even when no RBS sig actually proves
the method exists.
The kwarg accepts either scope: or environment:. The latter is for call sites that
don't carry a Scope (most are bottom-half dispatcher code paths called with only an
environment).
72 73 74 75 76 77 |
# File 'lib/rigor/reflection.rb', line 72 def rbs_class_known?(class_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return false if loader.nil? loader.class_known?(class_name) end |
.resolve_constant_type(name, scope: Scope.empty) ⇒ Object
Resolves a constant reference to its type through Ruby's lexical constant lookup: the
most-qualified candidate first (the enclosing class path joined to name), then
progressively less-qualified, then the bare name. Each candidate consults, in order,
the class registry (yielding a Singleton[C]), source-discovered classes, in-source
value constants, and finally RBS-side constants — in-source value constants winning over
RBS because the user's source is authoritative for its own constants. Returns the matched
Rigor::Type, or nil when no source knows the constant.
This is the shared owner of the lexical-constant resolution: Inference::ExpressionTyper
reads it to type a constant read, and Inference::Narrowing reads it to recognise a
value-pinned Constant[Regexp] match-predicate operand.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/rigor/reflection.rb', line 124 def resolve_constant_type(name, scope: Scope.empty) prefix = enclosing_class_path(scope) # Step 1 — `Module.nesting`, innermost first. Each entry contributes only its OWN constants. walker = prefix while walker && !walker.empty? hit = constant_type_at("#{walker}::#{name}", scope) return hit if hit idx = walker.rindex("::") walker = idx ? walker[0, idx] : nil end # Step 2 (#354) — the ancestors of the innermost cresting scope, which Ruby consults BEFORE # falling back to the top level. Skipping this step did not merely lose a resolution: when the # same name also exists at top level, step 3 answered a lookup Ruby gives to the ancestor, so # `KEY` inside `class Sub < Base` typed as the top-level constant rather than `Base::KEY` — a # wrong type on correct code, which outranks any worst-case reading (AGENTS.md # § "Implementation Guidelines"). Only project ancestors are walked; an RBS-known superclass # contributes no name here (see {.ancestor_constant_scopes}). if prefix ancestor_constant_scopes(prefix, scope).each do |ancestor| hit = constant_type_at("#{ancestor}::#{name}", scope) return hit if hit end end # Step 3 — the bare name (top level). constant_type_at(name, scope) end |
.singleton_definition(class_name, scope: nil, environment: nil) ⇒ Object
Returns the full RBS singleton-side class definition.
303 304 305 306 307 308 309 310 |
# File 'lib/rigor/reflection.rb', line 303 def singleton_definition(class_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return nil if loader.nil? loader.singleton_definition(class_name.to_s) rescue ::RBS::BaseError nil end |
.singleton_for_name(class_name, scope: Scope.empty) ⇒ Object
Returns the Rigor::Type::Singleton for the class name's class object, or nil when no
source knows the class.
93 94 95 |
# File 'lib/rigor/reflection.rb', line 93 def singleton_for_name(class_name, scope: Scope.empty) scope.environment.singleton_for_name(class_name) end |
.singleton_method_definition(class_name, method_name, scope: nil, environment: nil) ⇒ Object
Returns the RBS RBS::Definition::Method for the singleton (class-side) method, or
nil.
282 283 284 285 286 287 |
# File 'lib/rigor/reflection.rb', line 282 def singleton_method_definition(class_name, method_name, scope: nil, environment: nil) loader = rbs_loader_for(scope, environment) return nil if loader.nil? loader.singleton_method(class_name: class_name.to_s, method_name: method_name.to_sym) end |
Instance Method Details
#self?.class_known? ⇒ Boolean
3 |
# File 'sig/rigor/reflection.rbs', line 3
def self?.class_known?: (String | Symbol class_name, ?scope: Scope) -> bool
|
#self?.class_ordering ⇒ Symbol
5 |
# File 'sig/rigor/reflection.rbs', line 5
def self?.class_ordering: (String | Symbol lhs, String | Symbol rhs, ?scope: Scope) -> Symbol
|
#self?.class_type_param_names ⇒ Array[Symbol]
14 |
# File 'sig/rigor/reflection.rbs', line 14
def self?.class_type_param_names: (String | Symbol class_name, ?scope: Scope) -> Array[Symbol]
|
#self?.constant_type_for ⇒ Type::t?
8 |
# File 'sig/rigor/reflection.rbs', line 8
def self?.constant_type_for: (String | Symbol constant_name, ?scope: Scope) -> Type::t?
|
#self?.discovered_class? ⇒ Boolean
15 |
# File 'sig/rigor/reflection.rbs', line 15
def self?.discovered_class?: (String | Symbol class_name, ?scope: Scope) -> bool
|
#self?.discovered_method? ⇒ Boolean
16 |
# File 'sig/rigor/reflection.rbs', line 16
def self?.discovered_method?: (String | Symbol class_name, String | Symbol method_name, ?kind: Symbol, ?scope: Scope) -> bool
|
#self?.instance_definition ⇒ Object
12 |
# File 'sig/rigor/reflection.rbs', line 12
def self?.instance_definition: (String | Symbol class_name, ?scope: Scope) -> untyped
|
#self?.instance_method_definition ⇒ Object
10 |
# File 'sig/rigor/reflection.rbs', line 10
def self?.instance_method_definition: (String | Symbol class_name, String | Symbol method_name, ?scope: Scope) -> untyped
|
#self?.nominal_for_name ⇒ Type::Nominal?
6 |
# File 'sig/rigor/reflection.rbs', line 6
def self?.nominal_for_name: (String | Symbol class_name, ?scope: Scope) -> Type::Nominal?
|
#self?.rbs_class_known? ⇒ Boolean
4 |
# File 'sig/rigor/reflection.rbs', line 4
def self?.rbs_class_known?: (String | Symbol class_name, ?scope: Scope) -> bool
|
#self?.resolve_constant_type ⇒ Type::t?
9 |
# File 'sig/rigor/reflection.rbs', line 9
def self?.resolve_constant_type: (String | Symbol name, ?scope: Scope) -> Type::t?
|
#self?.singleton_definition ⇒ Object
13 |
# File 'sig/rigor/reflection.rbs', line 13
def self?.singleton_definition: (String | Symbol class_name, ?scope: Scope) -> untyped
|
#self?.singleton_for_name ⇒ Type::Singleton?
7 |
# File 'sig/rigor/reflection.rbs', line 7
def self?.singleton_for_name: (String | Symbol class_name, ?scope: Scope) -> Type::Singleton?
|
#self?.singleton_method_definition ⇒ Object
11 |
# File 'sig/rigor/reflection.rbs', line 11
def self?.singleton_method_definition: (String | Symbol class_name, String | Symbol method_name, ?scope: Scope) -> untyped
|