diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 0f1dbbd..9529527 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -1 +1 @@ -{:config-dirs ["org.suskalo/coffi"]} +{:config-paths ["org.suskalo/coffi"]} diff --git a/.clj-kondo/org.suskalo/coffi/coffi/hooks.clj b/.clj-kondo/org.suskalo/coffi/coffi/hooks.clj deleted file mode 120000 index ec39996..0000000 --- a/.clj-kondo/org.suskalo/coffi/coffi/hooks.clj +++ /dev/null @@ -1 +0,0 @@ -../../../../resources/clj-kondo.exports/org.suskalo/coffi/coffi/hooks.clj \ No newline at end of file diff --git a/.clj-kondo/org.suskalo/coffi/hooks/coffi.clj b/.clj-kondo/org.suskalo/coffi/hooks/coffi.clj new file mode 120000 index 0000000..e2437de --- /dev/null +++ b/.clj-kondo/org.suskalo/coffi/hooks/coffi.clj @@ -0,0 +1 @@ +../../../../resources/clj-kondo.exports/org.suskalo/coffi/hooks/coffi.clj \ No newline at end of file diff --git a/resources/clj-kondo.exports/org.suskalo/coffi/coffi/hooks.clj b/resources/clj-kondo.exports/org.suskalo/coffi/coffi/hooks.clj deleted file mode 100644 index ffab6be..0000000 --- a/resources/clj-kondo.exports/org.suskalo/coffi/coffi/hooks.clj +++ /dev/null @@ -1,9 +0,0 @@ -(ns ^:no-doc coffi.hooks - (:require - [clj-kondo.hooks-api :as api])) - -(defn defcfn - [{:keys [node]}] - (let [[] (rest (:children node))] - ;; TODO(Joshua): Add an implementation of this macro's hook - )) diff --git a/resources/clj-kondo.exports/org.suskalo/coffi/config.edn b/resources/clj-kondo.exports/org.suskalo/coffi/config.edn index 3cd09b1..a1224fe 100644 --- a/resources/clj-kondo.exports/org.suskalo/coffi/config.edn +++ b/resources/clj-kondo.exports/org.suskalo/coffi/config.edn @@ -1 +1,2 @@ -{:hooks {:analyze-call {coffi.ffi/defcfn coffi.hooks/defcfn}}} +{:hooks {:analyze-call {coffi.ffi/defcfn hooks.coffi/defcfn}} + :linters {:coffi.ffi/invalid-syntax {:level :error}}} diff --git a/resources/clj-kondo.exports/org.suskalo/coffi/hooks/coffi.clj b/resources/clj-kondo.exports/org.suskalo/coffi/hooks/coffi.clj new file mode 100644 index 0000000..e161722 --- /dev/null +++ b/resources/clj-kondo.exports/org.suskalo/coffi/hooks/coffi.clj @@ -0,0 +1,81 @@ +(ns ^:no-doc hooks.coffi + (:require + [clj-kondo.hooks-api :as api])) + +(defn validate-type + [node] + (when-not (or (qualified-keyword? (api/sexpr node)) + (and (api/vector-node? node) + (qualified-keyword? (api/sexpr (first (:children node)))))) + (api/reg-finding! + {:row (:row (meta node)) + :col (:col (meta node)) + :message "A type must be a qualified keyword or a vector with one as the first element." + :type :coffi.ffi/invalid-syntax}))) + +(defn defcfn + [{:keys [node]}] + (try + (let [[var-name-node & more] (rest (:children node)) + [docstring-node & more] (if (and (api/string-node? (first more)) + (not (api/vector-node? (second more)))) + more + (cons nil more)) + [attr-map-node & more] (if (api/map-node? (first more)) + more + (cons nil more)) + [symbol-node native-arglist-node return-type-node & more] more + _ (when-not (or (and (api/token-node? symbol-node) + (simple-symbol? (api/sexpr symbol-node))) + (api/string-node? symbol-node)) + (api/reg-finding! {:row (:row (meta symbol-node)) + :col (:col (meta symbol-node)) + :message "Native symbol must be a string or symbol." + :type :coffi.ffi/invalid-syntax})) + _ (run! validate-type (cons return-type-node (:children native-arglist-node))) + wrapper-nodes (when (seq more) + {:native-fn (first more) + :fn-tail (rest more)}) + _ (when (and (:native-fn wrapper-nodes) + (empty? (:fn-tail wrapper-nodes))) + (api/reg-finding! + {:row (:row (meta node)) + :col (:col (meta node)) + :message "A defcfn with a native-fn must have a function body." + :type :coffi.ffi/invalid-syntax})) + arglist-vec (api/vector-node + (mapv api/token-node + (repeatedly (count (:children native-arglist-node)) + #(gensym "arg")))) + fn-body (if wrapper-nodes + (:fn-tail wrapper-nodes) + (list + arglist-vec + arglist-vec)) + defn-node (api/list-node + (list* + (api/token-node 'defn) + var-name-node + (concat + (filter some? [docstring-node attr-map-node]) + fn-body))) + let-node (api/list-node + (list + (api/token-node 'let) + (api/vector-node + (cond->> nil + wrapper-nodes (concat [(:native-fn wrapper-nodes) + (api/list-node + (list + (api/token-node 'fn) + arglist-vec + arglist-vec))]) + :always vec)) + defn-node))] + {:node let-node}) + (catch Exception _ + (api/reg-finding! + {:row (:row (meta node)) + :col (:col (meta node)) + :message "Invalid syntax" + :type :coffi.ffi/invalid-syntax}))))