From d8485b383034190f305b246dffa1b74000668f52 Mon Sep 17 00:00:00 2001
From: Tassilo Horn <tassilo@member.fsf.org>
Date: Wed, 11 Jul 2012 09:18:38 +0200
Subject: [PATCH] CLJ-1024: Check for invalid varags/destrucuring uses.

Protocol, interface method declarations don't allow for varags and
destructuring support.  Currently, for example

  (defprotocol FooBar
    (foo [this & more]))

compiles just fine, and & is interpreted as a usual argument that happens to be
named & without special meaning.  But clearly, the user wanted to specify a
varags parameter here.  The same applies to definterface.

Similarly, providing method implementations via defrecord, deftype, and reify
don't allow for varags (but dynamic extenions via extend do).

So this patch makes defprotocol and definterface throw an
IllegalArgumentException if a user tries to use varargs and destructuring in
method signatures.

Similarly, defrecord, deftype, and reify throw an IllegalArgumentException if
any method implementation arglist contains a varargs argument.
---
 src/clj/clojure/core_deftype.clj | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index 505c147..24fdfc3 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -16,11 +16,31 @@
   [ns]
   (.replace (str ns) \- \_))
 
+(defn ^:private throw-on-varargs
+  "Throws an exception if arglist contains a varargs declaration.
+  Protocol/interface method impls defined with deftype, defrecord, and reify
+  don't support varags."
+  [arglist]
+  (when (some #(= '& %) arglist)
+    (throw (IllegalArgumentException.
+            "No varargs support for definterface and defprotocol method sigs;
+ditto for method impls defined with deftype, defrecord, and reify."))))
+
+(defn ^:private throw-on-varargs-or-destr
+  "Throws an exception if arglist contains a varargs declaration or a
+  destructuring form.
+  Protocol/interface method signatures shouldn't use varargs/destructuring."
+  [arglist]
+  (when (some #(or (= '& %) (coll? %)) arglist)
+    (throw (IllegalArgumentException.
+            "No varargs nor destructuring support for definterface and defprotocol method sigs."))))
+
 ;for now, built on gen-interface
 (defmacro definterface 
   [name & sigs]
   (let [tag (fn [x] (or (:tag (meta x)) Object))
         psig (fn [[name [& args]]]
+               (throw-on-varargs-or-destr args)
                (vector name (vec (map tag args)) (tag name) (map meta args)))
         cname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))]
     `(let [] 
@@ -53,6 +73,7 @@
                        (disj 'Object 'java.lang.Object)
                        vec)
         methods (map (fn [[name params & body]]
+                       (throw-on-varargs params)
                        (cons name (maybe-destructured params body)))
                      (apply concat (vals impls)))]
     (when-let [bad-opts (seq (remove #{:no-print} (keys opts)))]
@@ -597,6 +618,8 @@
                                   (if (vector? (first rs))
                                     (recur (conj as (first rs)) (next rs))
                                     [(seq as) (first rs)]))]
+                            (doseq [arglist arglists]
+                              (throw-on-varargs-or-destr arglist))
                             (when (some #{0} (map count arglists))
                               (throw (IllegalArgumentException. (str "Protocol fn: " mname " must take at least one arg"))))
                             (assoc m (keyword mname)
-- 
1.7.12

