Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
assoc
-ing an update with the same key is hard to read. update
is known and
idiomatic.
; avoid
(assoc coll :a (+ (:a coll) 5))
(assoc coll :a (+ (coll :a) 5))
(assoc coll :a (+ (get coll :a) 5))
; prefer
(update coll :a + 5)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 1.0 | 1.0 |
A macro that nests an unquote-splicing
in a macro with a & body
can lead
to subtle hard to debug errors. Better to wrap the unquote-splicing
in
a do
to force it into 'expression position'.
; avoid
`(binding [max mymax] ~@body)
; prefer
`(binding [max mymax] (let [res# (do ~@body)] res#))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
false | true | false | 1.18.0 | 1.18.0 |
When defining methods for a multimethod, everything after the dispatch-val is given directly to fn
. This allows for providing a name to the defmethod function, which will make stack traces easier to read.
Examples:
; avoid (defmethod some-multi :foo [arg1 arg2] (+ arg1 arg2))
; prefer (defmethod some-multi :foo some-multi--foo [arg1 arg2] (+ arg1 arg2))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Checks for (/ x 1)
.
; avoid
(/ x 1)
; prefer
x
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 1.2.1 |
map
is lazy, which carries a performance and memory cost. dorun
uses seq
iteration to realize the entire sequence, returning nil
. This style of iteration also carries a performance and memory cost. dorun
is intended for more complex sequences, whereas a simple map
can be accomplished with reduce
+ conj
.
run!
uses reduce
which non-lazy and has no seq
overhead.
; avoid
(dorun (map println (range 10)))
; prefer
(run! println (range 10))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1 | 0.1 |
Using the Obj/staticMethod
form maps the method call to Clojure's natural function position.
NOTE: This rule is disabled if lint/prefer-method-values
is enabled to prevent conflicting diagnostics.
This rule is unsafe, as it can misunderstand when a symbol is or is not a class.
; avoid
(. Obj staticMethod args)
(. Obj (staticMethod) args)
; prefer
(Obj/staticMethod args)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1 | 1.15.2 |
Using the .method
form maps the method symbol to Clojure's natural function position.
NOTE: This rule is disabled if lint/prefer-method-values
is enabled to prevent conflicting diagnostics.
This rule is unsafe, as it can misunderstand when a symbol is or is not a class.
; avoid
(. obj method args)
; prefer
(.method obj args)
Name | Default | Options |
---|---|---|
:chosen-style | :dot | :dot , :method-value |
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 1.16.0 | 1.16.0 |
It's an error to have duplicate case
test constants.
; avoid
(case x :foo :bar :foo :baz)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1.119 | 0.1.119 |
deftype
and defrecord
will throw errors if you define multiple fields
with the same name, but it's good to catch these things early too.
; avoid
(defrecord Foo [a b a])
; prefer
(defrecord Foo [a b c])
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1 | 0.1 |
Avoid wrapping functions in pass-through anonymous function defitions.
This rule is unsafe, as it can misunderstand when a function is or is not a method.
; avoid
(fn [num] (even? num))
; prefer
even?
; avoid
(let [f (fn [num] (even? num))] ...)
; prefer
(let [f even?] ...)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Idiomatic if
defines both branches. when
returns nil
in the else branch.
; avoid
(if (some-func) :a nil)
; prefer
(when (some-func) :a)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Idiomatic if-let
defines both branches. when-let
returns nil
in the else branch.
; avoid
(if-let [a 1] a nil)
; prefer
(when-let [a 1] a)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Idiomatic if
defines both branches. when-not
returns nil
in the truthy branch.
; avoid
(if (some-func) nil :a)
; prefer
(when-not (some-func) :a)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
if-not
exists, so use it.
; avoid
(if (not x) y z)
; prefer
(if-not x y z)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
when-not
already defines an implicit do
. Rely on it.
; avoid
(if-not x (do (println :a) (println :b) :c))
; prefer
(when-not x (println :a) (println :b) :c)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Two not
s cancel each other out.
; avoid
(if-not (not x) y z)
; prefer
(if x y z)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
or
exists so use it lol.
; avoid
(if x x y)
; prefer
(or x y)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
vec
and set
are succinct and meaningful.
; avoid
(into [] coll)
; prefer
(vec coll)
; avoid
(into #{} coll)
; prefer
(set coll)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1.69 | 0.1.69 |
if-let
exists so use it.
Suggestions can be wrong as there's no code-walking to determine if result
binding is used in falsy branch.
; avoid
(let [result (some-func)] (if result (do-stuff result) (other-stuff)))
; prefer
(if-let [result (some-func)] (do-stuff result) (other-stuff))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1.69 | 0.1.69 |
when-let
exists so use it.
Suggestions can be wrong as there's no code-walking to determine if result
binding is used in falsy branch.
; avoid
(let [result (some-func)] (when result (do-stuff result)))
; prefer
(when-let [result (some-func)] (do-stuff result))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | false | 1.16.0 | 1.16.0 |
Synchronizing on interned objects is really bad. If multiple places lock on the same type of interned objects, those places are competing for locks.
; avoid
(locking :hello (+ 1 1))
; prefer
(def hello (Object.))
(locking hello (+ 1 1))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
loop
has an implicit do
. Use it.
; avoid
(loop [] (do (println 1) (println 2)))
; prefer
(loop [] (println 1) (println 2))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
Empty loops with nested when
can be while
. Doesn't apply if the final expr of the when
isn't (recur)
, which includes any nested cases (let
, etc).
; avoid
(loop [] (when (some-func) (println 1) (println 2) (recur)))
; prefer
(while (some-func) (println 1) (println 2))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | false | 0.1.69 | 0.1.69 |
when
calls should have at least 1 expression after the condition.
; avoid
(when true)
(when (some-func))
; prefer
(when true (do-stuff))
(when (some-func) (do-stuff))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 1.2.0 |
seq
returns nil
when given an empty collection. empty?
is implemented as (not (seq coll))
so it's idiomatic to use seq
directly.
; avoid
(not (empty? coll))
; prefer (chosen style :seq (default))
(seq coll)
; prefer (chosen style :not-empty)
(not-empty coll)
Name | Default | Options |
---|---|---|
:chosen-style | :seq | :seq , :not-empty |
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 1.13 | 1.13 |
NOTE: Requires Clojure version 1.12.0.
Uniform qualified method values are a new syntax for calling into java code. They must resolve to a single static or instance method and to help with that, a new metadata syntax can be used: ^[]
aka ^{:param-tags []}
. Types are specified with classes, each corrosponding to an argument in the target method: (^[long String] SomeClass/.someMethod 1 "Hello world!")
. It compiles to a direct call without any reflection, guaranteeing optimal performance.
Given that, it is preferable to exclusively use method values.
; avoid
(.toUpperCase "noah")
(. "noah" toUpperCase)
; prefer
(^[] String/toUpperCase "noah")
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | false | 1.3.0 | 1.3.0 |
In the ns
form prefer :require :as
over :require :refer
over :require :refer :all
. Prefer :require
over :use
; the latter form should be considered deprecated for new code.
; avoid
(ns examples.ns
(:use clojure.zip))
; prefer
(ns examples.ns
(:require [clojure.zip :as zip]))
(ns examples.ns
(:require [clojure.zip :refer [lefts rights]]))
(ns examples.ns
(:require [clojure.zip :refer :all]))
Name | Default | Options |
---|---|---|
:chosen-style | :as | :as , :refer , :all |
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
A number of core functions take any number of arguments and return the arg if given only one. These calls are effectively no-ops, redundant, so they should be avoided.
Current list of clojure.core functions this linter checks:
->
, ->>
cond->
, cond->>
some->
, some->>
comp
, partial
, merge
min
, max
, distinct?
; avoid
(-> x)
(->> x)
(cond-> x)
(cond->> x)
(some-> x)
(some->> x)
(comp x)
(partial x)
(merge x)
(min x)
(max x)
(distinct? x)
; prefer
x
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 1.16.0 | 1.16.0 |
clojure.core/str
calls .toString()
on non-nil input. However, .toString()
on a string literal returns itself, making it a no-op. Likewise, clojure.core/format
unconditionally returns a string, making any calls to str
on the results a no-op.
; avoid
(str "foo")
(str (format "foo-%s" some-var))
; prefer
"foo"
(format "foo-%s" some-var)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
false | true | false | 1.13 | 1.13 |
NOTE: Requires Clojure version 1.12.0.
Uniform qualified method values are a new syntax for calling into java code. They must resolve to a single static or instance method and to help with that, a new metadata syntax can be used: ^[]
aka ^{:param-tags []}
. Types are specified with classes, each corrosponding to an argument in the target method: (^[long String] SomeClass/someMethod 1 "Hello world!")
If :param-tags
is left off of a method value, then the compiler treats it as taking no arguments (a 0-arity static method or a 1-arity instance method with the instance being the first argument). And an _
can be used as a wild-card in the cases where there is only a single applicable method (no overloads).
These last two features are where there can be trouble. If, for whatever reason, the Java library adds an overload on type, then both the lack of :param-tags
and a wild-card can lead to ambiguity. This is a rare occurence but risky/annoying enough that it's better to be explicit overall.
The styles are named after what they're looking for:
:missing
checks that there exists a :param-tags
on a method value.:wildcard
checks that there are no usages of _
in an existing :param-tags
.:both
checks both conditions.; avoid (chosen style :both or :missing)
(java.io.File/mkdir (clojure.java.io/file "a"))
; avoid (chosen style :both or :wildcard)
(^[_ _] java.io.File/createTempFile "abc" "b")
; prefer (chosen style :both or :missing)
(^[] java.io.File/mkdir (clojure.java.io/file "a"))
; prefer (chosen style :both or :wildcard (default))
(^[String String] java.io.File/createTempFile "abc" "b")
Name | Default | Options |
---|---|---|
:chosen-style | :wildcard | :both , :missing , :wildcard |
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 0.1 | 0.1 |
repeatedly
has an arity for limiting the number of repeats with take
.
; avoid
(take 5 (repeatedly (range 10))
; prefer
(repeatedly 5 (range 10))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | false | false | 0.1 | 1.17.0 |
Threading macros require more effort to understand so only use them with multiple args to help with readability.
Macros can be misinterpreted, leading to correct code being flagged:
(cond-> foo
pred? (-> (assoc :hello 123)
(dissoc :goodbye)))
; avoid
(-> x y)
(->> x y)
; prefer
(y x)
; avoid
(-> x (y z))
; prefer
(y x z)
; avoid
(->> x (y z))
; prefer
(y z x)
Name | Default | Options |
---|---|---|
:chosen-style | :inline | :inline , :avoid-collections |
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | true | 1.0 | 1.0 |
A macro that wraps a splicing unquote in a try-catch or try-finally can lead
to subtle hard to debug errors. Better to wrap the splicing unquote in a do
to force it into 'expression position'.
; avoid
`(try ~@body (finally :true))
; prefer
`(try (do ~@body) (finally :true))
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
true | true | false | 1.11 | 1.11 |
Due to munging rules, underscores in namespaces can confuse tools and libraries which expect that underscores in class names should be dashes in Clojure.
; avoid
(ns foo_bar.baz_qux)
; prefer
(ns foo-bar.baz-qux)
Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
---|---|---|---|---|
false | true | false | 1.8.0 | 1.8.0 |
Because we can't (or won't) check for interop, *warn-on-reflection*
should
be at the top of every file out of caution.
; avoid
(ns foo.bar)
(defn baz [a b] (+ a b))
; prefer
(ns foo.bar)
(set! *warn-on-reflection* true)
(defn baz [a b] (+ a b))
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close