Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NoMethodError: undefined method `required_positionals' #2025

Open
aaronmallen opened this issue Sep 22, 2024 · 8 comments
Open

NoMethodError: undefined method `required_positionals' #2025

aaronmallen opened this issue Sep 22, 2024 · 8 comments
Milestone

Comments

@aaronmallen
Copy link

aaronmallen commented Sep 22, 2024

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
@miharekar
Copy link

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

@aaronmallen
Copy link
Author

aaronmallen commented Oct 1, 2024

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
Copy link
Author

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

@soutaro
Copy link
Member

soutaro commented Oct 3, 2024

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

@aaronmallen
Copy link
Author

@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

@soutaro soutaro added this to the RBS 3.7 milestone Oct 4, 2024
@soutaro
Copy link
Member

soutaro commented Oct 4, 2024

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

@soutaro
Copy link
Member

soutaro commented Oct 6, 2024

@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 Better instance_eval/instance_exec detection #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...

@aaronmallen
Copy link
Author

aaronmallen commented Oct 7, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants