Skip to content

Commit c19c68e

Browse files
Add error hint for incorrect stacked indexing (#40934)
A common entry level mistake is to try to index a matrix with two sets of brackets, e.g. `a = [1 2; 3 4]; a[1][2] = 5` This will lead to an error that `setindex!()` on the element type of `a` is missing. This PR adds an error hint for the case where a MethodError is raised when `setindex!` is called with a `Number` as the first argument. I considered going broader than numbers, but it seems more likely that this kind of mistake would happen when working with simple number arrays vs. something more advanced. Could also consider if it is possible to do the same for when `getindex()` is called on a `Number`, which emits a BoundsError. Co-authored-by: Michael Abbott <[email protected]>
1 parent b3b2736 commit c19c68e

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

base/errorshow.jl

+25-2
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,31 @@ end
10221022

10231023
Experimental.register_error_hint(noncallable_number_hint_handler, MethodError)
10241024

1025+
# handler for displaying a hint in case the user tries to call setindex! on
1026+
# something that doesn't support it:
1027+
# - a number (probably attempting to use wrong indexing)
1028+
# eg: a = [1 2; 3 4]; a[1][2] = 5
1029+
# - a type (probably tried to initialize without parentheses)
1030+
# eg: d = Dict; d["key"] = 2
1031+
function nonsetable_type_hint_handler(io, ex, arg_types, kwargs)
1032+
@nospecialize
1033+
if ex.f == setindex!
1034+
T = arg_types[1]
1035+
if T <: Number
1036+
print(io, "\nAre you trying to index into an array? For multi-dimensional arrays, separate the indices with commas: ")
1037+
printstyled(io, "a[1, 2]", color=:cyan)
1038+
print(io, " rather than a[1][2]")
1039+
else isType(T)
1040+
Tx = T.parameters[1]
1041+
print(io, "\nYou attempted to index the type $Tx, rather than an instance of the type. Make sure you create the type using its constructor: ")
1042+
printstyled(io, "d = $Tx([...])", color=:cyan)
1043+
print(io, " rather than d = $Tx")
1044+
end
1045+
end
1046+
end
1047+
1048+
Experimental.register_error_hint(nonsetable_type_hint_handler, MethodError)
1049+
10251050
# Display a hint in case the user tries to use the + operator on strings
10261051
# (probably attempting concatenation)
10271052
function string_concatenation_hint_handler(io, ex, arg_types, kwargs)
@@ -1035,7 +1060,6 @@ end
10351060

10361061
Experimental.register_error_hint(string_concatenation_hint_handler, MethodError)
10371062

1038-
10391063
# Display a hint in case the user tries to use the min or max function on an iterable
10401064
# or tries to use something like `collect` on an iterator without defining either IteratorSize or length
10411065
function methods_on_iterable(io, ex, arg_types, kwargs)
@@ -1061,7 +1085,6 @@ end
10611085

10621086
Experimental.register_error_hint(methods_on_iterable, MethodError)
10631087

1064-
10651088
# ExceptionStack implementation
10661089
size(s::ExceptionStack) = size(s.stack)
10671090
getindex(s::ExceptionStack, i::Int) = s.stack[i]

test/errorshow.jl

+18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ include("testenv.jl")
99
Base.Experimental.register_error_hint(Base.noncallable_number_hint_handler, MethodError)
1010
Base.Experimental.register_error_hint(Base.string_concatenation_hint_handler, MethodError)
1111
Base.Experimental.register_error_hint(Base.methods_on_iterable, MethodError)
12+
Base.Experimental.register_error_hint(Base.nonsetable_type_hint_handler, MethodError)
13+
1214

1315
@testset "SystemError" begin
1416
err = try; systemerror("reason", Cint(0)); false; catch ex; ex; end::SystemError
@@ -745,6 +747,22 @@ let err_str
745747
@test count(==("Maybe you forgot to use an operator such as *, ^, %, / etc. ?"), split(err_str, '\n')) == 1
746748
end
747749

750+
let err_str
751+
a = [1 2; 3 4];
752+
err_str = @except_str (a[1][2] = 5) MethodError
753+
@test occursin("\nAre you trying to index into an array? For multi-dimensional arrays, separate the indices with commas: ", err_str)
754+
@test occursin("a[1, 2]", err_str)
755+
@test occursin("rather than a[1][2]", err_str)
756+
end
757+
758+
let err_str
759+
d = Dict
760+
err_str = @except_str (d[1] = 5) MethodError
761+
@test occursin("\nYou attempted to index the type Dict, rather than an instance of the type. Make sure you create the type using its constructor: ", err_str)
762+
@test occursin("d = Dict([...])", err_str)
763+
@test occursin(" rather than d = Dict", err_str)
764+
end
765+
748766
# Execute backtrace once before checking formatting, see #38858
749767
backtrace()
750768

0 commit comments

Comments
 (0)