From 20400c84bd125547d7c62806bad8a28ea673b1ab Mon Sep 17 00:00:00 2001 From: keychera Date: Fri, 12 Dec 2025 19:42:10 +0900 Subject: [PATCH 1/3] add clj-kondo hooks --- src/clj-kondo.exports/config.edn | 3 ++ src/clj-kondo.exports/odoyle/hooks.clj | 74 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/clj-kondo.exports/config.edn create mode 100644 src/clj-kondo.exports/odoyle/hooks.clj diff --git a/src/clj-kondo.exports/config.edn b/src/clj-kondo.exports/config.edn new file mode 100644 index 0000000..8bf1516 --- /dev/null +++ b/src/clj-kondo.exports/config.edn @@ -0,0 +1,3 @@ +{:linters {:odoyle/reserved {:level :warning} + :odoyle/hook-error {:level :error}} + :hooks {:analyze-call {odoyle.rules/ruleset odoyle.hooks/ruleset-hook}}} \ No newline at end of file diff --git a/src/clj-kondo.exports/odoyle/hooks.clj b/src/clj-kondo.exports/odoyle/hooks.clj new file mode 100644 index 0000000..fa2d674 --- /dev/null +++ b/src/clj-kondo.exports/odoyle/hooks.clj @@ -0,0 +1,74 @@ +(ns odoyle.hooks + (:require + [clj-kondo.hooks-api :as api])) + +(def rule-delimiter #{:what :when :then :then-finally}) +(def reserved-syms #{'session 'match}) + +(defn let-one-and-fake-use [sym-nodes faker] + (let [per-symbol-def (transduce (map #(map-indexed vector %)) concat (vals (group-by api/sexpr sym-nodes))) + let-symbols (transduce + (map (fn [[idx node]] + (if (= idx 0) + [node (api/token-node faker) (api/token-node '_) (api/token-node (api/sexpr node))] + [(api/token-node '_) node]))) + concat per-symbol-def)] + (into [] let-symbols))) + +(defn ruleset-node->lets-node [ruleset-node] + (eduction + (map (fn [[rule-name-node rule-block-node]] + [rule-name-node + (partition-by (fn [node] (rule-delimiter (api/sexpr node))) + (:children rule-block-node))])) + (map (fn [[rule-name-node blocks]] + (loop [[node & remaining] blocks + rule-block {:rule-name-node rule-name-node} + looking-at nil] + (if node + (if looking-at + (recur remaining (assoc rule-block looking-at node) nil) + (recur remaining rule-block (keyword (str (name (api/sexpr (first node))) "-block")))) + rule-block)))) + (map (fn [{:keys [rule-name-node what-block when-block then-block then-finally-block]}] + (let [what-nodes (flatten (map :children what-block)) + sym-nodes (into [] (filter (comp symbol? api/sexpr)) what-nodes) + _ (doseq [reserved-node (into [] (filter (comp reserved-syms api/sexpr)) sym-nodes)] + (api/reg-finding! + {:row (:row (meta reserved-node)) + :col (:col (meta reserved-node)) + :message "reserved symbol usage in :what block!" + :type :odoyle/reserved})) + sym-nodes (let-one-and-fake-use sym-nodes '[]) + keyword-nodes (into [] (filter (comp keyword? api/sexpr)) what-nodes) + keyword-nodes (conj keyword-nodes rule-name-node) + reserved-nodes (let-one-and-fake-use (into [] (map api/token-node) reserved-syms) '[])] + (api/list-node + (list* + (api/token-node 'let) + (api/vector-node (conj reserved-nodes + (api/token-node '_) + (api/list-node (list* (api/token-node 'vector) keyword-nodes)))) + (api/list-node (list (api/token-node 'prn) (api/token-node :separator))) + (api/list-node + (list* + (api/token-node 'let) + (api/vector-node (cond-> sym-nodes + when-block (conj (api/token-node '_) + (api/list-node (list* (api/token-node 'vector) when-block))))) + then-block)) + then-finally-block))))) + (partition 2 (:children ruleset-node)))) + +(defn ruleset-hook + [{:keys [node]}] + (try + (let [ruleset-node (nth (:children node) 1) + rule-nodes-as-lets (ruleset-node->lets-node ruleset-node)] + {:node (api/list-node (list* (api/token-node 'vector) rule-nodes-as-lets))}) + (catch Exception e + (api/reg-finding! + {:row (:row (meta node)) + :col (:col (meta node)) + :message (str "hook error:" (.getCause e)) + :type :odoyle/hook-error})))) From 1509b39bafb86d3167cc9e58e76728ea9901f3b8 Mon Sep 17 00:00:00 2001 From: keychera Date: Fri, 12 Dec 2025 19:50:11 +0900 Subject: [PATCH 2/3] exclude `then-finally-block` from `match` binding scope --- src/clj-kondo.exports/odoyle/hooks.clj | 29 +++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/clj-kondo.exports/odoyle/hooks.clj b/src/clj-kondo.exports/odoyle/hooks.clj index fa2d674..4eead6b 100644 --- a/src/clj-kondo.exports/odoyle/hooks.clj +++ b/src/clj-kondo.exports/odoyle/hooks.clj @@ -5,12 +5,12 @@ (def rule-delimiter #{:what :when :then :then-finally}) (def reserved-syms #{'session 'match}) -(defn let-one-and-fake-use [sym-nodes faker] +(defn let-one-and-fake-use [sym-nodes] (let [per-symbol-def (transduce (map #(map-indexed vector %)) concat (vals (group-by api/sexpr sym-nodes))) let-symbols (transduce (map (fn [[idx node]] (if (= idx 0) - [node (api/token-node faker) (api/token-node '_) (api/token-node (api/sexpr node))] + [node (api/token-node '[]) (api/token-node '_) (api/token-node (api/sexpr node))] [(api/token-node '_) node]))) concat per-symbol-def)] (into [] let-symbols))) @@ -39,24 +39,29 @@ :col (:col (meta reserved-node)) :message "reserved symbol usage in :what block!" :type :odoyle/reserved})) - sym-nodes (let-one-and-fake-use sym-nodes '[]) + sym-nodes (let-one-and-fake-use sym-nodes) keyword-nodes (into [] (filter (comp keyword? api/sexpr)) what-nodes) keyword-nodes (conj keyword-nodes rule-name-node) - reserved-nodes (let-one-and-fake-use (into [] (map api/token-node) reserved-syms) '[])] + session-scope (let-one-and-fake-use [(api/token-node 'session)]) + match-scope (let-one-and-fake-use [(api/token-node 'match)])] (api/list-node (list* (api/token-node 'let) - (api/vector-node (conj reserved-nodes + (api/vector-node (conj session-scope (api/token-node '_) (api/list-node (list* (api/token-node 'vector) keyword-nodes)))) (api/list-node (list (api/token-node 'prn) (api/token-node :separator))) (api/list-node - (list* - (api/token-node 'let) - (api/vector-node (cond-> sym-nodes - when-block (conj (api/token-node '_) - (api/list-node (list* (api/token-node 'vector) when-block))))) - then-block)) + (list + (api/token-node 'let) + (api/vector-node match-scope) + (api/list-node + (list* + (api/token-node 'let) + (api/vector-node (cond-> sym-nodes + when-block (conj (api/token-node '_) + (api/list-node (list* (api/token-node 'vector) when-block))))) + then-block)))) then-finally-block))))) (partition 2 (:children ruleset-node)))) @@ -70,5 +75,5 @@ (api/reg-finding! {:row (:row (meta node)) :col (:col (meta node)) - :message (str "hook error:" (.getCause e)) + :message (str "hook error: " e) :type :odoyle/hook-error})))) From 10447b7d8e3c9611fc9d59bc29d5a6d148d10556 Mon Sep 17 00:00:00 2001 From: keychera Date: Fri, 12 Dec 2025 20:00:19 +0900 Subject: [PATCH 3/3] fix export folder --- src/clj-kondo.exports/{ => net.sekao}/config.edn | 0 src/clj-kondo.exports/{ => net.sekao}/odoyle/hooks.clj | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/clj-kondo.exports/{ => net.sekao}/config.edn (100%) rename src/clj-kondo.exports/{ => net.sekao}/odoyle/hooks.clj (100%) diff --git a/src/clj-kondo.exports/config.edn b/src/clj-kondo.exports/net.sekao/config.edn similarity index 100% rename from src/clj-kondo.exports/config.edn rename to src/clj-kondo.exports/net.sekao/config.edn diff --git a/src/clj-kondo.exports/odoyle/hooks.clj b/src/clj-kondo.exports/net.sekao/odoyle/hooks.clj similarity index 100% rename from src/clj-kondo.exports/odoyle/hooks.clj rename to src/clj-kondo.exports/net.sekao/odoyle/hooks.clj