10000 [DOC] Adjust documentation related to backtraces (#12420) · github/ruby@58460b4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 58460b4

Browse files
authored
[DOC] Adjust documentation related to backtraces (ruby#12420)
1 parent 3be1baa commit 58460b4

File tree

3 files changed

+194
-73
lines changed

3 files changed

+194
-73
lines changed

doc/exceptions.md

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ that prints a message and exits the program (or thread):
2626

2727
```console
2828
$ ruby -e "raise"
29-
-e:1:in `<main>': unhandled exception
29+
-e:1:in '<main>': unhandled exception
3030
```
3131

3232
### Rescued Exceptions
@@ -201,7 +201,7 @@ Output:
201201

202202
```
203203
#<ZeroDivisionError: divided by 0>
204-
["t.rb:2:in `/'", "t.rb:2:in `<main>'"]
204+
["t.rb:2:in 'Integer#/'", "t.rb:2:in '<main>'"]
205205
```
206206

207207
##### Cause
@@ -362,8 +362,8 @@ Output:
362362
363363
```
364364
ruby t.rb
365-
t.rb:2:in `/': divided by 0 (ZeroDivisionError)
366-
from t.rb:2:in `<main>'
365+
t.rb:2:in 'Integer#/': divided by 0 (ZeroDivisionError)
366+
from t.rb:2:in '<main>'
367367
```
368368

369369
#### Retrying
@@ -501,28 +501,21 @@ These methods return backtrace information:
501501
of Thread::Backtrace::Location objects or `nil`.
502502
Each Thread::Backtrace::Location object gives detailed information about a called method.
503503

504-
An `Exception` object stores its backtrace value as one of:
504+
By default, Ruby sets the backtrace of the exception to the location where it
505+
was raised.
505506

506-
- An array of Thread::Backtrace::Location objects;
507-
this is the common case: the exception was raised by the Ruby core or the Ruby standard library.
508-
In this case:
507+
The developer might adjust this by either providing +backtrace+ argument
508+
to Kernel#raise, or using Exception#set_backtrace.
509509

510-
- Exception#backtrace_locations returns the array of Thread::Backtrace::Location objects.
511-
- Exception#backtrace returns the array of their string values
512-
(`Exception#backtrace_locations.map {|loc| loc.to_s }`).
513-
514-
- An array of strings;
515-
this is an uncommon case: the user manually set the backtrace to an array of strings;
516-
In this case:
517-
518-
- Exception#backtrace returns the array of strings.
519-
- Exception#backtrace_locations returns `nil`.
520-
521-
- `nil`, in which case both methods return `nil`.
522-
523-
These methods set the backtrace value:
524-
525-
- Exception#set_backtrace: sets the backtrace value to an array of strings, or to `nil`.
526-
- Kernel#raise: sets the backtrace value to an array of Thread::Backtrace::Location objects,
527-
or to an array of strings.
510+
Note that:
528511

512+
- by default, both +backtrace+ and +backtrace_locations+ represent the same backtrace;
513+
- if the developer sets the backtrace by one of the above methods to an array of
514+
Thread::Backtrace::Location, they still represent the same backtrace;
515+
- if the developer sets the backtrace to a string or an array of strings:
516+
- by Kernel#raise: +backtrace_locations+ become +nil+;
517+
- by Exception#set_backtrace: +backtrace_locations+ preserve the original
518+
value;
519+
- if the developer sets the backtrace to +nil+ by Exception#set_backtrace,
520+
+backtrace_locations+ preserve the original value; but if the exception is then
521+
reraised, both +backtrace+ and +backtrace_locations+ become the location of reraise.

error.c

Lines changed: 147 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,16 +1706,16 @@ check_order_keyword(VALUE opt)
17061706
* Output:
17071707
*
17081708
* "divided by 0"
1709-
* ["t.rb:3:in `/': divided by 0 (ZeroDivisionError)",
1710-
* "\tfrom t.rb:3:in `baz'",
1711-
* "\tfrom t.rb:10:in `bar'",
1712-
* "\tfrom t.rb:11:in `foo'",
1713-
* "\tfrom t.rb:12:in `<main>'"]
1714-
* ["t.rb:3:in `/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
1715-
* "\tfrom t.rb:3:in `baz'",
1716-
* "\tfrom t.rb:10:in `bar'",
1717-
* "\tfrom t.rb:11:in `foo'",
1718-
* "\tfrom t.rb:12:in `<main>'"]
1709+
* ["t.rb:3:in 'Integer#/': divided by 0 (ZeroDivisionError)",
1710+
* "\tfrom t.rb:3:in 'Object#baz'",
1711+
* "\tfrom t.rb:10:in 'Object#bar'",
1712+
* "\tfrom t.rb:11:in 'Object#foo'",
1713+
* "\tfrom t.rb:12:in '<main>'"]
1714+
* ["t.rb:3:in 'Integer#/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
1715+
* "\tfrom t.rb:3:in 'Object#baz'",
1716+
* "\tfrom t.rb:10:in 'Object#bar'",
1717+
* "\tfrom t.rb:11:in 'Object#foo'",
1718+
* "\tfrom t.rb:12:in '<main>'"]
17191719
*
17201720
* An overriding method should be careful with ANSI code enhancements;
17211721
* see {Messages}[rdoc-ref:exceptions.md@Messages].
@@ -1864,26 +1864,31 @@ exc_inspect(VALUE exc)
18641864
* call-seq:
18651865
* backtrace -> array or nil
18661866
*
1867-
* Returns a backtrace value for +self+;
1868-
* the returned value depends on the form of the stored backtrace value:
1867+
* Returns the backtrace (the list of code locations that led to the exception),
1868+
* as an array of strings.
18691869
*
1870-
* - \Array of Thread::Backtrace::Location objects:
1871-
* returns the array of strings given by
1872-
* <tt>Exception#backtrace_locations.map {|loc| loc.to_s }</tt>.
1873-
* This is the normal case, where the backtrace value was stored by Kernel#raise.
1874-
* - \Array of strings: returns that array.
1875-
* This is the unusual case, where the backtrace value was explicitly
1876-
* stored as an array of strings.
1877-
* - +nil+: returns +nil+.
1870+
* Example (assuming the code is stored in the file named <tt>t.rb</tt>):
18781871
*
1879-
* Example:
1872+
* def division(numerator, denominator)
1873+
* numerator / denominator
1874+
* end
18801875
*
18811876
* begin
1882-
* 1 / 0
1883-
* rescue => x
1884-
* x.backtrace.take(2)
1877+
* division(1, 0)
1878+
* rescue => ex
1879+
* p ex.backtrace
1880+
* # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
1881+
* loc = ex.backtrace.first
1882+
* p loc.class
1883+
* # String
18851884
* end
1886-
* # => ["(irb):132:in `/'", "(irb):132:in `<top (required)>'"]
1885+
*
1886+
* The value returned by this method migth be adjusted when raising (see Kernel#raise),
1887+
* or during intermediate handling by #set_backtrace.
1888+
*
1889+
* See also #backtrace_locations that provide the same value, as structured objects.
1890+
* (Note though that two values might not be consistent with each other when
1891+
* backtraces are manually adjusted.)
18871892
*
18881893
* see {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
18891894
*/
@@ -1930,20 +1935,37 @@ rb_get_backtrace(VALUE exc)
19301935
* call-seq:
19311936
* backtrace_locations -> array or nil
19321937
*
1933-
* Returns a backtrace value for +self+;
1934-
* the returned value depends on the form of the stored backtrace value:
1938+
* Returns the backtrace (the list of code locations that led to the exception),
1939+
* as an array of Thread::Backtrace::Location instances.
19351940
*
1936-
* - \Array of Thread::Backtrace::Location objects: returns that array.
1937-
* - \Array of strings or +nil+: returns +nil+.
1941+
* Example (assuming the code is stored in the file named <tt>t.rb</tt>):
19381942
*
1939-
* Example:
1943+
* def division(numerator, denominator)
1944+
* numerator / denominator
1945+
* end
19401946
*
19411947
* begin
1942-
* 1 / 0
1943-
* rescue => x
1944-
* x.backtrace_locations.take(2)
1948+
* division(1, 0)
1949+
* rescue => ex
1950+
* p ex.backtrace_locations
1951+
* # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
1952+
* loc = ex.backtrace_locations.first
1953+
* p loc.class
1954+
* # Thread::Backtrace::Location
1955+
* p loc.path
1956+
* # "t.rb"
1957+
* p loc.lineno
1958+
* # 2
1959+
* p loc.label
1960+
* # "Integer#/"
19451961
* end
1946-
* # => ["(irb):150:in `/'", "(irb):150:in `<top (required)>'"]
1962+
*
1963+
* The value returned by this method might be adjusted when raising (see Kernel#raise),
1964+
* or during intermediate handling by #set_backtrace.
1965+
*
1966+
* See also #backtrace that provide the same value as an array of strings.
1967+
* (Note though that two values might not be consistent with each other when
1968+
* backtraces are manually adjusted.)
19471969
*
19481970
* See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
19491971
*/
@@ -1985,15 +2007,100 @@ rb_check_backtrace(VALUE bt)
19852007
* call-seq:
19862008
* set_backtrace(value) -> value
19872009
*
1988-
* Sets the backtrace value for +self+; returns the given +value:
2010+
* Sets the backtrace value for +self+; returns the given +value+.
2011+
*
2012+
* The +value+ might be:
2013+
*
2014+
* - an array of Thread::Backtrace::Location;
2015+
* - an array of String instances;
2016+
* - a single String instance; or
2017+
* - +nil+.
2018+
*
2019+
* Using array of Thread::Backtrace::Location is the most consistent
2020+
* option: it sets both #backtrace and #backtrace_locations. It should be
2021+
* preferred when possible. The suitable array of locations can be obtained
2022+
* from Kernel#caller_locations, copied from another error, or just set to
2023+
* the adjusted result of the current error's #backtrace_locations:
2024+
*
2025+
* require 'json'
2026+
*
2027+
* def parse_payload(text)
2028+
* JSON.parse(text) # test.rb, line 4
2029+
* rescue JSON::ParserError => ex
2030+
* ex.set_backtrace(ex.backtrace_locations[2...])
2031+
* raise
2032+
* end
2033+
*
2034+
* parse_payload('{"wrong: "json"')
2035+
* # test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError)
2036+
* #
2037+
* # An error points to the body of parse_payload method,
2038+
* # hiding the parts of the backtrace related to the internals
2039+
* # of the "json" library
2040+
*
2041+
* # The error has both #backtace and #backtrace_locations set
2042+
* # consistently:
2043+
* begin
2044+
* parse_payload('{"wrong: "json"')
2045+
* rescue => ex
2046+
* p ex.backtrace
2047+
* # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
2048+
* p ex.backtrace_locations
2049+
* # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
2050+
* end
2051+
*
2052+
* When the desired stack of locations is not available and should
2053+
* be constructed from scratch, an array of strings or a singular
2054+
* string can be used. In this case, only #backtrace is affected:
2055+
*
2056+
* def parse_payload(text)
2057+
* JSON.parse(text)
2058+
* rescue JSON::ParserError => ex
2059+
* ex.set_backtrace(["dsl.rb:34", "framework.rb:1"])
2060+
* # The error have the new value in #backtrace:
2061+
* p ex.backtrace
2062+
* # ["dsl.rb:34", "framework.rb:1"]
2063+
*
2064+
* # but the original one in #backtrace_locations
2065+
* p ex.backtrace_locations
2066+
* # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
2067+
* end
2068+
*
2069+
* parse_payload('{"wrong: "json"')
19892070
*
1990-
* x = RuntimeError.new('Boom')
1991-
* x.set_backtrace(%w[foo bar baz]) # => ["foo", "bar", "baz"]
1992-
* x.backtrace # => ["foo", "bar", "baz"]
2071+
* Calling #set_backtrace with +nil+ clears up #backtrace but doesn't affect
2072+
* #backtrace_locations:
19932073
*
1994-
* The given +value+ must be an array of strings, a single string, or +nil+.
2074+
* def parse_payload(text)
2075+
* JSON.parse(text)
2076+
* rescue JSON::ParserError => ex
2077+
* ex.set_backtrace(nil)
2078+
* p ex.backtrace
2079+
* # nil
2080+
* p ex.backtrace_locations
2081+
* # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
2082+
* end
2083+
*
2084+
* parse_payload('{"wrong: "json"')
19952085
*
1996-
* Does not affect the value returned by #backtrace_locations.
2086+
* On reraising of such an exception, both #backtrace and #backtrace_locations
2087+
* is set to the place of reraising:
2088+
*
2089+
* def parse_payload(text)
2090+
* JSON.parse(text)
2091+
* rescue JSON::ParserError => ex
2092+
* ex.set_backtrace(nil)
2093+
* raise # test.rb, line 7
2094+
* end
2095+
*
2096+
* begin
2097+
* parse_payload('{"wrong: "json"')
2098+
* rescue => ex
2099+
* p ex.backtrace
2100+
* # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
2101+
* p ex.backtrace_locations
2102+
* # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
2103+
* end
19972104
*
19982105
* See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
19992106
*/

eval.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -782,16 +782,37 @@ rb_f_raise(int argc, VALUE *argv)
782782
*
783783
* See {Messages}[rdoc-ref:exceptions.md@Messages].
784784
*
785-
* Argument +backtrace+ sets the stored backtrace in the new exception,
786-
* which may be retrieved by method Exception#backtrace;
787-
* the backtrace must be an array of strings or +nil+:
785+
* Argument +backtrace+ might be used to modify the backtrace of the new exception,
786+
* as reported by Exception#backtrace and Exception#backtrace_locations;
787+
* the backtrace must be an array of Thread::Backtrace::Location, an array of
788+
* strings, a single string, or +nil+.
789+
*
790+
* Using the array of Thread::Backtrace::Location instances is the most consistent option
791+
* and should be preferred when possible. The necessary value might be obtained
792+
* from #caller_locations, or copied from Exception#backtrace_locations of another
793+
* error:
788794
*
789795
* begin
790-
* raise(StandardError, 'Boom', %w[foo bar baz])
791-
* rescue => x
792-
* p x.backtrace
796+
* do_some_work()
797+
* rescue ZeroDivisionError => ex
798+
* raise(LogicalError, "You have an error in your math", ex.backtrace_locations)
799+
* end
800+
*
801+
* The ways, both Exception#backtrace and Exception#backtrace_locations of the
802+
* raised error are set to the same backtrace.
803+
*
804+
* When the desired stack of locations is not available and should
805+
* be constructed from scratch, an array of strings or a singular
806+
* string can be used. In this case, only Exception#backtrace is set:
807+
*
808+
* begin
809+
* raise(StandardError, 'Boom', %w[dsl.rb:3 framework.rb:1])
810+
* rescue => ex
811+
* p ex.backtrace
812+
* # => ["dsl.rb:3", "framework.rb:1"]
813+
* p ex.backtrace_locations
814+
* # => nil
793815
* end
794-
* # => ["foo", "bar", "baz"]
795816
*
796817
* If argument +backtrace+ is not given,
797818
* the backtrace is set according to an array of Thread::Backtrace::Location objects,

0 commit comments

Comments
 (0)
0