Merge: lib: add standard/error.nit
authorJean Privat <jean@pryen.org>
Wed, 3 Sep 2014 09:51:32 +0000 (05:51 -0400)
committerJean Privat <jean@pryen.org>
Wed, 3 Sep 2014 09:51:32 +0000 (05:51 -0400)
This could help people to implement some error handling in libraries.

It is inspired by #701 and ideas from Alexandre Terrasa.

Pull-Request: #707
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

contrib/pep8analysis/src/backbone.nit
contrib/pep8analysis/src/cfg/cfg_base.nit
contrib/pep8analysis/src/cfg/sanity.nit
lib/standard/error.nit [new file with mode: 0644]
lib/standard/standard.nit

index a7e1185..02042bd 100644 (file)
@@ -73,7 +73,7 @@ class Warn
        redef fun prefix do return "Warning: "
 end
 
-class Error
+class P8Error
        super Note
        init (line: Location, msg: String) do super
        init range(from, to: Location, msg: String) do super
index 5e8c856..4d7264b 100644 (file)
@@ -172,7 +172,7 @@ class CFG
                                                b.successors.add(db)
                                                db.predecessors.add(b)
                                        else
-                                               manager.notes.add(new Error(line.location,
+                                               manager.notes.add(new P8Error(line.location,
                                                        "this instruction is not followed by valid code as it should (misplaced data or missing BR?)"))
                                        end
                                end
@@ -181,7 +181,7 @@ class CFG
                                        has_function_calls = true
                                        var next_addr = line.address+4
                                        if not addr_to_blocks.has_key(next_addr) then
-                                               manager.notes.add(new Error(line.location,
+                                               manager.notes.add(new P8Error(line.location,
                                                        "this CALL is not followed by valide code as it should"))
                                        else
                                                b.after_call = addr_to_blocks[next_addr]
@@ -345,7 +345,7 @@ class CFG
 
                                else if instr isa ARetInstruction then
                                                if to_link.is_empty then
-                                                       manager.notes.add( new Error(instr.location,"no CALL can be linked to this RET") )
+                                                       manager.notes.add( new P8Error(instr.location,"no CALL can be linked to this RET") )
                                                        return false
                                                else
                                                        var caller = to_link.pop
index 842296f..2399bf4 100644 (file)
@@ -64,13 +64,13 @@ redef class AnalysisManager
                        if i == len-1 or line.address + line.size != lines[i+1].address then
                                if error then
                                        if first == line then
-                                               manager.notes.add(new Error(first.location, msg))
+                                               manager.notes.add(new P8Error(first.location, msg))
                                        else
-                                               manager.notes.add(new Error.range(first.location, line.location, msg))
+                                               manager.notes.add(new P8Error.range(first.location, line.location, msg))
                                        end
                                else
                                        if first == line then
-                                               manager.notes.add(new Error(first.location, msg))
+                                               manager.notes.add(new P8Error(first.location, msg))
                                        else
                                                manager.notes.add(new Warn.range(first.location, line.location, msg))
                                        end
diff --git a/lib/standard/error.nit b/lib/standard/error.nit
new file mode 100644 (file)
index 0000000..f40444d
--- /dev/null
@@ -0,0 +1,91 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# Standard error-management infrastructure.
+#
+module error
+
+import string
+
+# Standard class for error messages
+class Error
+       # A short human-readable error message.
+       #
+       # This message is short and informative and could be displayed on the console, a dialog-box
+       # or written in a log file.
+       #
+       # Message should be explicative, autonomous and do not depend on contextual information.
+       #
+       # Eg. instead of "Fatal error: cannot open file",
+       # something like "File error, cannot open /some/path/document.ext, file not found." is preferred,
+       # where the message is informative as it, and the severity of the error is not assumed:
+       # while fatal for the library, it could be something benign for the program.
+       var message: String
+
+       # An original error that caused the creation of this error, if any.
+       #
+       # This is used to chain errors and track the implication of various sub-systems for a given error.
+       #
+       # When displaying an error the end user, causes can be recursively displayed.
+       var cause: nullable Error = null is writable
+
+       redef fun to_s do return message
+end
+
+# Helper class used as a return value of methods that may give errors instead of values.
+#
+# Functions that return useful values or errors could use it to simulate an easy-to use multiple-return.
+#
+# ~~~
+# fun division(a,b: Int): MaybeError[Int, Error]
+# do
+#   if b == 0 then return new MaybeError[Int, Error](null, new Error("Arithmetic Error: try to divide {a} by 0"))
+#   return new MaybeError[Int, Error](a / b, null)
+# end
+#
+# assert division(10, 2).is_error  == false
+# assert division(10, 0).is_error  == true
+# ~~~
+#
+# Clients has to handle the error:
+#
+# ~~~
+# var res = division(10, 2)
+# if res.is_error then
+#   print res.error
+#   exit 1
+# end
+# print res.value
+# assert res.value == 5
+# ~~~
+class MaybeError[V, E: Error]
+       # The value, if any
+       var maybe_value: nullable V
+
+       # The error, if any
+       var maybe_error: nullable E
+
+       # It there an error?
+       fun is_error: Bool do return maybe_error != null
+
+       # The value
+       # REQUIRE: `not is_error`
+       fun value: V do return maybe_value.as(V)
+
+       # The require
+       # REQUIRE: `is_error`
+       fun error: E do return maybe_error.as(E)
+
+       redef fun to_s do
+               var e = maybe_error
+               if e != null then return e.to_s
+               return value.to_s
+       end
+end
index 5becb03..21ff465 100644 (file)
@@ -30,3 +30,4 @@ import gc
 import bitset
 import queue
 import numeric
+import error