Module: Rigor::Inference::MacroBlockSelfType

Defined in:
lib/rigor/inference/macro_block_self_type.rb

Overview

ADR-16 Tier A — engine hook. Consults every registered plugin manifest’s ‘block_as_methods` entries to decide whether a block call site qualifies for `Scope#self_type` narrowing.

The match contract for a class-level DSL like Sinatra’s ‘class MyApp < Sinatra::Base; get ’/foo’ do … end; end`:

  • the call’s lexical receiver type is ‘Singleton` (the implicit-self in a class body, or an explicit `MyApp.get(…)` call);

  • the underlying class ‘X` equals or inherits from the entry’s ‘receiver_constraint`;

  • the call’s method name is in the entry’s ‘verbs`.

On a match the helper returns the instance type of the receiver class (‘Nominal`) — the narrowed `self_type` for the block body, matching Sinatra’s runtime semantics where ‘Sinatra::Base#generate_method` turns the block into an instance method of the user’s app class.

Slice 1b ships the floor only (per ADR-16 § WD13): bare-identifier method lookups inside the block resolve through the inference engine’s normal ‘self_type`-driven path, so methods declared on `Sinatra::Base` (RBS or otherwise) become visible. Precision additions —parameter-typed block params, declared per-verb argument contracts — are ceiling concerns for later slices.

Class Method Summary collapse

Class Method Details

.instance_type_for(class_name, environment) ⇒ Object



91
92
93
# File 'lib/rigor/inference/macro_block_self_type.rb', line 91

def instance_type_for(class_name, environment)
  environment.nominal_for_name(class_name) || Type::Nominal.new(class_name)
end

.matches?(entry, verb, receiver_class_name, environment) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
79
80
# File 'lib/rigor/inference/macro_block_self_type.rb', line 76

def matches?(entry, verb, receiver_class_name, environment)
  return false unless entry.verbs.include?(verb)

  receiver_class_inherits_from?(receiver_class_name, entry.receiver_constraint, environment)
end

.narrow_self_type_for(scope:, call_node:, receiver_type:) ⇒ Rigor::Type?

Returns the narrowed self-type, or ‘nil` when no registered entry matches the call shape.

Parameters:

Returns:

  • (Rigor::Type, nil)

    the narrowed self-type, or ‘nil` when no registered entry matches the call shape.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rigor/inference/macro_block_self_type.rb', line 44

def narrow_self_type_for(scope:, call_node:, receiver_type:)
  return nil if receiver_type.nil?

  environment = scope&.environment
  registry = environment&.plugin_registry
  return nil if registry.nil? || registry.empty?

  receiver_class_name = singleton_receiver_class_name(receiver_type)
  return nil if receiver_class_name.nil?

  verb = call_node.name
  registry.plugins.each do |plugin|
    plugin.manifest.block_as_methods.each do |entry| # rigor:disable undefined-method
      return instance_type_for(receiver_class_name, environment) if matches?(entry, verb, receiver_class_name,
                                                                             environment)
    end
  end
  nil
end

.receiver_class_inherits_from?(class_name, constraint, environment) ⇒ Boolean

Returns:

  • (Boolean)


82
83
84
85
86
87
88
89
# File 'lib/rigor/inference/macro_block_self_type.rb', line 82

def receiver_class_inherits_from?(class_name, constraint, environment)
  return true if class_name == constraint

  ordering = environment.class_ordering(class_name, constraint)
  %i[equal subclass].include?(ordering)
rescue StandardError
  false
end

.singleton_receiver_class_name(receiver_type) ⇒ Object

Tier A’s match contract is intentionally narrow: class-level DSL calls (receiver is ‘Singleton`) only. Instance-receiver calls and DSL forms whose block body binds a different `self` (Concern’s ‘included do`, `instance_eval { … }`) are handled by later slices (Concern walker, Tier D, etc.) — not Tier A.



70
71
72
73
74
# File 'lib/rigor/inference/macro_block_self_type.rb', line 70

def singleton_receiver_class_name(receiver_type)
  return nil unless receiver_type.is_a?(Type::Singleton)

  receiver_type.class_name
end