clj-kondo 2026-07-20

Trying to come up with a good name for a new linter that is going to fold two existing ones: :condition-always-true and :unreachable-code

(when (:foo {}) ...) <-- code in when never executes since the test is always nil, no matter what
Two candidate names: • :constant-test (but ... the word constant is often used for compile time constant, which this isn't) • :dead-test (I kind of dislike this one) Perhaps: :invariant-test but this might be less clear than :constant-test? After going through a lot of alternatives I still think :constant-test is the most obvious one, but I'm open to better suggestions in 🧵

✅ 1

maybe :constant-condition ?

ok that's what it's going to be! just consult the other linters for names, yeah why not

I enabled :shadowed-var as a warning (after getting bitten by a shadowing bug at the end of last week) and now I have nearly 900 warnings. Nearly all of these are coming from def in comment forms where I've created global versions of locals deliberately, to allow evaluation of code fragments in the REPL. This kinda makes the warning useless. I know I can :exclude certain binding names, but that means I might miss a real shadowing. Suggestions?

{:config-in-comment {:linters {:unresolved-namespace {:level :off}}}}
or whichever you need

But the warning is not in the comment. It's in code after the comment that sees the global def from the comment being shadowed by a local binding.

(comment
  (def db-spec {..})
  ..
  )

(defn foo []
  (let [db-spec ..] ; <-- this gets flagged!
    ..))

you could do {:skip-comments true} but that'll not flag anything in comments

FWIW, I have tried using this linter, but always ended up turning it off

but if you have thoughts about how to improve it, welcome

an off-the-cuff suggestion: :ignore-def-in-comments or something, that would know that defs in comments shouldn't count

but you do have this def lingering in your REPL state though, so sometimes you DO want to be warned about it.

and you will get unresolved symbols in your comment form when using the ignored var. so perhaps :skip-comments it the best workaround here

Yeah... Our config has this comment in it from six year ago 🙂

;; I'm not sure whether we want to skip (comment,,,) forms or not...
 ;; ...having it flag def for having no docstring is kind of annoying
 ;; but having it flag all sorts of other things in (comment,,,) is useful

you can do :config-in-comment to toggle some things

if you put the comment form under your foo fn it probably works too since db-spec didn't exist yet

➕ 1

Right, then I'd pretty much have to have all my comment forms at the end of files instead of intermixed as now 😞

I also have this awesome macro which is clj-kondo friendly where you basically make your let form the comment form :) https://github.com/borkdude/deflet

So I could use that to wrap blocks of defs inside comment forms so I could still eval individual (def ..) forms and then test fragments inside fns, but they wouldn't register as global defs...? Interesting. That would still mean a lot of code changes—and adding another dependency (that was only used inside comment)... 😞

Being able to tell Kondo that def (or defn) in a comment should not could for :shadowed-var would probably be my preference.

or just vendor it and try it out if you like it.

i.e., as a new option on the :shadowed-var linter itself.

yeah that makes sense

issue/PR welcome

maybe comment defs should also not conut as re-defined vars

so the scope is probably bigger

If I skip comments temporarily, I get just 14 shadowed var warnings—which is useful.

Including comments, I get nearly 900 which is not useful 🙂

😄 1

that's a good thing to mention in the issue :)

Turns out :skip-comments true also means things like "go to definition` (in Calva) stops working for fns mentioned in comment forms and those references don't show up in LSP at the definition. So that's a non-starter from that p.o.v.

1

I've sometimes wondered regarding :skip-comments if there'd be any sense in having two separate mechanism; analyse code in comments and/or lint code in comments. I don't always need or want comments to be actually linted, but it sure would be nice if they were analysed for LSP etc to work properly within comments.

You can just ignore all lints in comments with ;confjg-in-comment {:ignore true} but those defs will register vars

I guess we just need one extra setting to let defs not have any effect outside of comment

Or maybe that should just be the default behavior

@seancorfield on master: Don't count comment form defs for shadowed-var, unused-private-var and inline-def

😍 1

Awesome! Thank you!

I’m working on a library that has a macro which generates other macros:

(definheritance foo [x]
  (def value x))
This generates something like:
(defmacro inherit-foo
  {:clj-kondo/macroexpand-hook true}
  [x]
  '(def value ~x))
It looks like clj-kondo processes the initial definheritance hook and sees the generated macro, but doesn’t extract/register that generated macro as another source macro. This means later calls to inherit-foo can’t be expanded for analysis. Would it make sense for source-macro extraction to recursively process marked defmacro forms returned by another macro-expansion hook? Or is there another recommended way to support macro-generating macros like this?

yikes, I don't think I've ever written a macro that generates more macros, but I guess that's possible :)

😅 1

you need to mark the other macro with macroexpand-hook true as well at least

perhaps you can create repro github repro if you don't get it to work and I can take a look

+ github issue

Ya, it's macro-inception haha Will do!