steep icon indicating copy to clipboard operation
steep copied to clipboard

steep has totally wrong scope in class_eval

Open p-datadog opened this issue 5 months ago • 5 comments

When a module augments some class's functionality via class_eval, steep appears to completely overlook the fact that inside class_eval, self is the target class and not the module that contains the augmenting logic. Example:

class Base
  def self.foo
    puts 'hello foo'
  end
end

module Mod
  def self.augment(base)
    base.class_eval do
      foo
    end
  end
end

Mod.augment(Base)

sig:

class Base
  def self.foo: () -> untyped
end

module Mod
  def self.augment: (untyped base) -> untyped
end

The above code executes and prints "hello foo", but fails steep check:

class_eval.rb:10:6: [error] Type `singleton(::Mod)` does not have method `foo`
│ Diagnostic ID: Ruby::NoMethod
│
└       foo
        ~~~

Note that steep tries to find foo on Mod which is totally incorrect.

Conversely, steep is perfectly happy with the following code and type definition:

class Base2
end

module Mod2
  def foo
    puts 'hello foo'
  end

  def self.augment(base)
    base.class_eval do
      foo
    end
  end
end

Mod2.augment(Base2)
class Base2
end

module Mod2
  def self.augment: (untyped base) -> untyped
  def foo: () -> untyped
end

Here, I put foo into the module. But, obviously, the above is not valid Ruby:

% ruby class_eval_2.rb 
class_eval_2.rb:11:in `block in augment': undefined local variable or method `foo' for class Base2 (NameError)

      foo
      ^^^
	from class_eval_2.rb:10:in `class_eval'
	from class_eval_2.rb:10:in `augment'
	from class_eval_2.rb:16:in `<main>'

p-datadog avatar Sep 25 '24 14:09 p-datadog