clj-kondo

https://github.com/clj-kondo/clj-kondo
Dominic Pearson 2021-04-22T10:45:18.300300Z

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?

borkdude 2021-04-22T10:48:40.301200Z

@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.

borkdude 2021-04-22T10:48:59.301400Z

Check out: https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md

Dominic Pearson 2021-04-22T10:49:51.302400Z

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.

Dominic Pearson 2021-04-22T10:50:51.303300Z

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

Dominic Pearson 2021-04-22T10:51:49.304900Z

(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.

Dominic Pearson 2021-04-22T10:52:27.305800Z

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.

borkdude 2021-04-22T10:53:01.306500Z

@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.

borkdude 2021-04-22T10:53:08.306700Z

They have their place.

Dominic Pearson 2021-04-22T10:53:25.307400Z

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

borkdude 2021-04-22T10:53:40.307800Z

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

borkdude 2021-04-22T10:54:54.308300Z

e.g. the streams macro introduces a magical where: https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md#unresolved-symbol

Dominic Pearson 2021-04-22T10:58:45.309200Z

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.

borkdude 2021-04-22T10:59:26.310Z

yes, you are mistaking :)

borkdude 2021-04-22T10:59:47.310500Z

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

borkdude 2021-04-22T11:00:51.310700Z

unless I am mistaking, which is very well possible

Dominic Pearson 2021-04-22T11:02:00.311700Z

{: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.

borkdude 2021-04-22T11:02:55.312400Z

when you write

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

Dominic Pearson 2021-04-22T11:03:43.312900Z

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

Dominic Pearson 2021-04-22T11:04:24.313600Z

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

borkdude 2021-04-22T11:04:47.313800Z

:thumbsup: glad it's working now

Dominic Pearson 2021-04-22T11:06:09.314600Z

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.

borkdude 2021-04-22T11:06:56.315100Z

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

tobias 2021-04-22T11:09:35.315200Z

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?

borkdude 2021-04-22T11:11:12.315400Z

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

borkdude 2021-04-22T11:11:33.315600Z

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

borkdude 2021-04-22T11:11:46.315800Z

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

borkdude 2021-04-22T11:11:49.316Z

See #lsp

borkdude 2021-04-22T11:14:21.316200Z

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

tobias 2021-04-22T11:18:47.316400Z

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.

1❤️
ericdallo 2021-04-22T12:24:53.316700Z

clojure-lsp should work with intellij indeed: https://clojure-lsp.github.io/clojure-lsp/clients/#intellij-cursive

euccastro 2021-04-22T14:51:39.319Z

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

euccastro 2021-04-22T14:52:18.319800Z

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

borkdude 2021-04-22T14:52:34.320400Z

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

euccastro 2021-04-22T14:53:43.321700Z

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?

euccastro 2021-04-22T14:54:21.322100Z

i.e.,

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

2021-04-22T14:56:13.322200Z

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

1👍
borkdude 2021-04-22T14:59:05.322700Z

@euccastro that syntax should work I think

1
borkdude 2021-04-22T14:59:31.322800Z

?

euccastro 2021-04-22T15:01:16.323Z

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

euccastro 2021-04-22T17:39:41.323300Z

It does work indeed, thank you very much!