From 2b4bc409f75c07f81d62c014e10ed82366d58e32 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Fri, 14 Jan 2022 23:02:30 +0100 Subject: [PATCH 01/12] Add index hint to BoundsError message --- base/errorshow.jl | 34 ++++++++++++++++++++++++++++++++-- base/strings/string.jl | 3 +++ base/tuple.jl | 4 ++++ doc/src/manual/interfaces.md | 1 + test/arrayops.jl | 10 +++++----- test/errorshow.jl | 11 ++++++----- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 121fb50db91c1..79c7395df1177 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -32,9 +32,25 @@ showerror(io::IO, ex) = show(io, ex) show_index(io::IO, x::Any) = show(io, x) show_index(io::IO, x::Slice) = show_index(io, x.indices) show_index(io::IO, x::LogicalIndex) = summary(io, x.mask) -show_index(io::IO, x::OneTo) = print(io, "1:", x.stop) +function show_index(io::IO, x::OneTo) + if x.stop == 1 + return print(io, '1') + end + print(io, "1:", x.stop) +end show_index(io::IO, x::Colon) = print(io, ':') - +function show_index(io::IO, x::Tuple) + if length(x) == 1 + return show_index(io, only(x)) + end + print(io, '[') + show_index(io, first(x)) + for el in x[2:end] + print(io, ", ") + show_index(io, el) + end + print(io, ']') +end function showerror(io::IO, ex::BoundsError) print(io, "BoundsError") @@ -55,10 +71,24 @@ function showerror(io::IO, ex::BoundsError) end print(io, ']') end + print(io, ".\nLegal indices are ") + show_legal_indices(io, ex.a) end Experimental.show_error_hints(io, ex) end +""" + show_legal_indices(io, x) + +Describe legal ways to index `x` in human-readable form. This should be a continuation of +the sentence "Legal indices are ...". Will be shown to the user upon `BoundsError`. +""" +show_legal_indices(io::IO, x::Any) = print(io, "unknown."); +function show_legal_indices(io::IO, x::AbstractArray{<:Any}) + show_index(io, axes(x)) + print(io, '.') +end + function showerror(io::IO, ex::TypeError) print(io, "TypeError: ") if ex.expected === Bool diff --git a/base/strings/string.jl b/base/strings/string.jl index c818e2e1844fb..af61f98bde0b9 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -24,6 +24,9 @@ function Base.showerror(io::IO, exc::StringIndexError) end end end +function show_legal_indices(io::IO, s::AbstractString) + print(io, "between ", firstindex(s), " and ", thisind(s, ncodeunits(s)), '.') +end const ByteArray = Union{CodeUnits{UInt8,String}, Vector{UInt8},Vector{Int8}, FastContiguousSubArray{UInt8,1,CodeUnits{UInt8,String}}, FastContiguousSubArray{UInt8,1,Vector{UInt8}}, FastContiguousSubArray{Int8,1,Vector{Int8}}} diff --git a/base/tuple.jl b/base/tuple.jl index 5db0e40b495d3..3bd41fdbac89a 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -544,6 +544,10 @@ function in(x::Symbol, itr::Tuple{Vararg{Symbol}}) return sym_in(x, itr) end +function show_legal_indices(io::IO, x::Tuple) + show_index(io, axes(x)) + print(io, '.') +end """ empty(x::Tuple) diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index b2ac237dab1a4..38b073a6f8a29 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -242,6 +242,7 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref | `axes(A)` | `map(OneTo, size(A))` | Return a tuple of `AbstractUnitRange{<:Integer}` of valid indices | | `similar(A, ::Type{S}, inds)` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | | `similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | +| `show_legal_indices(io, A)` | `Base.show_index(io, axes(A)); print(io, '.');` | Human readable continuation of the sentence "Legal indices are ...", for the `BoundsError` message | If a type is defined as a subtype of `AbstractArray`, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See diff --git a/test/arrayops.jl b/test/arrayops.jl index abdf61e654c01..049f9889e2148 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2900,29 +2900,29 @@ end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2].\nLegal indices are [1:2, 1:2]." err = try x[10, trues(2)]; catch err; err; end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector].\nLegal indices are [1:2, 1:2]." # Also test : directly for custom types for which it may appear as-is err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :].\nLegal indices are [1:3, 1:3]." err = BoundsError(x, "bad index") showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"].\nLegal indices are [1:2, 1:2]." err = BoundsError(x, (10, "bad index")) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"].\nLegal indices are [1:2, 1:2]." end @testset "inference of Union{T,Nothing} arrays 26771" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index 72a2ebb1e9cbe..2249069b4ac0e 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -283,6 +283,7 @@ struct Bounded # not an AbstractArray end Base.getindex(b::Bounded, i) = checkindex(Bool, 1:b.bound, i) || throw(BoundsError(b, i)) Base.summary(io::IO, b::Bounded) = print(io, "$(b.bound)-size Bounded") +Base.show_legal_indices(io::IO, b::Bounded) = print(io, "1:$(b.bound).") let undefvar err_str = @except_strbt sqrt(-1) DomainError @test occursin("Try sqrt(Complex(x)).", err_str) @@ -297,17 +298,17 @@ let undefvar @test occursin("DomainError with [0.0 -1.0 …", err_str) err_str = @except_str (1, 2, 3)[4] BoundsError - @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]" + @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4].\nLegal indices are 1:3." err_str = @except_str [5, 4, 3][-2, 1] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1].\nLegal indices are 1:3." err_str = @except_str [5, 4, 3][1:5] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5].\nLegal indices are 1:3." err_str = @except_str [5, 4, 3][trues(6,7)] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix].\nLegal indices are 1:3." err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]" + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3].\nLegal indices are 1:2." err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context" From fb986ec9071ea54dd3ceb961757ededdb75163dc Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sun, 16 Jan 2022 16:08:11 +0100 Subject: [PATCH 02/12] Update tests and docs --- base/array.jl | 3 ++- base/docs/basedocs.jl | 12 ++++++++---- base/essentials.jl | 3 ++- base/strings/basic.jl | 12 ++++++++---- base/strings/io.jl | 2 +- doc/src/manual/arrays.md | 3 ++- doc/src/manual/strings.md | 6 ++++-- stdlib/Dates/docs/src/index.md | 3 ++- stdlib/Test/test/runtests.jl | 2 +- test/arrayops.jl | 2 +- 10 files changed, 31 insertions(+), 17 deletions(-) diff --git a/base/array.jl b/base/array.jl index 2fc1ccfdf7dda..bfaa176b194cf 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1343,7 +1343,8 @@ julia> popat!(a, 4, missing) missing julia> popat!(a, 4) -ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] +ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4]. +Legal indices are 1:3. [...] ``` """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 3cbe180233d9c..abaf72a036ef5 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -351,7 +351,8 @@ julia> b[1] = 2; a, b, c Assignment at out-of-bounds indices does not grow a collection. If the collection is a [`Vector`](@ref) it can instead be grown with [`push!`](@ref) or [`append!`](@ref). ```jldoctest julia> a = [1, 1]; a[3] = 2 -ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] +ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3]. +Legal indices are 1:2. [...] julia> push!(a, 2, 3) @@ -1482,17 +1483,20 @@ An indexing operation into an array, `a`, tried to access an out-of-bounds eleme julia> A = fill(1.0, 7); julia> A[8] -ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] +ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8]. +Legal indices are 1:7. julia> B = fill(1.0, (2,3)); julia> B[2, 4] -ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4] +ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4]. +Legal indices are [1:2, 1:3]. julia> B[9] -ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9] +ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9]. +Legal indices are [1:2, 1:3]. ``` """ diff --git a/base/essentials.jl b/base/essentials.jl index dd410b06cc8d9..8a2226d892da5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -523,7 +523,8 @@ julia> f1() = return g(1:2, -1); julia> f2() = @inbounds return g(1:2, -1); julia> f1() -ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] +ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1]. +Legal indices are 1:2. Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 515b836311698..1d8ccbe6b4228 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -425,11 +425,13 @@ julia> thisind("α", 3) 3 julia> thisind("α", 4) -ERROR: BoundsError: attempt to access 2-codeunit String at index [4] +ERROR: BoundsError: attempt to access 2-codeunit String at index [4]. +Legal indices are between 1 and 1. [...] julia> thisind("α", -1) -ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] +ERROR: BoundsError: attempt to access 2-codeunit String at index [-1]. +Legal indices are 1 and 1. [...] ``` """ @@ -479,7 +481,8 @@ julia> prevind("α", 1) 0 julia> prevind("α", 0) -ERROR: BoundsError: attempt to access 2-codeunit String at index [0] +ERROR: BoundsError: attempt to access 2-codeunit String at index [0]. +Legal indices are between 1 and 1. [...] julia> prevind("α", 2, 2) @@ -538,7 +541,8 @@ julia> nextind("α", 1) 3 julia> nextind("α", 3) -ERROR: BoundsError: attempt to access 2-codeunit String at index [3] +ERROR: BoundsError: attempt to access 2-codeunit String at index [3]. +Legal indices are between 1 and 1. [...] julia> nextind("α", 0, 2) diff --git a/base/strings/io.jl b/base/strings/io.jl index fffe7904ebf92..31556beb327aa 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -101,7 +101,7 @@ julia> sprint(show, 66.66666; context=:compact => true) "66.6667" julia> sprint(showerror, BoundsError([1], 100)) -"BoundsError: attempt to access 1-element Vector{Int64} at index [100]" +"BoundsError: attempt to access 1-element Vector{Int64} at index [100].\nLegal indices are 1." ``` """ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index f6e4350726269..8c5e1232fb022 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -907,7 +907,8 @@ julia> A[1, 3, 2] # Omits the fourth dimension (length 1) 19 julia> A[1, 3] # Attempts to omit dimensions 3 & 4 (lengths 2 and 1) -ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3] +ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3]. +Legal indices are [1:3, 1:4, 1:2, 1]. julia> A[19] # Linear indexing 19 diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 5628c4fa96d81..cd4c56b7779f9 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -213,11 +213,13 @@ Using an index less than `begin` (`1`) or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[begin-1] -ERROR: BoundsError: attempt to access 14-codeunit String at index [0] +ERROR: BoundsError: attempt to access 14-codeunit String at index [0]. +Legal indices are between 1 and 14. [...] julia> str[end+1] -ERROR: BoundsError: attempt to access 14-codeunit String at index [15] +ERROR: BoundsError: attempt to access 14-codeunit String at index [15]. +Legal indices are between 1 and 14. [...] ``` diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index 4975f175bbf16..02792e82f6984 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -367,7 +367,8 @@ function `dayabbr` will error. ```jldoctest tdate2 julia> Dates.dayabbr(t;locale="french") -ERROR: BoundsError: attempt to access 1-element Vector{String} at index [5] +ERROR: BoundsError: attempt to access 1-element Vector{String} at index [5]. +Legal indices are 1. Stacktrace: [...] ``` diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 260202462d81a..a7bf1ddc6925f 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -103,7 +103,7 @@ end @test endswith(sprint(show, @test_throws str->occursin("a t", str) error("a test")), "Message: \"a test\"") @test endswith(sprint(show, @test_throws ["BoundsError", "access", "1-element", "at index [2]"] [1][2]), - "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\"") + "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\\nLegal indices are 1.\"") @test_throws "\"" throw("\"") @test_throws Returns(false) throw(Returns(false)) end diff --git a/test/arrayops.jl b/test/arrayops.jl index 049f9889e2148..067d4c22c8530 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2912,7 +2912,7 @@ end err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :].\nLegal indices are [1:3, 1:3]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :].\nLegal indices are [1:2, 1:2]." err = BoundsError(x, "bad index") showerror(b, err) From 6a2a0e418332eda781762bd1407c5e9d3b1cb57c Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Wed, 19 Jan 2022 22:08:10 +0100 Subject: [PATCH 03/12] Fix doctest --- base/strings/basic.jl | 2 +- base/strings/io.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 1d8ccbe6b4228..fe5c422e77732 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -431,7 +431,7 @@ Legal indices are between 1 and 1. julia> thisind("α", -1) ERROR: BoundsError: attempt to access 2-codeunit String at index [-1]. -Legal indices are 1 and 1. +Legal indices are between 1 and 1. [...] ``` """ diff --git a/base/strings/io.jl b/base/strings/io.jl index 31556beb327aa..17605181979ee 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -101,7 +101,7 @@ julia> sprint(show, 66.66666; context=:compact => true) "66.6667" julia> sprint(showerror, BoundsError([1], 100)) -"BoundsError: attempt to access 1-element Vector{Int64} at index [100].\nLegal indices are 1." +"BoundsError: attempt to access 1-element Vector{Int64} at index [100].\\nLegal indices are 1." ``` """ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) From 93bc6b763a23fe12ad1635220487638b644b0974 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Thu, 20 Jan 2022 09:00:13 +0100 Subject: [PATCH 04/12] Fix another test typo --- stdlib/Test/test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index a7bf1ddc6925f..78ae3c93ec651 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -103,7 +103,7 @@ end @test endswith(sprint(show, @test_throws str->occursin("a t", str) error("a test")), "Message: \"a test\"") @test endswith(sprint(show, @test_throws ["BoundsError", "access", "1-element", "at index [2]"] [1][2]), - "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\\nLegal indices are 1.\"") + "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2].\\nLegal indices are 1.\"") @test_throws "\"" throw("\"") @test_throws Returns(false) throw(Returns(false)) end From 06f75255818db67f9c4dc7dc8fdf8e7f4f89f795 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 22 Jan 2022 18:23:09 +0100 Subject: [PATCH 05/12] Legal->Valid, use register_error_hint, expand docs --- base/abstractarray.jl | 6 ++++++ base/array.jl | 4 ++-- base/docs/basedocs.jl | 16 ++++++++-------- base/errorshow.jl | 28 +++++++++++++++++---------- base/essentials.jl | 4 ++-- base/strings/basic.jl | 16 ++++++++-------- base/strings/io.jl | 2 +- base/strings/string.jl | 11 ++++++++--- base/tuple.jl | 5 +++-- doc/src/manual/arrays.md | 4 ++-- doc/src/manual/interfaces.md | 35 +++++++++++++++++++++++++++++++++- doc/src/manual/strings.md | 8 ++++---- stdlib/Dates/docs/src/index.md | 4 ++-- stdlib/Test/test/runtests.jl | 2 +- test/arrayops.jl | 10 +++++----- test/errorshow.jl | 12 ++++++------ 16 files changed, 110 insertions(+), 57 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 2733b52222e37..6c4f89fdf3dfb 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -696,6 +696,12 @@ checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) +function describe_valid_indices(io::IO, a::AbstractArray{<:Any}, i=nothing) + print(io, "Valid indices are ") + show_index(io, axes(a)) + print(io, '.') +end + # check along a single dimension """ checkindex(Bool, inds::AbstractUnitRange, index) diff --git a/base/array.jl b/base/array.jl index bfaa176b194cf..2085c2b2cd988 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1343,8 +1343,8 @@ julia> popat!(a, 4, missing) missing julia> popat!(a, 4) -ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4]. -Legal indices are 1:3. +ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] +Valid indices are 1:3. [...] ``` """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index abaf72a036ef5..19de6d213b34a 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -351,8 +351,8 @@ julia> b[1] = 2; a, b, c Assignment at out-of-bounds indices does not grow a collection. If the collection is a [`Vector`](@ref) it can instead be grown with [`push!`](@ref) or [`append!`](@ref). ```jldoctest julia> a = [1, 1]; a[3] = 2 -ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3]. -Legal indices are 1:2. +ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] +Valid indices are 1:2. [...] julia> push!(a, 2, 3) @@ -1483,20 +1483,20 @@ An indexing operation into an array, `a`, tried to access an out-of-bounds eleme julia> A = fill(1.0, 7); julia> A[8] -ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8]. -Legal indices are 1:7. +ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] +Valid indices are 1:7. julia> B = fill(1.0, (2,3)); julia> B[2, 4] -ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4]. -Legal indices are [1:2, 1:3]. +ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4] +Valid indices are [1:2, 1:3]. julia> B[9] -ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9]. -Legal indices are [1:2, 1:3]. +ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9] +Valid indices are [1:2, 1:3]. ``` """ diff --git a/base/errorshow.jl b/base/errorshow.jl index 79c7395df1177..7a257722747b4 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -71,23 +71,31 @@ function showerror(io::IO, ex::BoundsError) end print(io, ']') end - print(io, ".\nLegal indices are ") - show_legal_indices(io, ex.a) end Experimental.show_error_hints(io, ex) end +Experimental.register_error_hint(BoundsError) do io, ex + print(io,'\n') + if ~isdefined(ex, :a) + return print(io, "No description of valid indices available.") + end + if isdefined(ex, :i) + return describe_valid_indices(io, ex.a, ex.i) + end + describe_valid_indices(io, ex.a) +end + """ - show_legal_indices(io, x) + describe_valid_indices(io, a, i) -Describe legal ways to index `x` in human-readable form. This should be a continuation of -the sentence "Legal indices are ...". Will be shown to the user upon `BoundsError`. +Describe valid ways to index `a` in human-readable form. This should typically be a full +sentence starting "Valid indices are ". Will be shown to the user upon `BoundsError`. + +`i` may be ignored, but could be used to determine what information to show. """ -show_legal_indices(io::IO, x::Any) = print(io, "unknown."); -function show_legal_indices(io::IO, x::AbstractArray{<:Any}) - show_index(io, axes(x)) - print(io, '.') -end +describe_valid_indices(io::IO, a, i) = print(io, "No description of valid indices available.") + function showerror(io::IO, ex::TypeError) print(io, "TypeError: ") diff --git a/base/essentials.jl b/base/essentials.jl index 8a2226d892da5..406d17c8e7837 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -523,8 +523,8 @@ julia> f1() = return g(1:2, -1); julia> f2() = @inbounds return g(1:2, -1); julia> f1() -ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1]. -Legal indices are 1:2. +ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] +Valid indices are 1:2. Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] diff --git a/base/strings/basic.jl b/base/strings/basic.jl index fe5c422e77732..be919f7651d40 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -425,13 +425,13 @@ julia> thisind("α", 3) 3 julia> thisind("α", 4) -ERROR: BoundsError: attempt to access 2-codeunit String at index [4]. -Legal indices are between 1 and 1. +ERROR: BoundsError: attempt to access 2-codeunit String at index [4] +Valid indices are between 1 and 1. [...] julia> thisind("α", -1) -ERROR: BoundsError: attempt to access 2-codeunit String at index [-1]. -Legal indices are between 1 and 1. +ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] +Valid indices are between 1 and 1. [...] ``` """ @@ -481,8 +481,8 @@ julia> prevind("α", 1) 0 julia> prevind("α", 0) -ERROR: BoundsError: attempt to access 2-codeunit String at index [0]. -Legal indices are between 1 and 1. +ERROR: BoundsError: attempt to access 2-codeunit String at index [0] +Valid indices are between 1 and 1. [...] julia> prevind("α", 2, 2) @@ -541,8 +541,8 @@ julia> nextind("α", 1) 3 julia> nextind("α", 3) -ERROR: BoundsError: attempt to access 2-codeunit String at index [3]. -Legal indices are between 1 and 1. +ERROR: BoundsError: attempt to access 2-codeunit String at index [3] +Valid indices are between 1 and 1. [...] julia> nextind("α", 0, 2) diff --git a/base/strings/io.jl b/base/strings/io.jl index 17605181979ee..d696935c4f4c7 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -101,7 +101,7 @@ julia> sprint(show, 66.66666; context=:compact => true) "66.6667" julia> sprint(showerror, BoundsError([1], 100)) -"BoundsError: attempt to access 1-element Vector{Int64} at index [100].\\nLegal indices are 1." +"BoundsError: attempt to access 1-element Vector{Int64} at index [100]\\nValid indices are 1." ``` """ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) diff --git a/base/strings/string.jl b/base/strings/string.jl index af61f98bde0b9..a8b705458f2f4 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -24,9 +24,14 @@ function Base.showerror(io::IO, exc::StringIndexError) end end end -function show_legal_indices(io::IO, s::AbstractString) - print(io, "between ", firstindex(s), " and ", thisind(s, ncodeunits(s)), '.') -end + +describe_valid_indices(io::IO, a::AbstractString, i=nothing) = print(io, + "Valid indices are between ", + firstindex(a), + " and ", + thisind(a, ncodeunits(a)), + '.' + ) const ByteArray = Union{CodeUnits{UInt8,String}, Vector{UInt8},Vector{Int8}, FastContiguousSubArray{UInt8,1,CodeUnits{UInt8,String}}, FastContiguousSubArray{UInt8,1,Vector{UInt8}}, FastContiguousSubArray{Int8,1,Vector{Int8}}} diff --git a/base/tuple.jl b/base/tuple.jl index 3bd41fdbac89a..3ec4a10ecaa14 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -544,8 +544,9 @@ function in(x::Symbol, itr::Tuple{Vararg{Symbol}}) return sym_in(x, itr) end -function show_legal_indices(io::IO, x::Tuple) - show_index(io, axes(x)) +function describe_valid_indices(io::IO, a::Tuple, i=nothing) + print(io, "Valid indices are ") + show_index(io, axes(a)) print(io, '.') end diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 8c5e1232fb022..5c3b8d0cddbeb 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -907,8 +907,8 @@ julia> A[1, 3, 2] # Omits the fourth dimension (length 1) 19 julia> A[1, 3] # Attempts to omit dimensions 3 & 4 (lengths 2 and 1) -ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3]. -Legal indices are [1:3, 1:4, 1:2, 1]. +ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3] +Valid indices are [1:3, 1:4, 1:2, 1]. julia> A[19] # Linear indexing 19 diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 38b073a6f8a29..0f002d574da79 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -242,7 +242,7 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref | `axes(A)` | `map(OneTo, size(A))` | Return a tuple of `AbstractUnitRange{<:Integer}` of valid indices | | `similar(A, ::Type{S}, inds)` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | | `similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | -| `show_legal_indices(io, A)` | `Base.show_index(io, axes(A)); print(io, '.');` | Human readable continuation of the sentence "Legal indices are ...", for the `BoundsError` message | +| `describe_valid_indices(io, A, i)` | | Human readable version of the sentence "Valid indices are ...", for the `BoundsError` message | If a type is defined as a subtype of `AbstractArray`, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See @@ -407,6 +407,39 @@ so that the `dims` argument (ordinarily a `Dims` size-tuple) can accept `Abstrac perhaps range-types `Ind` of your own design. For more information, see [Arrays with custom indices](@ref man-custom-indices). +When a user indexes into an array using an invalid index, a 'BoundsError' is +thrown. Along with this error, a hint is printed telling the user which indices +they *could* have used. If there is a more intuitive description of your type's +valid indices than the default, which uses `axes(A)`, you can define a method +to `describe_valid_indices(io::IO, A, i)`. If, for instance, we wanted to +protect our `SparseArray` from bounds errors, we could redefine `getindex`: + +```jldoctest squarevectype +julia> function Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} + any(I .> A.dims) && throw(BoundsError(A, I)) + get(A.data, I, zero(T)) + end + +julia> A[4, 3] +ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [0] +Valid indices are [1:3, 1:3]. +``` + +So far, this message is not entirely true. We implemented a check for indices +above the maxima, but `A[0, 0]` will happily evaluate. If this is by intention, +we can update the error message: + +```jldoctest squarevectype +julia> Base.describe_valid_indices(io::IO, A::SparseArray, i=nothing) = print(io, "Valid indices are <= ", A.dims, '.') + +julia> A[0, 0] +0.0 + +julia> A[4, 3] +ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] +Valid indices are <= (3, 3). +``` + ## [Strided Arrays](@id man-interface-strided-arrays) | Methods to implement | | Brief description | diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index cd4c56b7779f9..20d930cb53a3f 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -213,13 +213,13 @@ Using an index less than `begin` (`1`) or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[begin-1] -ERROR: BoundsError: attempt to access 14-codeunit String at index [0]. -Legal indices are between 1 and 14. +ERROR: BoundsError: attempt to access 14-codeunit String at index [0] +Valid indices are between 1 and 14. [...] julia> str[end+1] -ERROR: BoundsError: attempt to access 14-codeunit String at index [15]. -Legal indices are between 1 and 14. +ERROR: BoundsError: attempt to access 14-codeunit String at index [15] +Valid indices are between 1 and 14. [...] ``` diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index 02792e82f6984..ad711ce83b963 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -367,8 +367,8 @@ function `dayabbr` will error. ```jldoctest tdate2 julia> Dates.dayabbr(t;locale="french") -ERROR: BoundsError: attempt to access 1-element Vector{String} at index [5]. -Legal indices are 1. +ERROR: BoundsError: attempt to access 1-element Vector{String} at index [5] +Valid indices are 1. Stacktrace: [...] ``` diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 78ae3c93ec651..a601eb25265cc 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -103,7 +103,7 @@ end @test endswith(sprint(show, @test_throws str->occursin("a t", str) error("a test")), "Message: \"a test\"") @test endswith(sprint(show, @test_throws ["BoundsError", "access", "1-element", "at index [2]"] [1][2]), - "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2].\\nLegal indices are 1.\"") + "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\\nValid indices are 1.\"") @test_throws "\"" throw("\"") @test_throws Returns(false) throw(Returns(false)) end diff --git a/test/arrayops.jl b/test/arrayops.jl index 067d4c22c8530..5ad00ca057433 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2900,29 +2900,29 @@ end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2].\nLegal indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]\nValid indices are [1:2, 1:2]." err = try x[10, trues(2)]; catch err; err; end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector].\nLegal indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]\nValid indices are [1:2, 1:2]." # Also test : directly for custom types for which it may appear as-is err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :].\nLegal indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]\nValid indices are [1:2, 1:2]." err = BoundsError(x, "bad index") showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"].\nLegal indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]\nValid indices are [1:2, 1:2]." err = BoundsError(x, (10, "bad index")) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"].\nLegal indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]\nValid indices are [1:2, 1:2]." end @testset "inference of Union{T,Nothing} arrays 26771" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index 2249069b4ac0e..ca1f3aae03564 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -283,7 +283,7 @@ struct Bounded # not an AbstractArray end Base.getindex(b::Bounded, i) = checkindex(Bool, 1:b.bound, i) || throw(BoundsError(b, i)) Base.summary(io::IO, b::Bounded) = print(io, "$(b.bound)-size Bounded") -Base.show_legal_indices(io::IO, b::Bounded) = print(io, "1:$(b.bound).") +Base.describe_valid_indices(io::IO, b::Bounded, i=nothing) = print(io, "Valid indices are 1:$(b.bound).") let undefvar err_str = @except_strbt sqrt(-1) DomainError @test occursin("Try sqrt(Complex(x)).", err_str) @@ -298,17 +298,17 @@ let undefvar @test occursin("DomainError with [0.0 -1.0 …", err_str) err_str = @except_str (1, 2, 3)[4] BoundsError - @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4].\nLegal indices are 1:3." + @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][-2, 1] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1].\nLegal indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][1:5] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5].\nLegal indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][trues(6,7)] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix].\nLegal indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]\nValid indices are 1:3." err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3].\nLegal indices are 1:2." + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]\nValid indices are 1:2." err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context" From 30e07e2dd98d66345f8d10199a80e4ccfa48f071 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 22 Jan 2022 20:10:42 +0100 Subject: [PATCH 06/12] Doc typo --- doc/src/manual/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 0f002d574da79..d35e474905210 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -421,7 +421,7 @@ julia> function Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} end julia> A[4, 3] -ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [0] +ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] Valid indices are [1:3, 1:3]. ``` From d2c850c40bbaf31876caa0a3148f130d6bc2e74b Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Thu, 12 May 2022 14:17:04 +0200 Subject: [PATCH 07/12] Remove error hint for one-to arrays --- base/abstractarray.jl | 3 +++ base/array.jl | 1 - base/docs/basedocs.jl | 4 ---- base/essentials.jl | 1 - doc/src/manual/interfaces.md | 1 - test/arrayops.jl | 10 +++++----- test/errorshow.jl | 8 ++++---- 7 files changed, 12 insertions(+), 16 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 6c4f89fdf3dfb..3991ddcdb1762 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -697,6 +697,9 @@ checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) function describe_valid_indices(io::IO, a::AbstractArray{<:Any}, i=nothing) + if all(x->isa(axes(x), OneTo), a) + return nothing + end print(io, "Valid indices are ") show_index(io, axes(a)) print(io, '.') diff --git a/base/array.jl b/base/array.jl index 2085c2b2cd988..2fc1ccfdf7dda 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1344,7 +1344,6 @@ missing julia> popat!(a, 4) ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] -Valid indices are 1:3. [...] ``` """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 19de6d213b34a..3cbe180233d9c 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -352,7 +352,6 @@ Assignment at out-of-bounds indices does not grow a collection. If the collectio ```jldoctest julia> a = [1, 1]; a[3] = 2 ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] -Valid indices are 1:2. [...] julia> push!(a, 2, 3) @@ -1484,19 +1483,16 @@ julia> A = fill(1.0, 7); julia> A[8] ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] -Valid indices are 1:7. julia> B = fill(1.0, (2,3)); julia> B[2, 4] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4] -Valid indices are [1:2, 1:3]. julia> B[9] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9] -Valid indices are [1:2, 1:3]. ``` """ diff --git a/base/essentials.jl b/base/essentials.jl index 406d17c8e7837..dd410b06cc8d9 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -524,7 +524,6 @@ julia> f2() = @inbounds return g(1:2, -1); julia> f1() ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] -Valid indices are 1:2. Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index d35e474905210..7eb1e72c03d3d 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -422,7 +422,6 @@ julia> function Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} julia> A[4, 3] ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] -Valid indices are [1:3, 1:3]. ``` So far, this message is not entirely true. We implemented a check for indices diff --git a/test/arrayops.jl b/test/arrayops.jl index 5ad00ca057433..abdf61e654c01 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2900,29 +2900,29 @@ end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]" err = try x[10, trues(2)]; catch err; err; end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]" # Also test : directly for custom types for which it may appear as-is err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]" err = BoundsError(x, "bad index") showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]" err = BoundsError(x, (10, "bad index")) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]" end @testset "inference of Union{T,Nothing} arrays 26771" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index ca1f3aae03564..b0fd478cba1d0 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -301,14 +301,14 @@ let undefvar @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][-2, 1] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]" err_str = @except_str [5, 4, 3][1:5] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]" err_str = @except_str [5, 4, 3][trues(6,7)] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]" err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]\nValid indices are 1:2." + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]" err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context" From c1eb7e9d7fb9b3b51d32c1c726c0af8511be4418 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 7 Jan 2023 11:12:58 +0100 Subject: [PATCH 08/12] Revert "Remove error hint for one-to arrays" This reverts commit d2c850c40bbaf31876caa0a3148f130d6bc2e74b. --- base/abstractarray.jl | 3 --- base/array.jl | 1 + base/docs/basedocs.jl | 4 ++++ base/essentials.jl | 1 + doc/src/manual/interfaces.md | 1 + test/arrayops.jl | 10 +++++----- test/errorshow.jl | 6 +++--- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 1bd49d983aeb8..8c28a3f02b32b 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -744,9 +744,6 @@ checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) function describe_valid_indices(io::IO, a::AbstractArray{<:Any}, i=nothing) - if all(x->isa(axes(x), OneTo), a) - return nothing - end print(io, "Valid indices are ") show_index(io, axes(a)) print(io, '.') diff --git a/base/array.jl b/base/array.jl index 694a3913cacf4..f43e5cf35958e 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1365,6 +1365,7 @@ missing julia> popat!(a, 4) ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] +Valid indices are 1:3. [...] ``` """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 3e86095f181a9..8833fa0423e3f 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -401,6 +401,7 @@ Assignment at out-of-bounds indices does not grow a collection. If the collectio ```jldoctest julia> a = [1, 1]; a[3] = 2 ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] +Valid indices are 1:2. [...] julia> push!(a, 2, 3) @@ -1675,16 +1676,19 @@ julia> A = fill(1.0, 7); julia> A[8] ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] +Valid indices are 1:7. julia> B = fill(1.0, (2,3)); julia> B[2, 4] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4] +Valid indices are [1:2, 1:3]. julia> B[9] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9] +Valid indices are [1:2, 1:3]. ``` """ diff --git a/base/essentials.jl b/base/essentials.jl index 2093c792dd9b4..0b9da7088da95 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -608,6 +608,7 @@ julia> f2() = @inbounds return g(1:2, -1); julia> f1() ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] +Valid indices are 1:2. Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 88b40e0a06275..5a297eb1f0495 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -416,6 +416,7 @@ julia> function Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} julia> A[4, 3] ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] +Valid indices are [1:3, 1:3]. ``` So far, this message is not entirely true. We implemented a check for indices diff --git a/test/arrayops.jl b/test/arrayops.jl index e7ac6a1132568..fbb4b183edb52 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3020,29 +3020,29 @@ end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]\nValid indices are [1:2, 1:2]." err = try x[10, trues(2)]; catch err; err; end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]\nValid indices are [1:2, 1:2]." # Also test : directly for custom types for which it may appear as-is err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]\nValid indices are [1:2, 1:2]." err = BoundsError(x, "bad index") showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]\nValid indices are [1:2, 1:2]." err = BoundsError(x, (10, "bad index")) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]" + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]\nValid indices are [1:2, 1:2]." end @testset "inference of Union{T,Nothing} arrays 26771" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index 64ef4afc887d3..35db0c7bf9a81 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -307,11 +307,11 @@ let undefvar @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][-2, 1] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][1:5] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]\nValid indices are 1:3." err_str = @except_str [5, 4, 3][trues(6,7)] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]" + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]\nValid indices are 1:3." err_str = @except_str Bounded(2)[3] BoundsError @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]" From cb939a31906f79dc436d4bd54c08cad494924ed6 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 7 Jan 2023 11:08:32 +0100 Subject: [PATCH 09/12] More newlines --- base/abstractarray.jl | 2 +- base/errorshow.jl | 7 +++---- base/strings/string.jl | 2 +- base/tuple.jl | 2 +- test/errorshow.jl | 4 ++-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 8c28a3f02b32b..73ccbf533c8e9 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -744,7 +744,7 @@ checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) function describe_valid_indices(io::IO, a::AbstractArray{<:Any}, i=nothing) - print(io, "Valid indices are ") + print(io, "\nValid indices are ") show_index(io, axes(a)) print(io, '.') end diff --git a/base/errorshow.jl b/base/errorshow.jl index a23f9f609c1a8..18d87ea9c3365 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -76,9 +76,8 @@ function showerror(io::IO, ex::BoundsError) end Experimental.register_error_hint(BoundsError) do io, ex - print(io,'\n') if ~isdefined(ex, :a) - return print(io, "No description of valid indices available.") + return print(io, "\nNo description of valid indices available.") end if isdefined(ex, :i) return describe_valid_indices(io, ex.a, ex.i) @@ -90,11 +89,11 @@ end describe_valid_indices(io, a, i) Describe valid ways to index `a` in human-readable form. This should typically be a full -sentence starting "Valid indices are ". Will be shown to the user upon `BoundsError`. +sentence starting "\nValid indices are ". Will be shown to the user upon `BoundsError`. `i` may be ignored, but could be used to determine what information to show. """ -describe_valid_indices(io::IO, a, i) = print(io, "No description of valid indices available.") +describe_valid_indices(io::IO, a, i) = print(io, "\nNo description of valid indices available.") function showerror(io::IO, ex::TypeError) diff --git a/base/strings/string.jl b/base/strings/string.jl index 6c44aae3ae56a..4becc9ccc4741 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -28,7 +28,7 @@ function Base.showerror(io::IO, exc::StringIndexError) end describe_valid_indices(io::IO, a::AbstractString, i=nothing) = print(io, - "Valid indices are between ", + "\nValid indices are between ", firstindex(a), " and ", thisind(a, ncodeunits(a)), diff --git a/base/tuple.jl b/base/tuple.jl index e6434c1d06501..0a06e66c083ce 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -592,7 +592,7 @@ end in(x::Symbol, @nospecialize itr::Tuple{Vararg{Symbol}}) = sym_in(x, itr) function describe_valid_indices(io::IO, a::Tuple, i=nothing) - print(io, "Valid indices are ") + print(io, "\nValid indices are ") show_index(io, axes(a)) print(io, '.') end diff --git a/test/errorshow.jl b/test/errorshow.jl index 35db0c7bf9a81..f8845e1459047 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -289,7 +289,7 @@ struct Bounded # not an AbstractArray end Base.getindex(b::Bounded, i) = checkindex(Bool, 1:b.bound, i) || throw(BoundsError(b, i)) Base.summary(io::IO, b::Bounded) = print(io, "$(b.bound)-size Bounded") -Base.describe_valid_indices(io::IO, b::Bounded, i=nothing) = print(io, "Valid indices are 1:$(b.bound).") +Base.describe_valid_indices(io::IO, b::Bounded, i=nothing) = print(io, "\nValid indices are 1:$(b.bound).") let undefvar err_str = @except_strbt sqrt(-1) DomainError @test occursin("Try sqrt(Complex(x)).", err_str) @@ -314,7 +314,7 @@ let undefvar @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]\nValid indices are 1:3." err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]" + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]\nValid indices are 1:2." err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context" From 3a161bf2a2395b96666fc0701f3ed8cf002ca3c3 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 7 Jan 2023 16:38:55 +0100 Subject: [PATCH 10/12] Remove "between 1 and 1" --- base/strings/basic.jl | 8 ++++---- base/strings/string.jl | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index b6a8e4427c464..d0ac8848c72e6 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -429,12 +429,12 @@ julia> thisind("α", 3) julia> thisind("α", 4) ERROR: BoundsError: attempt to access 2-codeunit String at index [4] -Valid indices are between 1 and 1. +Valid indices are 1. [...] julia> thisind("α", -1) ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] -Valid indices are between 1 and 1. +Valid indices are 1. [...] ``` """ @@ -485,7 +485,7 @@ julia> prevind("α", 1) julia> prevind("α", 0) ERROR: BoundsError: attempt to access 2-codeunit String at index [0] -Valid indices are between 1 and 1. +Valid indices are 1. [...] julia> prevind("α", 2, 2) @@ -545,7 +545,7 @@ julia> nextind("α", 1) julia> nextind("α", 3) ERROR: BoundsError: attempt to access 2-codeunit String at index [3] -Valid indices are between 1 and 1. +Valid indices are 1. [...] julia> nextind("α", 0, 2) diff --git a/base/strings/string.jl b/base/strings/string.jl index 4becc9ccc4741..21fa8ac0728f1 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -27,13 +27,21 @@ function Base.showerror(io::IO, exc::StringIndexError) end end -describe_valid_indices(io::IO, a::AbstractString, i=nothing) = print(io, - "\nValid indices are between ", - firstindex(a), - " and ", - thisind(a, ncodeunits(a)), - '.' - ) +function describe_valid_indices(io::IO, a::AbstractString, i=nothing) + firstind = firstindex(a) + lastind = thisind(a, ncodeunits(a)) + if firstind == lastind + print(io, "\nValid indices are ", firstind, '.') + return + end + print(io, + "\nValid indices are between ", + firstindex(a), + " and ", + thisind(a, ncodeunits(a)), + '.' + ) +end const ByteArray = Union{CodeUnits{UInt8,String}, Vector{UInt8},Vector{Int8}, FastContiguousSubArray{UInt8,1,CodeUnits{UInt8,String}}, FastContiguousSubArray{UInt8,1,Vector{UInt8}}, FastContiguousSubArray{Int8,1,Vector{Int8}}} From 2c880d6e86fac2d5e8a96dc5b69899d9e354c6ef Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sat, 7 Jan 2023 16:39:12 +0100 Subject: [PATCH 11/12] Docstring typo --- doc/src/manual/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 5a297eb1f0495..c5ac7f8fac7a3 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -424,7 +424,7 @@ above the maxima, but `A[0, 0]` will happily evaluate. If this is by intention, we can update the error message: ```jldoctest squarevectype -julia> Base.describe_valid_indices(io::IO, A::SparseArray, i=nothing) = print(io, "Valid indices are <= ", A.dims, '.') +julia> Base.describe_valid_indices(io::IO, A::SparseArray, i=nothing) = print(io, "\nValid indices are <= ", A.dims, '.') julia> A[0, 0] 0.0 From 61b7d15d52d2e4e92b1900c5ca1a3decec73cf12 Mon Sep 17 00:00:00 2001 From: David Gustavsson Date: Sun, 8 Jan 2023 21:24:29 +0100 Subject: [PATCH 12/12] Fewer index hints, special-case 0 --- base/abstractarray.jl | 8 +++++--- base/array.jl | 1 - base/docs/basedocs.jl | 4 ---- base/errorshow.jl | 22 +++++----------------- base/essentials.jl | 1 - base/strings/basic.jl | 6 +----- base/strings/io.jl | 2 +- base/strings/string.jl | 11 ++--------- base/tuple.jl | 6 +----- doc/src/manual/arrays.md | 1 - doc/src/manual/interfaces.md | 16 +++++++--------- doc/src/manual/strings.md | 4 +--- stdlib/Dates/docs/src/index.md | 1 - stdlib/Test/test/runtests.jl | 2 +- test/arrayops.jl | 10 +++++----- test/errorshow.jl | 12 ++++++------ 16 files changed, 35 insertions(+), 72 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 73ccbf533c8e9..36c6d55700544 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -744,9 +744,11 @@ checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) function describe_valid_indices(io::IO, a::AbstractArray{<:Any}, i=nothing) - print(io, "\nValid indices are ") - show_index(io, axes(a)) - print(io, '.') + if any(x->!isa(x, OneTo), axes(a)) + print(io, ", valid indices are ") + return show_index(io, axes(a)) + end + any(iszero, i) && print(io, ", by default indices start from 1") end # check along a single dimension diff --git a/base/array.jl b/base/array.jl index f43e5cf35958e..694a3913cacf4 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1365,7 +1365,6 @@ missing julia> popat!(a, 4) ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] -Valid indices are 1:3. [...] ``` """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 8833fa0423e3f..3e86095f181a9 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -401,7 +401,6 @@ Assignment at out-of-bounds indices does not grow a collection. If the collectio ```jldoctest julia> a = [1, 1]; a[3] = 2 ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] -Valid indices are 1:2. [...] julia> push!(a, 2, 3) @@ -1676,19 +1675,16 @@ julia> A = fill(1.0, 7); julia> A[8] ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] -Valid indices are 1:7. julia> B = fill(1.0, (2,3)); julia> B[2, 4] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [2, 4] -Valid indices are [1:2, 1:3]. julia> B[9] ERROR: BoundsError: attempt to access 2×3 Matrix{Float64} at index [9] -Valid indices are [1:2, 1:3]. ``` """ diff --git a/base/errorshow.jl b/base/errorshow.jl index 18d87ea9c3365..bbe4d4fcdbc58 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -32,17 +32,9 @@ showerror(io::IO, ex) = show(io, ex) show_index(io::IO, x::Any) = show(io, x) show_index(io::IO, x::Slice) = show_index(io, x.indices) show_index(io::IO, x::LogicalIndex) = summary(io, x.mask) -function show_index(io::IO, x::OneTo) - if x.stop == 1 - return print(io, '1') - end - print(io, "1:", x.stop) -end +show_index(io::IO, x::OneTo) = print(io, "1:", x.stop) show_index(io::IO, x::Colon) = print(io, ':') function show_index(io::IO, x::Tuple) - if length(x) == 1 - return show_index(io, only(x)) - end print(io, '[') show_index(io, first(x)) for el in x[2:end] @@ -76,12 +68,8 @@ function showerror(io::IO, ex::BoundsError) end Experimental.register_error_hint(BoundsError) do io, ex - if ~isdefined(ex, :a) - return print(io, "\nNo description of valid indices available.") - end - if isdefined(ex, :i) - return describe_valid_indices(io, ex.a, ex.i) - end + isdefined(ex, :a) || return nothing + isdefined(ex, :i) && return describe_valid_indices(io, ex.a, ex.i) describe_valid_indices(io, ex.a) end @@ -89,11 +77,11 @@ end describe_valid_indices(io, a, i) Describe valid ways to index `a` in human-readable form. This should typically be a full -sentence starting "\nValid indices are ". Will be shown to the user upon `BoundsError`. +clause starting ", valid indices are ". Will be shown to the user upon `BoundsError`. `i` may be ignored, but could be used to determine what information to show. """ -describe_valid_indices(io::IO, a, i) = print(io, "\nNo description of valid indices available.") +describe_valid_indices(io::IO, a, i) = nothing function showerror(io::IO, ex::TypeError) diff --git a/base/essentials.jl b/base/essentials.jl index 0b9da7088da95..2093c792dd9b4 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -608,7 +608,6 @@ julia> f2() = @inbounds return g(1:2, -1); julia> f1() ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] -Valid indices are 1:2. Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] diff --git a/base/strings/basic.jl b/base/strings/basic.jl index d0ac8848c72e6..eecb80823eb9e 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -429,12 +429,10 @@ julia> thisind("α", 3) julia> thisind("α", 4) ERROR: BoundsError: attempt to access 2-codeunit String at index [4] -Valid indices are 1. [...] julia> thisind("α", -1) ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] -Valid indices are 1. [...] ``` """ @@ -484,8 +482,7 @@ julia> prevind("α", 1) 0 julia> prevind("α", 0) -ERROR: BoundsError: attempt to access 2-codeunit String at index [0] -Valid indices are 1. +ERROR: BoundsError: attempt to access 2-codeunit String at index [0], by default indices start from 1 [...] julia> prevind("α", 2, 2) @@ -545,7 +542,6 @@ julia> nextind("α", 1) julia> nextind("α", 3) ERROR: BoundsError: attempt to access 2-codeunit String at index [3] -Valid indices are 1. [...] julia> nextind("α", 0, 2) diff --git a/base/strings/io.jl b/base/strings/io.jl index 875b3b455a25f..e800002076d54 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -101,7 +101,7 @@ julia> sprint(show, 66.66666; context=:compact => true) "66.6667" julia> sprint(showerror, BoundsError([1], 100)) -"BoundsError: attempt to access 1-element Vector{Int64} at index [100]\\nValid indices are 1." +"BoundsError: attempt to access 1-element Vector{Int64} at index [100]" ``` """ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) diff --git a/base/strings/string.jl b/base/strings/string.jl index 21fa8ac0728f1..2cef69d7aa64d 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -28,18 +28,11 @@ function Base.showerror(io::IO, exc::StringIndexError) end function describe_valid_indices(io::IO, a::AbstractString, i=nothing) - firstind = firstindex(a) - lastind = thisind(a, ncodeunits(a)) - if firstind == lastind - print(io, "\nValid indices are ", firstind, '.') - return - end print(io, - "\nValid indices are between ", + ", valid indices are between ", firstindex(a), " and ", - thisind(a, ncodeunits(a)), - '.' + lastindex(a) ) end diff --git a/base/tuple.jl b/base/tuple.jl index 0a06e66c083ce..ac83b0b01fa95 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -591,11 +591,7 @@ function sym_in(x::Symbol, @nospecialize itr::Tuple{Vararg{Symbol}}) end in(x::Symbol, @nospecialize itr::Tuple{Vararg{Symbol}}) = sym_in(x, itr) -function describe_valid_indices(io::IO, a::Tuple, i=nothing) - print(io, "\nValid indices are ") - show_index(io, axes(a)) - print(io, '.') -end +describe_valid_indices(io::IO, a::Tuple, i=nothing) = iszero(i) && return print(io, ", tuple indices start from 1") """ empty(x::Tuple) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 8462bfec05356..f9e60d83ff052 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -920,7 +920,6 @@ julia> A[1, 3, 2] # Omits the fourth dimension (length 1) julia> A[1, 3] # Attempts to omit dimensions 3 & 4 (lengths 2 and 1) ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3] -Valid indices are [1:3, 1:4, 1:2, 1]. julia> A[19] # Linear indexing 19 diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index c5ac7f8fac7a3..9b41afaffac9a 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -402,10 +402,10 @@ perhaps range-types `Ind` of your own design. For more information, see [Arrays with custom indices](@ref man-custom-indices). When a user indexes into an array using an invalid index, a 'BoundsError' is -thrown. Along with this error, a hint is printed telling the user which indices -they *could* have used. If there is a more intuitive description of your type's -valid indices than the default, which uses `axes(A)`, you can define a method -to `describe_valid_indices(io::IO, A, i)`. If, for instance, we wanted to +thrown. Along with this error, a hint can be printed telling the user which +indices they *could* have used. If there is a more intuitive description of your +type's valid indices than the default, which uses `axes(A)`, you can define a +method to `describe_valid_indices(io::IO, A, i)`. If, for instance, we wanted to protect our `SparseArray` from bounds errors, we could redefine `getindex`: ```jldoctest squarevectype @@ -416,22 +416,20 @@ julia> function Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} julia> A[4, 3] ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] -Valid indices are [1:3, 1:3]. ``` -So far, this message is not entirely true. We implemented a check for indices +This message could be made more informative. We implemented a check for indices above the maxima, but `A[0, 0]` will happily evaluate. If this is by intention, we can update the error message: ```jldoctest squarevectype -julia> Base.describe_valid_indices(io::IO, A::SparseArray, i=nothing) = print(io, "\nValid indices are <= ", A.dims, '.') +julia> Base.describe_valid_indices(io::IO, A::SparseArray, i=nothing) = print(io, ", valid indices are <= ", A.dims) julia> A[0, 0] 0.0 julia> A[4, 3] -ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3] -Valid indices are <= (3, 3). +ERROR: BoundsError: attempt to access 3×3 SparseArray{Float64, 2} at index [4, 3], valid indices are <= (3, 3) ``` ## [Strided Arrays](@id man-interface-strided-arrays) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index add6403186851..674a8c79fa406 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -213,13 +213,11 @@ Using an index less than `begin` (`1`) or greater than `end` raises an error: ```jldoctest helloworldstring julia> str[begin-1] -ERROR: BoundsError: attempt to access 14-codeunit String at index [0] -Valid indices are between 1 and 14. +ERROR: BoundsError: attempt to access 14-codeunit String at index [0], valid indices are between 1 and 14 [...] julia> str[end+1] ERROR: BoundsError: attempt to access 14-codeunit String at index [15] -Valid indices are between 1 and 14. [...] ``` diff --git a/stdlib/Dates/docs/src/index.md b/stdlib/Dates/docs/src/index.md index c2d355daa36ab..e0e09a919a085 100644 --- a/stdlib/Dates/docs/src/index.md +++ b/stdlib/Dates/docs/src/index.md @@ -366,7 +366,6 @@ function `dayabbr` will throw an error. ```jldoctest tdate2 julia> Dates.dayabbr(t;locale="french") ERROR: BoundsError: attempt to access 1-element Vector{String} at index [5] -Valid indices are 1. Stacktrace: [...] ``` diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 265fa8bbbcde0..ac643e0ccfca2 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -103,7 +103,7 @@ end @test endswith(sprint(show, @test_throws str->occursin("a t", str) error("a test")), "Message: \"a test\"") @test endswith(sprint(show, @test_throws ["BoundsError", "access", "1-element", "at index [2]"] [1][2]), - "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\\nValid indices are 1.\"") + "Message: \"BoundsError: attempt to access 1-element Vector{$Int} at index [2]\"") @test_throws "\"" throw("\"") @test_throws Returns(false) throw(Returns(false)) end diff --git a/test/arrayops.jl b/test/arrayops.jl index fbb4b183edb52..e7ac6a1132568 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3020,29 +3020,29 @@ end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 1:2]" err = try x[10, trues(2)]; catch err; err; end b = IOBuffer() showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, 2-element BitVector]" # Also test : directly for custom types for which it may appear as-is err = BoundsError(x, (10, :)) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, :]" err = BoundsError(x, "bad index") showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [\"bad index\"]" err = BoundsError(x, (10, "bad index")) showerror(b, err) @test String(take!(b)) == - "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]\nValid indices are [1:2, 1:2]." + "BoundsError: attempt to access 2×2 Matrix{Float64} at index [10, \"bad index\"]" end @testset "inference of Union{T,Nothing} arrays 26771" begin diff --git a/test/errorshow.jl b/test/errorshow.jl index f8845e1459047..bd9e3b1608901 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -289,7 +289,7 @@ struct Bounded # not an AbstractArray end Base.getindex(b::Bounded, i) = checkindex(Bool, 1:b.bound, i) || throw(BoundsError(b, i)) Base.summary(io::IO, b::Bounded) = print(io, "$(b.bound)-size Bounded") -Base.describe_valid_indices(io::IO, b::Bounded, i=nothing) = print(io, "\nValid indices are 1:$(b.bound).") +Base.describe_valid_indices(io::IO, b::Bounded, i=nothing) = print(io, ", valid indices are 1:$(b.bound)") let undefvar err_str = @except_strbt sqrt(-1) DomainError @test occursin("Try sqrt(Complex(x)).", err_str) @@ -304,17 +304,17 @@ let undefvar @test occursin("DomainError with [0.0 -1.0 …", err_str) err_str = @except_str (1, 2, 3)[4] BoundsError - @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access Tuple{$Int, $Int, $Int} at index [4]" err_str = @except_str [5, 4, 3][-2, 1] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [-2, 1]" err_str = @except_str [5, 4, 3][1:5] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [1:5]" err_str = @except_str [5, 4, 3][trues(6,7)] BoundsError - @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]\nValid indices are 1:3." + @test err_str == "BoundsError: attempt to access 3-element Vector{$Int} at index [6×7 BitMatrix]" err_str = @except_str Bounded(2)[3] BoundsError - @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3]\nValid indices are 1:2." + @test err_str == "BoundsError: attempt to access 2-size Bounded at index [3], valid indices are 1:2" err_str = @except_str 0::Bool TypeError @test err_str == "TypeError: non-boolean ($Int) used in boolean context"