clj-kondo 2021-03-19

Hello! I am getting an :unused-private-var error for the code below:

(deftype ^:private SessionStore
         [session-service] ...)

(defn db-store [this]
  (SessionStore. this))
Is it something wrong with code on linter is false positive in this case?

about the function db-store?

I can't infer from your snippet that this function is used anywhere or not

sorry, corrected the example, about ns.some/->SessionStore

This is a false positive. Feel free to post an issue

👍 1

I think clj-kondo never encountered a private deftype before

would it make sense for clj-kondo to resolve ns aliases in syntax-quoted forms?

eg. this compiles even though there's no str alias in the namespace

(defmacro m [coll]
  `(str/join ~(conj coll "hello")))

when called from another namespace, it expands to the literal symbol str/join which is unresolved

I think that makes sense, but how can clj-kondo know str is not a fully qualified namespace in general - heuristics?

I think single segment is probably a safe heuristic

Yeah, that was what I thought - or anything that's not explicitly :required by the namespace?

ok, so warning: "Missing alias: str" maybe then? yeah

I think this can work in general. Right now clj-kondo warns about "Unresolved namespace str" , but I think we could also change that to "Unresolved alias" instead in case of single segment

I can imagine a strange unhygenic macro which behaves differently according to the aliases of the ns which calls it

but that's probably very discouraged

yeah, in that case you can use #_:clj-kondo/ignore

The bug I ran into actually involved a multi-segment alias (`c2d.c` for clojure2d.color) but I think that's a reasonable heuristic

eg. a macro-defining ns might want to use fully qualified vars without requiring the ns in advance

hmm, so this would still not have been caught for you right

Right now clj-kondo lets you use fully-qualified vars anywhere even though that might lead to subtle breakages with ns loading order - was that an explicit decision?

Can you give an example?

eg namespace A requires B and C, B requires D C refers to D/foo without explicitly requiring it, this works because D has been loaded in advance. Later on B drops the D requirement, causing A to break the next time you load it in a repl

(I've encountered this several times due to long-running repl sessions and shuffling namespaces around)

clj-kondo warns about namespaces that you have not explicitly required

e.g. when you type (clojure.string/join [:foo :bar]) without a require, you get a warning

oh..🤦 for some reason I didn't see that warning, might have been editor lag

could it just extend that analysis into syntax-quoted positions then?

I don't think that would be correct, since the namespace defining the macro isn't always the one who should require the namespace used in the macro expansion

although in 99% of the cases, maybe it should

hmm yeah, I wonder how often that happens in the wild

we can implement it as an optional linter and then see how much false positives we get

probably where a utility namespace is defining macros and would otherwise lead to circular requires

alright, should I open a GH issue?

is there a way to have clj-kondo print out the fully merged config it’s made? Trying to debug why my malli generated extra configs aren’t working & want to make sure they’re even being included

@tanzoniteblack you can view this from the REPL, when you invoke (clj-kondo.core/run! {:lint ["src"]}), the return value will give you a map with :config in it

hm…as far as I can tell this is correct, but the linter in the malli directory doesn’t actually seem to be running

:config {:config-paths ["configs/malli"],
          :linters {:type-mismatch {:level :error},
                    :format {:level :warning},
                    :redundant-let {:level :warning},
                    :misplaced-docstring {:level :warning},
                    :missing-test-assertion {:level :warning},
                    :duplicate-set-key {:level :error},
                    :duplicate-map-key {:level :error},
                    :missing-clause-in-try {:level :warning},
                    :inline-def {:level :warning},
                    :missing-body-in-when {:level :warning},
                    :missing-docstring {:level :off},
                    :conflicting-alias {:level :error},
                    :unused-import {:level :warning},
                    :unbound-destructuring-default {:level :warning},
                    :syntax {:level :error},
                    :unused-namespace {:level :warning, :exclude ["taoensso.timbre"]},
                    :not-a-function {:level :error, :skip-args [korma.core/aggregate]},
                    :unresolved-namespace {:level :error},
                    :invalid-arity {:level :error,
                                    :skip-args [yummly.util.korma-util/defentity-tagged
                                                korma.core/defentity
                                                yummly.mobile-util.moustache/app
                                                korma.core/select
                                                korma.core/update
                                                korma.core/delete]},
                    :constant-test-assertion {:level :warning},
                    :missing-map-value {:level :error},
                    :unreachable-code {:level :warning},
                    :redefined-var {:level :warning},
                    :unused-binding {:level :off}},
          :lint-as {taoensso.timbre/debugf clojure.tools.logging/debugf,
                    taoensso.timbre/infof clojure.tools.logging/infof,
                    taoensso.timbre/warnf clojure.tools.logging/warnf,
                    taoensso.timbre/errorf clojure.tools.logging/errorf,
                    clojure.core.async/go-loop clojure.core/loop,
                    potemkin/defrecord+ clojure.core/defrecord},
          :output {:exclude-files ["./test/yummly/util/tracing.clj"]},
          :cfg-dir "/Users/ryan/Code/api/.clj-kondo"},

~/Code/api(introduce-malli:zap:)» cat .clj-kondo/configs/malli/config.edn -p
{:lint-as #:malli.schema{defn schema.core/defn}, :linters {:type-mismatch {:namespaces {yummly.data.recipe-collection {get-sort-by-order-hugsql {:arities {1 {:args [:string], :ret :string}}}}}}}}

if things are merged correctly, you should see the malli config in that returned config

oh. and I don’t

gotcha, this is what I needed to debug 🙂

what version of clj-kondo is this?

:config-paths ["configs/malli"] at the top level does imply that I have a directory called .clj-kondo/configs/malli

right?

"2021.03.03"

figured it out. I’m using a custom config .clj-kondo/config-ci.edn; but it looks like :config-paths only gets picked up if .clj-kondo/config.edn exists & specifies the key

so if I do

(clj-kondo.core/run! {:lint ["src"]
                      :config "/Users/ryan/Code/api/.clj-kondo/config-ci.edn"})
with :config-paths in it, they don’t get picked up

if we specify custom config paths, is there a reason we shouldn’t resolve and merge in this specified in :config-paths? I can probably make a PR today or next week to handle that situation if you think it’s the right behavior

Can you lay this out step by step in an issue? I'm kind of swamped in all kinds of slack threads

Understood.

I think you're on to something and it probably should be fixed, but github issue is best with some kind of small repro

Lunch and bunch of meetings first, then I'll make an issue for it