Project

General

Profile

Actions

Feature #13083

closed

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

Added by kachick (Kenichi Kamiya) about 7 years ago. Updated over 4 years ago.

Status:
Closed
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

Updated by nobu (Nobuyoshi Nakada) about 7 years ago

  • Description updated (diff)

Updated by snood1205 (Eli Sadoff) about 7 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) about 7 years ago

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

Matz.

Updated by znz (Kazuhiro NISHIYAMA) over 4 years ago

  • Status changed from Open to Closed

Updated by rafaelfranca (Rafael França) over 4 years 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) over 4 years 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) over 4 years ago

+1 for deprecation. The removal is too harsh.

Updated by shugo (Shugo Maeda) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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.

Actions #16

Updated by Eregon (Benoit Daloze) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years ago

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

Updated by shugo (Shugo Maeda) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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?

Updated by naruse (Yui NARUSE) over 4 years ago

Eregon (Benoit Daloze) wrote:

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?

I also received the same feedback from Rails people.
nilable&.match?(/re/) sounds a reasonable option, but why do we provide a such pitfall?

Updated by matz (Yukihiro Matsumoto) over 4 years ago

It's not my intention to suffer users. Let's cancel the change.

Matz.

Actions #28

Updated by naruse (Yui NARUSE) over 4 years ago

  • Status changed from Open to Closed

Applied in changeset git|8852fa876039ed177fd5e867f36177d8a9ff411c.


Revert "Regexp#match{?} with nil raises TypeError as String, Symbol (#1506)"

This reverts commit 2a22a6b2d8465934e75520a7fdcf522d50890caf.
Revert [Feature #13083]

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0