/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
stdlib/InteractiveUtils/test/runtests.jl
1 120 строк
40 KB
Patrick Häcker
Add `@methods` macro (#62311)
17 июл 2026, 03:40
Не верифицирован
17 июл 2026, 03:40
8b766c8
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license using Test, InteractiveUtils @test isempty(Test.detect_closure_boxes(InteractiveUtils)) @testset "highlighting" begin include("highlighting.jl") end # test methodswith # `methodswith` relies on exported symbols export func4union, Base struct NoMethodHasThisType end @test isempty(methodswith(NoMethodHasThisType)) @test !isempty(methodswith(Int)) struct Type4Union end func4union(::Union{Type4Union,Int}) = () @test !isempty(methodswith(Type4Union, @__MODULE__)) # PR #19964 @test isempty(subtypes(Float64)) # Issue #20086 abstract type A20086{T,N} end struct B20086{T,N} <: A20086{T,N} end @test subtypes(A20086) == [B20086] @test subtypes(A20086{Int}) == [B20086{Int}] @test subtypes(A20086{T,3} where T) == [B20086{T,3} where T] @test subtypes(A20086{Int,3}) == [B20086{Int,3}] # supertypes @test supertypes(B20086) == (B20086, A20086, Any) @test supertypes(B20086{Int}) == (B20086{Int}, A20086{Int}, Any) @test supertypes(B20086{Int,2}) == (B20086{Int,2}, A20086{Int,2}, Any) @test supertypes(Any) == (Any,) # code_warntype module WarnType using Test, Random, InteractiveUtils function warntype_hastag(f, types, tag) iob = IOBuffer() code_warntype(iob, f, types) str = String(take!(iob)) return occursin(tag, str) end pos_stable(x) = x > 0 ? x : zero(x) pos_unstable(x) = x > 0 ? x : 0 tag = "UNION" @test warntype_hastag(pos_unstable, Tuple{Float64}, tag) @test !warntype_hastag(pos_stable, Tuple{Float64}, tag) for u in Any[ Union{Int, UInt}, Union{Nothing, Vector{Tuple{String, Tuple{Char, Char}}}}, Union{Char, UInt8, UInt}, Union{Tuple{Int, Int}, Tuple{Char, Int}, Nothing}, Union{Missing, Nothing} ] @test Base.Compiler.IRShow.is_expected_union(u) end for u in Any[ Union{Nothing, Tuple{Vararg{Char}}}, Union{Missing, Array}, Union{Int, Tuple{Any, Int}} ] @test !Base.Compiler.IRShow.is_expected_union(u) end mutable struct Stable{T,N} A::Array{T,N} end mutable struct Unstable{T} A::Array{T} end Base.getindex(A::Stable, i) = A.A[i] Base.getindex(A::Unstable, i) = A.A[i] tag = "ARRAY" @test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) @test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) @test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) # Make sure emphasis is not used for other functions tag = "ANY" iob = IOBuffer() show(iob, Meta.lower(Main, :(x -> x^2))) str = String(take!(iob)) @test !occursin(tag, str) # Make sure non used variables are not emphasized has_unused() = (a = rand(5)) @test !warntype_hastag(has_unused, Tuple{}, tag) # No variable information with the new optimizer. Eventually might be able to recover # some of this info with debug info. #@test warntype_hastag(has_unused, Tuple{}, "<optimized out>") # Make sure getproperty and setproperty! works with @code_... macros struct T1234321 t::Int end Base.getproperty(t::T1234321, ::Symbol) = "foo" @test (@code_typed T1234321(1).f).second == String Base.setproperty!(t::T1234321, ::Symbol, ::Symbol) = "foo" @test (@code_typed T1234321(1).f = :foo).second == String # Make sure `do` block works with `@code_...` macros @test (@code_typed map(1:1) do x; x; end).second == Vector{Int} @test (@code_typed open(`cat`; read=true) do _; 1; end).second == Int module ImportIntrinsics15819 # Make sure changing the lookup path of an intrinsic doesn't break # the heuristic for type instability warning. import Core.Intrinsics: sqrt_llvm, bitcast # Use import sqrt15819(x::Float64) = bitcast(Float64, sqrt_llvm(x)) # Use fully qualified name sqrt15819(x::Float32) = bitcast(Float32, Core.Intrinsics.sqrt_llvm(x)) end # module ImportIntrinsics15819 foo11122(x) = @fastmath x - 1.0 # issue #11122, #13568 and #15819 tag = "ANY" @test !warntype_hastag(+, Tuple{Int,Int}, tag) @test !warntype_hastag(-, Tuple{Int,Int}, tag) @test !warntype_hastag(*, Tuple{Int,Int}, tag) @test !warntype_hastag(/, Tuple{Int,Int}, tag) @test !warntype_hastag(foo11122, Tuple{Float32}, tag) @test !warntype_hastag(foo11122, Tuple{Float64}, tag) @test !warntype_hastag(foo11122, Tuple{Int}, tag) @test !warntype_hastag(sqrt, Tuple{Int}, tag) @test !warntype_hastag(sqrt, Tuple{Float64}, tag) @test !warntype_hastag(^, Tuple{Float64,Int32}, tag) @test !warntype_hastag(^, Tuple{Float32,Int32}, tag) @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float64}, tag) @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag) @testset "code_warntype OpaqueClosure" begin g = Base.Experimental.@opaque Tuple{Float64}->_ x -> 0.0 @test warntype_hastag(g, Tuple{Float64}, "::Float64") end end # module WarnType # Adds test for PR #17636 let a = @code_typed 1 + 1 b = @code_lowered 1 + 1 @test isa(a, Pair{Core.CodeInfo, DataType}) @test isa(b, Core.CodeInfo) @test isa(a[1].code, Array{Any,1}) @test isa(b.code, Array{Any,1}) function thing(a::Array, b::Real) println("thing") end function thing(a::AbstractArray, b::Int) println("blah") end @test_throws MethodError thing(rand(10), 1) a = @code_typed thing(rand(10), 1) b = @code_lowered thing(rand(10), 1) @test length(a) == 0 @test length(b) == 0 end # Issue #16326 mktemp() do f, io OLDSTDOUT = stdout redirect_stdout(io) @test try @code_native map(abs, rand(3)); true; catch; false; end redirect_stdout(OLDSTDOUT) nothing end module _test_varinfo_ module inner_mod inner_x = 1 end import Test: @test export x_exported x_exported = 1.0 y_not_exp = 1.0 z_larger = Vector{Float64}(undef, 3) a_smaller = Vector{Float64}(undef, 2) end using Test @test repr(varinfo(Main, r"^$")) == """ | name | size | summary | |:---- | ----:|:------- | """ let v = repr(varinfo(_test_varinfo_)) @test occursin("| x_exported | 8 bytes | Float64 |", v) @test !occursin("y_not_exp", v) @test !occursin("@test", v) @test !occursin("inner_x", v) end let v = repr(varinfo(_test_varinfo_, all = true)) @test occursin("x_exported", v) @test occursin("y_not_exp", v) @test !occursin("@test", v) @test findfirst("a_smaller", v)[1] < findfirst("z_larger", v)[1] # check for alphabetical @test !occursin("inner_x", v) end let v = repr(varinfo(_test_varinfo_, imported = true)) @test occursin("x_exported", v) @test !occursin("y_not_exp", v) @test occursin("@test", v) @test !occursin("inner_x", v) end let v = repr(varinfo(_test_varinfo_, all = true, sortby = :size)) @test findfirst("z_larger", v)[1] < findfirst("a_smaller", v)[1] # check for size order end let v = repr(varinfo(_test_varinfo_, sortby = :summary)) @test findfirst("Float64", v)[1] < findfirst("Module", v)[1] # check for summary order end let v = repr(varinfo(_test_varinfo_, all = true, recursive = true)) @test occursin("inner_x", v) end let v = repr(varinfo(_test_varinfo_, all = true, minsize = 9)) @test !occursin("x_exported", v) # excluded: 8 bytes @test occursin("a_smaller", v) end # Issue 14173 module Tmp14173 using Random export A A = randn(2000, 2000) end varinfo(Tmp14173) # warm up const MEMDEBUG = ccall(:jl_is_memdebug, Bool, ()) @test @allocated(varinfo(Tmp14173)) < (MEMDEBUG ? 300000 : 125000) # PR #24997: test that `varinfo` doesn't fail when encountering `missing` module A using InteractiveUtils export missing varinfo(A) end # PR #23075 @testset "versioninfo" begin # check that versioninfo(io; verbose=true) doesn't error, produces some output mktempdir() do dir buf = PipeBuffer() versioninfo(buf, verbose=true) ver = read(buf, String) @test startswith(ver, "Julia Version $VERSION") @test occursin("Environment:", ver) end let exename = `$(Base.julia_cmd()) --startup-file=no` @test !occursin("Environment:", read(setenv(`$exename -e 'using InteractiveUtils; versioninfo()'`, String[]), String)) @test occursin("Environment:", read(setenv(`$exename -e 'using InteractiveUtils; versioninfo()'`, String["JULIA_CPU_THREADS=1"]), String)) end end const curmod = @__MODULE__ const curmod_name = fullname(curmod) const curmod_str = curmod === Main ? "Main" : join(curmod_name, ".") @test_throws ErrorException("\"this_is_not_defined\" is not defined in module $curmod_str") @which this_is_not_defined # issue #13264 @test (@which vcat(1...)).name === :vcat @testset "@methods" begin ms = @methods sort(::AbstractVector) @test ms isa Base.MethodList @test ms == methods(sort, (AbstractVector,)) # arguments are interpreted as values, like `@which` @test (@methods sort([1, 2, 3])) == methods(sort, (Vector{Int},)) # qualified callable @test (@methods Base.sort(::AbstractVector)) == methods(sort, (AbstractVector,)) # unlike `@which`, lists every matching method when types are abstract @test length(@methods +(::Integer, ::Integer)) > 1 end # PR #28122, issue #25474 @test (@which [1][1]).name === :getindex @test (@which [1][1] = 2).name === :setindex! @test (@which [1]).name === :vect @test (@which [1 2]).name === :hcat @test (@which [1; 2]).name === :vcat @test (@which Int[1 2]).name === :typed_hcat @test (@which Int[1; 2]).name === :typed_vcat @test (@which [1 2;3 4]).name === :hvcat @test (@which Int[1 2;3 4]).name === :typed_hvcat # issue #39426 let x..y = 0 @test (@which 1..2).name === :.. end # issue #53691 let a = -1 @test (@which 2^a).name === :^ @test (@which 2^0x1).name === :^ end let w = Vector{Any}(undef, 9) @testset "@which x^literal" begin w[1] = @which 2^0 w[2] = @which 2^1 w[3] = @which 2^2 w[4] = @which 2^3 w[5] = @which 2^-1 w[6] = @which 2^-2 w[7] = @which 2^10 w[8] = @which big(2.0)^1 w[9] = @which big(2.0)^-1 @test all(getproperty.(w, :name) .=== :literal_pow) @test length(Set(w)) == length(w) # all methods distinct end end # PR 53713 if Int === Int64 # literal_pow only for exponents x: -2^63 <= x < 2^63 #53860 (all Int) @test (@which 2^-9223372036854775809).name === :^ @test (@which 2^-9223372036854775808).name === :literal_pow @test (@which 2^9223372036854775807).name === :literal_pow @test (@which 2^9223372036854775808).name === :^ elseif Int === Int32 # literal_pow only for exponents x: -2^31 <= x < 2^31 #53860 (all Int) @test (@which 2^-2147483649).name === :^ @test (@which 2^-2147483648).name === :literal_pow @test (@which 2^2147483647).name === :literal_pow @test (@which 2^2147483648).name === :^ end # issue #13464 try @which x = 1 error("unexpected") catch err13464 @test startswith(err13464.msg, "expression is not a function call") end @testset "Single-argument forms" begin a = which(+, (Int, Int)) b = which((typeof(+), Int, Int)) c = which(Tuple{typeof(+), Int, Int}) @test a == b == c a = functionloc(+, (Int, Int)) b = functionloc((typeof(+), Int, Int)) c = functionloc(Tuple{typeof(+), Int, Int}) @test a == b == c end # PR 57909 @testset "Support for type annotations as arguments" begin @testset "`getindex`/`setindex!`" begin @test (@which (::Vector{Int})[::Int]).name === :getindex @test (@which [1, 2][::Int]).name === :getindex @test (@which [1 2][begin, ::Int]).name === :getindex @test (@which [1 2][::Int, end]).name === :getindex @test (@which [1 2][begin, end]).name === :getindex @test (@which [1 2][1:end]).name === :getindex @test (@which [1 2][begin:end]).name === :getindex @test_throws "`begin` or `end` cannot be used" (@which (::Vector{Int})[begin]) @test_throws "`begin` or `end` cannot be used" (@which (::Vector{Int})[end]) @test (@which (::Vector{Int})[::Int] = ::Int).name === :setindex! end @testset "`getproperty`/`setproperty!`" begin @test (@which (::Base.RefValue{Int}).x).name === :getproperty @test (@which (::Base.RefValue{Int}).x = ::Int).name === :setproperty! end @testset "Array syntax" begin @test (@which [::Int]).name === :vect @test (@which [undef_var::Int]).name === :vect @test (@which [::Int 2]).name === :hcat @test (@which [::Int; 2]).name === :vcat @test (@which Int[::Int 2]).name === :typed_hcat @test (@which Int[::Int; 2]).name === :typed_vcat @test (@which [::Int 2;3 (::Int)]).name === :hvcat @test (@which Int[::Int 2;3 (::Int)]).name === :typed_hvcat @test (@which (::Type{Int})[::Int 2;3 (::Int)]).name === :typed_hvcat @test (@which (::Type{T})[::T 2;3 (::T)] where {T<:Real}).name === :typed_hvcat end @test (@which (::Float64)^2).name === :literal_pow @test (@which (::Vector{Float64})').name === :adjoint @test (@which "$(::Symbol) is a symbol").sig === Tuple{typeof(string), Vararg{Union{Char, String, Symbol}}} @test (@which (::Int)^4).name === :literal_pow @test (@which +(some_x::Int, some_y::Float64)).name === :+ @test (@which +(::Any, ::Any, ::Any, ::Any...)).sig === Tuple{typeof(+), Any, Any, Any, Vararg{Any}} @test (@which +(::Any, ::Any, ::Any, ::Vararg{Any})).sig === Tuple{typeof(+), Any, Any, Any, Vararg{Any}} n = length(@code_typed +(::Float64, ::Vararg{Float64})) @test n ≥ 2 @test length(@code_typed +(::Float64, ::Float64...)) == n @test (@which +(1, ::Float64)).sig === Tuple{typeof(+), Number, Number} @test (@which +((1, 2)...)).name === :+ @test (@which (::typeof(+))(::Int, ::Float64)).sig === Tuple{typeof(+), Number, Number} @test (@code_typed .+(::Float64, ::Vector{Float64})) isa Pair @test (@code_typed .+(::Float64, .*(::Vector{Float64}, ::Int))) isa Pair @test (@which +(::T, ::T) where {T<:Number}).sig === Tuple{typeof(+), T, T} where {T<:Number} @testset "Keyword arguments" begin @test (@which round(::Float64; digits=3)).name === :round @test (@which round(1.2; digits = ::Int)).name === :round @test (@which round(1.2; digits::Int)).name === :round @test (@code_typed round(::T; digits = ::T) where {T<:Float64})[2] === Union{} @test (@code_typed round(::T; digits = ::T) where {T<:Int})[2] === Float64 base = 10 kwargs_1 = (; digits = 3) kwargs_2 = (; sigdigits = 3) @test (@which round(1.2; kwargs_1...)).name === :round @test (@which round(1.2; digits = 1, kwargs_1...)).name === :round @test (@code_typed round(1.2; digits = ::Float64, kwargs_1...))[2] === Float64 # picks `3::Int` from `kwargs_1` @test (@code_typed round(1.2; kwargs_1..., digits = ::Float64))[2] === Union{} # picks `::Float64` from parameters @test (@which round(1.2; digits = ::Float64, kwargs_1...)).name === :round @test (@which round(1.2; sigdigits = ::Int, kwargs_1...)).name === :round @test (@which round(1.2; kwargs_1..., kwargs_2..., base)).name === :round end @testset "Broadcasting" begin @test (@code_typed optimize=false round.([1.0, 2.0]; digits = ::Int64))[2] == Vector{Float64} @test (@code_typed optimize=false round.(::Vector{Float64}, base = 2; digits = ::Int64))[2] == Vector{Float64} @test (@code_typed optimize=false round.(base = ::Int64, ::Vector{Float64}; digits = ::Int64))[2] == Vector{Float64} @test (@code_typed optimize=false [1, 2] .= ::Int)[2] == Vector{Int} @test (@code_typed optimize=false ::Vector{Int} .= ::Int)[2] == Vector{Int} @test (@code_typed optimize=false ::Vector{Float64} .= 1 .+ ::Vector{Int})[2] == Vector{Float64} @test (@code_typed optimize=false ::Vector{Float64} .= 1 .+ round.(base = ::Int, ::Vector{Int}; digits = 3))[2] == Vector{Float64} end @testset "Callable objects" begin @test (@code_typed (::Base.Fix2{typeof(+), Float64})(3))[2] === Float64 @test (@code_typed optimize=false (::Returns{Float64})(::Int64; name::String))[2] === Float64 @test (@code_typed (::Returns{T})(3.0) where {T<:Real})[2] === Real end @testset "Opaque closures" begin opaque_f(@nospecialize(x::Type), @nospecialize(y::Type)) = sizeof(x) == sizeof(y) src, _ = only(code_typed(opaque_f, (Type, Type))) src.slottypes[1] = Tuple{} # from CodeInfo oc = Core.OpaqueClosure(src; sig = Tuple{Type, Type}, rettype = Bool, nargs = 2) ret = @code_typed oc(Int64, Float64) @test [ret] == code_typed(oc) _, rt = ret @test rt === Bool # from optimized IR ir = Core.Compiler.inflate_ir(src) oc = Core.OpaqueClosure(ir) ret = @code_typed oc(Int64, Float64) @test [ret] == code_typed(oc) _, rt = ret @test rt === Bool end @testset "Vararg handling" begin @test_throws "More than one `Core.Vararg`" @eval @code_typed +(1, 2::Vararg{Int}, 3, 4::Vararg{Float64}) @test_throws "Inconsistent type `Float64`" @eval @code_typed +(1, 2, 3, 4::Vararg{Int}, 5.0) @test_throws "Inconsistent type `Float64`" @eval @code_typed +(1, 2, 3, 4::Int..., 5.0) @test_throws "Inconsistent type `Float64`" @eval @code_typed +(1, 2, 3, 4::Vararg{Int}, ::Float64) @test_throws "Inconsistent type `Any`" @eval @code_typed +(1, 2, 3, 4::Vararg{Int}, ::Any) @test_throws r"at most 2 types .* found 3 instead" @eval @code_typed +(1, 2, 3, 4::Vararg{Int,2}, 5, 6) @test (@code_typed +(1, 2, 3, 4::Vararg{Int}))[2] === Int @test (@code_typed +(1, 2, 3, 4::Vararg{Int}, 5))[2] === Int @test (@code_typed +(1, 2, 3, 4::Vararg{Int, 3}))[2] === Int @test (@code_typed +(1, 2, 3, 4::Vararg{Int, 3}, 5))[2] === Int @test (@code_typed +(1, 2, 3, 4::Vararg{Int, 3}, 5, 6))[2] === Int @test (@code_typed +(1, 2, 3, 4::Vararg))[2] === Any @test (@code_typed +(1, 2, 3, 4::Vararg, 5.0))[2] === Any @test (@code_typed +(1, 2, 3, ::Int...))[2] === Int @test (@code_typed +(1, 2, 3, ::Int..., 5))[2] === Int # We just ignore the checks with `where` parameters for simplicity of implementation. @test isa((@code_typed +(::T, ::Vararg{T}, ::T) where {T}), Vector{Any}) @test isa((@code_typed +(::T, ::Vararg{T}, ::Float64) where {T<:Real}), Vector{Any}) @test isa((@code_typed +(::T, ::Vararg{T}, ::Float64) where {T<:Real}), Vector{Any}) @test isa((@code_typed +(::T, ::Vararg{T}, ::Int) where {T<:Real}), Vector{Any}) @test isa((@code_typed +(::T, ::Vararg{Vector{T}}, ::Int) where {T<:Real}), Vector{Any}) @test isa((@code_typed +(::T, ::Vararg{Int}, ::T) where {T<:Real}), Vector{Any}) end end module HygieneTest const Int = Float64 using InteractiveUtils: @code_typed macro escape_argument(ex) :(@code_typed $(esc(ex)) * 2.0) end macro escape_type_annotation(ex) :(@code_typed identity(::(typeof($(esc(ex)))))) end macro escape_all(ex) esc(:(@__MODULE__().@code_typed $ex)) end end # module (; var"@escape_argument", var"@escape_type_annotation", var"@escape_all") = HygieneTest @testset "Macro hygiene interactions" begin _f = sum @test (@escape_argument _f(Int[]))[2] == Float64 @test (@escape_type_annotation _f(Int[]))[2] == Int @test (@escape_all _f(Int[]))[2] == Int end module MacroTest var"@which" = parentmodule(@__MODULE__).var"@which" macro macrotest(x::Int, y::Symbol) end macro macrotest(x::Int, y::Int) end end let a = 1 m = MacroTest.var"@macrotest" @test which(m, Tuple{LineNumberNode, Module, Int, Symbol}) == @eval MacroTest @which @macrotest 1 a @test which(m, Tuple{LineNumberNode, Module, Int, Int}) == @eval MacroTest @which @macrotest 1 1 @test first(methods(m, Tuple{LineNumberNode, Module, Int, Int})) == @which MacroTest.@macrotest 1 1 @test functionloc(@eval MacroTest @which @macrotest 1 1) == @functionloc MacroTest.@macrotest 1 1 end mutable struct A18434 end A18434(x; y=1) = 1 global counter18434 = 0 function get_A18434() global counter18434 counter18434 += 1 return A18434 end @which get_A18434()(1; y=2) @test counter18434 == 1 @which get_A18434()(1, y=2) @test counter18434 == 2 let _true = Ref(true), f, g, h @noinline f() = ccall((:time, "error_library_doesnt_exist\0"), Cvoid, ()) # should throw error during runtime @noinline g() = _true[] ? 0 : h() @noinline h() = (g(); f()) @test g() == 0 @test_throws ErrorException h() end # manually generate a broken function, which will break codegen # and make sure Julia doesn't crash (when using a non-asserts build) is_asserts() = ccall(:jl_is_assertsbuild, Cint, ()) == 1 if !is_asserts() @eval @noinline Base.@constprop :none f_broken_code() = 0 let m = which(f_broken_code, ()) let src = Base.uncompressed_ast(m) src.code = Any[ Expr(:meta, :noinline) Core.ReturnNode(Expr(:invalid)) ] m.source = src end end _true = true # and show that we can still work around it @noinline g_broken_code() = _true ? 0 : h_broken_code() @noinline h_broken_code() = (g_broken_code(); f_broken_code()) let errf = tempname(), old_stderr = stderr, new_stderr = open(errf, "w") try redirect_stderr(new_stderr) @test occursin("f_broken_code", sprint(code_native, h_broken_code, ())) Libc.flush_cstdio() println(new_stderr, "start") flush(new_stderr) @test_throws "could not compile the specified method" sprint(io -> code_native(io, f_broken_code, (), dump_module=true)) Libc.flush_cstdio() println(new_stderr, "middle") flush(new_stderr) @test !isempty(sprint(io -> code_native(io, f_broken_code, (), dump_module=false))) Libc.flush_cstdio() println(new_stderr, "later") flush(new_stderr) @test invokelatest(g_broken_code) == 0 Libc.flush_cstdio() println(new_stderr, "end") flush(new_stderr) finally Libc.flush_cstdio() redirect_stderr(old_stderr) close(new_stderr) let errstr = read(errf, String) @test startswith(errstr, """start Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) context=errstr @test occursin("""\nmiddle Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """, errstr) context=errstr @test occursin("""\nlater Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """, errstr) context=errstr @test endswith(errstr, "\nend\n") context=errstr end rm(errf) end end end # Issue #33163 A33163(x; y) = x + y B33163(x) = x let (@code_typed A33163(1, y=2))[1] (@code_typed optimize=false A33163(1, y=2))[1] (@code_typed optimize=false B33163(1))[1] end @test_throws MethodError (@code_lowered wrongkeyword=true 3 + 4) # Issue #14637 @test (@which Base.Base.Base.nothing) == Core @test_throws ErrorException (@functionloc Base.nothing) @test (@code_typed (3//4).num)[2] == Int struct A14637 x end a14637 = A14637(0) @test (@which a14637.x).name === :getproperty @test (@functionloc a14637.x)[2] isa Integer # Issue #28615 @test_throws ErrorException (@which [1, 2] .+ [3, 4]) @test (@code_typed optimize=true max.([1,7], UInt.([4])))[2] == Vector{UInt} @test (@code_typed Ref.([1,2])[1].x)[2] == Int @test (@code_typed max.(Ref(true).x))[2] == Bool @test (@code_typed optimize=false round.([1.0, 2.0]; digits = 3))[2] == Vector{Float64} @test (@code_typed optimize=false round.([1.0, 2.0], base = 2; digits = 3))[2] == Vector{Float64} @test (@code_typed optimize=false round.(base = 2, [1.0, 2.0], digits = 3))[2] == Vector{Float64} @test (@code_typed optimize=false [1, 2] .= 2)[2] == Vector{Int} @test (@code_typed optimize=false [1, 2] .<<= 2)[2] == Vector{Int} @test (@code_typed optimize=false [1, 2.0] .= 1 .+ [2, 3])[2] == Vector{Float64} @test (@code_typed optimize=false [1, 2.0] .= 1 .+ round.(base = 1, [1, 3]; digits = 3))[2] == Vector{Float64} @test (@code_typed optimize=false [1] .+ [2])[2] == Vector{Int} @test !isempty(@code_typed optimize=false max.(Ref.([5, 6])...)) expansion = string(@macroexpand @code_typed optimize=false max.(Ref.([5, 6])...)) @test contains(expansion, "(x1) =") # presence of wrapper function # Make sure broadcasts in nested arguments are not processed. v = Any[1] expansion = string(@macroexpand @code_typed v[1] = rand.(Ref(1))) @test contains(expansion, "Core.Typeof(rand.(Ref(1)))") @test !contains(expansion, "(x1) =") # Issue # 45889 @test !isempty(@code_typed 3 .+ 6) @test !isempty(@code_typed 3 .+ 6 .+ 7) @test !isempty(@code_typed optimize=false (.- [3,4])) @test !isempty(@code_typed optimize=false (6 .- [3,4])) @test !isempty(@code_typed optimize=false (.- 0.5)) # Issue #36261 @test (@code_typed max.(1 .+ 3, 5 - 7))[2] == Int f36261(x,y) = 3x + 4y A36261 = Float64[1.0, 2.0, 3.0] let @code_typed f36261.(A36261, pi)[1] @code_typed f36261.(A36261, 1 .+ pi)[1] @code_typed f36261.(A36261, 1 + pi)[1] end module ReflectionTest using Test, Random, InteractiveUtils function test_ast_reflection(freflect, f, types) @test !isempty(freflect(f, types)) nothing end function test_bin_reflection(freflect, f, types) iob = IOBuffer() freflect(iob, f, types) str = String(take!(iob)) @test !isempty(str) nothing end function test_code_reflection(freflect, f, types, tester) tester(freflect, f, types) tester(freflect, f, (types.parameters...,)) nothing end function test_code_reflections(tester, freflect) test_code_reflection(freflect, occursin, Tuple{Regex, AbstractString}, tester) # abstract type test_code_reflection(freflect, +, Tuple{Int, Int}, tester) # leaftype signature test_code_reflection(freflect, +, Tuple{Array{Float32}, Array{Float32}}, tester) # incomplete types test_code_reflection(freflect, Module, Tuple{}, tester) # Module() constructor (transforms to call) test_code_reflection(freflect, Array{Int64}, Tuple{Array{Int32}}, tester) # with incomplete types test_code_reflection(freflect, muladd, Tuple{Float64, Float64, Float64}, tester) end test_code_reflections(test_bin_reflection, code_llvm) test_code_reflections(test_bin_reflection, code_native) end # module ReflectionTest @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 ErrorException("argument tuple type must contain only types") code_native(sum, (Int64,1)) @test_throws ErrorException("expected tuple type") code_native(sum, Vector{Int64}) # Issue #18883, code_llvm/code_native for generated functions @generated f18883() = nothing @test !isempty(sprint(code_llvm, f18883, Tuple{})) @test !isempty(sprint(code_llvm, (typeof(f18883),))) @test !isempty(sprint(code_native, f18883, Tuple{})) @test !isempty(sprint(code_native, (typeof(f18883),))) ix86 = r"i[356]86" if Sys.ARCH === :x86_64 || occursin(ix86, string(Sys.ARCH)) function linear_foo() return 5 end rgx = r"%" buf = IOBuffer() #test that the string output is at&t syntax by checking for occurrences of '%'s code_native(buf, linear_foo, (), syntax = :att, debuginfo = :none) output = replace(String(take!(buf)), r"#[^\r\n]+" => "") @test occursin(rgx, output) #test that the code output is intel syntax by checking it has no occurrences of '%' code_native(buf, linear_foo, (), syntax = :intel, debuginfo = :none) output = replace(String(take!(buf)), r"#[^\r\n]+" => "") @test !occursin(rgx, output) code_native(buf, linear_foo, (), debuginfo = :none) output = replace(String(take!(buf)), r"#[^\r\n]+" => "") @test !occursin(rgx, output) @testset "binary" begin # check the RET instruction (opcode: C3) ret = r"^; [0-9a-f]{4}: c3$"m # without binary flag (default) code_native(buf, linear_foo, (), dump_module=false) output = String(take!(buf)) @test !occursin(ret, output) # with binary flag for binary in false:true code_native(buf, linear_foo, (); binary, dump_module=false) output = String(take!(buf)) @test occursin(ret, output) == binary end end end @testset "error message" begin err = ErrorException("expression is not a function call or symbol") @test_throws err @code_lowered "" @test_throws err @code_lowered 1 @test_throws err @code_lowered 1.0 @test_throws "dot expressions are not lowered to a single function call" @which a .= 1 + 2 @test_throws "invalid keyword argument syntax" @eval @which round(1; digits(3)) end using InteractiveUtils: editor # Issue #13032 withenv("JULIA_EDITOR" => nothing, "VISUAL" => nothing, "EDITOR" => nothing) do # Make sure editor doesn't error when no ENV editor is set. @test isa(editor(), Cmd) # Invalid editor ENV["JULIA_EDITOR"] = "" @test_throws ErrorException editor() # Note: The following testcases should work regardless of whether these editors are # installed or not. # Editor on the path. ENV["JULIA_EDITOR"] = "vim" @test editor() == `vim` # Absolute path to editor. ENV["JULIA_EDITOR"] = "/usr/bin/vim" @test editor() == `/usr/bin/vim` # Editor on the path using arguments. ENV["JULIA_EDITOR"] = "subl -w" @test editor() == `subl -w` # Absolute path to editor with spaces. ENV["JULIA_EDITOR"] = "/Applications/Sublime\\ Text.app/Contents/SharedSupport/bin/subl" @test editor() == `'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl'` # Paths with spaces and arguments (#13032). ENV["JULIA_EDITOR"] = "/Applications/Sublime\\ Text.app/Contents/SharedSupport/bin/subl -w" @test editor() == `'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl' -w` ENV["JULIA_EDITOR"] = "'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl' -w" @test editor() == `'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl' -w` ENV["JULIA_EDITOR"] = "\"/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl\" -w" @test editor() == `'/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl' -w` end # clipboard functionality if Sys.isapple() let str = "abc\0def" clipboard(str) @test clipboard() == str end end if Sys.iswindows() || Sys.isapple() for str in ("Hello, world.", "∀ x ∃ y", "") clipboard(str) @test clipboard() == str end end @static if Sys.iswindows() @test_broken false # CI has trouble with this test ## concurrent access error #hDesktop = ccall((:GetDesktopWindow, "user32"), stdcall, Ptr{Cvoid}, ()) #ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), hDesktop) == 0 && Base.windowserror("OpenClipboard") #try # @test_throws Base.SystemError("OpenClipboard", 0, Base.WindowsErrorInfo(0x00000005, nothing)) clipboard() # ACCESS_DENIED #finally # ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror("CloseClipboard") #end # empty clipboard failure ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL) == 0 && Base.windowserror("OpenClipboard") try ccall((:EmptyClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror("EmptyClipboard") finally ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror("CloseClipboard") end @test clipboard() == "" # nul error (unsupported data) @test_throws ArgumentError("Windows clipboard strings cannot contain NUL character") clipboard("abc\0") end # buildbot path updating file, ln = functionloc(versioninfo, Tuple{}) @test isfile(file) @test isfile(pathof(InteractiveUtils)) @test isdir(pkgdir(InteractiveUtils)) module ModuleWithoutPathForEdit end @test_throws ErrorException("could not find source file for module: $(ModuleWithoutPathForEdit)") edit(ModuleWithoutPathForEdit) # compiler stdlib path updating file, ln = functionloc(Core.Compiler.tmeet, Tuple{Int, Float64}) @test isfile(file) @testset "buildbot path updating" begin file, ln = functionloc(versioninfo, Tuple{}) @test isfile(file) e = try versioninfo("wat") catch e e end @test e isa MethodError m = @which versioninfo() s = sprint(showerror, e) m = match(Regex("@ .+ (.*?):$(m.line)"), s) @test isfile(expanduser(m.captures[1])) g() = x e, bt = try code_llvm(g, Tuple{Int}) catch e e, catch_backtrace() end @test e isa Exception s = sprint(showerror, e, bt) m = match(r"(\S*InteractiveUtils[\/\\]src\S*):", s) @test isfile(expanduser(m.captures[1])) end @testset "Issue #34434" begin io = IOBuffer() code_native(io, eltype, Tuple{Int}) @test occursin("eltype", String(take!(io))) end @testset "Issue #41010" begin struct A41010 end struct B41010 a::A41010 end export B41010 ms = methodswith(A41010, @__MODULE__) |> collect @test ms[1].name === :B41010 end # macro options should accept both literals and variables let opt = false @test length(first(@code_typed optimize=opt sum(1:10)).code) == length((@code_lowered sum(1:10)).code) end @testset "@time_imports, @trace_compile, @trace_dispatch" begin mktempdir() do dir cd(dir) do try pushfirst!(LOAD_PATH, dir) foo_file = joinpath(dir, "Foo3242.jl") write(foo_file, """ module Foo3242 function foo() Base.Experimental.@force_compile foo(1) end foo(x) = x function bar() Base.Experimental.@force_compile bar(1) end bar(x) = x end """) Base.compilecache(Base.PkgId("Foo3242")) fname = tempname() f = open(fname, "w") redirect_stdout(f) do @eval @time_imports using Foo3242 end close(f) buf = read(fname) rm(fname) @test occursin("ms Foo3242", String(buf)) fname = tempname() f = open(fname, "w") redirect_stderr(f) do @trace_compile @eval Foo3242.foo() end close(f) buf = read(fname) rm(fname) @test occursin("ms =# precompile(", String(buf)) fname = tempname() f = open(fname, "w") redirect_stderr(f) do @trace_dispatch @eval Foo3242.bar() end close(f) buf = read(fname) rm(fname) @test occursin("precompile(", String(buf)) finally filter!((≠)(dir), LOAD_PATH) end end end end let # `default_tt` should work with any function with one method @test (code_warntype(devnull, function () sin(42) end); true) @test (code_warntype(devnull, function (a::Int) sin(a) end); true) @test (code_llvm(devnull, function () sin(42) end); true) @test (code_llvm(devnull, function (a::Int) sin(a) end); true) @test (code_native(devnull, function () sin(42) end); true) @test (code_native(devnull, function (a::Int) sin(a) end); true) end let # specifying calls as argtypes (incl. arg0) should be supported @test (code_warntype(devnull, (typeof(function () sin(42) end),)); true) @test (code_warntype(devnull, (typeof(function (a::Int) sin(42) end), Int)); true) @test (code_llvm(devnull, (typeof(function () sin(42) end),)); true) @test (code_llvm(devnull, (typeof(function (a::Int) sin(42) end), Int)); true) @test (code_native(devnull, (typeof(function () sin(42) end),)); true) @test (code_native(devnull, (typeof(function (a::Int) sin(42) end), Int)); true) end @testset "code_llvm on opaque_closure" begin let ci = code_typed(+, (Int, Int))[1][1] ir = Core.Compiler.inflate_ir(ci) ir.argtypes[1] = Tuple{} @test ir.debuginfo.def === nothing ir.debuginfo.def = Symbol(@__FILE__) oc = Core.OpaqueClosure(ir) @test (code_llvm(devnull, oc, Tuple{Int, Int}); true) let io = IOBuffer() code_llvm(io, oc, Tuple{}) @test occursin(InteractiveUtils.OC_MISMATCH_WARNING, String(take!(io))) end end end @testset "begin/end in gen_call_with_extracted_types users" begin mktemp() do f, io redirect_stdout(io) do a = [1,2] @test (@code_typed a[1:end]).second == Vector{Int} @test (@code_llvm a[begin:2]) === nothing @test (@code_native a[begin:end]) === nothing end end end @test Base.infer_effects(sin, (Int,)) == InteractiveUtils.@infer_effects sin(42) @test Base.infer_return_type(sin, (Int,)) == InteractiveUtils.@infer_return_type sin(42) @test Base.infer_exception_type(sin, (Int,)) == InteractiveUtils.@infer_exception_type sin(42) @test first(InteractiveUtils.@code_ircode sin(42)) isa Core.Compiler.IRCode @test first(InteractiveUtils.@code_ircode optimize_until="CC: INLINING" sin(42)) isa Core.Compiler.IRCode # Test.@inferred also uses `gen_call_with_extracted_types` @test Test.@inferred round(1.2) isa Float64 @test Test.@inferred round(1.3; digits = 3) isa Float64 # ensure proper inference of the macro output of `@inferred` @test Base.infer_return_type(x -> Test.@inferred(round(x)), (Float64,)) === Float64 @test Base.infer_return_type(x -> Test.@inferred(round(x; digits = 3)), (Float64,)) === Float64 @testset "Docstrings" begin @test isempty(Docs.undocumented_names(InteractiveUtils)) end # issue https://github.com/JuliaIO/ImageMagick.jl/issues/235 module OuterModule module InternalModule struct MyType x::Int end Base.@deprecate_binding MyOldType MyType export MyType end using .InternalModule export MyType, MyOldType end # module @testset "Subtypes and deprecations" begin using .OuterModule @test_nowarn subtypes(Integer); end let code = """ using InteractiveUtils @activate Compiler[:codegen, :reflection] println("done compiling") """ orig_compiler = realpath(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "Compiler")) mktempdir() do dir new_compiler = joinpath(dir, "Compiler") cp(orig_compiler, new_compiler) output = read(`$(Base.julia_cmd()) -g0 -O0 --startup-file=no --project=$(new_compiler) -e $code`, String) @test occursin("done compiling", output) end end var_line = @__LINE__()+1 """ docs for a global variable """ const _interactiveutils_some_var_ = 0 @test InteractiveUtils.varloc(@__MODULE__, :_interactiveutils_some_var_) == (@__FILE__, var_line) @testset "world argument for varinfo and subtypes" begin # varinfo: a non-const binding added after the recorded world should not # appear when querying that older world (its partition does not yet exist # at world_no_var, so `isdefined` in that world returns false). M_varinfo = @eval module $(gensym()) end world_no_var = Base.get_world_counter() @eval M_varinfo begin export tracked_var tracked_var = 42 end @test occursin("tracked_var", repr(varinfo(M_varinfo))) @test !occursin("tracked_var", repr(varinfo(M_varinfo; world=world_no_var))) # subtypes: subtype added after the recorded world should not appear when # querying that older world. M_sub = @eval module $(gensym()) abstract type MyAbstractParent end end world_no_subtype = Base.get_world_counter() @eval M_sub struct MyConcreteChild <: MyAbstractParent end @test length(subtypes(M_sub, M_sub.MyAbstractParent)) == 1 @test isempty(subtypes(M_sub, M_sub.MyAbstractParent; world=world_no_subtype)) end