diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 748f075b4b81b..8a780d3304102 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -415,13 +415,12 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) Expr(:call, typesof, map(esc, ex0.args[2:end])...)) end end - exret = Expr(:none) - is_macro = false - ex = expand(__module__, ex0) if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* - is_macro = true - exret = Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) - elseif !isa(ex, Expr) + return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) + end + ex = expand(__module__, ex0) + exret = Expr(:none) + if !isa(ex, Expr) exret = Expr(:call, :error, "expression is not a function call or symbol") elseif ex.head == :call if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && @@ -445,12 +444,12 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) end end end - if (!is_macro && ex.head == :thunk) || exret.head == :none + if ex.head == :thunk || exret.head == :none exret = Expr(:call, :error, "expression is not a function call, " * "or is too complex for @$fcn to analyze; " * "break it down to simpler parts if possible") end - exret + return exret end for fname in [:which, :less, :edit, :functionloc, :code_warntype, diff --git a/src/ast.c b/src/ast.c index 847a19df05451..27378832f664b 100644 --- a/src/ast.c +++ b/src/ast.c @@ -981,7 +981,6 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule JL_GC_PUSHARGS(margs, nargs); int i; margs[0] = jl_array_ptr_ref(args, 0); - margs[0] = jl_toplevel_eval(*ctx, margs[0]); // __source__ argument jl_value_t *lno = jl_array_ptr_ref(args, 1); margs[1] = lno; @@ -1011,13 +1010,33 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule size_t last_age = ptls->world_age; size_t world = jl_world_counter; ptls->world_age = world; - jl_method_instance_t *mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); - if (mfunc == NULL) { - jl_method_error((jl_function_t*)margs[0], margs, nargs, world); - // unreachable + jl_value_t *result; + JL_TRY { + margs[0] = jl_toplevel_eval(*ctx, margs[0]); + jl_method_instance_t *mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); + if (mfunc == NULL) { + jl_method_error((jl_function_t*)margs[0], margs, nargs, world); + // unreachable + } + *ctx = mfunc->def.method->module; + result = jl_call_method_internal(mfunc, margs, nargs); + } + JL_CATCH { + if (jl_loaderror_type == NULL) { + jl_rethrow(); + } + else { + jl_value_t *lno = margs[1]; + jl_value_t *file = jl_fieldref(lno, 1); + if (jl_is_symbol(file)) + margs[0] = jl_cstr_to_string(jl_symbol_name((jl_sym_t*)file)); + else + margs[0] = jl_cstr_to_string(""); + margs[1] = jl_fieldref(lno, 0); // extract and allocate line number + jl_rethrow_other(jl_new_struct(jl_loaderror_type, margs[0], margs[1], + ptls->exception_in_transit)); + } } - *ctx = mfunc->def.method->module; - jl_value_t *result = jl_call_method_internal(mfunc, margs, nargs); ptls->world_age = last_age; JL_GC_POP(); return result; diff --git a/test/docs.jl b/test/docs.jl index 635aa36dc205e..6b6ab27a489e1 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -706,7 +706,18 @@ end ) # Issue #13905. -@test @macroexpand(@doc "" f() = @x) == Expr(:error, UndefVarError(Symbol("@x"))) +let err = try; @macroexpand(@doc "" f() = @x); false; catch ex; ex; end + __source__ = LineNumberNode(@__LINE__() - 1, Symbol(@__FILE__)) + err::LoadError + @test err.file === string(__source__.file) + @test err.line === __source__.line + err = err.error::LoadError + @test err.file === string(__source__.file) + @test err.line === __source__.line + err = err.error::UndefVarError + @test err.var == Symbol("@x") + end + # Undocumented DataType Summaries. diff --git a/test/enums.jl b/test/enums.jl index ea4169af7b8aa..6ded32a9a19eb 100644 --- a/test/enums.jl +++ b/test/enums.jl @@ -7,7 +7,17 @@ using Base.Test @test_throws MethodError convert(Enum, 1.0) -@test_throws ArgumentError eval(:(@enum Foo)) +macro macrocall(ex) + @assert Meta.isexpr(ex, :macrocall) + ex.head = :call + for i in 2:length(ex.args) + ex.args[i] = QuoteNode(ex.args[i]) + end + insert!(ex.args, 3, __module__) + return esc(ex) +end + +@test_throws ArgumentError("no arguments given for Enum Foo") @macrocall(@enum Foo) @enum Fruit apple orange kiwi @test typeof(Fruit) == DataType @@ -72,14 +82,18 @@ end @test Int(_neg4) === -4 @test Int(_neg3) === -3 -@test_throws ArgumentError eval(:(@enum Test1 _zerofp=0.0)) -@test_throws ArgumentError eval(:(@enum Test11 _zerofp2=0.5)) +@test_throws ArgumentError("invalid value for Enum Test1, _zerofp = 0.0=0.0; values must be integers") @macrocall(@enum Test1 _zerofp=0.0) +@test_throws ArgumentError("invalid value for Enum Test11, _zerofp2 = 0.5=0.5; values must be integers") @macrocall(@enum Test11 _zerofp2=0.5) @enum Test111 _zerobi=BigInt(1) @test Integer(_zerobi) == 1 # can't use non-identifiers as enum members -@test_throws ArgumentError eval(:(@enum Test2 x ? 1 : 2)) -@test_throws ArgumentError eval(:(@enum Test22 1=2)) +@test_throws ArgumentError("""invalid argument for Enum Test2: if x + 1 + else + 2 + end""") @macrocall(@enum Test2 x ? 1 : 2) +@test_throws ArgumentError("invalid argument for Enum Test22: 1 = 2") @macrocall(@enum Test22 1=2) # other Integer types of enum members @enum Test3::UInt8 _one_Test3=0x01 _two_Test3=0x02 _three_Test3=0x03 @@ -98,9 +112,9 @@ end @test typeof(convert(Integer, _one_Test6)) == UInt128 # enum values must be integers -@test_throws ArgumentError eval(:(@enum Test7 _zero="zero")) -@test_throws ArgumentError eval(:(@enum Test8 _zero='0')) -@test_throws ArgumentError eval(:(@enum Test9 _zero=0.5)) +@test_throws ArgumentError("invalid value for Enum Test7, _zero = \"zero\"=zero; values must be integers") @macrocall(@enum Test7 _zero="zero") +@test_throws ArgumentError("invalid value for Enum Test8, _zero = '0'=0; values must be integers") @macrocall(@enum Test8 _zero='0') +@test_throws ArgumentError("invalid value for Enum Test9, _zero = 0.5=0.5; values must be integers") @macrocall(@enum Test9 _zero=0.5) # test macro handles keyword arguments @enum(Test11, _zero_Test11=2, @@ -114,10 +128,10 @@ end @test Int(_three_Test11) == 6 # don't allow enum value to overflow -@test_throws ArgumentError @eval(@enum EnumOvf x=typemax(Int32) y) +@test_throws ArgumentError("overflow in value \"y\" of Enum EnumOvf") @macrocall(@enum EnumOvf x=typemax(Int32) y) # test for unique Enum values -@test_throws ArgumentError eval(:(@enum(Test14, _zero_Test14, _one_Test14, _two_Test14=0))) +@test_throws ArgumentError("values for Enum Test14 are not unique") @macrocall(@enum(Test14, _zero_Test14, _one_Test14, _two_Test14=0)) @test repr(apple) == "apple::$(string(Fruit)) = 0" @test string(apple) == "apple" diff --git a/test/printf.jl b/test/printf.jl index 2885ddc8df3bd..90c0e10769621 100644 --- a/test/printf.jl +++ b/test/printf.jl @@ -1,5 +1,18 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +macro test_throws(ty, ex) + return quote + Test.@test_throws $(esc(ty)) try + $(esc(ex)) + catch err + @test err isa LoadError + @test err.file === $(string(__source__.file)) + @test err.line === $(__source__.line) + rethrow(err.error) + end + end +end + # printf # int @test (@sprintf "%d" typemax(Int64)) == "9223372036854775807" @@ -189,14 +202,14 @@ end # escape % @test (@sprintf "%%") == "%" @test (@sprintf "%%s") == "%s" -@test_throws ArgumentError eval(:(@sprintf "%")) +@test_throws ArgumentError("invalid printf format string: \"%\"") @macroexpand(@sprintf "%") #" (fixes syntax highlighting) # argument count -@test_throws ArgumentError eval(:(@sprintf "%s")) -@test_throws ArgumentError eval(:(@sprintf "%s" "1" "2")) +@test_throws ArgumentError("@sprintf: wrong number of arguments (0) should be (1)") @macroexpand(@sprintf "%s") +@test_throws ArgumentError("@sprintf: wrong number of arguments (2) should be (1)") @macroexpand(@sprintf "%s" "1" "2") # no interpolation -@test_throws ArgumentError eval(:(@sprintf "$n")) +@test_throws ArgumentError("@sprintf: format must be a plain static string (no interpolation or prefix)") @macroexpand(@sprintf "$n") # type width specifier parsing (ignored) @test (@sprintf "%llf" 1.2) == "1.200000" @@ -242,7 +255,7 @@ end # invalid format specifiers, not "diouxXDOUeEfFgGaAcCsSpn" for c in "bBhHIjJkKlLmMNPqQrRtTvVwWyYzZ" fmt_str = string("%", c) - @test_throws ArgumentError eval(:(@sprintf $fmt_str 1)) + @test_throws ArgumentError("@sprintf: first argument must be a format string") @macroexpand(@sprintf $fmt_str 1) end # combo @@ -255,7 +268,7 @@ end @test (@sprintf "%s %s %s %d %d %d %f %f %f" Any[10^x+y for x=1:3,y=1:3 ]...) == "11 101 1001 12 102 1002 13.000000 103.000000 1003.000000" # @printf -@test_throws ArgumentError eval(:(@printf 1)) +@test_throws ArgumentError("@printf: first or second argument must be a format string") @macroexpand(@printf 1) # Check bug with trailing nul printing BigFloat @test (@sprintf("%.330f", BigFloat(1)))[end] != '\0' diff --git a/test/reflection.jl b/test/reflection.jl index f7564fb0b99ae..dc0af73e23987 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -246,26 +246,27 @@ let @test TestMod7648.TestModSub9475 == @which a9475 end -@test_throws ArgumentError which(===, Tuple{Int, Int}) -@test_throws ArgumentError code_typed(===, Tuple{Int, Int}) -@test_throws ArgumentError code_llvm(===, Tuple{Int, Int}) -@test_throws ArgumentError code_native(===, Tuple{Int, Int}) -@test_throws ArgumentError Base.return_types(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") which(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_typed(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_llvm(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_native(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") Base.return_types(===, Tuple{Int, Int}) module TestingExported using Base.Test +include("testenv.jl") # for curmod_str import Base.isexported global this_is_not_defined export this_is_not_defined -@test_throws ErrorException which(:this_is_not_defined) -@test_throws ErrorException @which this_is_not_defined -@test_throws ErrorException which(:this_is_not_exported) +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module Main") which(:this_is_not_defined) +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module $curmod_str") @which this_is_not_defined +@test_throws ErrorException("\"this_is_not_exported\" is not defined in module Main") which(:this_is_not_exported) @test isexported(@__MODULE__, :this_is_not_defined) @test !isexported(@__MODULE__, :this_is_not_exported) const a_value = 1 @test Base.which_module(@__MODULE__, :a_value) === @__MODULE__ @test @which(a_value) === @__MODULE__ -@test_throws ErrorException which(:a_value) +@test_throws ErrorException("\"a_value\" is not defined in module Main") which(:a_value) @test which(:Core) === Main @test !isexported(@__MODULE__, :a_value) end @@ -668,7 +669,8 @@ let @test @inferred wrapperT(ReflectionExample{T, Int64} where T) == ReflectionExample @test @inferred wrapperT(ReflectionExample) == ReflectionExample @test @inferred wrapperT(Union{ReflectionExample{Union{},1},ReflectionExample{Float64,1}}) == ReflectionExample - @test_throws ErrorException Base.typename(Union{Int, Float64}) + @test_throws(ErrorException("typename does not apply to unions whose components have different typenames"), + Base.typename(Union{Int, Float64})) end # Issue #20086 diff --git a/test/simdloop.jl b/test/simdloop.jl index bbac5f1dfa82a..31b28a9eec51a 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -74,24 +74,37 @@ import Base.SimdLoop.SimdError # Test that @simd rejects inner loop body with invalid control flow statements # issue #8613 -@test_throws SimdError eval(:(begin +macro test_throws(ty, ex) + return quote + Test.@test_throws $(esc(ty)) try + $(esc(ex)) + catch err + @test err isa LoadError + @test err.file === $(string(__source__.file)) + @test err.line === $(__source__.line + 1) + rethrow(err.error) + end + end +end + +@test_throws SimdError("break is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x == 1 && break end -end)) +end -@test_throws SimdError eval(:(begin +@test_throws SimdError("continue is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x < 5 && continue end -end)) +end -@test_throws SimdError eval(:(begin +@test_throws SimdError("@goto is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x == 1 || @goto exit_loop end @label exit_loop -end)) +end # @simd with cartesian iteration function simd_cartesian_range!(indexes, crng)