Module: Rigor::Reflection

Defined in:
lib/rigor/reflection.rb

Overview

Read-side facade over Rigor’s three reflection sources:

  1. **‘Rigor::Environment::ClassRegistry`** — Ruby `Class` / `Module` objects (Integer, Float, Set, Pathname, …) registered at boot. Static; never changes during a `rigor check` run.

  2. **‘Rigor::Environment::RbsLoader`** — RBS-side declarations (instance / singleton methods, class hierarchy, constants). Loaded on demand from the project’s ‘sig/` directory + the bundled stdlib RBS.

  3. **‘Rigor::Scope` discovered facts** — source-side discoveries produced by `Rigor::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)

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 Method Details

.class_known?(class_name, scope: Scope.empty) ⇒ Boolean

Parameters:

  • class_name (String, Symbol)
  • scope (Rigor::Scope) (defaults to: Scope.empty)

Returns:

  • (Boolean)


60
61
62
63
64
# File 'lib/rigor/reflection.rb', line 60

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

Returns:

  • (Symbol)

    one of ‘:equal`, `:subclass`, `:superclass`, `:disjoint`, `:unknown`.



88
89
90
# File 'lib/rigor/reflection.rb', line 88

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`), or `[]` when the class is non-generic / not in RBS. Used by the dispatcher when binding generic method types to a concrete receiver.



165
166
167
168
169
170
# File 'lib/rigor/reflection.rb', line 165

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.



108
109
110
111
112
113
114
# File 'lib/rigor/reflection.rb', line 108

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

Returns:

  • (Boolean)

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



188
189
190
# File 'lib/rigor/reflection.rb', line 188

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.

Parameters:

  • kind (:instance, :singleton) (defaults to: :instance)

Returns:

  • (Boolean)

    true when the ScopeIndexer recorded a ‘def` for the given method on the given class with the matching kind.



196
197
198
# File 'lib/rigor/reflection.rb', line 196

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



142
143
144
145
146
147
148
149
# File 'lib/rigor/reflection.rb', line 142

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.



121
122
123
124
125
126
# File 'lib/rigor/reflection.rb', line 121

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.



94
95
96
# File 'lib/rigor/reflection.rb', line 94

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

Returns:

  • (Boolean)


79
80
81
82
83
84
# File 'lib/rigor/reflection.rb', line 79

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

.singleton_definition(class_name, scope: nil, environment: nil) ⇒ Object

Returns the full RBS singleton-side class definition.



152
153
154
155
156
157
158
159
# File 'lib/rigor/reflection.rb', line 152

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.



100
101
102
# File 'lib/rigor/reflection.rb', line 100

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.



130
131
132
133
134
135
# File 'lib/rigor/reflection.rb', line 130

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