Fork me on GitHub
#clj-kondo
<
2023-12-06
>
Alexander Kouznetsov00:12:02

I have a macro letm that looks like let but returns all the defined bindings as a map. If I lint it as let, it then reports unused-bindings in it as in the following example:

(is (= {:x 5} (letm [x 5])))
Is it possible to configure unused-bindings to be disabled inside the body of letm ? I was able to achieve this with {:config-in-call {letm {:linters {:unused-binding {:level :off}}}} config but I found that it applies to all nested structures. So for example the following unused z won’t be reported:
(println (letm [x (let [z 1] 5)]))
Is it possible to have a narrower focus of a config-in-call rule so that it doesn’t report x as unused but reports z as such? One possible solution might be to create a hook the prepends every variable definition inside the binding with #_{:clj-kondo/ignore [:unused-binding]} as in (let [#_{:clj-kondo/ignore [:unused-binding]} x (let [z 1] 5)]) but I do not see how that can be created with rewrite-clj. Am I missing anything?

Sam Ritchie03:12:25

Try a hook that expands to a let and then returns a vector of all of the variables

Sam Ritchie03:12:27

So expand to (let [z 1 a 2 b 3] [z a b <actual return value>])

Sam Ritchie03:12:57

Or (let [z 1 a 2 b 3] [z a b] <actual return value>)

valerauko07:12:45

Why not rewrite it to the actual expansion?

👍 1
borkdude14:12:05

Or use lint-as let

Noah Bogart14:12:53

i think lint-as let won't work because it'll flag as unused bindings

💯 1
Noah Bogart14:12:36

given:

(defmacro letm [bindings]
  (let [ks (take-nth 2 bindings)
        kws (mapv keyword ks)]
    `(let ~bindings
       (hash-map ~@(mapcat vector ks kws)))))

(letm [a 1 b 2 c (+ a b)])
using :macroexpand config, this works;
(defmacro letm
  [bindings]
  (let [ks (take-nth 2 bindings)
        kws (mapv keyword ks)]
    `(let ~bindings
       (hash-map ~@(mapcat vector ks kws)))))

borkdude14:12:03

I’ve used a more decomplected macro in the past which just captured locals by name as a map: (->m a b c), it didn’t need any kondo config, also no unused binding

Noah Bogart14:12:37

that's not exactly the same as letm. might still be useful

borkdude15:12:10

It’s not the same, it’s more decomplected as I said. It composes with existing let and fn bindings and lets you capture only the binding you want and not any auxiliary bindings

Alexander Kouznetsov19:12:43

> Try a hook that expands to a let and then returns a vector of all of the variables > Why not rewrite it to the actual expansion? Given that let allows a number of destructuring options, figuring out every symbol I need to rewrite to doesn’t look easy. Consider these examples;

(letm [[x {:keys [y] b :b}] val])

Alexander Kouznetsov19:12:40

> Or use lint-as let lint-as reports unused bindings.

Alexander Kouznetsov19:12:12

Am I correct that the suggestion is to use the macroexpand hook with pretty much the original macro content?

Alexander Kouznetsov19:12:05

Thanks! I’ll give it a try!

Noah Bogart19:12:24

If that doesn’t work, i can try again but I’ll need to see the full macro

Alexander Kouznetsov19:12:08

Do you know what kind of macros do not work with :macroexpand hooks?

borkdude20:12:50

I think you can pretty much make anything work but often with simplifications

Alexander Kouznetsov23:12:26

This letm macro implementation is calling destructure function from clojure.core before generating the nodes and it looks like that is an unsupported external dependency.

Noah Bogart23:12:19

I was afraid you might say that

borkdude03:12:12

I should just bump SCI which exposes destructure in the latest release

Ryan18:12:32

I just wired up clj-kondo to my cljs project and I’m noticing that it can’t resolve args in my re-frame event handlers, e.g.

(reg-event-fx ::close-work
  (fn-traced [{:keys [db]} [_ {:keys [navigate]}]]
      {:dispatch-debounce {:id :info-update :action :flush}
       :navigate          navigate}))
it (rightly, I suppose) can’t resolve db or navigate.. Is there a way to tell it to ignore resolving them? FWIW, I have same problem in cursive’s built in checking and its a bit annoying

borkdude18:12:11

lint-as fn-traced as fn maybe?

borkdude18:12:30

I don't know where fn-traced is coming from, but that would probably be the solution

Ryan18:12:45

Aha, its re-frame-10x (the add on debugger)

Ryan18:12:17

So is that something I can put in the .clj-kondo file? first time using it so very green 🙂

Ryan18:12:50

e.g.

{:lint-as {day8.re-frame.tracing/fn-traced clojure.core/defn}}

Ryan18:12:33

I mean, in a config file in the .clj-kondo directory 🙂

borkdude18:12:23

I’d choose fn over defn

Ryan19:12:57

Hmm it doesn’t seem to help, if its useful, here is the fn-traced

(defmacro fn-traced
  "Defines a traced fn"
  {:arglists '[(fn name? [params*] exprs*) (fn name? ([params*] exprs*) +)]}
  [& definition]
  `(if (is-trace-enabled?)
     (fn-traced* ~@definition)
     (fn ~@definition)))

Ryan19:12:18

turns out I need to name the file config.edn 🙂 sorted it!

👍 1