Fork me on GitHub
#clj-kondo
<
2020-10-05
>
ghadi14:10:29

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 ...)?

borkdude14:10:27

welcome ghadi! not sure, needs some thought

borkdude14:10:24

@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

borkdude14:10:58

e.g. now it has:

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

ghadi14:10:04

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

borkdude14:10:53

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

ghadi14:10:43

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

borkdude14:10:57

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

borkdude14:10:16

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

borkdude14:10:34

as a first form of experimentation

borkdude15:10:10

@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

ghadi15:10:15

neat hook API

ghadi15:10:26

cool! that gives me a starting point

ghadi15:10:52

does clj-kondo support spec regexes?

borkdude15:10:37

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

borkdude15:10:11

@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

borkdude15:10:50

also (require '[spec :as s])

borkdude15:10:19

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

ghadi16:10:53

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

ghadi16:10:09

(assuming it's sci)

borkdude16:10:42

@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

3
borkdude16:10:35

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

borkdude16:10:37

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