Check keyword args of make-process
authorHelmut Eller <eller.helmut@gmail.com>
Thu, 3 Aug 2023 06:33:40 +0000 (08:33 +0200)
committerMattias Engdegård <mattiase@acm.org>
Tue, 8 Aug 2023 16:23:00 +0000 (18:23 +0200)
The functions make-process and make-network-process have many
keyword args and it's easy to misspell some of them.

Use a compiler macro to warn about some possible mistakes.

* lisp/emacs-lisp/bytecomp.el (bytecomp--check-keyword-args): New
  helper.
  (make-process, make-network-process): Define a compiler macro that
  performs some checks but doesn't anything else.

* test/lisp/emacs-lisp/bytecomp-tests.el: Add some tests.

* test/lisp/emacs-lisp/bytecomp-resources/:
  (warn-make-process-missing-keyword-arg.el,
   warn-make-process-missing-keyword-value.el,
   warn-make-process-repeated-keyword-arg.el,
   warn-make-process-unknown-keyword-arg.el): New test files

lisp/emacs-lisp/bytecomp.el
test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-arg.el [new file with mode: 0644]
test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-value.el [new file with mode: 0644]
test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-repeated-keyword-arg.el [new file with mode: 0644]
test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-unknown-keyword-arg.el [new file with mode: 0644]
test/lisp/emacs-lisp/bytecomp-tests.el

index f6ba6ff9ea0fb3b636f86f34e2cd243ce678af14..0df7b0bfe2a4c573f2d9c262184355cb32a2501e 100644 (file)
@@ -5782,6 +5782,67 @@ and corresponding effects."
       form    ; arity error
     `(forward-word (- (or ,arg 1)))))
 
+(defun bytecomp--check-keyword-args (form arglist allowed-keys required-keys)
+  (let ((fun (car form)))
+    (cl-flet ((missing (form keyword)
+               (byte-compile-warn-x
+                form
+                "`%S´ called without required keyword argument %S"
+                fun keyword))
+             (unrecognized (form keyword)
+               (byte-compile-warn-x
+                form
+                "`%S´ called with unknown keyword argument %S"
+                fun keyword))
+             (duplicate (form keyword)
+               (byte-compile-warn-x
+                form
+                "`%S´ called with repeated keyword argument %S"
+                fun keyword))
+              (missing-val (form keyword)
+               (byte-compile-warn-x
+                form
+                "missing value for keyword argument %S"
+                keyword)))
+      (let* ((seen '())
+            (l arglist))
+       (while (consp l)
+         (let ((key (car l)))
+           (cond ((and (keywordp key) (memq key allowed-keys))
+                  (cond ((memq key seen)
+                         (duplicate l key))
+                        (t
+                         (push key seen))))
+                 (t (unrecognized l key)))
+            (when (null (cdr l))
+              (missing-val l key)))
+         (setq l (cddr l)))
+        (dolist (key required-keys)
+         (unless (memq key seen)
+           (missing form key))))))
+  form)
+
+(put 'make-process 'compiler-macro
+     #'(lambda (form &rest args)
+         (bytecomp--check-keyword-args
+          form args
+          '(:name
+            :buffer :command :coding :noquery :stop :connection-type
+            :filter :sentinel :stderr :file-handler)
+          '(:name :command))))
+
+(put 'make-network-process 'compiler-macro
+     #'(lambda (form &rest args)
+         (bytecomp--check-keyword-args
+          form args
+          '(:name
+            :buffer :host :service :type :family :local :remote :coding
+            :nowait :noquery :stop :filter :filter-multibyte :sentinel
+            :log :plist :tls-parameters :server :broadcast :dontroute
+            :keepalive :linger :oobinline :priority :reuseaddr :bindtodevice
+            :use-external-socket)
+          '(:name :service))))
+
 (provide 'byte-compile)
 (provide 'bytecomp)
 
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-arg.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-arg.el
new file mode 100644 (file)
index 0000000..9369e78
--- /dev/null
@@ -0,0 +1,3 @@
+;;; -*- lexical-binding: t -*-
+(defun foo ()
+  (make-process :name "ls"))
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-value.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-missing-keyword-value.el
new file mode 100644 (file)
index 0000000..4226349
--- /dev/null
@@ -0,0 +1,3 @@
+;;; -*- lexical-binding: t -*-
+(defun foo ()
+  (make-process :name "ls" :command))
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-repeated-keyword-arg.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-repeated-keyword-arg.el
new file mode 100644 (file)
index 0000000..18250f1
--- /dev/null
@@ -0,0 +1,3 @@
+;;; -*- lexical-binding: t -*-
+(defun foo ()
+  (make-process :name "ls" :command "ls" :name "ls"))
diff --git a/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-unknown-keyword-arg.el b/test/lisp/emacs-lisp/bytecomp-resources/warn-make-process-unknown-keyword-arg.el
new file mode 100644 (file)
index 0000000..4721035
--- /dev/null
@@ -0,0 +1,4 @@
+;;; -*- lexical-binding: t -*-
+(defun foo ()
+  (make-process :name "ls" :command "ls"
+                :coding-system 'binary))
index 19e08e8d19971575d4c16f68bf86c5664aca6fa5..26325c1ef11d1b39120be1158fbd81129ed5f9ff 100644 (file)
@@ -1204,6 +1204,22 @@ byte-compiled.  Run with dynamic binding."
  "nowarn-inline-after-defvar.el"
  "Lexical argument shadows" 'reverse)
 
+(bytecomp--define-warning-file-test
+ "warn-make-process-missing-keyword-arg.el"
+ "called without required keyword argument :command")
+
+(bytecomp--define-warning-file-test
+ "warn-make-process-unknown-keyword-arg.el"
+ "called with unknown keyword argument :coding-system")
+
+(bytecomp--define-warning-file-test
+ "warn-make-process-repeated-keyword-arg.el"
+ "called with repeated keyword argument :name")
+
+(bytecomp--define-warning-file-test
+ "warn-make-process-missing-keyword-value.el"
+ "missing value for keyword argument :command")
+
 \f
 ;;;; Macro expansion.