From d89c9ec2dbeb49f06879e21c8817863951a445d2 Mon Sep 17 00:00:00 2001 From: jake bolewski Date: Thu, 12 Feb 2015 12:25:09 -0500 Subject: [PATCH] assert now throws an more specific AssertionError exception type * add `assert` and `@assert` tests * make `assert` take an optional message argument * update doc and NEWS.md --- NEWS.md | 2 ++ base/base.jl | 7 ++++ base/error.jl | 15 +++++---- base/exports.jl | 1 + base/replutil.jl | 1 + doc/stdlib/base.rst | 10 ++++-- test/misc.jl | 80 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index a9ee58c8b0c56..3da1376a9427c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -101,6 +101,8 @@ Library improvements * Other improvements + * `assert`, `@assert` now throws an `AssertionError` exception type ([#9734]). + * `convert` now checks for overflow when truncating integers or converting between signed and unsigned ([#5413]). diff --git a/base/base.jl b/base/base.jl index 473c285dd62ad..edb66ae732aae 100644 --- a/base/base.jl +++ b/base/base.jl @@ -119,6 +119,13 @@ type DimensionMismatch <: Exception end DimensionMismatch() = DimensionMismatch("") +type AssertionError <: Exception + msg::AbstractString + + AssertionError() = new("") + AssertionError(msg) = new(msg) +end + # For passing constants through type inference immutable Val{T} end diff --git a/base/error.jl b/base/error.jl index 9f39cac1d3a62..74b8adfaa1d83 100644 --- a/base/error.jl +++ b/base/error.jl @@ -38,17 +38,18 @@ systemerror(p, b::Bool) = b ? throw(SystemError(string(p))) : nothing ## assertion functions and macros ## -assert(x) = x ? nothing : error("assertion failed") -macro assert(ex,msgs...) +assert(x) = x ? nothing : throw(AssertionError()) +assert(x, msgs...) = x ? nothing : throw(AssertionError(msgs[1])) +macro assert(ex, msgs...) msg = isempty(msgs) ? ex : msgs[1] if !isempty(msgs) && isa(msg, Expr) # message is an expression needing evaluating - msg = :(string("assertion failed: ", $(esc(msg)))) - elseif isdefined(Base,:string) - msg = string("assertion failed: ", msg) + msg = :(string($(esc(msg)))) + elseif isdefined(Base, :string) + msg = string(msg) else # string() might not be defined during bootstrap - msg = :(string("assertion failed: ", $(Expr(:quote,msg)))) + msg = :(string($(Expr(:quote,msg)))) end - :($(esc(ex)) ? $(nothing) : error($msg)) + :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end diff --git a/base/exports.jl b/base/exports.jl index 1203408719618..74b0c51d59faa 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -160,6 +160,7 @@ export ProcessExitedException, SystemError, TypeError, + AssertionError, # Global constants and variables ARGS, diff --git a/base/replutil.jl b/base/replutil.jl index 8925ea34f4e59..bc8cf507e9a28 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -119,6 +119,7 @@ showerror(io::IO, ex::ErrorException) = print(io, ex.msg) showerror(io::IO, ex::KeyError) = print(io, "KeyError: $(ex.key) not found") showerror(io::IO, ex::InterruptException) = print(io, "InterruptException:") showerror(io::IO, ex::ArgumentError) = print(io, "ArgumentError: $(ex.msg)") +showerror(io::IO, ex::AssertionError) = print(io, "AssertionError: $(ex.msg)") function showerror(io::IO, ex::MethodError) print(io, "MethodError: ") diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 152c5c3cc34ad..3ee0cb98ec509 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -785,11 +785,15 @@ Errors .. function:: assert(cond, [text]) - Raise an error if ``cond`` is false. Also available as the macro ``@assert expr``. + Throw an ``AssertionError`` if ``cond`` is false. Also available as the macro ``@assert expr``. -.. function:: @assert +.. function:: @assert cond [text] - Raise an error if ``cond`` is false. Preferred syntax for writings assertions. + Throw an ``AssertionError`` if ``cond`` is false. Preferred syntax for writings assertions. + +.. data:: AssertionError + + The asserted condition did not evalutate to ``true``. .. data:: ArgumentError diff --git a/test/misc.jl b/test/misc.jl index 1678a3be5fe76..ca6939775e9e3 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -25,3 +25,83 @@ let bt = backtrace() @test contains(l, b) end end + +# test assert() method +@test_throws AssertionError assert(false) +let res = assert(true) + @test res === nothing +end +let + try + assert(false) + @unexpected + catch ex + @test isa(ex, AssertionError) + @test isempty(ex.msg) + end +end +let + try + assert(false, "this is a test") + @unexpected + catch ex + @test isa(ex, AssertionError) + @test ex.msg == "this is a test" + end +end + +# test @assert macro +@test_throws AssertionError (@assert 1 == 2) +@test_throws AssertionError (@assert false) +@test_throws AssertionError (@assert false "this is a test") +@test_throws AssertionError (@assert false "this is a test" "another test") +@test_throws AssertionError (@assert false :a) +let + try + @assert 1 == 2 + @unexpected + catch ex + @test isa(ex, AssertionError) + @test contains(ex.msg, "1 == 2") + end +end +# test @assert message +let + try + @assert 1 == 2 "this is a test" + @unexpected + catch ex + @test isa(ex, AssertionError) + @test ex.msg == "this is a test" + end +end +# @assert only uses the first message string +let + try + @assert 1 == 2 "this is a test" "this is another test" + @unexpected + catch ex + @test isa(ex, AssertionError) + @test ex.msg == "this is a test" + end +end +# @assert calls string() on second argument +let + try + @assert 1 == 2 :random_object + @unexpected + catch ex + @test isa(ex, AssertionError) + @test !contains(ex.msg, "1 == 2") + @test contains(ex.msg, "random_object") + end +end +# if the second argument is an expression, c +let deepthought(x, y) = 42 + try + @assert 1 == 2 string("the answer to the ultimate question: ", deepthought(6,9)) + catch ex + @test isa(ex, AssertionError) + @test ex.msg == "the answer to the ultimate question: 42" + end +end