diff --git a/Project.toml b/Project.toml index 2e32a9c02..bbf0c04cd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Compat" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.46.2" +version = "3.47.0" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/README.md b/README.md index 56dfa1381..57a73f2b4 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,9 @@ changes in `julia`. ## Supported features + +* `@compat public foo, bar` marks `foo` and `bar` as public in Julia 1.11+ and is a no-op in Julia 1.10 and earlier. ([#50105]) (since Compat 3.47.0) + * `popat!` removes the item at the given `i` and returns it ([#36070]). (since Compat 3.45.0) @@ -325,3 +328,4 @@ Note that you should specify the correct minimum version for `Compat` in the [#39245]: https://github.com/JuliaLang/julia/issues/39245 [#42351]: https://github.com/JuliaLang/julia/issues/42351 [#43334]: https://github.com/JuliaLang/julia/issues/43334 +[#50105]: https://github.com/JuliaLang/julia/issues/50105 diff --git a/src/compatmacro.jl b/src/compatmacro.jl index bcc663e23..e5f9c8a95 100644 --- a/src/compatmacro.jl +++ b/src/compatmacro.jl @@ -20,11 +20,11 @@ function _compat(ex::Expr) @static if VERSION < v"1.7.0-DEV.364" if Meta.isexpr(ex, :(=)) && Meta.isexpr(ex.args[1], :tuple) && Meta.isexpr(ex.args[1].args[1], :parameters) - + ex = _destructure_named_tuple(ex) end end - + return Expr(ex.head, map(_compat, ex.args)...) end @@ -116,3 +116,46 @@ function _destructure_named_tuple(ex::Expr) push!(ex.args, values) return ex end + +# https://github.com/JuliaLang/julia/pull/50105 +# No ambiguity with import because this requires the first argument to be a symbol. +macro compat(public::Symbol, symbols_expr::Union{Expr, Symbol}) + public == :public || throw(ArgumentError("Invalid Syntax: `@compat $public $symbols_expr`")) + symbols = _get_symbols(symbols_expr) + if VERSION >= v"1.11.0-DEV.469" + esc(Expr(:public, symbols...)) + end +end + +""" + _valid_macro(expr) + +Check if `expr` is a valid macro call with no arguments. +""" +_valid_macro(expr) = Meta.isexpr(expr, :macrocall) && length(expr.args) == 2 && + expr.args[1] isa Symbol && string(expr.args[1])[1] == '@' && + expr.args[2] isa LineNumberNode + +_var_str_macro(expr) = Meta.isexpr(expr, :macrocall) && length(expr.args) == 3 && + expr.args[1] === Symbol("@var_str") && + expr.args[2] isa LineNumberNode + +_get_symbols(symbol::Symbol) = [symbol] +function _get_symbols(expr::Expr) + _valid_macro(expr) && return [expr.args[1]] + _var_str_macro(expr) && return [Symbol(expr.args[3])] + expr.head == :tuple || throw(ArgumentError("cannot mark `$expr` as public. Try `@compat public foo, bar`.")) + symbols = Vector{Symbol}(undef, length(expr.args)) + for (i, arg) in enumerate(expr.args) + if arg isa Symbol + symbols[i] = arg + elseif _valid_macro(arg) + symbols[i] = arg.args[1] + elseif _var_str_macro(arg) + symbols[i] = Symbol(expr.args[3]) + else + throw(ArgumentError("cannot mark `$arg` as public. Try `@compat public foo, bar`.")) + end + end + symbols +end diff --git a/test/runtests.jl b/test/runtests.jl index a1835e51e..d06760013 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1585,3 +1585,35 @@ end @test_throws ArgumentError stack([]) @test_throws ArgumentError stack(x for x in 1:3 if false) end + + +module Mod50105 + using Compat + @compat public foo, var"#", baz + @compat public var"blah" + @compat public @mac1 + @compat public f00, @mac2, @mac3 + @compat public @mac4, @mac5 +end + +# https://github.com/JuliaLang/julia/pull/50105 +@testset "@compat public" begin + @compat public foo_50105 + # foo_50105 = 4 # Uncommenting this line would cause errors due to https://github.com/JuliaLang/julia/issues/51325 + @test Base.isexported(@__MODULE__, :foo_50105) === false + VERSION >= v"1.11.0-DEV.469" && @test Base.ispublic(@__MODULE__, :foo_50105) + for sym in [:foo, Symbol("#"), :baz, Symbol("@mac1"), :f00, Symbol("@mac2"), Symbol("@mac3"), Symbol("@mac4"), Symbol("@mac5")] + @test Base.isexported(Mod50105, sym) === false + VERSION >= v"1.11.0-DEV.469" && @test Base.ispublic(Mod50105, sym) + end + + @test_throws LoadError @eval @compat public 4, bar + @test_throws LoadError @eval @compat public foo bar + @test_throws LoadError @eval @compat publac foo, bar + @test_throws LoadError @eval @compat public 4, @bar + @test_throws LoadError @eval @compat public foo @bar + @test_throws LoadError @eval @compat publac foo, @bar + @test_throws LoadError @eval @compat public @bar, 4 + @test_throws LoadError @eval @compat public @bar foo + @test_throws LoadError @eval @compat publac @bar, foo +end