rbs icon indicating copy to clipboard operation
rbs copied to clipboard

NoMethodError: undefined method `required_positionals'

Open aaronmallen opened this issue 1 year ago • 8 comments

Hi, I am running into an issue with blocks in rbs. It should be noted that this spec passes with rbs disabled.

rbs 3.5.3

given the following RSpec spec:

describe '.parameter' do
  it 'adds a parameter to the parameter set' do
    described_class.parameter(:test) do
      desc 'A test parameter'
    end

    expect(described_class.parameters.test).to be_an_instance_of(Domainic::Type::Constraint::Parameter)
      .and(have_attributes(name: :test, description: 'A test parameter'))
  end
end

and the following signatures:

class Base
  class << self
    def parameter(parameter_name, &)
      parameter_builder.define(parameter_name, &).build!
    end
    
    private
  
    def parameter_builder
      @parameter_builder ||= DSL::ParameterBuilder.new(self)
    end
  end
end
class Base
 def self.parameter: (::String | Symbol parameter_name) { (?) -> DSL::ParameterBuilder } -> void
 
 private

 def self.parameter_builder: -> DSL::ParameterBuilder
end
module DSL
  class ParameterBuilder
    def define(parameter_name, &)
      @current_parameter = @data[parameter_name.to_sym] ||=
        PARAMETER_DEFAULTS.transform_values(&:dup).merge(name: parameter_name.to_sym)
      instance_exec(&) if block_given?
      self
    end

    def description(description_string)
      raise ArgumentError, 'No parameter is currently being defined' if @current_parameter.nil?

      @current_parameter[:description] = description_string
      self
    end
    alias desc description
  end
end
module DSL
 class ParameterBuilder
   def define: (::String | ::Symbol parameter_name) { (?) -> self } -> self
   def description: (::String description_string) -> self
   alias desc description
 end
end

I get:

NoMethodError: undefined method `required_positionals' for #<RBS::Types::UntypedFunction:0x0000000110c3fe68 @return_type=#<RBS::Types::ClassInstance:0x0000000110c3ff30 @name=#<RBS::TypeName:0x0000000110c12030 @namespace=#<RBS::Namespace:0x0000000110c12080 @path=[:Domainic, :Type, :DSL], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11fb8 @path=[:Domainic, :Type], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11ec8 @path=[:Domainic], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11dd8 @path=[], @absolute=true>>>>, @name=:ParameterBuilder, @kind=:class>, @args=[], @location=#<RBS::Location:11700 buffer=domainic-type/sig/domainic/type/constraint/base/base.rbs, start=15:72, pos=369...390, children=name,?args source="DSL::ParameterBuilder">>>

  0) Domainic::Type::Constraint::Base.parameter adds a parameter to the parameter set
     Failure/Error:
       described_class.parameter(:test) do
         desc 'A test parameter'
       end

     NoMethodError:
       undefined method `required_positionals' for #<RBS::Types::UntypedFunction:0x0000000110c3fe68 @return_type=#<RBS::Types::ClassInstance:0x0000000110c3ff30 @name=#<RBS::TypeName:0x0000000110c12030 @namespace=#<RBS::Namespace:0x0000000110c12080 @path=[:Domainic, :Type, :DSL], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11fb8 @path=[:Domainic, :Type], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11ec8 @path=[:Domainic], @absolute=true, @parent=#<RBS::Namespace:0x0000000110c11dd8 @path=[], @absolute=true>>>>, @name=:ParameterBuilder, @kind=:class>, @args=[], @location=#<RBS::Location:11700 buffer=domainic-type/sig/domainic/type/constraint/base/base.rbs, start=15:72, pos=369...390, children=name,?args source="DSL::ParameterBuilder">>>
     # ./domainic-type/spec/domainic/type/constraint/base_spec.rb:11:in `block (3 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # NoMethodError:
     #   undefined method `desc' for #<RSpec::ExampleGroups::DomainicTypeConstraintBase::Parameter "adds a parameter to the parameter set" (./domainic-type/spec/domainic/type/constraint/base_spec.rb:10)>
     #   ./domainic-type/spec/domainic/type/constraint/base_spec.rb:12:in `block (4 levels) in <top (required)>'

Additionally the recommended annotation to skip tests does not fix the issue:

%a{rbs:test:skip} def self.parameter: (::String | Symbol parameter_name) { (?) -> DSL::ParameterBuilder } -> void

aaronmallen avatar Sep 22 '24 22:09 aaronmallen

Related issue on Ruby LSP which seems to be caused because of this: https://github.com/Shopify/ruby-lsp/issues/2630

miharekar avatar Sep 30 '24 12:09 miharekar

module Domainic
  module Type
    module DSL
      class ParameterBuilder
        ...
        def coercer: (?(Proc | Symbol)? proc_or_symbol) { (?) -> void } -> self
        alias coerce coercer
        
        def define: (String | Symbol parameter_name) ?{ (self) -> self } -> self
        ...
      end
    end
  end
end
class ParameterBuilder
  ...
  def define(parameter_name, &)
    @current_parameter = @data[parameter_name.to_sym] ||=
      PARAMETER_DEFAULTS.transform_values { |value| value == UNSPECIFIED ? value : value.dup } .merge(name: parameter_name.to_sym)
    instance_exec(&) if block_given?
    self
  end
  ...
end
class BaseConstraint
  def parameter(parameter_name, &)
    parameter_builder.define(parameter_name, &).build!
  end
end
# This spec passes without signatures

it 'defines coercers for the parameter' do
  coercer = ->(value) { value.to_s }
  constraint_class.parameter(parameter_name) do
    coercer coercer
  end
  parameter = constraint_class.parameters.public_send(parameter_name)
  
  expect(parameter.instance_variable_get(:@coercers)).to include(coercer)
end
  1) Domainic::Type::Constraint::BaseConstraint.parameter defines coercers for the parameter
     Failure/Error:
       constraint_class.parameter(parameter_name) do
         coercer coercer
       end
     
     RBS::Test::Tester::TypeError:
       TypeError: [Domainic::Type::Constraint::BaseConstraint.parameter] BlockArgumentError: expected method type (::String | ::Symbol parameter_name) ?{ (::Domainic::Type::DSL::ParameterBuilder) -> ::Domainic::Type::DSL::ParameterBuilder } -> void
     # ./domainic-type/spec/domainic/type/constraint/base_constraint_spec.rb:25:in `block (3 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # NoMethodError:
     #   undefined method `coercer' for #<RSpec::ExampleGroups::DomainicTypeConstraintBaseConstraint::Parameter "defines coercers for the parameter" (./domainic-type/spec/domainic/type/constraint/base_constraint_spec.rb:23)>
     #   ./domainic-type/spec/domainic/type/constraint/base_constraint_spec.rb:26:in `block (4 levels) in <top (required)>'

aaronmallen avatar Oct 01 '24 13:10 aaronmallen

Related issue on Ruby LSP which seems to be caused because of this: Shopify/ruby-lsp#2630

I'm using rubymine which I believe doesn't utilize lsp

aaronmallen avatar Oct 01 '24 13:10 aaronmallen

@aaronmallen How about upgrading to rbs-3.6? There are some fixes related to the runtime test and UntypedFunction.

soutaro avatar Oct 03 '24 08:10 soutaro

@aaronmallen How about upgrading to rbs-3.6? There are some fixes related to the runtime test and UntypedFunction.

I've upgraded and still run into the same issues

aaronmallen avatar Oct 03 '24 14:10 aaronmallen

Thanks. Hmm… Will look at the problem. 🙇‍♂️

soutaro avatar Oct 04 '24 04:10 soutaro

@aaronmallen I have two things to share with you.

  1. undefined method "desc" for #<RSpec::...> might be the source of the problem. I found the code you shared doesn't work with the current instance_eval detection, and composed a patch #2052.
  2. Adding %a{rbs:test:skip} annotation to the define method would work.

I couldn't reproduce the required_positionals error myself, but at least one problem will be fixed...

soutaro avatar Oct 06 '24 13:10 soutaro

I couldn't reproduce the required_positionals error myself, but at least one problem will be fixed...

I'm no longer running into the required_positionals error after applying the suggestions from https://github.com/ruby/rbs/discussions/2039 now its just:

Failure/Error:
  desc ''

NoMethodError:
  undefined method `desc' for BaseConstraint:Class

I look forward to your patch as I've pulled defining signatures out of scope for my project until a fix is released.

aaronmallen avatar Oct 07 '24 06:10 aaronmallen