Fork me on GitHub
#clj-kondo
<
2021-04-22
>
dsp10:04:18

Hi, I have an unusual linter use-case, I'm not sure how to resolve it. I have a pair of macros defined, simplified example:

(defmacro defn-frame-binding
  ([name args body]
   `(defn ~name ~args (do (with-frame-bindings ~body))))
  ([name docstr args body]
   `(defn ~name ~docstr ~args (do (with-frame-bindings ~body)))))

(defmacro with-frame-bindings
  ([body]
   `(with-frame-bindings ~'frame ~body))
  ([frame body]
   `(let [frame# ~frame
          ~'state (:state ~'connection)]
      (~@body))))
let's say this is in the kondo-test.macro namespace. then in kondo-test.core,
(defn-frame-binding example
  [frame connection]
  (do
    state))
kondo-test.core> (example nil {:state "hello, world!"}) "hello, world!" However, any variables introduced in the with-frame-bindings macro, for e.g. state in this case, are linted as unresolved symbols. I figure I have to do something like:
{:lint-as {kondo-test.macro/defn-frame-binding  clojure.core/defn
           kondo-test.macro/with-frame-bindings clojure.core/let}}
Though, while it works for the custom defn-like macro, bindings set in with-frame-bindings don't appear to be picked up, probably because it doesn't look like a let, but is an anaphoric macro. Any ideas?

borkdude10:04:40

@dsp First of all, anaphoric macros are bad. :) But you can tell clj-kondo in the unresolved-symbol config to ignore certain symbols within specific functions.

dsp10:04:51

I wondered if there was some more generic way to do that, since otherwise it will get quite unwieldy. I use this macro in lots of functions, so will probably be a lot of repeated lines.

dsp10:04:51

Also, why are anaphoric macros bad? I'm using it in the case of a DSL.

dsp10:04:49

(defn-frame-binding Tauth
  "auth – messages to establish a connection

  size[4] Tauth tag[2] afid[4] uname[s] aname[s]
  size[4] Rauth tag[2] aqid[13]"
  [frame connection]
  (error! "no authentication required"))
within these functions, frame-{fieldname} get predefined and pulled out of the network frame.

dsp10:04:27

Makes it much more convenient, especially if I modify the underlying data type, since I can just modify the macro that pulls them out, separating the concern for anyone using them.

borkdude10:04:01

@dsp I mean, they are bad in the sense that they are a bit magical, but there are trade-offs, I was being a bit blunt.

borkdude10:04:08

They have their place.

dsp10:04:25

Fair. I worry that they will not play well with clj-kondo out-of-the-box.

borkdude10:04:40

Can you please take a look at the docs? There are good examples for other anaphoric macros and it works quite well.

dsp10:04:45

Yes, I had looked at that already. However, it seems like I'd have to define it for any function that calls the macro, unless I'm mistaken.

borkdude10:04:26

yes, you are mistaking :)

borkdude10:04:47

you only write it once for streams and then it works everywhere you call the streams macro

borkdude11:04:51

unless I am mistaking, which is very well possible

dsp11:04:00

{:linters
  {:unresolved-symbol
   {:exclude [(kondo-test.macro/with-frame-bindings [state])]}}}
With this, (if I have interpreted the docs correctly), I would expect state to no longer be reported as unresolved.

borkdude11:04:55

when you write

(require '[kondo-test.macro :refer [with-frame-bindings]]) 
(with-frame-bindings state)
that should then work without error

dsp11:04:43

You are correct. It is a spacemacs bug, had to kill all the buffers to get it to update with that.

dsp11:04:24

Thanks. Saved me from pulling my hair out wondering why it wasn't working. 🙂

borkdude11:04:47

:thumbsup: glad it's working now

dsp11:04:09

spacemacs integration seems quite janky, but that is not the fault of the tool. Lots of strange behaviours, but I guess I will investigate that separately. Thanks a lot for the help.

borkdude11:04:56

@jtlocsei An alternative is to try the intellij LSP plugin. Some people have reported it worked better for them than the filewatcher approach

tobias11:04:35

Yup, LSP works great, I tried it after I couldn't get the filewatcher way working. I noticed the clj-kondo docs say "The LSP server does not provide features other than diagnostics." Does that mean that the LSP approach somehow has less functionality than the filewatcher approach?

borkdude11:04:12

No, it means that an LSP server usually also provides things like finding references, refactoring etc

borkdude11:04:33

Having said that, you might even be able to get clojure-lsp working with the LSP plugin, which does provide a lot more

borkdude11:04:46

it also uses clj-kondo under the hood so you will get the same diagnostics

borkdude11:04:21

I have changed the wording a little bit, hopefully it's less confusing now

tobias11:04:47

OK, got it! So LSP + clj-kondo has all the same functionality as filewatcher + clj-kondo. Thank you! BTW, I'm a huge fan of the tools you've created. I use Babashka for scripts now and it's awesome.

❤️ 2
euccastro14:04:39

I'm making a macro that should be linted like a let with ternary bindings (it's an in-house convenience gizmo and there's a method to my madness...) something like:

(with-new [a "app/object" {:label "a"}
           b "app/type" {:id 25}]
  (str a b))

euccastro14:04:18

is there a way to tell clj-kondo how to lint this?

borkdude14:04:34

@euccastro either disable the unresolved-symbol linter in this macro or write a hook

euccastro14:04:43

thanks! I'll take a look at hooks... worst case, I'll require the second and third expression to be within a vector and then I guess I can just lint it like a let , right?

euccastro14:04:21

i.e.,

(with-new [a ["app/object" {:label "a"}]
           b ["app/type" {:id 25}]]
  (str a b))

delaguardo14:04:13

then clj-kondo will not help in case ["app/type"]

👍 2
euccastro15:04:16

I think he means it won't flag it as an error? I'm fine with that

borkdude14:04:05

@euccastro that syntax should work I think

thanks2 2
euccastro17:04:41

It does work indeed, thank you very much!