summary refs log tree commit diff stats
path: root/lib/system/assertions.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/assertions.nim')
-rw-r--r--lib/system/assertions.nim89
1 files changed, 89 insertions, 0 deletions
diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim
new file mode 100644
index 000000000..42a5b61d7
--- /dev/null
+++ b/lib/system/assertions.nim
@@ -0,0 +1,89 @@
+include "system/helpers"
+
+when not declared(sysFatal):
+  include "system/fatal"
+
+
+proc raiseAssert*(msg: string) {.noinline, noReturn.} =
+  sysFatal(AssertionError, msg)
+
+proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
+  # trick the compiler to not list ``AssertionError`` when called
+  # by ``assert``.
+  type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect,
+                                    tags: [].}
+  Hide(raiseAssert)(msg)
+
+template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) =
+  const loc = $instantiationInfo(-1, true)
+  bind instantiationInfo
+  mixin failedAssertImpl
+  when enabled:
+    # for stacktrace; fixes #8928 ; Note: `fullPaths = true` is correct
+    # here, regardless of --excessiveStackTrace
+    {.line: instantiationInfo(fullPaths = true).}:
+      if not cond:
+        failedAssertImpl(loc & " `" & expr & "` " & msg)
+
+template assert*(cond: untyped, msg = "") =
+  ## Raises ``AssertionError`` with `msg` if `cond` is false. Note
+  ## that ``AssertionError`` is hidden from the effect system, so it doesn't
+  ## produce ``{.raises: [AssertionError].}``. This exception is only supposed
+  ## to be caught by unit testing frameworks.
+  ##
+  ## The compiler may not generate any code at all for ``assert`` if it is
+  ## advised to do so through the ``-d:release`` or ``--assertions:off``
+  ## `command line switches <nimc.html#command-line-switches>`_.
+  const expr = astToStr(cond)
+  assertImpl(cond, msg, expr, compileOption("assertions"))
+
+template doAssert*(cond: untyped, msg = "") =
+  ## same as ``assert`` but is always turned on regardless of ``--assertions``
+  const expr = astToStr(cond)
+  assertImpl(cond, msg, expr, true)
+
+template onFailedAssert*(msg, code: untyped): untyped {.dirty.} =
+  ## Sets an assertion failure handler that will intercept any assert
+  ## statements following `onFailedAssert` in the current module scope.
+  ##
+  ## .. code-block:: nim
+  ##  # module-wide policy to change the failed assert
+  ##  # exception type in order to include a lineinfo
+  ##  onFailedAssert(msg):
+  ##    var e = new(TMyError)
+  ##    e.msg = msg
+  ##    e.lineinfo = instantiationInfo(-2)
+  ##    raise e
+  ##
+  template failedAssertImpl(msgIMPL: string): untyped {.dirty.} =
+    let msg = msgIMPL
+    code
+
+template doAssertRaises*(exception: typedesc, code: untyped): typed =
+  ## Raises ``AssertionError`` if specified ``code`` does not raise the
+  ## specified exception. Example:
+  ##
+  ## .. code-block:: nim
+  ##  doAssertRaises(ValueError):
+  ##    raise newException(ValueError, "Hello World")
+  var wrong = false
+  when Exception is exception:
+    try:
+      if true:
+        code
+      wrong = true
+    except Exception:
+      discard
+  else:
+    try:
+      if true:
+        code
+      wrong = true
+    except exception:
+      discard
+    except Exception:
+      raiseAssert(astToStr(exception) &
+                  " wasn't raised, another error was raised instead by:\n"&
+                  astToStr(code))
+  if wrong:
+    raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))