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?@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.
Check out: https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md
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.
Also, why are anaphoric macros bad? I'm using it in the case of a DSL.
(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.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.
@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.
They have their place.
Fair. I worry that they will not play well with clj-kondo out-of-the-box.
Can you please take a look at the docs? There are good examples for other anaphoric macros and it works quite well.
e.g. the streams
macro introduces a magical where
:
https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md#unresolved-symbol
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.
yes, you are mistaking :)
you only write it once for streams
and then it works everywhere you call the streams macro
unless I am mistaking, which is very well possible
{: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.when you write
(require '[kondo-test.macro :refer [with-frame-bindings]])
(with-frame-bindings state)
that should then work without errorYou are correct. It is a spacemacs bug, had to kill all the buffers to get it to update with that.
Thanks. Saved me from pulling my hair out wondering why it wasn't working. 🙂
:thumbsup: glad it's working now
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.
@jtlocsei An alternative is to try the intellij LSP plugin. Some people have reported it worked better for them than the filewatcher approach
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?
No, it means that an LSP server usually also provides things like finding references, refactoring etc
Having said that, you might even be able to get clojure-lsp working with the LSP plugin, which does provide a lot more
it also uses clj-kondo under the hood so you will get the same diagnostics
See #lsp
I have changed the wording a little bit, hopefully it's less confusing now
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.
clojure-lsp should work with intellij indeed: https://clojure-lsp.github.io/clojure-lsp/clients/#intellij-cursive
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))
is there a way to tell clj-kondo how to lint this?
@euccastro either disable the unresolved-symbol linter in this macro or write a hook
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?
i.e.,
(with-new [a ["app/object" {:label "a"}]
b ["app/type" {:id 25}]]
(str a b))
then clj-kondo will not help in case ["app/type"]
@euccastro that syntax should work I think
?
I think he means it won't flag it as an error? I'm fine with that
It does work indeed, thank you very much!