Project

General

Profile

Feature #13083

Regexp#{match,match?} with a nil argument are deprecated and will raise a TypeError in Ruby 3.0

Added by kachick (Kenichi Kamiya) almost 3 years ago. Updated 3 days ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
[ruby-core:78891]

Description

Just for consistency

Currently behaves as ( ruby --version: ruby 2.5.0dev (2016-12-28 trunk 57228) [x86_64-darwin16] )

'string'.__send__(:=~, nil) #=> nil
'string'.match(nil)         #=> TypeError: wrong argument type nil (expected Regexp)
'string'.match?(nil)        #=> TypeError: wrong argument type nil (expected Regexp)
:symbol.__send__(:=~, nil)  #=> nil
:symbol.match(nil)          #=> TypeError: wrong argument type nil (expected Regexp)
:symbol.match?(nil)         #=> TypeError: wrong argument type nil (expected Regexp)
/regex/.__send__(:=~, nil)  #=> nil
/regex/.match(nil)          #=> nil
/regex/.match?(nil)         #=> false

Expected to

'string'.__send__(:=~, nil) #=> nil
'string'.match(nil)         #=> nil
'string'.match?(nil)        #=> false
:symbol.__send__(:=~, nil)  #=> nil
:symbol.match(nil)          #=> nil
:symbol.match?(nil)         #=> false
/regex/.__send__(:=~, nil)  #=> nil
/regex/.match(nil)          #=> nil
/regex/.match?(nil)         #=> false

Associated revisions

Revision 4fe89e08
Added by Eregon (Benoit Daloze) 26 days ago

Add entry for Feature #13083 in NEWS

  • Move Unicode changes under String / Unicode for consistency with the rest.

Revision fbacfe68
Added by Eregon (Benoit Daloze) 18 days ago

Update NEWS entry for Feature #13083

History

Updated by nobu (Nobuyoshi Nakada) almost 3 years ago

  • Description updated (diff)

Updated by snood1205 (Eli Sadoff) almost 3 years ago

I think that the way it currently exists is logical. As =~ is an Object method, it is worth while keeping the more permissible behavior whereas errors should be thrown for match and match? if nil is passed. The change that I would propose would actually to have Regexp#match and Regexp#match? both raise a type error if nil is passed as an argument. The inconsistency seems to be stronger there.

Updated by matz (Yukihiro Matsumoto) over 2 years ago

Those methods (but =~) should consistently raise exceptions.

Matz.

Updated by znz (Kazuhiro NISHIYAMA) about 1 month ago

  • Status changed from Open to Closed

Updated by rafaelfranca (Rafael França) 21 days ago

I know this change is to make the code consistent but this is a backward incompatible change and I don't think it should be applied to Ruby 2.7 without deprecation. The fact that we need to change Rails and many other gems to make this work is a good indication.

I thought we were avoiding making backward incompatible changes before Ruby 3.0.

Updated by sam.saffron (Sam Saffron) 21 days ago

I am 100% with Rafael here, this is a very risky change, Rails and Sprockets are only a couple of projects, there will be tons of stuff that will not be caught.

Why not deprecate this for now and then next release it can be removed?

Updated by shyouhei (Shyouhei Urabe) 21 days ago

+1 for deprecation. The removal is too harsh.

Updated by shugo (Shugo Maeda) 21 days ago

+1 for deprecation too. My text editor had been broken by this change, and Vim was needed to fix it.

Updated by matz (Yukihiro Matsumoto) 20 days ago

Thank you for the input. If you can provide a more detailed situation, it would be better.

Matz.

Updated by rafaelfranca (Rafael França) 20 days ago

The detailed situation.

Rails and other libraries and codebases rely on /regex/.match?(nil) to return false. This change is making that method to raise an error in that situation.

Changing this behavior on Ruby 2.7 means that applications will break because of this change.

This change also have dubious value. While it makes things consistent now it require the callers to do one more check.

Code that before was:

 /foo/.match?(some_variable)

Need to become:

some_variable && /foo/.match?(some_variable)

or

/foo/.match?(some_variable) unless some_variable

If you ask me, I believe this change is good, but it doesn't require a breaking change. We can deprecate the old behavior and then in a next version remove it.

The codebases can change their code, of course, but so they can change for any breaking change in Ruby. If we are being careful to not introduce breaking changes in Ruby, this change should also falls in that category in my opinion. If we are ok with this breaking change, why not with others like frozen string as default? Both change will require changes to codebases and both changes have dubious value.

Updated by Eregon (Benoit Daloze) 20 days ago

I think everyone agrees this is too breaking (just look at how many call sites need to be changed in Rails and other gems),
and it's very easy to deprecate the old behavior by warning when passing nil in Ruby 2.7.

The only question for me is: who will change it to a deprecation?

Updated by kachick (Kenichi Kamiya) 20 days ago

  • Subject changed from {String|Symbol}#match{?} with nil returns falsy as Regexp#match{?} to {String|Symbol}#match{?} with nil returns falsy as Regexp#match{?} since ruby 3.0

Updated by shugo (Shugo Maeda) 19 days ago

matz (Yukihiro Matsumoto) wrote:

Thank you for the input. If you can provide a more detailed situation, it would be better.

My situation is the same as Rafael's, and I fixed my code using the safe navigation operator:

https://github.com/shugo/textbringer/commit/c859d13

Code like /regex/ =~ str has often been used when str may be nil, and such code has been rewritten using Regexp#match? after Ruby 2.4.
This change will break all such code.

#16

Updated by Eregon (Benoit Daloze) 18 days ago

  • Subject changed from {String|Symbol}#match{?} with nil returns falsy as Regexp#match{?} since ruby 3.0 to Regexp#{match,match?} with a nil argument are deprecated and will raise a TypeError in Ruby 3.0

Updated by Eregon (Benoit Daloze) 18 days ago

kachick (Kenichi Kamiya) wrote:

How about https://github.com/ruby/ruby/pull/2637?

I merged that PR, thank you.
Regexp#{match,match?} with a nil argument now just warns.

The plan is then to make them raise TypeError in Ruby 3.0.

Updated by Eregon (Benoit Daloze) 18 days ago

shugo (Shugo Maeda) wrote:

Code like /regex/ =~ str has often been used when str may be nil, and such code has been rewritten using Regexp#match? after Ruby 2.4.
This change will break all such code.

That's a good point.
We can still discuss on this ticket whether we should change behavior at all,
but I think so far everyone was fine with deprecation (warning) and then removal later on, so I merged the PR

Updated by Dan0042 (Daniel DeLorme) 17 days ago

Is there a benefit to the change apart from consistency? A concrete benefit I mean.

Updated by shugo (Shugo Maeda) 17 days ago

  • Status changed from Closed to Open

Eregon (Benoit Daloze) wrote:

shugo (Shugo Maeda) wrote:

Code like /regex/ =~ str has often been used when str may be nil, and such code has been rewritten using Regexp#match? after Ruby 2.4.
This change will break all such code.

That's a good point.
We can still discuss on this ticket whether we should change behavior at all,
but I think so far everyone was fine with deprecation (warning) and then removal later on, so I merged the PR

Thank you.
I reopen this issue for further discussion.

Updated by naruse (Yui NARUSE) 16 days ago

  • Target version set to 2.7

NOTE: warning message should be improved if this is deprecated before 2.7.0.

Updated by Eregon (Benoit Daloze) 14 days ago

Dan0042 (Daniel DeLorme) wrote:

Is there a benefit to the change apart from consistency? A concrete benefit I mean.

I think consistency is good here, and also it seems to me regexp.match?(s) with s being nil could actually catch bugs where s is not expected to be nil.
It seems rather odd to me to validate some input and even accept nil as an "OK" value to match against a Regexp.
nil means typically "missing value" in such a case, and often deserves its own behavior (e.g., could be empty string is not provided, could be an error, could be special behavior just for nil, could be some non-empty default String which would help readability).

In the Rails PR I noticed a case that doesn't but is fairly close to end up in nil.to_i, which would likely be a bug/unintended:
https://github.com/rails/rails/pull/37504/files#diff-c226a4680f86689c3c170d4bc5911e96

Updated by shevegen (Robert A. Heiler) 7 days ago

Dan0042 wrote:

Is there a benefit to the change apart from consistency? A concrete benefit I mean.

This is only one point of view.

Sometimes there are "wrong" use cases or use cases that were not anticipated by the core team. In
my opinion, whenever possible, it is best to encourage consistency and clarity, as close as possible
to matz' original design consideration (because I think that this way people will not be confused
as much; remember the confusion from people thinking that there is a singular "principle of least
surprise", but then everyone using their own definition for that - that can not work).

To the suggestion itself: I have no particular pro or con opinion either way. None of the code that
I use would be affected by the change, should it come. Ruby 3.0 is not so far away so there may
be lots of changes coming to ruby past 3.0 anyway. :)

Updated by knu (Akinori MUSHA) 3 days ago

I'm a bit surprised and confused. The change that has been made is not what this issue was originally about, and actually the opposite, I think.

I believe many of us have got used to the original behavior, that is, methods of a Regexp object work permissively and accept nil, and we know we've migrated many pieces of code from /re/ =~ nilable / /re/ === nilable to /re/.match?(nilable) for the sake of performance and readability just as shugo said above.

Can't we reconsider this? Or we'll be doomed to back out all those changes we believed to improve performance.

Updated by Eregon (Benoit Daloze) 3 days ago

knu (Akinori MUSHA) wrote:

I'm a bit surprised and confused. The change that has been made is not what this issue was originally about, and actually the opposite, I think.

Per matz's decision: https://bugs.ruby-lang.org/issues/13083#note-3
I renamed the issue in https://bugs.ruby-lang.org/issues/13083#note-16 to match the current change.
Now it's just a deprecation warning.

I believe many of us have got used to the original behavior, that is, methods of a Regexp object work permissively and accept nil, and we know we've migrated many pieces of code from /re/ =~ nilable / /re/ === nilable to /re/.match?(nilable) for the sake of performance and readability just as shugo said above.

Can't we reconsider this? Or we'll be doomed to back out all those changes we believed to improve performance.

nilable&.match?(/re/) would be an easy way to rewrite those cases, no?

matz (Yukihiro Matsumoto) and others: what do you think, should we make Regexp#{match,match?} consistent and not accept nil or accept them as special-case to match the =~ and === operators?

Also available in: Atom PDF