diff --git a/CHANGELOG.md b/CHANGELOG.md index 74772332ff..ea05413a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ For a list of breaking changes, check [here](#breaking-changes). - [#2431](https://github.com/clj-kondo/clj-kondo/issues/2431): only apply redundant-nested-call linter for nested exprs - Relax `:redundant-nested-call` for `comp`, `concat`, `every-pred` and `some-fn` since it may affect performance - [#2446](https://github.com/clj-kondo/clj-kondo/issues/2446): false positive `:redundant-ignore` +- [#2448](https://github.com/clj-kondo/clj-kondo/issues/2448): redundant nested call in hook gen'ed code ## 2024.11.14 diff --git a/corpus/issue-2448/.clj-kondo/config.edn b/corpus/issue-2448/.clj-kondo/config.edn new file mode 100644 index 0000000000..f68ad15c7d --- /dev/null +++ b/corpus/issue-2448/.clj-kondo/config.edn @@ -0,0 +1,4 @@ +{:hooks {:analyze-call {ductile.util/if+ hooks/ifplus-hook + ductile.util/when+ hooks/whenplus-hook + ductile.util/cond+ hooks/condplus-hook}} + :linters {:redundant-nested-call {:level :warning}}} diff --git a/corpus/issue-2448/.clj-kondo/hooks.clj b/corpus/issue-2448/.clj-kondo/hooks.clj new file mode 100644 index 0000000000..4d0d365f94 --- /dev/null +++ b/corpus/issue-2448/.clj-kondo/hooks.clj @@ -0,0 +1,93 @@ +(ns hooks + (:require + [clj-kondo.hooks-api :as api])) + +(defn transform-conds [cond-forms body-form] + (if (empty? cond-forms) + body-form + (let [cond-form (first cond-forms)] + (if (and (api/keyword-node? cond-form) (= :let (:k cond-form))) + (api/list-node + (list + (api/token-node 'let) + (second cond-forms) + (transform-conds (nnext cond-forms) body-form))) + (api/list-node + (list + (api/token-node 'when) + cond-form + (transform-conds (next cond-forms) body-form))))))) + +(defn ifplus-hook [form] + (let [[_ cond-form then-form else-form] (:children (:node form)) + new-node (or + (when (api/list-node? cond-form) + (let [[f & conds] (:children cond-form)] + (when (and (api/token-node? f) + (= 'and (:value f))) + (api/list-node + (list + (api/token-node 'or) + (transform-conds conds then-form) + else-form))))) + (api/list-node + (list + (api/token-node 'if) + cond-form + then-form + else-form)))] + {:node + new-node})) + +(defn whenplus-hook [form] + (let [[_ cond-form & body-forms] (:children (:node form))] + {:node + (api/list-node + (list + (api/token-node 'ductile.util/if+) + cond-form + (api/list-node + (list* + (api/token-node 'do) + body-forms)) + (api/token-node nil)))})) + +(defn condplus-hook [form] + (let [[_ test expr & rest] (:children (:node form)) + tail (if rest + (api/list-node + (list* + (api/token-node 'ductile.util/cond+) + rest)) + (api/coerce nil)) + new-node (cond + (and (api/keyword-node? test) (= :do (:k test))) + (api/list-node + (list + (api/token-node 'do) + expr + tail)) + + (and (api/keyword-node? test) (= :let (:k test))) + (api/list-node + (list + (api/token-node 'let) + expr + tail)) + + (and (api/keyword-node? test) (= :some (:k test))) + (api/list-node + (list + (api/token-node 'or) + expr + tail)) + + :else + (api/list-node + (list + (api/token-node 'ductile.util/if+) + test + expr + tail)))] + {:node new-node})) + diff --git a/corpus/issue-2448/src/foobar.clj b/corpus/issue-2448/src/foobar.clj new file mode 100644 index 0000000000..376946a12d --- /dev/null +++ b/corpus/issue-2448/src/foobar.clj @@ -0,0 +1,13 @@ +(ns foobar + (:require [ductile.util :as util])) + +(util/cond+ + false 1 + (and true + :let [x 2] + (= 3 x)) 4 + (and true + :let [x 3] + (= 3 x)) x) + +(or (or 1 2) 2) diff --git a/src/clj_kondo/impl/linters.clj b/src/clj_kondo/impl/linters.clj index e31427e872..025046cc7c 100644 --- a/src/clj_kondo/impl/linters.clj +++ b/src/clj_kondo/impl/linters.clj @@ -222,7 +222,8 @@ (defn lint-redundant-nested-call "Lints calls of variadic functions/macros when nested." [call] - (let [[[call-ns call-name :as c] parent] (:callstack call)] + (let [[[call-ns call-name :as c] parent] (:callstack call) + mc (meta c)] (when (and (utils/one-of call-ns [clojure.core cljs.core]) (utils/one-of call-name [* *' + +' and or @@ -231,8 +232,8 @@ ) (= [call-ns call-name] parent) ;; Exclude instances of nesting when directly inside threading macros - (let [mc (meta c) - {call-row :row + (not (:derived-location mc)) + (let [{call-row :row call-col :col} mc {parent-row :row parent-col :col} (meta parent)] diff --git a/test/clj_kondo/hooks_test.clj b/test/clj_kondo/hooks_test.clj index f3f67c9449..c7cb6a660a 100644 --- a/test/clj_kondo/hooks_test.clj +++ b/test/clj_kondo/hooks_test.clj @@ -474,3 +474,14 @@ my-ns/special-map \" (lint! (fs/file "corpus" "issue-2446" "src" "foobar.clj") "--config" (slurp (fs/file "corpus" "issue-2446" ".clj-kondo" "config.edn")) "--config-dir" (fs/file "corpus" "issue-2446" ".clj-kondo")))) + +(deftest issue-2448-hooks-redundant-nested-call-test + (assert-submaps2 + [{:file "corpus/issue-2448/src/foobar.clj", + :row 13, + :col 5, + :level :warning, + :message "Redundant nested call: or"}] + (lint! (fs/file "corpus" "issue-2448" "src" "foobar.clj") + "--config" (slurp (fs/file "corpus" "issue-2448" ".clj-kondo" "config.edn")) + "--config-dir" (fs/file "corpus" "issue-2448" ".clj-kondo"))))