diff --git a/base/boot.jl b/base/boot.jl index 5d2527efd44c0..69fd58691dc6f 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -206,7 +206,7 @@ export InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, - UndefKeywordError, ConcurrencyViolationError, + UndefKeywordError, ConcurrencyViolationError, FieldError, # AST representation Expr, QuoteNode, LineNumberNode, GlobalRef, # object model functions @@ -404,6 +404,11 @@ struct AssertionError <: Exception end AssertionError() = AssertionError("") +struct FieldError <: Exception + type::DataType + field::Symbol +end + abstract type WrappedException <: Exception end struct LoadError <: WrappedException diff --git a/base/errorshow.jl b/base/errorshow.jl index 567339c668600..cf1518a3cb533 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -367,6 +367,12 @@ function showerror(io::IO, ex::MethodError) nothing end +function showerror(io::IO, exc::FieldError) + @nospecialize + print(io, "FieldError: type $(exc.type |> nameof) has no field $(exc.field)") + Base.Experimental.show_error_hints(io, exc) +end + striptype(::Type{T}) where {T} = T striptype(::Any) = nothing @@ -1086,6 +1092,20 @@ end Experimental.register_error_hint(methods_on_iterable, MethodError) +# Display a hint in case the user tries to access non-member fields of container type datastructures +function fielderror_hint_handler(io, exc) + @nospecialize + field = exc.field + type = exc.type + if type <: AbstractDict + print(io, "\nDid you mean to access dict values using key: `:$field` ? Consider using indexing syntax ") + printstyled(io, "dict[:$(field)]", color=:cyan) + println(io) + end +end + +Experimental.register_error_hint(fielderror_hint_handler, FieldError) + # ExceptionStack implementation size(s::ExceptionStack) = size(s.stack) getindex(s::ExceptionStack, i::Int) = s.stack[i] diff --git a/base/reflection.jl b/base/reflection.jl index 3fead12f2eb8e..4417091acd7a7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -976,7 +976,7 @@ julia> struct Foo end julia> Base.fieldindex(Foo, :z) -ERROR: type Foo has no field z +ERROR: FieldError: type Foo has no field z Stacktrace: [...] diff --git a/src/codegen.cpp b/src/codegen.cpp index 51d383e9a61e7..82ea1852ecb11 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4080,7 +4080,7 @@ static jl_llvm_functions_t jl_value_t *rettype, jl_codegen_params_t ¶ms); -static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_sym_t *type, jl_cgval_t name); +static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatype_t *type, jl_cgval_t name); static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ArrayRef argv, size_t nargs, jl_value_t *rt, @@ -4590,7 +4590,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, assert(jl_svec_len(fn) == 1); Value *typ_sym = literal_pointer_val(ctx, jl_svecref(fn, 0)); Value *cond = ctx.builder.CreateICmpEQ(mark_callee_rooted(ctx, typ_sym), mark_callee_rooted(ctx, boxed(ctx, fld))); - emit_hasnofield_error_ifnot(ctx, cond, utt->name->name, fld); + emit_hasnofield_error_ifnot(ctx, cond, utt, fld); *ret = emit_getfield_knownidx(ctx, obj, 0, utt, order); return true; } @@ -4598,7 +4598,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *index = ctx.builder.CreateCall(prepare_call(jlfieldindex_func), {emit_typeof(ctx, obj, false, false), boxed(ctx, fld), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)}); Value *cond = ctx.builder.CreateICmpNE(index, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), -1)); - emit_hasnofield_error_ifnot(ctx, cond, utt->name->name, fld); + emit_hasnofield_error_ifnot(ctx, cond, utt, fld); Value *idx2 = ctx.builder.CreateAdd(ctx.builder.CreateIntCast(index, ctx.types().T_size, false), ConstantInt::get(ctx.types().T_size, 1)); // getfield_unknown is 1 based if (emit_getfield_unknownidx(ctx, ret, obj, idx2, utt, jl_false, order)) return true; @@ -5429,7 +5429,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo // --- accessing and assigning variables --- -static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_sym_t *type, jl_cgval_t name) +static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_datatype_t *type, jl_cgval_t name) { ++EmittedUndefVarErrors; assert(name.typ == (jl_value_t*)jl_symbol_type); diff --git a/src/datatype.c b/src/datatype.c index abbec420bb617..1a4694a594510 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1736,7 +1736,7 @@ JL_DLLEXPORT int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err) } } if (err) - jl_has_no_field_error(t->name->name, fld); + jl_has_no_field_error(t, fld); return -1; } diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 79ff437841879..ff79966b2b01b 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -138,6 +138,7 @@ XX(jl_uint8_type) \ XX(jl_undefref_exception) \ XX(jl_undefvarerror_type) \ + XX(jl_fielderror_type) \ XX(jl_unionall_type) \ XX(jl_uniontype_type) \ XX(jl_upsilonnode_type) \ diff --git a/src/jltypes.c b/src/jltypes.c index 59807226fb4a9..fb65a9fca5d7e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3727,6 +3727,7 @@ void post_boot_hooks(void) jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); + jl_fielderror_type = (jl_datatype_t*)core("FieldError"); jl_atomicerror_type = (jl_datatype_t*)core("ConcurrencyViolationError"); jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException")); jl_boundserror_type = (jl_datatype_t*)core("BoundsError"); diff --git a/src/julia.h b/src/julia.h index 6910167dae46d..ccd70bf01f23c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -869,6 +869,7 @@ extern JL_DLLIMPORT jl_datatype_t *jl_initerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_typeerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_methoderror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_undefvarerror_type JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_datatype_t *jl_fielderror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_atomicerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_missingcodeerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_lineinfonode_type JL_GLOBALLY_ROOTED; @@ -2024,7 +2025,7 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname, jl_value_t *ty JL_MAYBE_UNROOTED, jl_value_t *got JL_MAYBE_UNROOTED); JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t *scope JL_MAYBE_UNROOTED); -JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_sym_t *type_name, jl_sym_t *var); +JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var); JL_DLLEXPORT void JL_NORETURN jl_atomic_error(char *str); JL_DLLEXPORT void JL_NORETURN jl_bounds_error(jl_value_t *v JL_MAYBE_UNROOTED, jl_value_t *t JL_MAYBE_UNROOTED); diff --git a/src/rtutils.c b/src/rtutils.c index 7df3230755e63..4a2e5c230883e 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -152,9 +152,9 @@ JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t * jl_throw(jl_new_struct(jl_undefvarerror_type, var, scope)); } -JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_sym_t *type_name, jl_sym_t *var) +JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var) { - jl_errorf("type %s has no field %s", jl_symbol_name(type_name), jl_symbol_name(var)); + jl_throw(jl_new_struct(jl_fielderror_type, t, var)); } JL_DLLEXPORT void JL_NORETURN jl_atomic_error(char *str) // == jl_exceptionf(jl_atomicerror_type, "%s", str) diff --git a/src/staticdata.c b/src/staticdata.c index 28051d52eb105..ea2fac6c0fce6 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -100,7 +100,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 190 +#define NUM_TAGS 191 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -235,6 +235,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_loaderror_type); INSERT_TAG(jl_initerror_type); INSERT_TAG(jl_undefvarerror_type); + INSERT_TAG(jl_fielderror_type); INSERT_TAG(jl_stackovf_exception); INSERT_TAG(jl_diverror_exception); INSERT_TAG(jl_interrupt_exception); diff --git a/stdlib/LinearAlgebra/test/bunchkaufman.jl b/stdlib/LinearAlgebra/test/bunchkaufman.jl index d2305844db63e..68c519d1197ed 100644 --- a/stdlib/LinearAlgebra/test/bunchkaufman.jl +++ b/stdlib/LinearAlgebra/test/bunchkaufman.jl @@ -114,7 +114,7 @@ bimint = rand(1:5, n, 2) bc1 = bunchkaufman(Symmetric(asym, uplo)) @test getproperty(bc1, uplo)*bc1.D*transpose(getproperty(bc1, uplo)) ≈ asym[bc1.p, bc1.p] @test getproperty(bc1, uplo)*bc1.D*transpose(getproperty(bc1, uplo)) ≈ bc1.P*asym*transpose(bc1.P) - @test_throws ErrorException bc1.Z + @test_throws FieldError bc1.Z @test_throws ArgumentError uplo === :L ? bc1.U : bc1.L end # test Base.iterate diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index 45b3c2445a187..f6910a19632a9 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -17,7 +17,7 @@ function unary_ops_tests(a, ca, tol; n=size(a, 1)) @test logabsdet_ca[1] ≈ logabsdet_a[1] @test logabsdet_ca[2] ≈ logabsdet_a[2] @test isposdef(ca) - @test_throws ErrorException ca.Z + @test_throws FieldError ca.Z @test size(ca) == size(a) @test Array(copy(ca)) ≈ a @test tr(ca) ≈ tr(a) skip=ca isa CholeskyPivoted diff --git a/stdlib/LinearAlgebra/test/eigen.jl b/stdlib/LinearAlgebra/test/eigen.jl index 174deffbc53e9..a82c745436009 100644 --- a/stdlib/LinearAlgebra/test/eigen.jl +++ b/stdlib/LinearAlgebra/test/eigen.jl @@ -80,7 +80,7 @@ aimg = randn(n,n)/2 @test eigvecs(asym_sg, ASG2) == f.vectors @test eigvals(f) === f.values @test eigvecs(f) === f.vectors - @test_throws ErrorException f.Z + @test_throws FieldError f.Z d,v = eigen(asym_sg, ASG2) @test d == f.values @@ -141,7 +141,7 @@ aimg = randn(n,n)/2 @test f.values ≈ eigvals(a1_nsg, a2_nsg; sortby = sortfunc) @test prod(f.values) ≈ prod(eigvals(a1_nsg/a2_nsg, sortby = sortfunc)) atol=50000ε @test eigvecs(a1_nsg, a2_nsg; sortby = sortfunc) == f.vectors - @test_throws ErrorException f.Z + @test_throws FieldError f.Z g = eigen(a1_nsg, Diagonal(1:n1)) @test a1_nsg*g.vectors ≈ (Diagonal(1:n1)*g.vectors) * Diagonal(g.values) diff --git a/stdlib/LinearAlgebra/test/hessenberg.jl b/stdlib/LinearAlgebra/test/hessenberg.jl index 90846ffc1defd..767f40aa1e53f 100644 --- a/stdlib/LinearAlgebra/test/hessenberg.jl +++ b/stdlib/LinearAlgebra/test/hessenberg.jl @@ -148,7 +148,7 @@ let n = 10 @test size(H.Q, 2) == size(A, 2) @test size(H.Q) == size(A) @test size(H) == size(A) - @test_throws ErrorException H.Z + @test_throws FieldError H.Z @test convert(Array, H) ≈ A @test (H.Q * H.H) * H.Q' ≈ A ≈ (Matrix(H.Q) * Matrix(H.H)) * Matrix(H.Q)' @test (H.Q' * A) * H.Q ≈ H.H diff --git a/stdlib/LinearAlgebra/test/lq.jl b/stdlib/LinearAlgebra/test/lq.jl index 44f920db25557..c3499f7f46fa6 100644 --- a/stdlib/LinearAlgebra/test/lq.jl +++ b/stdlib/LinearAlgebra/test/lq.jl @@ -50,7 +50,7 @@ rectangularQ(Q::LinearAlgebra.LQPackedQ) = convert(Array, Q) for (ii, lq_obj) in enumerate(lqa) @test ref_obs[ii] == lq_obj end - @test_throws ErrorException lqa.Z + @test_throws FieldError lqa.Z @test Array(copy(adjoint(lqa))) ≈ a' @test q*squareQ(q)' ≈ Matrix(I, n, n) @test l*q ≈ a diff --git a/stdlib/LinearAlgebra/test/lu.jl b/stdlib/LinearAlgebra/test/lu.jl index 0cb45047a118c..56a402d70493e 100644 --- a/stdlib/LinearAlgebra/test/lu.jl +++ b/stdlib/LinearAlgebra/test/lu.jl @@ -59,7 +59,7 @@ dimg = randn(n)/2 κ = cond(a,1) @testset "(Automatic) Square LU decomposition" begin lua = factorize(a) - @test_throws ErrorException lua.Z + @test_throws FieldError lua.Z l,u,p = lua.L, lua.U, lua.p ll,ul,pl = @inferred lu(a) @test ll * ul ≈ a[pl,:] @@ -88,7 +88,7 @@ dimg = randn(n)/2 lud = @inferred lu(d) @test LinearAlgebra.issuccess(lud) @test @inferred(lu(lud)) == lud - @test_throws ErrorException lud.Z + @test_throws FieldError lud.Z @test lud.L*lud.U ≈ lud.P*Array(d) @test lud.L*lud.U ≈ Array(d)[lud.p,:] @test AbstractArray(lud) ≈ d diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl index aef2e23740487..b6e9ce3a82743 100644 --- a/stdlib/LinearAlgebra/test/qr.jl +++ b/stdlib/LinearAlgebra/test/qr.jl @@ -50,7 +50,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) @testset "QR decomposition (without pivoting)" begin qra = @inferred qr(a) q, r = qra.Q, qra.R - @test_throws ErrorException qra.Z + @test_throws FieldError qra.Z @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) @test q*squareQ(q)' ≈ Matrix(I, a_1, a_1) @test q'*Matrix(1.0I, a_1, a_1)' ≈ squareQ(q)' @@ -79,7 +79,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) @testset "Thin QR decomposition (without pivoting)" begin qra = @inferred qr(a[:, 1:n1], NoPivot()) q,r = qra.Q, qra.R - @test_throws ErrorException qra.Z + @test_throws FieldError qra.Z @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) @test q'*rectangularQ(q) ≈ Matrix(I, a_1, n1) @test q*r ≈ a[:, 1:n1] @@ -106,7 +106,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) qrpa = factorize(a[1:n1,:]) q,r = qrpa.Q, qrpa.R - @test_throws ErrorException qrpa.Z + @test_throws FieldError qrpa.Z p = qrpa.p @test q'*squareQ(q) ≈ Matrix(I, n1, n1) @test q*squareQ(q)' ≈ Matrix(I, n1, n1) @@ -134,7 +134,7 @@ rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) @testset "(Automatic) Thin (pivoted) QR decomposition" begin qrpa = factorize(a[:,1:n1]) q,r = qrpa.Q, qrpa.R - @test_throws ErrorException qrpa.Z + @test_throws FieldError qrpa.Z p = qrpa.p @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) @test q*squareQ(q)' ≈ Matrix(I, a_1, a_1) diff --git a/stdlib/LinearAlgebra/test/schur.jl b/stdlib/LinearAlgebra/test/schur.jl index c9a5d92dbdae8..f3d494fba7942 100644 --- a/stdlib/LinearAlgebra/test/schur.jl +++ b/stdlib/LinearAlgebra/test/schur.jl @@ -33,7 +33,7 @@ aimg = randn(n,n)/2 @test sort(imag(f.values)) ≈ sort(imag(d)) @test istriu(f.Schur) || eltype(a)<:Real @test convert(Array, f) ≈ a - @test_throws ErrorException f.A + @test_throws FieldError f.A sch, vecs, vals = schur(UpperTriangular(triu(a))) @test vecs*sch*vecs' ≈ triu(a) @@ -68,7 +68,7 @@ aimg = randn(n,n)/2 O = ordschur(S, select) sum(select) != 0 && @test S.values[findall(select)] ≈ O.values[1:sum(select)] @test O.vectors*O.Schur*O.vectors' ≈ ordschura - @test_throws ErrorException f.A + @test_throws FieldError f.A Snew = LinearAlgebra.Schur(S.T, S.Z, S.values) SchurNew = ordschur!(copy(Snew), select) @test O.vectors ≈ SchurNew.vectors @@ -88,7 +88,7 @@ aimg = randn(n,n)/2 @test f.Q*f.T*f.Z' ≈ a2_sf @test istriu(f.S) || eltype(a)<:Real @test istriu(f.T) || eltype(a)<:Real - @test_throws ErrorException f.A + @test_throws FieldError f.A sstring = sprint((t, s) -> show(t, "text/plain", s), f.S) tstring = sprint((t, s) -> show(t, "text/plain", s), f.T) diff --git a/stdlib/LinearAlgebra/test/svd.jl b/stdlib/LinearAlgebra/test/svd.jl index 8f462dee94d06..9e8b5d5cda7d2 100644 --- a/stdlib/LinearAlgebra/test/svd.jl +++ b/stdlib/LinearAlgebra/test/svd.jl @@ -75,7 +75,7 @@ aimg = randn(n,n)/2 @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ a @test convert(Array, usv) ≈ a @test usv.Vt' ≈ usv.V - @test_throws ErrorException usv.Z + @test_throws FieldError usv.Z b = rand(eltya,n) @test usv\b ≈ a\b @test Base.propertynames(usv) == (:U, :S, :V, :Vt) @@ -94,7 +94,7 @@ aimg = randn(n,n)/2 @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ transform(a) @test convert(Array, usv) ≈ transform(a) @test usv.Vt' ≈ usv.V - @test_throws ErrorException usv.Z + @test_throws FieldError usv.Z b = rand(eltya,n) @test usv\b ≈ transform(a)\b end @@ -106,8 +106,8 @@ aimg = randn(n,n)/2 @test gsvd.U*gsvd.D1*gsvd.R*gsvd.Q' ≈ a @test gsvd.V*gsvd.D2*gsvd.R*gsvd.Q' ≈ a_svd @test usv.Vt' ≈ usv.V - @test_throws ErrorException usv.Z - @test_throws ErrorException gsvd.Z + @test_throws FieldError usv.Z + @test_throws FieldError gsvd.Z @test gsvd.vals ≈ svdvals(a,a_svd) α = eltya == Int ? -1 : rand(eltya) β = svd(α) @@ -148,7 +148,7 @@ aimg = randn(n,n)/2 @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ T(asym) @test convert(Array, usv) ≈ T(asym) @test usv.Vt' ≈ usv.V - @test_throws ErrorException usv.Z + @test_throws FieldError usv.Z b = rand(eltya,n) @test usv\b ≈ T(asym)\b end diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index f9b8b1c5fb74d..529ee7b611448 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -859,7 +859,7 @@ foo50964(1) # Shouldn't assert! # https://github.com/JuliaLang/julia/issues/51233 obj51233 = (1,) -@test_throws ErrorException obj51233.x +@test_throws FieldError obj51233.x # Very specific test for multiversioning if Sys.ARCH === :x86_64 diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 7d6e42a4b5731..6fe050bbd5935 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -479,7 +479,7 @@ end @test f15259(1,2) == (1,2,1,2) # check that error cases are still correct @eval g15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); a.z) -@test_throws ErrorException g15259(1,1) +@test_throws FieldError g15259(1,1) @eval h15259(x,y) = (a = $(Expr(:new, :A15259, :x, :y)); getfield(a, 3)) @test_throws BoundsError h15259(1,1) diff --git a/test/core.jl b/test/core.jl index 5a65b8a92c3fd..8d8db28eac062 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1924,9 +1924,9 @@ end # issue #4526 f4526(x) = isa(x.a, Nothing) -@test_throws ErrorException f4526(1) -@test_throws ErrorException f4526(im) -@test_throws ErrorException f4526(1+2im) +@test_throws FieldError f4526(1) +@test_throws FieldError f4526(im) +@test_throws FieldError f4526(1+2im) # issue #4528 function f4528(A, B) diff --git a/test/errorshow.jl b/test/errorshow.jl index 5d0640601b398..80352ddeaa9cf 100644 --- a/test/errorshow.jl +++ b/test/errorshow.jl @@ -10,7 +10,7 @@ Base.Experimental.register_error_hint(Base.noncallable_number_hint_handler, Meth Base.Experimental.register_error_hint(Base.string_concatenation_hint_handler, MethodError) Base.Experimental.register_error_hint(Base.methods_on_iterable, MethodError) Base.Experimental.register_error_hint(Base.nonsetable_type_hint_handler, MethodError) - +Base.Experimental.register_error_hint(Base.fielderror_hint_handler, FieldError) @testset "SystemError" begin err = try; systemerror("reason", Cint(0)); false; catch ex; ex; end::SystemError @@ -808,6 +808,44 @@ end @test_throws ArgumentError("invalid index: \"foo\" of type String") [1]["foo"] @test_throws ArgumentError("invalid index: nothing of type Nothing") [1][nothing] +# issue #53618 +@testset "FieldErrorHint" begin + struct FieldFoo + a::Float32 + b::Int + end + + s = FieldFoo(1, 2) + + test = @test_throws FieldError s.c + + ex = test.value::FieldError + + # Check error message first + errorMsg = sprint(Base.showerror, ex) + @test occursin("FieldError: type FieldFoo has no field c", errorMsg) + + d = Dict(s => 1) + + for fld in fieldnames(Dict) + ex = try + getfield(d, fld) + catch e + print(e) + end + @test !(ex isa Type) || ex <: FieldError + end + test = @test_throws FieldError d.c + + ex = test.value::FieldError + + errorMsg = sprint(Base.showerror, ex) + @test occursin("FieldError: type Dict has no field c", errorMsg) + # Check hint message + hintExpected = "Did you mean to access dict values using key: `:c` ? Consider using indexing syntax dict[:c]\n" + @test occursin(hintExpected, errorMsg) +end + # test showing MethodError with type argument struct NoMethodsDefinedHere; end let buf = IOBuffer() diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 48aa8ea4a2591..2c9c1ef3cff53 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -28,13 +28,13 @@ @test (x=4, y=5, z=6)[()] == NamedTuple() @test (x=4, y=5, z=6)[:] == (x=4, y=5, z=6) @test NamedTuple()[()] == NamedTuple() -@test_throws ErrorException (x=4, y=5, z=6).a +@test_throws FieldError (x=4, y=5, z=6).a @test_throws BoundsError (a=2,)[0] @test_throws BoundsError (a=2,)[2] -@test_throws ErrorException (x=4, y=5, z=6)[(:a,)] -@test_throws ErrorException (x=4, y=5, z=6)[(:x, :a)] -@test_throws ErrorException (x=4, y=5, z=6)[[:a]] -@test_throws ErrorException (x=4, y=5, z=6)[[:x, :a]] +@test_throws FieldError (x=4, y=5, z=6)[(:a,)] +@test_throws FieldError (x=4, y=5, z=6)[(:x, :a)] +@test_throws FieldError (x=4, y=5, z=6)[[:a]] +@test_throws FieldError (x=4, y=5, z=6)[[:x, :a]] @test_throws ErrorException (x=4, y=5, z=6)[(:x, :x)] @test length(NamedTuple()) == 0 @@ -255,7 +255,7 @@ function abstr_nt_22194_2() a = NamedTuple[(a=1,), (b=2,)] return a[1].b end -@test_throws ErrorException abstr_nt_22194_2() +@test_throws FieldError abstr_nt_22194_2() @test Base.return_types(abstr_nt_22194_2, ()) == Any[Any] mutable struct HasAbstractNamedTuples @@ -442,7 +442,7 @@ end for NT in (NamedTuple{(:a, :b), Union{}}, NamedTuple{(:a, :b), T} where T<:Union{}) @test fieldtype(NT, 1) == Union{} @test fieldtype(NT, :b) == Union{} - @test_throws ErrorException fieldtype(NT, :c) + @test_throws FieldError fieldtype(NT, :c) @test_throws BoundsError fieldtype(NT, 0) @test_throws BoundsError fieldtype(NT, 3) @test Base.return_types((Type{NT},)) do NT; fieldtype(NT, :a); end == Any[Type{Union{}}] diff --git a/test/syntax.jl b/test/syntax.jl index 0a2cc491463ad..f2dc464d4cca4 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2831,7 +2831,7 @@ end @test a == 5 @test b == 6 - @test_throws ErrorException (; a, b) = (x=1,) + @test_throws FieldError (; a, b) = (x=1,) @test Meta.isexpr(Meta.@lower(begin (a, b; c) = x end), :error) @test Meta.isexpr(Meta.@lower(begin (a, b; c) = x, y end), :error) @@ -2840,7 +2840,7 @@ end f((; a, b)) = a, b @test f((b=3, a=4)) == (4, 3) @test f((b=3, c=2, a=4)) == (4, 3) - @test_throws ErrorException f((;)) + @test_throws FieldError f((;)) # with type annotation let num, den, a, b