Fork me on GitHub
#clj-kondo
<
2021-11-22
>
orestis08:11:07

Perhaps if not a macro, then something that clj-kondo could do (in principle)? https://clojurians.slack.com/archives/C03S1KBA2/p1637568739382000

borkdude08:11:11

@orestis you can make custom macro/function hooks in which you can do this checking yourself

orestis08:11:54

hm, would hiccup syntax also work? e.g. the ideal use case is that the callsite is: [text/Text {:variant "Large" :color "Main"} "here is some text"] and I can validate "Large" and "Main" against a static set of values.

borkdude08:11:09

then you would have to add a hook for text/Text (with the namespace fully qualified)

orestis08:11:50

even if it's not a function call?

borkdude08:11:46

no that currently doesn't work

borkdude08:11:14

you could consider using https://github.com/borkdude/grasp to search for this pattern, but this is more offline than in your editor

borkdude08:11:08

(g/vec (g/rsym 'full.ns.text/Text) (s/and map? ...) g/*)

borkdude09:11:07

@orestis Here is a full example:

user=> (require '[grasp.api :as g])
nil
user=> (def spec (g/vec (g/rsym 'foo.text/Text) (s/and map? #(:variant %))))
#'user/spec
user=>
user=> (g/grasp-string "(require '[foo.text :as text]) [text/Text {:variant \"Foo\"}])" spec)
[[text/Text {:variant "Foo"}]]
user=>
user=> (def results *1)
#'user/results
user=> (map (fn [res] (-> res second :variant)) results)
("Foo")
user=>

orestis09:11:04

oh that's nice.

Benjamin10:11:05

what do I do wrong if update-vals is an unresolved symbol? It's in clojure.core

2
delaguardo11:11:44

Most likely you are using clojure before 1.11

☝️ 1
borkdude11:11:07

@benjamin.schwerdtner It works out of the box with clj-kondo, but if you have a dependency of Clojure older than 1.11 alpha such and so, it's not in there. And if this dependency is used for linting then it overwrites the built-in cache for clojure.

2
Benjamin11:11:24

so the issue is that the linting version is too old

borkdude11:11:42

it depends on what you are linting yes

Benjamin11:11:16

my /usr/bin/clojure is clojure 1.10 so and so. I guess that is the problem? The lint target uses 1.11

borkdude11:11:53

are you using clojure-lsp? or just vanilla clj-kondo?

borkdude11:11:34

have you linted your dependencies and how? which version of clj-kondo are you using?

Benjamin11:11:52

clj-kondo v2021.06.18

borkdude11:11:59

please upgrade

Benjamin11:11:27

upgrading to 2021.10.19-1 from the AUR

borkdude11:11:46

that should fix it

🎉 1
Joshua Suskalo11:11:34

So this is back on friday, but I also get the lsp errors in my flycheck error list that I get integration for with treemacs-list-errors, and I definitely go through them all, and I definitely get the errors that don't have line numbers that way.

Joshua Suskalo11:11:46

Ah, I see that was resolved.

Joshua Suskalo13:11:14

What would be a reason I'm getting an unresolved var warning for a #' var form with the fully qualified name of a public var?

borkdude13:11:01

unresolved var means: could not be found in the analysis of the other namespace

Joshua Suskalo14:11:31

hmm. I'll take a look in the ns to see how it's defined

Joshua Suskalo14:11:20

ah, they're using a custom def macro.

dharrigan15:11:12

Funnily enough, I'm getting this, but it's a bit weird, since I'm seeing, in the same namespace, where it is saying unsolved var (yet the var exists in the other namespace), and just above the undefined vars, are vars it can find, that have the same macro.

Joshua Suskalo15:11:32

kondo only reports a single var once per file I believe

borkdude15:11:17

it does, but it is configurable to show them all at once

dharrigan15:11:45

Right, but have a looksee at this:

dharrigan15:11:12

the two hightlighted do exist in the namespace referenced

dharrigan15:11:32

and both do follow the same structure at the ones that can be found

dharrigan15:11:47

each of those "public routes" is defined using the same macro

borkdude15:11:55

but did you visit that file recently? e.g. has clj-kondo linted that one - perhaps it has recent modifications or you checked out a git branch?

dharrigan15:11:01

I visit it a lot

dharrigan15:11:18

that's just a snippet. Imagine, if you will, that that list continues on for several more lines. clj-kondo finds most of the public-routes, but raises the same exception for others in the list (that do exist!)

dharrigan15:11:01

I count 12 lines it flags that error with

borkdude15:11:19

as always repro welcome. something that I can run locally.

dharrigan15:11:57

Yeah. I know. Pity I can't share the code as-is, etc.. etc..

borkdude15:11:08

making a minimal repro sometimes isn't that hard. yesterday I cut down a minimal repro from several hundred lines to 5 characters in a minute ;)

borkdude15:11:54

and this is the only way I can help. I haven't got any other ideas without more info

dharrigan15:11:58

I'm trying to do now

dharrigan15:11:20

I have a nice template that will generate a project for me, having to try to reproduce there

mynomoto15:11:09

Is there something like grasp that also transforms what it find?

borkdude15:11:37

@mynomoto the idea is that you can find matching results and then transform those results, e.g. using s/conform or whatever you like

borkdude15:11:45

or do you mean, in the file of the code itself?

mynomoto15:11:16

Yeah, in the code itself.

mynomoto15:11:34

Like grep can replace.

borkdude15:11:45

There you will need something like grasp + rewrite-clj. Grasp gives you the locations, and rewrite-clj allows you to update clojure code.

borkdude15:11:00

See #rewrite-clj

mynomoto15:11:54

You have a fork that ignores white spaces right?

borkdude15:11:40

yes, but this fork cannot be used to update code. since it does not have the whitespace

mynomoto15:11:36

Hum, interesting. Even if I do not care about the whitespace?

borkdude15:11:27

if you use rewrite-clj's zippers, you can navigate while it also takes into account whitespace. yes: my fork just ignore the whitespace during reading. if you don't have those tokens, you cannot produce the code reliably anymore. well, I guess you could by just looking at the locations and inserting whitespace, but you won't know if there were tabs or spaces.

borkdude15:11:05

I did this basically because I wanted to use rewrite-clj only for analysis, not rewriting

borkdude15:11:16

and I also didn't want to use zippers

lread15:11:17

@mynomoto like borkdude suggests, the rewrite-clj zip API will skip whitespace and comments while navigating but it preserves them.

borkdude15:11:08

@mynomoto carve is a tool which uses clj-kondo for analysis, gets locations and then uses the original rewrite-clj to update code

borkdude15:11:37

I think it's good to have separate tools for what you want: finding problem locations in code vs updating code using locations

mynomoto16:11:15

Ok, thank you both for the pointers/suggestions!

dharrigan16:11:45

Would it be significant that if I run clj-kondo on the command line, for that particular file, it does not report unresolved var, but when editing the file in my editor, that uses clojure-lsp (with clj-kondo under the hood), that the warning is produced?

borkdude16:11:40

yes, the cache is versioned with the version of clj-kondo, so if clojure-lsp uses a different version, then the used cache won't be the same

borkdude16:11:57

you will have to lint your dependencies in the same way that clojure-lsp does it

dharrigan16:11:03

I delete both the .clj-kondo directory and the .lsp directory each time

borkdude16:11:23

that's fine but clojure-lsp will automatically index all your dependencies

borkdude16:11:45

so the warnings will only appear if you linted certain namespaces

borkdude16:11:02

because then clj-kondo thinks it knows all the vars in those namespaces

borkdude16:11:09

and then warns about those that it thinks are not in them

dharrigan16:11:32

Right, if I get clj-kondo to lint the entire source directory, the warning is displayed.

dharrigan16:11:43

so, at least that is something to rule out.

ericdallo16:11:52

yeah, the unresolved-var linter from kondo is one of the linters that relies on the cache, since it need to know the namespaces available + their var-definitions

ericdallo16:11:16

you can repro manually with kondo if you lint with the same version clojure-lsp as well

ericdallo16:11:53

would you mind me ask why you want to run kondo manually for your case?

dharrigan16:11:43

I was simply trying to determine by a process of elimination what is causing the warning to be displayed

dharrigan16:11:53

so I wanted to try clj-kondo "raw" just to see if it produced the same result.

ericdallo16:11:48

in the past we had false-positives with this linter, but AFAIK we fixed them

dharrigan16:11:21

yeah, in this case I still don't know why I'm getting this oddness.

dharrigan16:11:33

I'm unable to reproduce atm in my test project

dharrigan17:11:18

I can reproduce it!

dharrigan16:11:20

@borkdude did you say that it's possible for clj-kondo to show all unresolved vars, not just the first one?

dharrigan16:11:40

where is that configured?

dharrigan16:11:35

Ah, I see :report-duplicates

dharrigan17:11:31

@borkdude I can reproduce it. I'm going to raise an issue for you to have a look when you have time 🙂 I also have a project you can use.

borkdude19:11:10

thanks, I'll take a look soon

dharrigan19:11:19

You're most welcome. Let me know if I can be of further assistance.

borkdude19:11:17

I think a better solution would be if compojure.sweet.api had some proper clj-kondo hook expansions, but I'm not sure how much this is used anymore, it has becomes a little bit of a legacy compared to reitit right?

dharrigan20:11:53

Yes, reitit has superceeded it

seancorfield20:11:30

What's the recommended approach for projects that get clj-kondo configs exported into them from dependencies? Check that into version control or not?

seancorfield20:11:47

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	../.clj-kondo/com.github.seancorfield/next.jdbc/

seancorfield20:11:13

(because the latest version of next.jdbc exports its hooks etc)

borkdude20:11:36

@seancorfield yes, just check it into source control. so anyone who check outs your project has the same linting, and also CI has the same linting.

borkdude20:11:03

you also have to add this to .clj-kondo/config.edn :config-paths ["com.github.seancorfield/next.jdbc"] I'm considering doing this automatically (implicitly) but I'm not yet sure if this is a good idea or what could go wrong

🙌 1
borkdude20:11:58

the danger would be: malfunctioning configs or never-ending loops in hooks for example

borkdude20:11:36

We could have a file to which we append automatically when importing/copying configs .clj-kondo/config-paths.edn (wrapped implicitly in a map):

"com.github.seancorfield/next.jdbc" true
".../..." false
and when there is already an entry (e.g. with false ) then we do not override that. thinking-face

seancorfield20:11:01

Hmm, glad I asked. I didn't realize I needed to manually update config when something got exported into my project.

borkdude20:11:41

yes, the reason is that I think people should be able to opt in or opt out of these configs

borkdude20:11:35

we could also just have a .config_ignore file which contains paths not to ever import.

borkdude20:11:26

or just part of config.edn :config-ignore-paths [".."]

Mateusz Mazurczak21:11:36

Hi, I wrote my own hook is it enough to just run clj-kondo --lint "src" after updating config.edn or am I missing something? Because it seems like it doesn't include my newest changes

Mateusz Mazurczak21:11:33

config.edn

{:linters {:translate/unwrapped {:level :error}}
 :hooks {:analyze-call {foo/unwrapped hooks.unwrapped/register-unwrapped}}}
and the hook:
(ns hooks.unwrapped
    (:require [clj-kondo.hooks-api :as api]))

(defn register-unwrapped [{:keys [:node]}]
    (let [test (println "HELLO")]
          (api/reg-finding!
            {:message "HELLO WORLD"
             :type :translate/unwrapped
             :row (:row (meta node))
             :col (:col (meta node))})
      ))

borkdude21:11:24

@U0281QDFE1X in which directory did you put the hook code?

borkdude21:11:34

and what is the file called?

Mateusz Mazurczak21:11:13

@borkdude .clj-kondo/hooks/unwrapped.clj

Mateusz Mazurczak21:11:34

it's the hook file path from the root of the project

borkdude21:11:39

that looks good

borkdude21:11:17

and you don't see anything when you lint

(require '[foo.bar :as foo])
(foo/unwrapped 1 2 3)
?

Mateusz Mazurczak21:11:19

So I run clj-kondo --lint "src" and I get old warnings, but no errors, also no println

Mateusz Mazurczak21:11:39

It seems that clj-kondo works, but it does not run the hook. As there should be errors from this hook

borkdude21:11:03

yes, with "see" I meant see the output from println

borkdude21:11:18

can you give an example including the require/ns form of what you are linting?

Mateusz Mazurczak22:11:18

Hmm what do you mean by > and you don't see anything when you lint > (require '[foo.bar :as foo]) > (foo/unwrapped 1 2 3) ohh so this hook is not run automatically through every namespace?

borkdude22:11:05

can you give me an example?

borkdude22:11:12

so I can see what you are doing

borkdude22:11:36

you need to use the fully qualified namespace in the config

borkdude22:11:43

not an alias

borkdude22:11:54

this is why I want to check in your example

Mateusz Mazurczak22:11:07

I think I've misunderstood how hooks work. I was expecting it to run this hook through all functions in "src" directory and if the hook function gets to the (reg-finding! ...) it reports error

borkdude22:11:39

:analyze-call {foo.bar/baz ....} is executed for functions like this:

(require '[foo.bar :as fbar])
(fbaz/baz 1 2 3) ;; <-- the hook sees this node

borkdude22:11:02

what would be your use case for running a hook on all function calls?

Mateusz Mazurczak22:11:54

Now I understand how hook works. Thanks! I want to add a lint rule, for checking reagent dom tree. There is a bug that if there is text that is not wrapped in :span tag, google translate add tags to it and react throws error that it cannot remove the child. So I wanted to check every function if there is for example "[:div "Hello"] and throw message that this "Hello" needs to be wrapped in [:span]

borkdude22:11:26

Makes sense. Currently hooks don't really provide access to this. Perhaps you can use something like https://github.com/borkdude/grasp. I gave an example of that here: https://clojurians.slack.com/archives/CHY97NXE2/p1637573107258800

borkdude22:11:18

This allows you to basically "grep" Clojure code using clojure.specs

borkdude22:11:23

So this spec would be something like:

(g/vec #{:div} string? g/*)

Mateusz Mazurczak22:11:02

Thanks! But with hook, couldn't I just hook to every "defn" function call to see if there is div?

borkdude22:11:06

yes, I guess you could

borkdude22:11:31

full grasp example:

user=> (require '[grasp.api :as g])
nil
user=> (def results (g/grasp-string "(defn foo [] [:div \"Hello\"])" (g/vec #{:div} string? g/*)))
#'user/results
user=> results
[[:div "Hello"]]
user=> (map meta results)
({:line 1, :column 14})

❤️ 1