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 Feb 26, 2024
1 parent ace53da commit 23b70e0
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ For a list of breaking changes, check [here](#breaking-changes).

## Unreleased

- [#1923](https://github.com/clj-kondo/clj-kondo/issues/1923): Lint invalid fn name for threaded fn literals
- [#2276](https://github.com/clj-kondo/clj-kondo/issues/2276): New Clojure 1.12 array notation (`String*`) may occur outside of metadata
- [#2278](https://github.com/clj-kondo/clj-kondo/issues/2278): `bigint` in CLJS is a known symbol in `extend-type`
- [#2288](https://github.com/clj-kondo/clj-kondo/issues/2288): fix static method analysis and suppressing `:java-static-field-call` locally
Expand Down
61 changes: 39 additions & 22 deletions src/clj_kondo/impl/analyzer.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2107,9 +2107,19 @@
:full-fn-name
:row :col
:expr] :as m}]
(let [not-is-dot (and (not= '. full-fn-name)

(let [some-anon-fn-literals? (some #(identical? :fn (tag %)) (:children expr))
not-is-dot (and (not= '. full-fn-name)
(not= '.. full-fn-name))]
(cond
some-anon-fn-literals?
(recur ctx (update-in m [:expr :children]
(fn [children]
(for [node children]
(cond-> node
(identical? :fn (tag node))
(-> macroexpand/expand-fn
(vary-meta assoc :clj-kondo.impl/expanded? true)))))))
(and not-is-dot
(str/ends-with? full-fn-name "."))
(recur ctx
Expand Down Expand Up @@ -2772,6 +2782,26 @@
(lint-discouraged-tags! ctx tag-expr)
(analyze-children ctx children)))

(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]
(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")))
(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"))))

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

(defn analyze-expression**
Expand All @@ -2782,7 +2812,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 @@ -2837,26 +2867,13 @@
(analyze-children (update ctx
:callstack #(cons [nil t] %))
children))
:fn (do
(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
4 changes: 2 additions & 2 deletions src/clj_kondo/impl/analyzer/usages.clj
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,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 @@ -295,7 +295,7 @@
(or core? test?)
(not (:clj-kondo.impl/generated (meta parent-call)))
(if core?
(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 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 @@ -512,7 +512,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 @@ -3480,7 +3480,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 @@ -146,3 +146,12 @@
'{:linters {:unused-value {:level :warning}}
:config-in-call {hyperfiddle.rcf/tests {:linters {:unresolved-symbol {:level :off}
:unused-value {:level :off}}}}})))

(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 23b70e0

Please sign in to comment.