/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/deprecation_exec.jl
350 строк
13 KB
Jameson Nash
module: warn consistently on deprecated bindings imported via `using` (#62450)
10 авг 2026, 16:15
Не верифицирован
10 авг 2026, 16:15
ffa3bc8
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license # Tests for deprecated functionality. # # These can't be run with --depwarn=error, so currently require special # treatment when run inside the test system. using Test using Logging module DeprecationTests # to test @deprecate f() = true const source_file = Symbol(@__FILE__) # test the Symbol path of @deprecate const f1_method_line = @__LINE__() + 1 @deprecate f1 f @deprecate f2 f false # test that f2 is not exported # test the Expr path of @deprecate const f3_method_line = @__LINE__() + 1 @deprecate f3() f() @deprecate f4() f() false # test that f4 is not exported @deprecate f5(x::T) where T f() # test deprecation of a constructor struct A{T} end @deprecate A{T}(x::S) where {T, S} f() module Sub f1() = true function f2 end end @deprecate Sub.f1() f() false @deprecate Sub.f2 f false # test that @deprecate_moved can be overridden by an import const foo1234_method_line = @__LINE__() + 1 Base.@deprecate_moved foo1234 "Foo" Base.@deprecate_moved bar "Bar" false # test that positional and keyword arguments are forwarded when # there is no explicit type annotation new_return_args(args...; kwargs...) = args, NamedTuple(kwargs) @deprecate old_return_args new_return_args end # module module Foo1234 export foo1234 foo1234(x) = x+1 end # issue #21972 struct T21972 @noinline function T21972() Base.depwarn("something", :T21972) new() end end # Create a consistent call frame for nowarn tests @noinline call(f, args...) = @noinline f(args...) # Given this is a sub-processed test file, not using @testsets avoids # leaking the report print into the Base test runner report begin # @deprecate using .DeprecationTests using .Foo1234 @test foo1234(3) == 4 @test_throws ErrorException DeprecationTests.bar(3) # 22845 ex = :(module M22845; import ..DeprecationTests: bar; bar(x::Number) = x + 3; end) @test_warn "importing deprecated binding" eval(ex) @Core.latestworld @test @test_nowarn(DeprecationTests.bar(4)) == 7 @test @test_warn "`f1` is deprecated, use `f` instead." f1() @test_throws UndefVarError f2() # not exported @test @test_warn "`f2` is deprecated, use `f` instead." DeprecationTests.f2() @test @test_warn "`f3()` is deprecated, use `f()` instead." f3() @test_throws UndefVarError f4() # not exported @test @test_warn "`f4()` is deprecated, use `f()` instead." DeprecationTests.f4() @test @test_warn "`f5(x::T) where T` is deprecated, use `f()` instead." f5(1) @test @test_warn "`A{T}(x::S) where {T, S}` is deprecated, use `f()` instead." A{Int}(1.) @test @test_warn "`Sub.f1()` is deprecated, use `f()` instead." DeprecationTests.Sub.f1() # @deprecate-generated methods should report the macro call site. let m = only(methods(DeprecationTests.f1)) @test m.file == DeprecationTests.source_file @test m.line == DeprecationTests.f1_method_line end let m = only(methods(DeprecationTests.f3)) @test m.file == DeprecationTests.source_file @test m.line == DeprecationTests.f3_method_line end let m = only(methods(DeprecationTests.foo1234)) @test m.file == DeprecationTests.source_file @test m.line == DeprecationTests.foo1234_method_line end redirect_stderr(devnull) do @test call(f1) @test call(DeprecationTests.f2) @test call(f3) @test call(DeprecationTests.f4) @test call(f5, 1) @test call(A{Int}, 1.) @test call(DeprecationTests.Sub.f1) @test call(DeprecationTests.Sub.f2) end @test @test_nowarn call(f1) @test @test_nowarn call(DeprecationTests.f2) @test @test_nowarn call(f3) @test @test_nowarn call(DeprecationTests.f4) @test @test_nowarn call(f5, 1) @test @test_nowarn call(A{Int}, 1.) @test @test_nowarn call(DeprecationTests.Sub.f1) @test @test_nowarn call(DeprecationTests.Sub.f2) # issue #21972 @noinline function f21972() T21972() end @test_deprecated "something" f21972() @noinline deprecated_typename(T) = Base.typename(T) @noinline deprecated_nameof(T) = nameof(T) @noinline deprecated_parentmodule(T) = parentmodule(T) @noinline deprecated_isabstracttype(T) = isabstracttype(T) deprecated_type_ref = Ref{Any}(Type{Int}) @test @test_warn "calling `typename` on `Type` is deprecated" deprecated_typename(deprecated_type_ref[]) === TypeEq.name @test @test_warn "calling `nameof` on `Type` is deprecated" deprecated_nameof(deprecated_type_ref[]) === :Type @test @test_warn "calling `parentmodule` on `Type` is deprecated" deprecated_parentmodule(deprecated_type_ref[]) === Core @test @test_warn "calling `isabstracttype` on a `Type{...}` is deprecated" deprecated_isabstracttype(deprecated_type_ref[]) === true # test that positional and keyword arguments are forwarded when # there is no explicit type annotation @test_logs (:warn,) @test DeprecationTests.old_return_args(1, 2, 3) == ((1, 2, 3),(;)) @test_logs (:warn,) @test DeprecationTests.old_return_args(1, 2, 3; a = 4, b = 5) == ((1, 2, 3), (a = 4, b = 5)) end f24658() = depwarn24658() depwarn24658() = Base.firstcaller(backtrace(), :_func_not_found_) begin # firstcaller # issue #24658 @test eval(:(if true; f24658(); end)) == (Ptr{Cvoid}(0),StackTraces.UNKNOWN) end # issue #25130 f25130() = Base.depwarn("f25130 message", :f25130) # The following test is for the depwarn behavior of expressions evaluated at # top-level, so we can't use the usual `collect_test_logs()` / `with_logger()` testlogger = Test.TestLogger() prev_logger = global_logger(testlogger) # Each call at top level should be distinct. This won't be true if they're # attributed to internal C frames (including generic dispatch machinery) f25130() f25130() testlogs = testlogger.logs @test length(testlogs) == 2 @test testlogs[1].id != testlogs[2].id @test testlogs[1].kwargs[:caller].func === Symbol("top-level scope") @test all(l.message == "f25130 message" for l in testlogs) global_logger(prev_logger) #------------------------------------------------------------------------------- # BEGIN 1.14 deprecations begin # SubString noshift deprecations @test_deprecated SubString{String}("abcd", 0, 1, Val(:noshift)) == "a" let ss = SubString("abcd", 2, 3) @test_deprecated (SubString{typeof(ss)}(ss, 0, 1, Val(:noshift)) == "b") @test_deprecated (SubString{typeof(ss)}(ss, 0, 1, Val(:noshift)) isa SubString{typeof(ss)}) end end # END 1.14 deprecations #------------------------------------------------------------------------------- # BEGIN 0.7 deprecations begin # parser syntax deprecations # #15524 # @test (@test_deprecated Meta.parse("for a=b f() end")) == :(for a=b; f() end) @test_broken length(Test.collect_test_logs(()->Meta.parse("for a=b f() end"))[1]) > 0 end # END 0.7 deprecations begin # tuple indexed by float deprecation @test_deprecated getindex((1,), 1.0) === 1 @test_deprecated getindex((1,2), 2.0) === 2 @test Base.JLOptions().depwarn == 1 @test_throws Exception @test_warn r"`getindex(t::Tuple, i::Real)` is deprecated" getindex((), 1.0) @test_throws Exception @test_warn r"`getindex(t::Tuple, i::Real)` is deprecated" getindex((1,2), 0.0) @test_throws Exception @test_warn r"`getindex(t::Tuple, i::Real)` is deprecated" getindex((1,2), -1.0) end begin #@deprecated error message @test_throws( "if the third `export_old` argument is not specified or `true`,", @eval @deprecate M.f() g() ) @test_throws( "if the third `export_old` argument is not specified or `true`,", @eval @deprecate M.f() g() true ) # Given `@deprecated Old{T} where {...} new`, it is unclear if we should generate # `Old{T}(args...) where {...} = new(args...)` or # `(Old{T} where {...})(args...) = new(args...)`. # Since nobody has requested this feature yet, make sure that it throws, until we # consciously define @test_throws( "invalid usage of @deprecate", @eval @deprecate Foo{T} where {T <: Int} g true ) end # A deprecated binding reached through `using` must warn on access, # consistently for constants and globals, matching a direct access and the # runtime `getglobal`. An explicit `import M: x` still suppresses the warning # (the import site is what should warn). The constant must still be # constant-folded despite the deprecation. module DeprecatedUsingTest using Test module Src export DepC const _real = 0x1234 Base.@deprecate_binding DepC _real false end using .Src read_dep() = DepC fold_dep() = DepC === 0x1234 ? true : nothing @test (@test_warn "DepC is deprecated" read_dep()) === Src._real @test (@test_warn "DepC is deprecated" getglobal(@__MODULE__, :DepC)) === Src._real @test Base.infer_return_type(fold_dep, Tuple{}) === Bool @test Base.isdeprecated(@__MODULE__, :DepC) ex = :(module Consumer; import ..Src: DepC; getdep() = DepC; end) @test_warn "importing deprecated binding Src.DepC into Consumer, use _real instead." eval(ex) @test (@test_nowarn Consumer.getdep()) === Src._real @test !Base.isdeprecated(Consumer, :DepC) end # Importing the same deprecated constant from two modules must unify by value, not report an # ambiguity, and still warn (with each source's deprecation message) on access. Constants with # unequal values are still ambiguous, and a non-deprecated peer still wins (no warning). module DeprecatedUsingUnifyTest using Test # Two deprecated constants with equal values. module A export DepC const _a = 0x123456789 Base.@deprecate_binding DepC _a false end module B export DepC const _b = 0x123456789 Base.@deprecate_binding DepC _b false end using .A, .B read2() = DepC fold2() = DepC === 0x123456789 ? true : nothing # The compiled/const-folded read and the dynamic read both warn, and the warning reports # each contributing source module's deprecation message. @test (@test_warn r"DepC is deprecated, use _a instead\., use _b instead\."s read2()) === 0x123456789 @test (@test_warn "DepC is deprecated, use _a instead." getglobal(@__MODULE__, :DepC)) === 0x123456789 @test Base.isdeprecated(@__MODULE__, :DepC) # The value still const-folds through the unified resolution. @test Base.infer_return_type(fold2, Tuple{}) === Bool # Two deprecated constants with unequal values stay ambiguous. module C export DiffC const _c = 0x0001 Base.@deprecate_binding DiffC _c false end module D export DiffC const _d = 0x0002 Base.@deprecate_binding DiffC _d false end using .C, .D @test_throws UndefVarError getglobal(@__MODULE__, :DiffC) # A deprecated and a non-deprecated constant naming the same value: # the non-deprecated one wins, so access should not warn. module E export MixC const _e = 0xff Base.@deprecate_binding MixC _e false end module F export MixC const MixC = 0xff end using .E, .F readmix() = MixC @test (@test_nowarn readmix()) === 0xff @test !Base.isdeprecated(@__MODULE__, :MixC) end # The deprecation is carried on the importer's partition for globals as well as constants, and # transparently through reexports: a `using` of a module that only reexports a deprecated # binding still reports it deprecated (`isdeprecated`) and warns on access, without walking to # the defining owner. An explicit `import` still suppresses the use-site warning. module DeprecatedReexportTest using Test module Src export DepC, DepG const _c = 0x1234 Base.@deprecate_binding DepC _c false newg() = 1 Base.@deprecate_binding DepG newg false ", use newg instead." false end module Reexport using ..Src export DepC, DepG # reexport both end using .Reexport readc() = DepC readg() = DepG # Reached through `using .Reexport`; the deprecation is visible on this module's partition. @test Base.isdeprecated(@__MODULE__, :DepC) @test Base.isdeprecated(@__MODULE__, :DepG) @test (@test_warn "DepC is deprecated" readc()) === Src._c @test (@test_warn "DepG is deprecated, use newg instead." readg()) === Src.newg # An explicit selective import of the reexported binding suppresses the use-site warning. ex = :(module Consumer; import ..Reexport: DepC; getdep() = DepC; end) @test_warn "importing deprecated binding" eval(ex) @test (@test_nowarn Consumer.getdep()) === Src._c @test !Base.isdeprecated(Consumer, :DepC) end