diff --git a/src/MatchImpl.jl b/src/MatchImpl.jl index 88e9d29..0ef7627 100644 --- a/src/MatchImpl.jl +++ b/src/MatchImpl.jl @@ -1,6 +1,6 @@ module MatchImpl export is_enum, - pattern_uncall, pattern_unref, pattern_unmacrocall, @switch, @match, Where, gen_match, gen_switch + pattern_uncall, pattern_unref, pattern_unmacrocall, @switch, @tryswitch, @match, Where, gen_match, gen_switch export Q import MLStyle using MLStyle: mlstyle_report_deprecation_msg! @@ -384,6 +384,36 @@ macro switch(val, ex) res = init_cfg(res) esc(res) end +""" + @tryswitch begin + @case + + end + +Very similar to [`@switch`](@ref), except that a failure to match does nothing instead of throwing a "match non-exhaustive" error. + +It is equivalent to + +```julia +@switch begin + @case + + @case _ + nothing + end +``` +""" +macro tryswitch(val, ex) + @assert Meta.isexpr(ex, :block) + insert_case = length(ex.args) >= 2 || begin + case, line = ex.args[end-2:end] + Meta.isexpr(case, [:macrocall], 3) && + case.args[1] == Symbol("@case") && + case.args[3] == :_ + end + insert_case && push!(ex.args, Expr(:macrocall, Symbol("@case"), __source__, :_), :nothing) + :($(esc(:($(@__MODULE__).@switch $val $ex)))) +end @specialize function gen_switch(val, ex, __source__::LineNumberNode, __module__::Module) @assert Meta.isexpr(ex, :block) diff --git a/test/runtests.jl b/test/runtests.jl index 60e3727..48d5ece 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -54,6 +54,7 @@ MODULE = TestModule include("issues/109.jl") include("when.jl") +include("switch.jl") include("untyped_lam.jl") include("active_patterns.jl") include("uncomp.jl") diff --git a/test/switch.jl b/test/switch.jl new file mode 100644 index 0000000..827c332 --- /dev/null +++ b/test/switch.jl @@ -0,0 +1,49 @@ +@testcase "@switch" begin + flag = Ref(0) + function try_setflag(x) + @switch x begin + @case (::Integer, 2) + flag[] = 1 + @case ::Bool + nothing + flag[] = 2 + @case ::String + flag[] = 0 + end + end + + try_setflag((1, 2)) + @test flag[] == 1 + try_setflag(false) + @test flag[] == 2 + try_setflag("") + @test flag[] == 0 + # Non-exhaustive matches throw an error. + @test_throws ErrorException try_setflag(:a) + + flag2 = Ref(0) + function try_setflag_2(x) + @tryswitch x begin + @case (::Integer, 2) + flag[] = 1 + @case ::Bool + flag[] = 2 + @tryswitch x begin + @case true + flag2[] = 2 + end + end + end + + try_setflag_2((1, 2)) + @test flag[] == 1 + try_setflag_2(false) + @test flag[] == 2 + @test flag2[] == 0 + try_setflag_2(true) + @test flag[] == 2 + @test flag2[] == 2 + + # Non-exhaustive matches fail silently. + try_setflag_2("") +end