Class: Rigor::Plugin::AdditionalInitializer

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/additional_initializer.rb

Overview

ADR-38 declaration: "on receiver_constraint (and its subclasses), every method named in methods (def-form) or block_methods (block-form) also establishes instance-variable state — treat it like initialize for the read-before-write nil soundness gate."

Def-form (methods:) — applies when the ivar write lives in a named def body. Example: Minitest def setup; @conn = …; end.

Block-form (block_methods:) — applies when the ivar write lives in a block passed to a method call. Example: RSpec before { @user = create(:user) } / let(:x) { @y = … }. ScopeIndexer descends the block body of any CallNode whose method name is in block_methods, collecting ivar writes exactly as it would for a def-form initializer.

At least one of methods: or block_methods: must be non-empty.

Authored on a plugin manifest:

# def-form (Minitest):
AdditionalInitializer.new(
receiver_constraint: "Minitest::Test",
methods: [:setup]
)

# block-form (RSpec):
AdditionalInitializer.new(
receiver_constraint: "RSpec::ExampleGroup",
block_methods: [:before, :let, :subject]
)

The Ruby analogue of PHPStan's AdditionalConstructorsExtension. Rigor::Inference::ScopeIndexer consults the aggregated set at its read-before-write gate.

The contribution can only ever suppress a nil widening — it never makes the analyzer stricter — so a missed or over-broad match is false-positive-safe by construction (ADR-38 § "Why this is FP-safe").

Fields

  • receiver_constraint — fully-qualified class name (String). The entry applies to that class and its subclasses.
  • methods — Array of Symbol def-form method names (may be empty when only block_methods is used).
  • block_methods — Array of Symbol call-with-block method names (may be empty when only methods is used).

Ractor-shareability

All fields are frozen at construction (ADR-15 Phase 1); Ractor.shareable? returns true after #initialize.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(receiver_constraint:, methods: [], block_methods: []) ⇒ AdditionalInitializer

Returns a new instance of AdditionalInitializer.



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rigor/plugin/additional_initializer.rb', line 54

def initialize(receiver_constraint:, methods: [], block_methods: [])
  validate_receiver_constraint!(receiver_constraint)
  validate_method_list!(methods, :methods)
  validate_method_list!(block_methods, :block_methods)
  validate_at_least_one!(methods, block_methods)

  @receiver_constraint = receiver_constraint.dup.freeze
  @methods = methods.map(&:to_sym).freeze
  @block_methods = block_methods.map(&:to_sym).freeze
  freeze
end

Instance Attribute Details

#block_methodsObject (readonly)

Returns the value of attribute block_methods.



52
53
54
# File 'lib/rigor/plugin/additional_initializer.rb', line 52

def block_methods
  @block_methods
end

#methodsObject (readonly)

Returns the value of attribute methods.



52
53
54
# File 'lib/rigor/plugin/additional_initializer.rb', line 52

def methods
  @methods
end

#receiver_constraintObject (readonly)

Returns the value of attribute receiver_constraint.



52
53
54
# File 'lib/rigor/plugin/additional_initializer.rb', line 52

def receiver_constraint
  @receiver_constraint
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



84
85
86
# File 'lib/rigor/plugin/additional_initializer.rb', line 84

def ==(other)
  other.is_a?(AdditionalInitializer) && to_h == other.to_h
end

#covers_block_method?(method_name) ⇒ Boolean

True when method_name (a Symbol) is declared a block-form initializer by this entry.

Returns:

  • (Boolean)


72
73
74
# File 'lib/rigor/plugin/additional_initializer.rb', line 72

def covers_block_method?(method_name)
  block_methods.include?(method_name)
end

#covers_method?(method_name) ⇒ Boolean

True when method_name (a Symbol) is declared a def-form initializer by this entry.

Returns:

  • (Boolean)


67
68
69
# File 'lib/rigor/plugin/additional_initializer.rb', line 67

def covers_method?(method_name)
  methods.include?(method_name)
end

#hashObject



89
90
91
# File 'lib/rigor/plugin/additional_initializer.rb', line 89

def hash
  to_h.hash
end

#to_hObject



76
77
78
79
80
81
82
# File 'lib/rigor/plugin/additional_initializer.rb', line 76

def to_h
  {
    "receiver_constraint" => receiver_constraint,
    "methods" => methods.map(&:to_s),
    "block_methods" => block_methods.map(&:to_s)
  }
end