Fork me on GitHub
#clj-kondo
<
2022-03-04
>
orestis12:03:17

I'm looking to do some static analysis: collect all usages of foo.bar/translate , and collect the following information: 1. literal value of the first argument (e.g. if called like (t/t "Hello") I'd like to collect "Hello" 2. filename, line, column I'm looking at the clj-kondo analysis option and in the output file I can see row, col/`end-col` which means I can probably read the source file manually, skip to row and parse the Sexpr manually somehow. Am I missing something? Would I use something like edamame to safely parse the s-expression? (I guess clojure.edn could also work for 95% of the cases)...

borkdude12:03:53

@orestis Is this for linting or just gathering information?

orestis12:03:17

This is only to gather information

borkdude12:03:27

I think you could do this using a hook. Just write a hook for your function, collect the first argument, call (api/reg-finding! (assoc (meta arg-node) :message "collect some info" :type :my-custom/linter)) and then register {:linters {:my-custom/linter {:level :info}}}

orestis12:03:43

(Eventually we may write a custom linter if the approach holds, but for now we need to collect info)

borkdude12:03:02

and then call clj-kondo in the jvm and collect :type :my-custom/linter from the :findings

borkdude12:03:46

An alternative without clj-kondo could be using https://github.com/borkdude/grasp

borkdude12:03:13

If you're planning to add linting later, I'd go for the clj-kondo approach

orestis12:03:52

Can I call clj-kondo with only this single linter?

borkdude12:03:19

@orestis yes, {:linters ^:replace {:my-custom/linter {:level :info}}}

orestis12:03:43

Looking into the hook

borkdude12:03:30

@orestis example:

$ clj-kondo --lint code.clj --config .clj-kondo/find_hello.edn
code.clj:4:20: info: Hello

$ cat code.clj
(ns code
  (:require [foo.bar]))

(foo.bar/translate "Hello")

$ cat .clj-kondo/hooks/hello.clj
(ns hooks.hello
  (:require [clj-kondo.hooks-api :as api]))

(defn translate-hook [{:keys [node]}]
  (let [arg (second (:children node))]
    (when (= "Hello" (api/sexpr arg))
      (api/reg-finding! (assoc (meta arg)
                               :message "Hello"
                               :type :my-custom/linter)))))

$ cat .clj-kondo/find_hello.edn
{:linters ^:replace {:my-custom/linter {:level :info}}
 :hooks {:analyze-call {foo.bar/translate hooks.hello/translate-hook}}}

borkdude13:03:44

^ slightly adapted it @orestis

orestis13:03:48

Hm, I'm having trouble with the config. The file is getting linted with my main config...

borkdude13:03:18

See example: `

--config .clj-kondo/find_hello.edn

orestis13:03:38

Sorry, it gets linted with the new config in addition to the previous one. So I'm also getting warnings about unused requires etc.

borkdude13:03:52

See ^:replace in the config

orestis13:03:03

Yep, it's there:

{:linters ^:replace {:nosco.i18n/translate {:level :info}}
 :hooks {:analyze-call {nosco.i18n.translate-server/t hooks/translate-server}}}

borkdude13:03:27

And how are you calling it on the command line?

orestis13:03:02

clj-kondo --lint src/nosco/reports/ideas.clj --config .clj-kondo/i18n-config.edn

orestis13:03:32

Clearly the i18n-config.edn is picked up as the new linter doesn't exist in the base config.

borkdude13:03:20

Are you planning to use this in the JVM or the command line?

orestis13:03:35

Ideally I'd invoke it via the JVM as then I wouldn't need to parse the text output.

borkdude13:03:54

clj-kondo supports outputting EDN and JSON as well btw

borkdude13:03:30

$ clj-kondo --lint code.clj --config .clj-kondo/find_hello.edn --config '{:output {:format :edn}}'
{:findings [{:row 4, :col 20, :end-row 4, :end-col 27, :message "Hello", :type :my-custom/linter, :filename "code.clj", :level :info}]}

orestis13:03:54

Good to know! In this case it doesn't really matter how does it get invoked - but it would be good to avoid other linters.

borkdude13:03:07

$ clj-kondo --lint code.clj --config .clj-kondo/find_hello.edn --config '{:output {:format :edn}}' | jet --func '#(->> % :findings (filter (comp #{:my-custom/linter} :type)))'
({:row 4, :col 20, :end-row 4, :end-col 27, :message "Hello", :type :my-custom/linter, :filename "code.clj", :level :info})

borkdude13:03:23

#(->> % :findings (filter (comp #{:my-custom/linter} :type)))

borkdude13:03:48

I think I would just use that as the ^:replace solution is pretty hard to get right

orestis13:03:02

If I put the ^:replace on the outside of the map, I get this error WARNING: file hooks.clj not found while loading hook

borkdude13:03:06

yes, but that will disable your :hook config as well, so just don't use it

orestis13:03:19

Thanks for the guidance, this is promising. I will have to finish it some other time though

orestis14:03:54

Managed to finish this, via invoking by the JVM

orestis14:03:59

Thanks for the guidance!

Sam Ritchie15:03:52

@borkdude for this issue https://github.com/sicmutils/sicmutils/pull/478, I wrote :kondo/ignore but the actual code correctly used :clj-kondo/ignore ;

borkdude16:03:06

Could not reproduce. Please make a standalone small repro and I'll try again.

Sam Ritchie16:03:08

repro achieved. my issue with overriding the error in the config was wrong, that was my mistake. but the inline override still does not work, and reproduces here on my machine

Sam Ritchie16:03:26

Even a bare #_:clj-kondo/ignore seems to fail with my custom warning. You can see the linter triggering on the top form but not the bottom form:

borkdude17:03:35

Thanks! I'll re-open the issue with your repro

❤️ 1
borkdude20:03:59

Found the problem, reported in the issue.

Sam Ritchie20:03:14

awesome, thank you! I responded too and took your recommendation for all linters exported by sicmutils.