Skip to content

Commit

Permalink
Expand fn-literals in lists
Browse files Browse the repository at this point in the history
  • Loading branch information
tomdl89 committed Nov 5, 2024
1 parent 8e6bdb2 commit 86511ea
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ For a list of breaking changes, check [here](#breaking-changes).
- [#2416](https://github.com/clj-kondo/clj-kondo/issues/2416): detect empty `require` and `:require` forms ([@NoahTheDuke](https://github.com/NoahTheDuke))
- [#1786](https://github.com/clj-kondo/clj-kondo/issues/1786): Support `gen-interface` (by suppressing unresolved symbols)
- [#2420](https://github.com/clj-kondo/clj-kondo/issues/2420): Detect uneven number of clauses in cond-> and cond->>
- [#1923](https://github.com/clj-kondo/clj-kondo/issues/1923): Lint invalid fn name for threaded fn literals

## 2024.09.27

Expand Down
69 changes: 45 additions & 24 deletions src/clj_kondo/impl/analyzer.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2876,7 +2876,7 @@
(and (not generated?)
core?
(not (:clj-kondo.impl/generated (meta parent-call)))
(one-of core-sym [do fn defn defn-
(one-of core-sym [do fn fn* defn defn-
let when-let loop binding with-open
doseq try when when-not when-first
when-some future]))]
Expand All @@ -2886,6 +2886,39 @@
:message "Unused value"
:filename (:filename ctx))))))))

(defn update-literal-fn-ctx [ctx expr]
(cond-> (assoc ctx :arg-types nil :in-fn-literal true)
(:clj-kondo.impl/fn-has-first-arg (meta expr))
(update :bindings assoc '% {})))

(defn analyze-literal-fn! [lang ctx expr]
(lint-unused-value ctx expr)
(when (and (:in-fn-literal ctx)
(not (:clj-kondo.impl/generated expr)))
(findings/reg-finding! ctx (assoc (meta expr)
:filename (:filename ctx)
:level :error
:type :syntax
:message "Nested #()s are not allowed")))
(when (identical? :edn lang)
(findings/reg-finding! ctx (assoc (meta expr)
:filename (:filename ctx)
:level :error
:type :syntax
:message "#()s are not allowed in EDN"))))

(defn expand-fns
"For every child of expr (assumed to be a call) expand function
literals (i.e. #(...) forms) to (fn* ...) forms and mark these as expanded."
[expr]
(update expr :children
(fn [children]
(for [node children]
(cond-> node
(identical? :fn (tag node))
(-> macroexpand/expand-fn
(vary-meta assoc :clj-kondo.impl/expanded? true)))))))

#_(requiring-resolve 'clojure.set/union)

(defn analyze-expression**
Expand All @@ -2896,7 +2929,7 @@
(:quoted ctx))
(meta/lift-meta-content2 (dissoc ctx :arg-types) expr)
expr)
t (tag expr)
t (if (:clj-kondo.impl/expanded? (meta expr)) :exp-fn (tag expr))
{:keys [row col]} (meta expr)
arg-count (count (rest children))]
(utils/handle-ignore ctx expr)
Expand Down Expand Up @@ -2963,27 +2996,15 @@
(analyze-children (update ctx
:callstack #(cons [nil t] %))
children))
:fn (do
(lint-unused-value ctx expr)
(when (and (:in-fn-literal ctx)
(not (:clj-kondo.impl/generated expr)))
(findings/reg-finding! ctx (assoc (meta expr)
:filename (:filename ctx)
:level :error
:type :syntax
:message "Nested #()s are not allowed")))
(when (identical? :edn lang)
(findings/reg-finding! ctx (assoc (meta expr)
:filename (:filename ctx)
:level :error
:type :syntax
:message "#()s are not allowed in EDN")))
(let [expanded-node (macroexpand/expand-fn expr)
m (meta expanded-node)
has-first-arg? (:clj-kondo.impl/fn-has-first-arg m)]
(recur (cond-> (assoc ctx :arg-types nil :in-fn-literal true)
has-first-arg? (update :bindings assoc '% {}))
expanded-node)))

:exp-fn (do (analyze-literal-fn! lang ctx expr)
(recur (update-literal-fn-ctx ctx expr)
(vary-meta expr assoc :clj-kondo.impl/expanded? false)))
:fn (do (analyze-literal-fn! lang ctx expr)
(let [expanded-node (macroexpand/expand-fn expr)]
(recur (update-literal-fn-ctx ctx expanded-node)
expanded-node)))

:token
(let [edn? (= :edn lang)]
(if (or edn?
Expand Down Expand Up @@ -3077,7 +3098,7 @@
:full-fn-name full-fn-name
:row row
:col col
:expr expr})
:expr (expand-fns expr)})
_ (dorun ret) ;; realize all returned expressions
;; to not be bitten by laziness
maybe-call (some #(when (= id (:id %)) %) ret)]
Expand Down
2 changes: 1 addition & 1 deletion src/clj_kondo/impl/linters.clj
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@
;; doseq always return nil
(utils/one-of core-sym [doseq])
(< idx (dec (:len call))))
(utils/one-of core-sym [do fn defn defn-
(utils/one-of core-sym [do fn fn* defn defn-
let when-let loop binding with-open
doseq try when when-not when-first
when-some future]))
Expand Down
3 changes: 2 additions & 1 deletion src/clj_kondo/impl/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
;;; export rewrite-clj functions

(defn tag [expr]
(node/tag expr))
(when (satisfies? node/Node expr)
(node/tag expr)))

(def map-node seq/map-node)
(def vector-node seq/vector-node)
Expand Down
6 changes: 5 additions & 1 deletion test/clj_kondo/main_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3507,7 +3507,11 @@ foo/")))
'({:file "<stdin>", :row 1, :col 7, :level :error, :message "Function name must be simple symbol but got: :foo"}
{:file "<stdin>", :row 1, :col 20, :level :error, :message "Function name must be simple symbol but got: :foo"}
{:file "<stdin>", :row 1, :col 33, :level :error, :message "Function name must be simple symbol but got: \"foo\""})
(lint! "(defn :foo []) (fn :foo []) (fn \"foo\" [])")))
(lint! "(defn :foo []) (fn :foo []) (fn \"foo\" [])"))
(assert-submaps
'({:file "<stdin>", :row 1, :col 5, :level :error, :message "Function name must be simple symbol but got: :foo"}
{:file "<stdin>", :row 1, :col 24, :level :error, :message "Function name must be simple symbol but got: \"foo\""})
(lint! "(-> :foo #(inc %)) (-> \"foo\" #(inc %))")))

(deftest lint-stdin-exclude-files-test
(is (empty?
Expand Down
9 changes: 9 additions & 0 deletions test/clj_kondo/unused_value_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,12 @@ bar)"
(is (empty?
(lint! "(fn [] '({:a 1} {:b 2} {:c 3}))"
{:linters {:unused-value {:level :warning}}}))))

(deftest thread-last-test
(assert-submaps
'({:file "<stdin>", :row 1, :col 12, :level :warning, :message "Unused value"})
(lint! "(->> :foo #(name %))"
{:linters {:unused-value {:level :warning}
:redundant-fn-wrapper {:level :off}}}))
(is (empty? (lint! "(->> :foo (#(name %)))" {:linters {:unused-value {:level :warning}
:redundant-fn-wrapper {:level :off}}}))))

0 comments on commit 86511ea

Please sign in to comment.