From cddccf6338c1daa23a9169720fe9541eb186eb9c Mon Sep 17 00:00:00 2001 From: Miguel de Benito Delgado Date: Mon, 5 Oct 2026 12:10:14 +0200 Subject: [PATCH] :paperclip: Improve clj-kondo hooks, including if-let --- .clj-kondo/config.edn | 17 ++++--- .clj-kondo/hooks/benches.clj | 26 ++++++++++ .clj-kondo/hooks/core.clj | 97 ++++++++++++++++++++++++++++++++++++ .clj-kondo/hooks/export.clj | 20 +++----- 4 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 .clj-kondo/hooks/benches.clj create mode 100644 .clj-kondo/hooks/core.clj diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index cfb98161d8..e1883ae4d4 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -24,9 +24,10 @@ shadow.lazy/loadable hooks.export/rumext-lazycomponent app.util.i18n/tr hooks.i18n/tr-dynamic app.common.i18n/tr hooks.i18n/tr-dynamic - benches.render-wasm.declarations/defscene hooks.export/benches-defscene - benches.render-wasm.declarations/defcase hooks.export/benches-defcase - }} + clojure.core/let hooks.core/check-let + cljs.core/let hooks.core/check-let + benches.render-wasm.declarations/defscene hooks.benches/defscene + benches.render-wasm.declarations/defcase hooks.benches/defcase}} :output {:exclude-files @@ -103,6 +104,10 @@ :unused-binding {:exclude-destructured-as true - :exclude-destructured-keys-in-fn-args false - } - }} + :exclude-destructured-keys-in-fn-args false} + + :core/use-if-let + {:level :warning} + + :core/use-when-let + {:level :warning}}} diff --git a/.clj-kondo/hooks/benches.clj b/.clj-kondo/hooks/benches.clj new file mode 100644 index 0000000000..03c81b3582 --- /dev/null +++ b/.clj-kondo/hooks/benches.clj @@ -0,0 +1,26 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns hooks.benches + (:require [clj-kondo.hooks-api :as api])) + +(defn defscene + "Analyzes scene options and its compiled builder body." + [{:keys [node]}] + (let [[_ _ opts argv & body] (:children node)] + (if (and argv (api/vector-node? argv)) + {:node (api/list-node [(api/token-node (quote do)) opts + (api/list-node (into [(api/token-node (quote fn)) argv] body))])} + {:node node}))) + +(defn defcase + "Analyzes case options and its compiled operation body." + [{:keys [node]}] + (let [[_ _ opts argv & body] (:children node)] + (if (and (seq body) argv (api/vector-node? argv)) + {:node (api/list-node [(api/token-node (quote do)) opts + (api/list-node (into [(api/token-node (quote fn)) argv] body))])} + {:node node}))) diff --git a/.clj-kondo/hooks/core.clj b/.clj-kondo/hooks/core.clj new file mode 100644 index 0000000000..38c28ff8f9 --- /dev/null +++ b/.clj-kondo/hooks/core.clj @@ -0,0 +1,97 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns hooks.core + (:require [clj-kondo.hooks-api :as api])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; if-let, when-let +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- use-if-let? + [form] + (let [[_ bindings & body] form] + (and + ;; Exactly one binding pair. + (vector? bindings) + (= 2 (count bindings)) + + ;; Exactly one body form. + (= 1 (count body)) + + (let [[binding-sym _] bindings + if-form (first body)] + (and + ;; Exclude destructuring for v0. + (symbol? binding-sym) + + ;; Exactly: (if binding-sym then else) + (seq? if-form) + (= 4 (count if-form)) + (= 'if (first if-form)) + (= binding-sym (second if-form))))))) + + +(defn- use-if-let + "Flags: + + (let [x (foo)] + (if x + (bar x) + (baz))) + " + [{:keys [node]}] + (when (use-if-let? (api/sexpr node)) + (api/reg-finding! + (assoc (meta (first (:children node))) + :type :core/use-if-let + :message "Use if-let instead of let followed by if."))) + nil) + +(defn- use-when-let? + [form] + (let [[_ bindings & body] form] + (and + ;; Exactly one binding pair. + (vector? bindings) + (= 2 (count bindings)) + + ;; Exactly one body form. + (= 1 (count body)) + + (let [[binding-sym _] bindings + when-form (first body)] + (and + ;; Exclude destructuring for v0. + (symbol? binding-sym) + + ;; (when binding-sym & body) with at least one body form. + (seq? when-form) + (<= 3 (count when-form)) + (= 'when (first when-form)) + (= binding-sym (second when-form))))))) + +(defn- use-when-let + "Flags: + + (let [x (foo)] + (when x + (bar x))) + " + [{:keys [node]}] + (when (use-when-let? (api/sexpr node)) + (api/reg-finding! + (assoc (meta (first (:children node))) + :type :core/use-when-let + :message "Use when-let instead of let followed by when."))) + nil) + +(defn check-let + "Runs the let-body checks (if-let, when-let)." + [{:keys [node] :as m}] + (use-if-let m) + (use-when-let m) + nil) \ No newline at end of file diff --git a/.clj-kondo/hooks/export.clj b/.clj-kondo/hooks/export.clj index 8845a28573..f4c0ebcf0c 100644 --- a/.clj-kondo/hooks/export.clj +++ b/.clj-kondo/hooks/export.clj @@ -1,3 +1,9 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + (ns hooks.export (:require [clj-kondo.hooks-api :as api])) @@ -10,20 +16,6 @@ sname])] {:node result})) -(defn benches-defscene - [{:keys [node]}] - (let [[_ _ opts argv & body] (:children node)] - (if (and argv (api/vector-node? argv)) - {:node (api/list-node (into [(api/token-node (quote fn)) argv] body))} - {:node node}))) - -(defn benches-defcase - [{:keys [node]}] - (let [[_ _ opts argv & body] (:children node)] - (if (and (seq body) argv (api/vector-node? argv)) - {:node (api/list-node (into [(api/token-node (quote fn)) argv] body))} - {:node node}))) - (def registry (atom {}))