Skip to content

Commit

Permalink
add macro linenumber-node to invoke-macro error
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed Sep 6, 2017
1 parent d7169ae commit 0b08a7e
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 48 deletions.
15 changes: 7 additions & 8 deletions base/interactiveutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand All @@ -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,
Expand Down
33 changes: 26 additions & 7 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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("<macrocall>");
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;
Expand Down
13 changes: 12 additions & 1 deletion test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
34 changes: 24 additions & 10 deletions test/enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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"
Expand Down
25 changes: 19 additions & 6 deletions test/printf.jl
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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'
22 changes: 12 additions & 10 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 19 additions & 6 deletions test/simdloop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 0b08a7e

Please sign in to comment.