Commits

seanmcl committed 7e51927

error handling, improved interface (issue #140)

Comments (0)

Files changed (12)

+new stuff
+=========
+
+- Support for missing package errors, eg
+
+  *** omake: targets were not rebuilt because of errors:
+     lib/bug.cmx
+        depends on: lib/bug.ml
+        depends on: lib/bug.mli
+        - build lib bug.cmx
+        + ocamlfind ocamlopt -thread -w @a-4-7-9-29-28 -pp 'camlp4o /usr/local/lib/ocaml/site-lib/type-conv/pa_type_conv.cma /usr/local/lib/ocaml/site-lib/sexplib/pa_sexp_conv.cma /usr/local/lib/ocaml/site-lib/fieldslib/pa_fields_conv.cma /usr/local/lib/ocaml/site-lib/pa_pipebang/pa_pipebang.cma' -strict-sequence -annot -inline 20 -nodynlink -g -package core -package core_extended -package async -package pcre -c bug.mli
+        ocamlfind: Package `core_extended' not found
+        - exit lib bug.cmx, 0.10 sec, code 2
+
+- New error handling framework.  For example, when you try to start omake in a
+  tramp buffer, show a message in the minibuffer and abort the current stack
+  but don't raise an exn.
+
+changes
+=========
+
+bug fixes
+=========
+
+
+--------------------------------------------------------------------------------
 
 new stuff
 =========
 =========
 
 - Support new error-enabled warnings format in ocaml 4.0
-- Verbose mode shows the settings of the environment variables 
+- Verbose mode shows the settings of the environment variables
   on the next compilation as well as the current one.
-- Changed variable 
+- Changed variable
   Omake.create-dedicated-frame --> Omake.create-dedicated-status-frame
 - In project buffer, 'w' now toggles between watched and unwatched.
   'u' was removed.
 
 bug fixes
 =========
+
 - Eliminated nohup.out files
 - toggle-env/set-env prompts are the same now
 

elisp/omake/make.sh

 file=/tmp/all.el
 
 files="
+omake-errors.el
 omake-version.el
 omake-custom.el
 omake-face.el

elisp/omake/omake-connection.el

 ;; (macroexpand '(Omake.with-connection 5))
 
 (defmacro Omake.with-updated-projects (&rest body)
-  "wEnsure the project list is up to date"
+  "Ensure the project list is up to date"
   (declare (indent defun))
   (declare (debug (body)))
   `(Omake.with-connection

elisp/omake/omake-interface.el

 
 (defun Omake.next-error (&optional user-num id)
   (interactive "P")
-  ;; We'll definitely show the error window, so uniconify the error frame if it exists
-  (Omake.Frame.uniconify 'status)
-  (assert (or (null id) (Omake.Id.is id)))
-  (let* ((id (if id id (Omake.Id.current)))
-         (model (Omake.Model.get id))
-         (project (Omake.Model.project model))
-         (result (Omake.Model.result model))
-         (status (Omake.Model.status model))
-         (comp-dir (Omake.Project.compilation-dir project))
-         (current-file (buffer-file-name)))
-    (if (Omake.Result.failure-p result)
-        (message "There is problem with omake.")
-      ;; Otherwise find the error.  The tricky thing is figuring out
-      ;; which error is next.  Recall C-u resets the error index to 0.
-      ;; Cases:
-      ;;   C-u ---> 0
-      ;;   current error (visited)
-      ;;     C-0 ---> 0 (a special case of the next rule)
-      ;;     C-N ---> N mod num-errors
-      ;;   current error (pending)
-      ;;     C-N ---> N mod num-errors       (N <= 0)
-      ;;     C-N ---> (M+N-1) mod num-errors (N >  0)
-      (let* ((ring (Omake.Result.ring result))
-             (ring (if current-file
-                       (Omake.Ring.current-file-errors-to-front ring current-file)
-                     ring)))
-        (setf (Omake.Model.result model) (Omake.Result.Ring ring))
-        (if (Omake.Ring.is-empty ring)
-            (let* ((ws (Omake.show-status-buffer id))
-                   (cw (car ws))
-                   (ew (cdr ws)))
-              (when (equal (window-buffer cw) (window-buffer ew))
-                (let ((db (dired-noselect comp-dir)))
-                  (set-window-buffer cw db)))
-              (when Omake.Frame.Status (raise-frame Omake.Frame.Status))
-              (if (Omake.Status.polling-p status)
-                  (message "There are no errors.")
-                (message "There are no errors, but omake is still running")))
-          (let* ((current (Omake.Ring.current ring))
-                 (user-num
-                  (cond
-                   ;; \C-u sends (list N) where N>0.
-                   ((consp user-num) 0)
-                   ;; No arg is equivalent to \C-1
-                   ((null user-num) 1)
-                   ;; I'm not sure what type '-' has interactively
-                   ;; \C-- is equivalent to \C--\C-1
-                   ((equal (prin1-to-string user-num) "-") -1)
-                   ((integerp user-num) user-num)
-                   (t (error "I can't parse user-num: %s" (prin1-to-string user-num)))))
-                 (user-num
-                  (if (<= user-num 0) user-num
-                    (if current user-num (- user-num 1))))
-                 (num-errors (Omake.Ring.num-errors ring))
-                 (n (mod user-num num-errors))
-                 (_ (assert (< n num-errors)))
-                 (e (Omake.Ring.nth ring n)))
-            ;; Make sure the code frame is visible
-            (Omake.Frame.uniconify 'code)
-            (message "There are errors")
-            (Omake.Error.eval e)))))))
+  (Omake.handle-errors
+    ;; We'll definitely show the error window, so uniconify the error frame if it exists
+    (Omake.Frame.uniconify 'status)
+    (assert (or (null id) (Omake.Id.is id)))
+    (let* ((id (if id id (Omake.Id.current)))
+           (ids (Omake.Id.to-string id))
+           (_ (unless (Omake.Model.has id)
+                (let ((cont 
+                       (Emacs.y-or-n-p
+                        (format "You are not watching %s.  Compile/watch it and go to the first error? " ids))))
+                  (if (not cont) (signal 'Omake.Errors.abort nil)
+                    (Omake.start-project)))))
+           (model (Omake.Model.get id))
+           (project (Omake.Model.project model))
+           (result (Omake.Model.result model))
+           (status (Omake.Model.status model))
+           (comp-dir (Omake.Project.compilation-dir project))
+           (current-file (buffer-file-name)))
+      (if (Omake.Result.failure-p result)
+          (message "There is problem with omake.")
+        ;; Otherwise find the error.  The tricky thing is figuring out
+        ;; which error is next.  Recall C-u resets the error index to 0.
+        ;; Cases:
+        ;;   C-u ---> 0
+        ;;   current error (visited)
+        ;;     C-0 ---> 0 (a special case of the next rule)
+        ;;     C-N ---> N mod num-errors
+        ;;   current error (pending)
+        ;;     C-N ---> N mod num-errors       (N <= 0)
+        ;;     C-N ---> (M+N-1) mod num-errors (N >  0)
+        (let* ((ring (Omake.Result.ring result))
+               (ring (if current-file
+                         (Omake.Ring.current-file-errors-to-front ring current-file)
+                       ring)))
+          (setf (Omake.Model.result model) (Omake.Result.Ring ring))
+          (if (Omake.Ring.is-empty ring)
+              (let* ((ws (Omake.show-status-buffer id))
+                     (cw (car ws))
+                     (ew (cdr ws)))
+                (when (equal (window-buffer cw) (window-buffer ew))
+                  (let ((db (dired-noselect comp-dir)))
+                    (set-window-buffer cw db)))
+                (when Omake.Frame.Status (raise-frame Omake.Frame.Status))
+                (if (Omake.Status.polling-p status)
+                    (message "There are no errors.")
+                  (message "There are no errors, but omake is still running")))
+            (let* ((current (Omake.Ring.current ring))
+                   (user-num
+                    (cond
+                     ;; \C-u sends (list N) where N>0.
+                     ((consp user-num) 0)
+                     ;; No arg is equivalent to \C-1
+                     ((null user-num) 1)
+                     ;; I'm not sure what type '-' has interactively
+                     ;; \C-- is equivalent to \C--\C-1
+                     ((equal (prin1-to-string user-num) "-") -1)
+                     ((integerp user-num) user-num)
+                     (t (error "I can't parse user-num: %s" (prin1-to-string user-num)))))
+                   (user-num
+                    (if (<= user-num 0) user-num
+                      (if current user-num (- user-num 1))))
+                   (num-errors (Omake.Ring.num-errors ring))
+                   (n (mod user-num num-errors))
+                   (_ (assert (< n num-errors)))
+                   (e (Omake.Ring.nth ring n)))
+              ;; Make sure the code frame is visible
+              (Omake.Frame.uniconify 'code)
+              (message "There are errors")
+              (Omake.Error.eval e))))))))
 
 ;;----------------------------------------------------------------------------;;
 ;; Starting and watching projects                                             ;;
 (defun Omake.start-project (&optional read-command)
   "Create a project from the current directory."
   (interactive "P")
-  (Omake.with-updated-projects
-    (let* ((path (Filename.default-directory))
-           (id (Omake.Id.of-path path))
-           (root-dir (Omake.Path.omakeroot-dir path))
-           ;; path must be a full path for ocaml
-           (path (expand-file-name path))
-           (project (Omake.Project.find id)))
-      (catch 'exit
-        ;; If the project exists but is not being watched, watch it.
-        (if (and project (not (Omake.Project.is-watching id)))
-            (progn
-              (Omake.Project.watch id)
-              (Omake.show-status-buffer id))
-          ;; If it exists and is being watched, ask to kill and restart
-          (when project
-            (let* ((num-watchers (Omake.Project.num-watchers project))
-                   (watch-msg
-                    (if (< 1 num-watchers)
-                        "\nThis will affect other processes watching this project."
-                      ""))
-                   (kill
-                    (Emacs.y-or-n-p
-                     (format "\
+  (Omake.handle-errors
+    (Omake.with-updated-projects
+      (let* ((path (Filename.default-directory))
+             (id (Omake.Id.of-path path))
+             (root-dir (Omake.Path.omakeroot-dir path))
+             ;; path must be a full path for ocaml
+             (path (expand-file-name path))
+             (project (Omake.Project.find id)))
+        (catch 'exit
+          ;; If the project exists but is not being watched, watch it.
+          (if (and project (not (Omake.Project.is-watching id)))
+              (progn
+                (Omake.Project.watch id)
+                (Omake.show-status-buffer id))
+            ;; If it exists and is being watched, ask to kill and restart
+            (when project
+              (let* ((num-watchers (Omake.Project.num-watchers project))
+                     (watch-msg
+                      (if (< 1 num-watchers)
+                          "\nThis will affect other processes watching this project."
+                        ""))
+                     (kill
+                      (Emacs.y-or-n-p
+                       (format "\
 OMake is already building:
   %s
 Proceeding will kill OMake and restart it on:
   %s%s
 Proceed? "
-                             (Omake.Project.compilation-dir project) path watch-msg))))
-              (if (not kill) (throw 'exit t)
-                (Omake.kill-project id :force t))))
-          ;; Now there is no running project, so we start it.
-          (let* ((config (Omake.Ocaml.Config.omake-command))
-                 (user-command
-                  (if read-command
-                      (read-from-minibuffer "Command: " (concat config " "))
-                    config)))
-            (message "Starting compilation.")
-            (Omake.Server.create-project
-             :id id
-             :omakeroot-dir root-dir
-             :compilation-dir path
-             :user-command user-command)
-            ;; omake must start and the new project must make it back
-            ;; to this Emacs instance before we can call watch.
-            (Emacs.protect-from-quit (sleep-for 2))
-            (Omake.Project.watch id)
-            (Omake.Frame.maybe-create-status)
-            (Omake.show-status-buffer id)
-            (Omake.Frame.raise-status)))
-        (assert (Omake.Project.find id))
-        (assert (Omake.Project.is-watching id))))))
+                               (Omake.Project.compilation-dir project) path watch-msg))))
+                (if (not kill) (throw 'exit t)
+                  (Omake.kill-project id :force t))))
+            ;; Now there is no running project, so we start it.
+            (let* ((config (Omake.Ocaml.Config.omake-command))
+                   (user-command
+                    (if read-command
+                        (read-from-minibuffer "Command: " (concat config " "))
+                      config)))
+              (message "Starting compilation.")
+              (Omake.Server.create-project
+               :id id
+               :omakeroot-dir root-dir
+               :compilation-dir path
+               :user-command user-command)
+              ;; omake must start and the new project must make it back
+              ;; to this Emacs instance before we can call watch.
+              (Emacs.protect-from-quit (sleep-for 2))
+              (Omake.Project.watch id)
+              (Omake.Frame.maybe-create-status)
+              (Omake.show-status-buffer id)
+              (Omake.Frame.raise-status)))
+          (assert (Omake.Project.find id))
+          (assert (Omake.Project.is-watching id)))))))
 
 (defun Omake.watch (&optional id)
   (interactive)
   (assert (or (null id) (Omake.Id.is id)))
-  (Omake.with-updated-projects
-    (let* ((ps (mapcar (lambda (p) (Omake.Id.to-string (Omake.Project.id p)))
-                       Omake.Project.list))
-           (ws (mapcar (lambda (m) (Omake.Id.to-string (Omake.Model.id m)))
-                       (Omake.Model.models)))
-           (ps (set-difference ps ws))
-           (cid (condition-case nil (Omake.Id.to-string (Omake.Id.current)) (error nil)))
-           (initial (when (member cid ps) cid))
-           (id (if id id (Omake.Id.of-path
-                          (completing-read "Project: " ps nil t initial)))))
-      (Omake.Project.watch id)
-      (Omake.show-status-buffer id))))
+  (Omake.handle-errors
+    (Omake.with-updated-projects
+      (let* ((ps (mapcar (lambda (p) (Omake.Id.to-string (Omake.Project.id p)))
+                         Omake.Project.list))
+             (ws (mapcar (lambda (m) (Omake.Id.to-string (Omake.Model.id m)))
+                         (Omake.Model.models)))
+             (ps (set-difference ps ws))
+             (cid (condition-case nil (Omake.Id.to-string (Omake.Id.current)) (error nil)))
+             (initial (when (member cid ps) cid))
+             (id (if id id (Omake.Id.of-path
+                            (completing-read "Project: " ps nil t initial)))))
+        (Omake.Project.watch id)
+        (Omake.show-status-buffer id)))))
 
 (defun Omake.unwatch (&optional id)
   (interactive)
   (assert (or (null id) (Omake.Id.is id)))
-  (Omake.with-updated-projects
-    (let* ((ps (mapcar (lambda (m) (Omake.Id.to-string (Omake.Model.id m)))
-                       (Omake.Model.models)))
-           (cid (condition-case nil (Omake.Id.current) (error nil)))
-           (initial (when (and cid (Omake.Model.has cid)) (Omake.Id.to-string cid)))
-           (id (if id id (Omake.Id.of-path
-                          (completing-read "Project: " ps nil t initial)))))
-      (Omake.Project.unwatch id))))
+  (Omake.handle-errors
+    (Omake.with-updated-projects
+      (let* ((ps (mapcar (lambda (m) (Omake.Id.to-string (Omake.Model.id m)))
+                         (Omake.Model.models)))
+             (cid (condition-case nil (Omake.Id.current) (error nil)))
+             (initial (when (and cid (Omake.Model.has cid)) (Omake.Id.to-string cid)))
+             (id (if id id (Omake.Id.of-path
+                            (completing-read "Project: " ps nil t initial)))))
+        (Omake.Project.unwatch id)))))
 
 (defun Omake.kill-project (&optional id &key force)
   "Kill a project"
   (interactive)
-  (Omake.with-updated-projects
-    (let* ((ps (mapcar (lambda (p) (Omake.Id.to-string (Omake.Project.id p)))
-                       Omake.Project.list))
-           (cid (condition-case nil (Omake.Id.current) (error nil)))
-           (initial (when (and cid (Omake.Model.has cid))
-                      (Omake.Id.to-string cid)))
-           (id (if id id (Omake.Id.of-path
-                          (completing-read "Project: " ps nil t initial)))))
-      (when (or force
-                (not Omake.prompt-before-killing-project)
-                (y-or-n-p (format "Really kill project %s "
-                                  (Omake.Id.to-string id))))
-        (Omake.Model.kill id)
-        (Omake.Server.kill-project id)
-        (message "Killed")))))
+  (Omake.handle-errors
+    (Omake.with-updated-projects
+      (let* ((ps (mapcar (lambda (p) (Omake.Id.to-string (Omake.Project.id p)))
+                         Omake.Project.list))
+             (cid (condition-case nil (Omake.Id.current) (error nil)))
+             (initial (when (and cid (Omake.Model.has cid))
+                        (Omake.Id.to-string cid)))
+             (id (if id id (Omake.Id.of-path
+                            (completing-read "Project: " ps nil t initial)))))
+        (when (or force
+                  (not Omake.prompt-before-killing-project)
+                  (y-or-n-p (format "Really kill project %s "
+                                    (Omake.Id.to-string id))))
+          (Omake.Model.kill id)
+          (Omake.Server.kill-project id)
+          (message "Killed"))))))
 
 (defun Omake.shutdown ()
   (interactive)
 
 (defun Omake.show-projects-buffer ()
   (interactive)
-  (Omake.Project.create-buffer :display t))
+  (Omake.with-updated-projects
+    (Omake.Project.create-buffer :display t)))
 
 ;;----------------------------------------------------------------------------;;
 ;; Env variables                                                              ;;
 ;;----------------------------------------------------------------------------;;
 
-;; CR sweeks: Do we need Omake.set-server-program anymore?
-(defun Omake.set-server-program (p)
-  (unless (equal Omake.Server.program p)
-    (if (Omake.Server.running-p)
-        (message "The server is running. Kill it with M-x Omake.shutdown before changing programs.")
-      (setq Omake.Server.program p))))
-
 (defun Omake.getenv ()
   (interactive)
   (let ((id (Omake.Id.current)))

elisp/omake/omake-path.el

 ;; Paths                                                                      ;;
 ;;============================================================================;;
 
+(defun Omake.Path.is-tramp (path)
+  "Tramp filenames begin with /scp:.  We don't support tramp, but would like
+to give the user a good error message."
+  (assert (stringp path))
+  (string-match "^/scp:" path))
+
 (defun Omake.Path.ok (path)
   "A legal path has no spaces and doesn't end with a slash"
   (assert (stringp path))
+  (when (Omake.Path.is-tramp path) (signal 'Omake.Errors.tramp nil))
   (let ((legal (progn
                  (string-match "[.~a-zA-Z0-9/_-]*[.a-zA-Z0-9~_-]" path)
                  (match-string 0 path))))

elisp/omake/omake-project.el

 ;; in the middle of a synchronous update when the async updating occurrs.
 ;; (defconst Omake.Project.buffer-semaphore (Semaphore.create))
 
+;; NB: Don't call Omake.Server.update-projects here (equivalently
+;; Omake.with-updated-projects.  It hammers the server.
 (defun* Omake.Project.create-buffer (&key display)
   (interactive)
-  (let* ((buf (Omake.Buffer.get 'projects))
-         (lines (List.mapi 'Omake.Project.to-line Omake.Project.list)))
-    (Buffer.edit-read-only buf
-      (let ((pt (point)))
-        (Buffer.clear buf)
-        (insertf "%3s %-8s %-11s %-25s %s \n\n" "" "watching" "#watching" "omakeroot" "dir")
-        (mapc 'insert lines)
-        (goto-char pt)
-        (omake-project-mode 1)))
-    (when display (display-buffer buf))))
+    (let* ((buf (Omake.Buffer.get 'projects))
+           (lines (List.mapi 'Omake.Project.to-line Omake.Project.list)))
+      (Buffer.edit-read-only buf
+        (let ((pt (point)))
+          (Buffer.clear buf)
+          (insertf "%3s %-8s %-11s %-25s %s \n\n" "" "watching" "#watching" "omakeroot" "dir")
+          (mapc 'insert lines)
+          (goto-char pt)
+          (omake-project-mode 1)))
+      (when display (display-buffer buf))))
 ;; (Omake.Project.create-buffer)
 
 (defun Omake.Project.update (ps)

elisp/omake/omake.el

 (require 'jane-lib)
 (require 'warnings)
 
+(require 'omake-errors)
 (require 'omake-version)
 (require 'omake-custom)
 (require 'omake-face)

ocaml/omake/Makefile

 
 LIB=/usr/local/lib/ocaml/site-lib
 
-PP='camlp4o $(LIB)/type-conv/pa_type_conv.cma $(LIB)/sexplib/pa_sexp_conv.cma $(LIB)/fieldslib/pa_fields_conv.cma $(LIB)/pa_pipebang/pa_pipebang.cma'
+PP='camlp4o $(LIB)/type_conv/pa_type_conv.cma $(LIB)/sexplib/pa_sexp_conv.cma $(LIB)/fieldslib/pa_fields_conv.cma $(LIB)/pa_pipebang/pa_pipebang.cma'
 
 OCAMLOPT=ocamlfind ocamlopt -thread -w @a-4-7-9-29-28 \
         -pp $(PP) \

ocaml/omake/env.ml

         Log.printf
           "Can't read env file for %s (%s).  Writing new file from bash env."
           (Id.to_string id)
-          (Exn.to_string error);
+          (Core.Error.to_string_hum error);
         create_default id
     end
   | false -> create_default id

ocaml/omake/omake.ml

 
 open Std
 
-type error = Error.t with sexp
-
 (* -------------------------------------------------------------------------- *)
 (*  Util                                                                      *)
 (* -------------------------------------------------------------------------- *)
     let error6 = " *Error: Files" in
     let error7 = " *File \".*\\.cmx\"" in
     let error8 = "\\*\\*\\* omake: deadlock" in
-    Regex.of_string (sprintf "(%s|%s|%s|%s|%s|%s|%s|%s)"
-                       error1 error2 error3 error4 error5 error6 error7 error8)
+    let error9 = "ocamlfind: Package `.*' not found" in
+    Regex.of_string (sprintf "(%s|%s|%s|%s|%s|%s|%s|%s|%s)"
+                       error1 error2 error3 error4 error5 error6
+                       error7 error8 error9)
 end
 
 (* -------------------------------------------------------------------------- *)
   type t = {
     msg : string;
     window : string;
-  } with sexp
+  } with sexp_of
   val equal : t -> t -> bool
   val to_string : t -> string
 end = struct
   type t = {
     msg : string;
     window : string;
-  } with sexp
+  } with sexp_of
   let equal { msg; window } { msg = m; window = w } = msg = m && window = w
   let to_string = Sexp.to_string_hum ** sexp_of_t
 end
   | Reading_omakefiles_failed
   | Finished_omakefiles
   | Building
-  with sexp
+  with sexp_of
   let to_string s = Sexp.to_string (sexp_of_t s)
   let to_elisp = function
     | Starting -> "Omake.Status.Starting"
     | Reading_omakefiles_failed -> "Omake.Status.Reading_omakefiles_failed"
     | Finished_omakefiles -> "Omake.Status.Finished_omakefiles"
     | Building -> "Omake.Status.Building"
+  let _ = ignore_unused_warning (to_string)
 end
 
 (* -------------------------------------------------------------------------- *)
   | Refresh_file of [`Dir of path] * [`Filename_with_no_extension of string]
   | Done
   | Last_line of string
-  with sexp
+  with sexp_of
   val parse_events : string Pipe.Reader.t -> t result Pipe.Reader.t
 end = struct
   type t =
   | Refresh_file of [`Dir of path] * [`Filename_with_no_extension of string]
   | Done
   | Last_line of string
-  with sexp
+  with sexp_of
 
   let parse_events string_reader =
     let (event_reader, event_writer) = Pipe.create () in

ocaml/omake/server.ml

 
 let start () =
   (* Ignore SIGTERM due to a bug where emacsclient mysteriously
-     kills the server. (Issue #56) *)
-  Signal.handle [Signal.term] ~f:ignore;
+     kills the server. (Issue #56)
+     Update: Try to remove this, as it makes it harder to kill the server. *)
+  (* Signal.handle [Signal.term] ~f:ignore; *)
   Ashell.rm Files.socket >>= fun () ->
   (* Wait for the log to open *)
   Log.wait ()
         Reader.close reader >>= fun () ->
         failwith "Read error"
       | `Ok s ->
+        (* Don't close the reader.  The writer gets saved in the hashtable. *)
         Log.printf "omake server received: %s" (Sexp.to_string s);
-        (* Don't close the reader.  The writer gets saved in the hashtable. *)
+        (* When Emacs is killed, the server will still be writing messages to
+           its writer for a few seconds until the server notices emacs is dead.
+           Ignore the pipe failures. *)
+        Writer.set_raise_epipe writer false;
         Query.handle writer (Query.t_of_sexp s)) >>= function
     | Ok () -> Deferred.unit
     | Error exn ->

ocaml/omake/std.ml

 let ( ** ) f g x = f (g x)
 
 let some x = Some x
+
+let ignore_unused_warning x = x