clj-kondo

https://github.com/clj-kondo/clj-kondo
ghadi 2020-10-05T14:40:29.083300Z

kondo newb here: how hard would it be to make a linter that searched for over eagerness / redundant mapv's, e.g. (into coll (mapv ...)?

borkdude 2020-10-05T14:49:27.083800Z

welcome ghadi! not sure, needs some thought

borkdude 2020-10-05T14:50:24.084600Z

@ghadi clj-kondo already has some type checking related stuff, so we can check for vector instead of seq as the second arg of into and do something with that

borkdude 2020-10-05T14:51:58.084900Z

e.g. now it has:

$ clj-kondo --lint - <<< '(into [] :foo)'
<stdin>:1:10: error: Expected: seqable collection, received: keyword.

ghadi 2020-10-05T14:53:04.086100Z

basically I'm looking for ->> threads where there is a mapv not as the terminal operation

borkdude 2020-10-05T14:53:53.086400Z

is this a one time analysis or something for general usage?

ghadi 2020-10-05T14:57:02.086600Z

general

ghadi 2020-10-05T14:57:43.087400Z

obviously it can't be perfect, but I think an analysis of the symbols would be Good Enoughâ„¢, no need for static analysis

borkdude 2020-10-05T14:58:57.088300Z

ok, feel free to post an issue for this: https://github.com/borkdude/clj-kondo/issues there's also one potentially related issue: https://github.com/borkdude/clj-kondo/issues/323 someone wrote a hook as a solution for that: https://github.com/borkdude/clj-kondo/issues/323#issuecomment-691247062

borkdude 2020-10-05T14:59:16.088600Z

so I expect that this can also be written as a hook maybe

borkdude 2020-10-05T14:59:34.088900Z

as a first form of experimentation

borkdude 2020-10-05T15:01:48.089100Z

(hook docs: https://github.com/borkdude/clj-kondo/blob/master/doc/hooks.md)

borkdude 2020-10-05T15:32:10.089700Z

@ghadi

borkdude@MBP2019 /tmp $ cat .clj-kondo/config.edn
{:hooks {:analyze-call {clojure.core/into strict/into-check}}
 :linters {:into-strict {:level :warning}}}
borkdude@MBP2019 /tmp $ cat .clj-kondo/strict.clj
(ns strict
  (:require [clj-kondo.hooks-api :as api]))

(defn into-check [{:keys [:node]}]
  (let [forms (rest (:children node))]
    (when (= 2 (count forms)) ;; only check 2-arity for now
      (let [from (last forms)
            sexpr (api/sexpr from)]
        (when (seq? sexpr)
          (when (= 'mapv (first sexpr))
            (let [loc (meta from)]
              (api/reg-finding! (merge {:type :into-strict
                                        :message "Avoid strictness in from"}
                                       loc)))))))))
borkdude@MBP2019 /tmp $ clj-kondo --lint - <<< '(into [] (mapv inc [1 2 3]))'
<stdin>:1:10: warning: Avoid strictness in from
linting took 12ms, errors: 0, warnings: 1

ghadi 2020-10-05T15:34:15.090400Z

neat hook API

ghadi 2020-10-05T15:34:26.090500Z

cool! that gives me a starting point

ghadi 2020-10-05T15:35:52.090900Z

does clj-kondo support spec regexes?

borkdude 2020-10-05T15:37:37.091800Z

interesting point. that's a consideration for the future. I'm considering adding it to babashka and also to clj-kondo (both use sci for interpreting Clojure in the native binary). but there is the issue of alpha-ness: https://github.com/borkdude/babashka/issues/558

borkdude 2020-10-05T15:46:03.092300Z

Hold on

borkdude 2020-10-05T15:48:11.093600Z

@ghadi I made a hacked version of spartan.spec which runs with the current clj-kondo: https://gist.github.com/borkdude/85ad5edd88a1877ad2802a0d49ef6c3c so if you save that as spec.clj into .clj-kondo, then this works in the hook:

(s/def ::foo (s/cat :x int? :y string?))
(prn (s/conform ::foo [1 "foo"]))
$ clj-kondo --lint - <<< '(into [] (mapv inc [1 2 3]))'
{:x 1, :y "foo"}
<stdin>:1:10: warning: Avoid strictness in from
linting took 66ms, errors: 0, warnings: 1
Note that this is a hack but it might work for you

borkdude 2020-10-05T15:48:50.094Z

also (require '[spec :as s])

borkdude 2020-10-05T15:49:19.094500Z

but don't expect spec to be present in clj-kondo releases soon since it's not clear to wait for spec2, etc.

ghadi 2020-10-05T16:09:53.095100Z

what are the things in clojure.spec that sci can't handle?

ghadi 2020-10-05T16:10:09.095500Z

(assuming it's sci)

borkdude 2020-10-05T16:11:42.097300Z

@ghadi it is. at the time I wrote the spartan.spec port of spec, sci didn't support protocols yet, so I changed it all to just maps. but I'm pretty sure spec 1 and 2 can be made GraalVM compatible and hooked into the sci bindings of clj-kondo and babashka, so the spec functions themselves run at native speed instead of from interpreted code

1
borkdude 2020-10-05T16:14:35.098700Z

if you now try to run clojure spec from source with bb, you'll get this:

$ bb -cp $(clojure -Spath) -e "(require '[clojure.spec.alpha])"
...
128:       (symbol (clojure.lang.Compiler/demunge f-ns) (clojure.lang.Compiler/demunge f-n)))))
                    ^--- Could not resolve symbol: clojure.lang.Compiler/demunge
This is because bb doesn't have the clojure.lang.Compiler class in its sci bindings

borkdude 2020-10-05T16:15:37.099500Z

and I'm pretty sure there's a few of these issues left when you work around that. The way forward for me would be to not run it from source but bind it natively. Spartan.spec is a stub meanwhile