clj-kondo

https://github.com/clj-kondo/clj-kondo
2020-09-22T11:43:38.034Z

Is it possible to disable the arity-check for keywords? We have an embedded language that uses keywords in function position, with an arbitrary number of arguments.

borkdude 2020-09-22T11:57:40.034400Z

@hugod yes, let me get back to you after a meeting

borkdude 2020-09-22T11:57:59.034700Z

meanwhile you might want to look at https://github.com/borkdude/clj-kondo/blob/master/doc/config.md

borkdude 2020-09-22T11:59:44.035700Z

alternatively you could write a hook for your macro, but that's more work than just suppressing warnings

2020-09-22T12:04:03.035800Z

No urgency. Thanks for the config pointers. I see how to disable the check for a specific function/macro, but not for keywords in general. The set of keywords we use like this is open, although there is a limited set that probably covers most usage. Maybe that is good enough.

borkdude 2020-09-22T12:05:08.036Z

Writing a hook is probably the best solution, but requires some work

borkdude 2020-09-22T12:06:03.036200Z

I assume these keywords are only used within a macro right?

2020-09-22T12:36:21.036400Z

Within various macros, right. I’ll have a look at hooks. Thanks

2020-09-22T13:46:37.036600Z

Um, looks like it is not possible to define a hook on an unqualified symbol pulled in via a :refer :all

borkdude 2020-09-22T13:50:08.036800Z

it depends if clj-kondo knows that namespace (e.g. it's been linted before)

borkdude 2020-09-22T13:50:14.037Z

btw, I think I have a hook here:

borkdude 2020-09-22T13:50:32.037200Z

borkdude 2020-09-22T13:51:53.037600Z

I assume the macro you use is required?

borkdude 2020-09-22T13:53:17.038Z

This is the code:

(ns hooks.kw-macro
  (:require [clj-kondo.hooks-api :as api]))

(defn rewrite [node]
  (let [sexpr (api/sexpr node)]
    (if (and (seq? sexpr)
             (keyword? (first sexpr)))
      (let [children (rest (:children node))
            children (mapv rewrite children)]
        (with-meta (api/vector-node children)
          (meta node)))
      (with-meta
        (update node :children #(mapv rewrite %))
        (meta node)))))

(defn kw-macro [{:keys [:node]}]
  (let [new-node (api/vector-node (mapv rewrite (rest (:children node))))]
    ;; for debugging:
    ;;(prn :new-node new-node)
    {:node new-node}))

borkdude 2020-09-22T13:54:32.038200Z

I basically rewrite (my/kw-macro (:foo 1 2 3) (:bar 1 2 3)) to [[:foo 1 2 3] [:bar 1 2 3]]

2020-09-22T13:59:16.038400Z

That should work with some modification to use walk to pick up nested forms. 🙂

borkdude 2020-09-22T13:59:46.038600Z

walk unfortunately doesn't preserve metadata

2020-09-22T14:00:18.038800Z

I think I have a walk that does - thanks for the reminder

2020-09-22T14:01:06.039Z

the macro is required with a :refer :all and is actually defined in a different namespace via an import-vars like mechanism.

2020-09-22T14:01:18.039200Z

I can’t get the hook to trigger at all

borkdude 2020-09-22T14:01:51.039400Z

you're out of luck there, clj-kondo has no way of knowing what var you are calling

borkdude 2020-09-22T14:02:39.039600Z

it does have support for import-vars (the widely used one). so if you use the same syntax as that one and use lint-as AND lint all of your namespaces, it might work

2020-09-22T14:07:17.039800Z

Is there any existing macro to inspect how clj-kondo is currently parsing an expression? eg, something I can insert around a form that prints the rewrite map

borkdude 2020-09-22T14:09:12.040Z

yes, look at the example I provided you

borkdude 2020-09-22T14:09:20.040200Z

;;(prn :new-node new-node)

2020-09-22T15:11:23.040400Z

duh, upgrading clj-kondo helps a lot

borkdude 2020-09-22T15:11:47.040600Z

:)

2020-09-22T18:26:19.040800Z

How do you develop hooks? clj-kondo seems to just silently ignore the hook if it fails to load.

borkdude 2020-09-22T19:41:16.041700Z

Clj-kondo will print a warning if the hook isn’t found but is in your config

borkdude 2020-09-22T19:58:40.041900Z

e.g. with this config:

{:hooks {:analyze-call {my/kw-macro hooks.kw-macro/kw-macro2}}}
borkdude@MBP2019 /tmp/proj $ clj-kondo --lint example.clj
WARNING: error while trying to read hook for my/kw-macro: Could not resolve symbol: hooks.kw-macro/kw-macro2 [at line 2, column 1]
example.clj:4:14: error: keyword :foo is called with 3 args but expects 1 or 2
example.clj:5:14: error: clojure.core/inc is called with 3 args but expects 1
example.clj:6:15: error: keyword :foo is called with 3 args but expects 1 or 2
example.clj:6:28: error: clojure.core/inc is called with 3 args but expects 1
linting took 59ms, errors: 4, warnings: 0
^ @hugod

borkdude 2020-09-22T19:59:16.042100Z

and correcting it:

$ clj-kondo --lint example.clj
example.clj:5:14: error: clojure.core/inc is called with 3 args but expects 1
example.clj:6:28: error: clojure.core/inc is called with 3 args but expects 1
linting took 33ms, errors: 2, warnings: 0

2020-09-22T20:02:29.042300Z

if there is a compile error in the hook itself, it seems to be silently ignored.

borkdude 2020-09-22T20:03:05.042500Z

let me make a deliberate mistake

borkdude 2020-09-22T20:03:29.042700Z

I'm putting a random x in my hook code:

$ clj-kondo --lint example.clj
WARNING: error while trying to read hook for my/kw-macro: Could not resolve symbol: x [at /private/tmp/proj/.clj-kondo/hooks/kw_macro.clj, line 5, column 3]

2020-09-22T20:05:09.042900Z

um, I wonder what’s different here

borkdude 2020-09-22T20:06:15.043100Z

yeah, you might have hit a different case. repro welcome, maybe something could be improved

2020-09-22T20:08:48.043300Z

hooks are very flexible :)

borkdude 2020-09-22T20:14:11.043500Z

powered by the same interpreter as babashka

borkdude 2020-09-22T20:40:25.043700Z

@hugod could also be a laziness issue so the error is never reached?

2020-09-22T20:41:51.043900Z

Good thought. I’ll try and investigate more later.