Issue #20335 has been updated by Eregon (Benoit Daloze).
I think omitting the first N frames is useful.
However I see little reason to have a `length` or `Range`, one can just `break/return` out
of the `Thread.each_caller_location`, and it seems pretty rare to need that.
So I think it's best to only add `start = 1` for now.
In fact the `length` argument is already an issue for `caller_locations`: if it's too
high it causes `caller_locations` to return an empty Array instead of whatever is
available:
```
irb(main):001:0> caller_locations(10)
=>
["/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb/ruby-lex.rb:248:in
`catch'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb/ruby-lex.rb:248:in
`each_top_level_statement'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb.rb:566:in
`eval_input'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb.rb:500:in `block in
run'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb.rb:499:in `catch'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb.rb:499:in `run'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/3.2.0/irb.rb:421:in `start'",
"/home/eregon/.rubies/ruby-3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in
`<top (required)>'",
"/home/eregon/.rubies/ruby-3.2.2/bin/irb:25:in `load'",
"/home/eregon/.rubies/ruby-3.2.2/bin/irb:25:in `<main>'"]
irb(main):002:0> caller_locations(20)
=> []
```
That implies it needs to count how many frames are available, which is inefficient.
So we certainly wouldn't want that behavior for `each_caller_location` as it would
ruin a significant part of its performance advantage.
(we should probably also fix this for `caller_locations`, but that should be a separate
ticket)
Similarly the Range form is also problematic as it also requires iterating all the stack
to find how many frames there are to know which frames `0..-2` should use.
----------------------------------------
Feature #20335: `Thread.each_caller_location` should accept the same arguments as `caller`
and `caller_locations`
https://bugs.ruby-lang.org/issues/20335#change-107264
* Author: byroot (Jean Boussier)
* Status: Open
----------------------------------------
`Thread.each_caller_location` was added to Ruby 3.2 as part of [Feature #16663] and is a
very useful API for emitting warnings with a proper source location and similar use
cases.
However in many of the cases where I used it, or seen it used, it was needed to skip the
first, or a couple frames:
Examples:
Sorbet:
https://github.com/Shopify/sorbet/blob/b27a14c247ace7cabdf0f348bfb11fdf0b7e…
```ruby
def self.find_caller
skiped_first = false
Thread.each_caller_location do |loc|
unless skiped_first
skiped_first = true
next
end
next if loc.path&.start_with?("<internal:")
return loc if yield(loc)
end
nil
end
```
@fxn 's PR:
https://github.com/ruby/ruby/blob/9c2e686719a5a4df5ea0b8a3b6a373ca6003c229/…
```ruby
frames_to_skip = 2
location = nil
Thread.each_caller_location do |cl|
if frames_to_skip >= 1
frames_to_skip -= 1
next
end
# snipp...
```
### Proposal
I think it would be very useful if `Thread.each_caller_location` accepted the same
arguments as `caller` and `caller_locations`:
```ruby
#each_caller_location(start = 1, length = nil)
#each_caller_location(range)
```
@jeremyevans0 what do you think?
--
https://bugs.ruby-lang.org/