Skip to content
  • Jeremy Evans's avatar
    ca204a20
    Fix keyword splat passing as regular argument · ca204a20
    Jeremy Evans authored
    
    
    Since Ruby 3.0, Ruby has passed a keyword splat as a regular
    argument in the case of a call to a Ruby method where the
    method does not accept keyword arguments, if the method
    call does not contain an argument splat:
    
    ```ruby
    def self.f(obj) obj end
    def self.fs(*obj) obj[0] end
    h = {a: 1}
    f(**h).equal?(h)  # Before: true; After: false
    fs(**h).equal?(h) # Before: true; After: false
    
    a = []
    f(*a, **h).equal?(h)  # Before and After: false
    fs(*a, **h).equal?(h) # Before and After: false
    ```
    
    The fact that the behavior differs when passing an empty
    argument splat makes it obvious that something is not
    working the way it is intended.  Ruby 2 always copied
    the keyword splat hash, and that is the expected behavior
    in Ruby 3.
    
    This bug is because of a missed check in setup_parameters_complex.
    If the keyword splat passed is not mutable, then it points to
    an existing object and not a new object, and therefore it must
    be copied.
    
    Now, there are 3 specs for the broken behavior of directly
    using the keyword splatted hash.  Fix two specs and add a
    new version guard. Do not keep the specs for the broken
    behavior for earlier Ruby versions, in case this fix is
    backported. For the ruby2_keywords spec, just remove the
    related line, since that line is unrelated to what the
    spec is testing.
    
    Co-authored-by: default avatarNobuyoshi Nakada <nobu@ruby-lang.org>
    ca204a20
    Fix keyword splat passing as regular argument
    Jeremy Evans authored
    
    
    Since Ruby 3.0, Ruby has passed a keyword splat as a regular
    argument in the case of a call to a Ruby method where the
    method does not accept keyword arguments, if the method
    call does not contain an argument splat:
    
    ```ruby
    def self.f(obj) obj end
    def self.fs(*obj) obj[0] end
    h = {a: 1}
    f(**h).equal?(h)  # Before: true; After: false
    fs(**h).equal?(h) # Before: true; After: false
    
    a = []
    f(*a, **h).equal?(h)  # Before and After: false
    fs(*a, **h).equal?(h) # Before and After: false
    ```
    
    The fact that the behavior differs when passing an empty
    argument splat makes it obvious that something is not
    working the way it is intended.  Ruby 2 always copied
    the keyword splat hash, and that is the expected behavior
    in Ruby 3.
    
    This bug is because of a missed check in setup_parameters_complex.
    If the keyword splat passed is not mutable, then it points to
    an existing object and not a new object, and therefore it must
    be copied.
    
    Now, there are 3 specs for the broken behavior of directly
    using the keyword splatted hash.  Fix two specs and add a
    new version guard. Do not keep the specs for the broken
    behavior for earlier Ruby versions, in case this fix is
    backported. For the ruby2_keywords spec, just remove the
    related line, since that line is unrelated to what the
    spec is testing.
    
    Co-authored-by: default avatarNobuyoshi Nakada <nobu@ruby-lang.org>
Loading