Skip to content

Commit

Permalink
Add invlaid-fn-name linter
Browse files Browse the repository at this point in the history
  • Loading branch information
tomdl89 committed Dec 15, 2023
1 parent 233d1d2 commit d04066a
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For a list of breaking changes, check [here](#breaking-changes).

## Unreleased

- ...
- [#2159](https://github.com/clj-kondo/clj-kondo/issues/2159): New linter, `:invalid-fn-name`

## 2023.12.15

Expand Down
16 changes: 16 additions & 0 deletions doc/linters.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ configuration. For general configurations options, go [here](config.md).
- [Format](#format)
- [Def + fn instead of defn](#def--fn-instead-of-defn)
- [Inline def](#inline-def)
- [Invalid fn name](#invalid-fn-name)
- [Invalid arity](#invalid-arity)
- [Conflicting arity](#conflicting-arity)
- [Reduce without initial value](#reduce-without-initial-value)
Expand Down Expand Up @@ -845,6 +846,21 @@ See [issue](https://github.com/clj-kondo/clj-kondo/issues/1920).

*Example message:* `inline def`.

### Invalid fn name

**Keyword:** `:invalid-fn-name`.

*Description:* warn when a function's name is not valid. If present, it should
be an unquoted symbol.

*Default level:* `:error`.

*Example trigger:* `(fn :fn-name [x] (inc x))`.

*Example message:* `First arg of fn should be a symbol, params vector or arity clause`.

*Config:*

### Invalid arity

**Keyword:** `:invalid-arity`.
Expand Down
19 changes: 19 additions & 0 deletions src/clj_kondo/impl/analyzer.clj
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,21 @@
(defn- let? [x]
(one-of x [[clojure.core let] [cljs.core let]]))

(defn- invalid-fn-name? [?name-expr]
(let [valid? (some-fn utils/list-node? ; fn body (usually 1 of many)
utils/vector-node? ; fn args
utils/symbol-token?)] ; fn name
(not (valid? ?name-expr))))

(defn- reg-invalid-fn-name! [ctx expr filename]
(findings/reg-finding!
ctx
(node->line
filename
expr
:invalid-fn-name
"First arg of fn should be a symbol, params vector or arity clause")))

(defn- def-fn? [{:keys [callstack]}]
(let [[_ parent extra-parent] callstack]
(or (def? parent)
Expand Down Expand Up @@ -985,6 +1000,10 @@
(when (and (not (linter-disabled? ctx :def-fn))
(def-fn? ctx))
(reg-def-fn! ctx expr filename))
(when (and (not (linter-disabled? ctx :valid-fn-name))
?name-expr
(invalid-fn-name? ?name-expr))
(reg-invalid-fn-name! ctx expr filename))
(with-meta parsed-bodies
(when arities
(cond-> {:arity {:fixed-arities fixed-arities
Expand Down
1 change: 1 addition & 0 deletions src/clj_kondo/impl/config.clj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
:private-call {:level :error}
:inline-def {:level :warning}
:def-fn {:level :off}
:invalid-fn-name {:level :error}
:redundant-do {:level :warning}
:redundant-let {:level :warning}
:cond-else {:level :warning}
Expand Down
4 changes: 4 additions & 0 deletions src/clj_kondo/impl/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
(and (instance? clj_kondo.impl.rewrite_clj.node.seq.SeqNode n)
(identical? :set (tag n))))

(defn vector-node? [n]
(and (instance? clj_kondo.impl.rewrite_clj.node.seq.SeqNode n)
(identical? :vector (tag n))))

;;; end export

(defn print-err! [& strs]
Expand Down
12 changes: 12 additions & 0 deletions test/clj_kondo/main_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@
(is (empty? (lint! "(def x (reify Object (toString [_] \"x\")))" "--lang" (name lang) "--config" (pr-str config))))
(is (empty? (lint! "(require '[some.ns :refer [my-reify]]) (def x (my-reify Object (toString [_] \"x\")))" "--lang" (name lang) "--config" (pr-str config)))))))

(deftest invalid-fn-name-test
(assert-submaps
'({:row 1, :col 1, :level :error, :message "First arg of fn should be a symbol, params vector or arity clause"})
(lint! "(fn \"fn-name\" [x] (inc x))"))
(assert-submaps
'({:row 1, :col 6, :level :error, :message "First arg of fn should be a symbol, params vector or arity clause"})
(lint! "(map (fn 'symbol ([x] (inc x))) coll)"))
(assert-submaps
'({:row 1, :col 7, :level :error, :message "First arg of fn should be a symbol, params vector or arity clause"})
(lint! "(-> 7 (fn [x] (inc x)))"))
(is (empty? (lint! "(fn fn-name [x] (inc x))"))))

(deftest redundant-let-test
(let [linted (lint! (io/file "corpus" "redundant_let.clj"))
row-col-files (map #(select-keys % [:row :col :file])
Expand Down

0 comments on commit d04066a

Please sign in to comment.