Project

General

Profile

Actions

Bug #20043

open

`defined?` checks for method existence but only sometimes

Added by tenderlovemaking (Aaron Patterson) 5 months ago. Updated 2 months ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 3.3.0dev (2023-12-05T21:25:34Z master 56eccb350b) [arm64-darwin23]
[ruby-core:115595]

Description

When an expression is passed to defined?, it will sometimes check if a method in a sub-expression is defined and sometimes it won't.

For example:

$ ./miniruby -e'p defined?(a)'
nil
$ ./miniruby -e'p defined?([a])'
nil

In the above case, Ruby will check whether or not the method a is defined, and it returns nil. However, if you use a splat, it will not check:

$ ./miniruby -e'p defined?([*a])'
"expression"

The same thing seems to happen with method parameters:

$ ./miniruby -e'p defined?(itself)'
"method"
$ ./miniruby -e'p defined?(itself(a))'
nil
$ ./miniruby -e'p defined?(itself(*a))'
"method"

Oddly, defined? will check contents of arrays, but won't check contents of hashes:

$ ./miniruby -e'p defined?([[[[a]]]])'
nil
$ ./miniruby -e'p defined?({ a => a })'
"expression"

I think all of the cases that refer to a should check whether or not a is defined regardless of splats or hashes.


Files

Updated by Eregon (Benoit Daloze) 5 months ago

I wonder if defined? should only be defined for constant paths and method call without arguments, e.g. defined?(RubyVM.keep_script_lines).
Is there any use case for defined? besides those?

IOW, I think the simpler defined? is the better (less complexity in Ruby implementations, I think little value in practice).
That could mean don't recurse into nested nodes or so, except for constant paths.

Updated by byroot (Jean Boussier) 5 months ago

Is there any use case for defined? besides those?

Instance variables for sure: defined?(@ivar).

Also checking for super: defined?(super).

Some people use it for a sequence of calls: defined?(foo.bar.baz).

Updated by Dan0042 (Daniel DeLorme) 4 months ago

I didn't know about this way of using defined? and it's interesting to me that we can check if multiple things are defined at once. Although the 3rd case below is another example that seems odd. But both that and defined?([*a]) return "expression" since ruby 1.9, not 2.2

x = 1
defined?(x) and defined?(y) and defined?(z) #=> nil
defined?([x, y, z])                         #=> nil (shorter!)
defined?(x && y && z)                       #=> "expression" (???)
defined?(x & y & z)                         #=> nil

Updated by tenderlovemaking (Aaron Patterson) 4 months ago

ko1 (Koichi Sasada) wrote in #note-3:

It seems a bug from Ruby 2.2.

I think itself was added in Ruby 2.2. Seems this bug is perhaps from Ruby 1.9?

Dan0042 (Daniel DeLorme) wrote in #note-4:

I didn't know about this way of using defined? and it's interesting to me that we can check if multiple things are defined at once.

Yes, I thought so too. I can understand the utility of defined?([x, y, z]), but I can't tell if that was intended? Especially considering the x && y && z case.

Also defined?([x, y, z]) will return "expression" if x, y, and z are defined. But [x, y, z] seems like an expression regardless of whether or not those are defined (the expression just might raise though).

Updated by jeremyevans0 (Jeremy Evans) 4 months ago

I submitted a pull request to fix this: https://github.com/ruby/ruby/pull/9500

Updated by ko1 (Koichi Sasada) 2 months ago

In future, should we deprecate defined? usecase which return expression?

Actions

Also available in: Atom PDF

Like1
Like0Like1Like0Like0Like0Like0Like0