Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support @deprecateing qualified function names #44394

Merged
merged 5 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 33 additions & 19 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,43 +54,57 @@ will define and deprecate a method `old(x::Int)` that mirrors `new(x::Int)` but
define nor deprecate the method `old(x::Float64)`.
"""
macro deprecate(old, new, export_old=true)
function cannot_export_nonsymbol()
error(
"if the third `export_old` argument is not specified or `true`, the first",
" argument must be of form",
" (1) `f(...)` where `f` is a symbol,",
" (2) `T{...}(...)` where `T` is a symbol, or",
" (3) a symbol.",
)
end
meta = Expr(:meta, :noinline)
if isa(old, Symbol)
oldname = Expr(:quote, old)
newname = Expr(:quote, new)
Expr(:toplevel,
export_old ? Expr(:export, esc(old)) : nothing,
:(function $(esc(old))(args...)
$meta
depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name)
$(esc(new))(args...)
end))
elseif isa(old, Expr) && (old.head === :call || old.head === :where)
if isa(old, Expr) && (old.head === :call || old.head === :where)
remove_linenums!(new)
oldcall = sprint(show_unquoted, old)
newcall = sprint(show_unquoted, new)
# if old.head is a :where, step down one level to the :call to avoid code duplication below
callexpr = old.head === :call ? old : old.args[1]
if callexpr.head === :call
if isa(callexpr.args[1], Symbol)
oldsym = callexpr.args[1]::Symbol
elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head === :curly
oldsym = callexpr.args[1].args[1]::Symbol
fnexpr = callexpr.args[1]
if fnexpr isa Expr && fnexpr.head === :curly
fnexpr = fnexpr.args[1]
end
if export_old
if fnexpr isa Symbol
maybe_export = Expr(:export, esc(fnexpr))
else
cannot_export_nonsymbol()
end
else
error("invalid usage of @deprecate")
maybe_export = nothing
end
else
error("invalid usage of @deprecate")
end
Expr(:toplevel,
export_old ? Expr(:export, esc(oldsym)) : nothing,
maybe_export,
:($(esc(old)) = begin
$meta
depwarn($"`$oldcall` is deprecated, use `$newcall` instead.", Core.Typeof($(esc(oldsym))).name.mt.name)
depwarn($"`$oldcall` is deprecated, use `$newcall` instead.", Core.Typeof($(esc(fnexpr))).name.mt.name)
$(esc(new))
end))
else
error("invalid usage of @deprecate")
if export_old && !(old isa Symbol)
cannot_export_nonsymbol()
end
Expr(:toplevel,
export_old ? Expr(:export, esc(old)) : nothing,
:(function $(esc(old))(args...)
$meta
depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name)
$(esc(new))(args...)
end))
end
end

Expand Down
34 changes: 34 additions & 0 deletions test/deprecation_exec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ module DeprecationTests # to test @deprecate
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
Base.@deprecate_moved foo1234 "Foo"
Base.@deprecate_moved bar "Bar" false
Expand Down Expand Up @@ -74,13 +81,17 @@ begin # @deprecate

@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()

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)
Expand All @@ -89,6 +100,8 @@ begin # @deprecate
@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()
Expand Down Expand Up @@ -143,3 +156,24 @@ begin # tuple indexed by float deprecation
@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

@testset "@deprecated error message" begin
@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
# conciously define
@test_throws(
"invalid usage of @deprecate",
@eval @deprecate Foo{T} where {T <: Int} g true
)
end