clj-kondo

sheluchin 2025-03-21T15:26:42.468329Z

Is there a reason why var-usages might contain :name-col nil?

sheluchin 2025-03-21T15:39:26.397879Z

I'm thinking maybe something here is not working as intended https://clojurians.slack.com/archives/CHY97NXE2/p1664897879130349 sorry I never got around to testing this back when it came up initially.

borkdude 2025-03-21T15:39:26.476539Z

not sure why, but I bet you have a repro with some code? ;)

sheluchin 2025-03-21T15:41:44.894219Z

I'm looking at the Dewey dataset and seeing quite a few nils in :row, :col, :name-col and :name-row. Just trying to confirm we're not aware of a logical reason why it might be this way before going further with the detective work.

borkdude 2025-03-21T15:46:01.399249Z

I'm afraid it's hard to say in general without a repro and it'll be detective work for someone

sheluchin 2025-03-21T15:47:22.475849Z

Okay, fair enough. If you don't have a hunch I'll see if I can pull together a working example. Thank you @borkdude.

sheluchin 2025-03-21T18:19:47.980939Z

@borkdude https://github.com/sheluchin/clj-kondo-nil-usage-locs lmk if there's anything else I can add that would help

borkdude 2025-03-21T18:24:04.173849Z

thanks. in addition to this, a smaller repro would definitely help, e.g. if you can find a specific piece of code from fast-edn that returns nil for some fields, then we can look more into detail into that specific code. with those pieces of data, please file an issue

borkdude 2025-03-21T18:32:49.573659Z

my suspicion is that . and is etc generate name-col name when they are actually the result of an expansion of some other undesugared form or macro, e.g. from are

borkdude 2025-03-21T18:33:05.302519Z

so in this case there isn't a location for the name of the call

borkdude 2025-03-21T18:35:22.098799Z

e.g. here is a node that is generated without a location: https://github.com/clj-kondo/clj-kondo/blob/19a89107b94d133f6bf3e4dc31f7cbd6bd1fe8aa/src/clj_kondo/impl/analyzer/test.clj#L45 so I think that's the answer probably

sheluchin 2025-03-21T18:35:51.437579Z

Would you consider that a bug or just something that needs to be documented?

borkdude 2025-03-21T18:36:25.828779Z

not sure about the other normal functions like assoc but perhaps some more detective work would help here. I don't consider the above a bug, it doesn't make sense to make up some location for stuff that isn't really there in code

sheluchin 2025-03-21T18:37:22.507799Z

Does it make sense for it to be counted as a usage there? I guess it's something like an "indirect usage" in this case.

borkdude 2025-03-21T18:37:46.058149Z

it's an indirect usage indeed

sheluchin 2025-03-21T18:38:15.311649Z

I can investigate assoc, conj and say count... unless there are some specific names you want me to check out.

borkdude 2025-03-21T18:38:52.798099Z

does it also say something like "generated location" in the usage data? I forgot the exact term, but I did add this to communicate that it's a generated node

sheluchin 2025-03-21T18:39:38.684949Z

random example:

Name             |Value                       |
-----------------+----------------------------+
fixed-arities    |                            |
end-row          |35                          |
name-end-col     |                            |
name-end-row     |                            |
private          |                            |
name-row         |                            |
name             |.                           |
defmethod        |                            |
dispatch-val-str |                            |
lang             |                            |
filename         |clojure/fira_code/glyphs.clj|
alias            |                            |
from             |fira-code.glyphs            |
macro            |                            |
col              |29                          |
deprecated       |                            |
name-col         |                            |
from-var         |parse-escaped-string!       |
end-col          |44                          |
arity            |3                           |
varargs-min-arity|                            |
refer            |                            |
row              |35                          |
to               |clojure.core                |
basis-id         |0                           |

sheluchin 2025-03-21T18:40:38.481129Z

I don't see anything to the tune of "generated loc".

borkdude 2025-03-21T18:41:08.538619Z

no wait, it doesn't make sense to say "generated location" if there is no location. It does sometimes say this when it has inherited a location from a parent node in the case of macro hooks

borkdude 2025-03-21T18:41:42.805749Z

ok, that's interesting that it does have a col but nothing else

borkdude 2025-03-21T18:41:58.391559Z

let me check out the code

sheluchin 2025-03-21T18:41:59.672209Z

end-col too

borkdude 2025-03-21T18:44:01.568119Z

row 35 says this: https://github.com/tonsky/FiraCode/blob/7e5bff99c479b731eb18d528320f8bca98a6ac19/clojure/fira_code/glyphs.clj#L35 which version of fira-code is this?

sheluchin 2025-03-21T18:44:40.164509Z

7e5bff99c479b731eb18d528320f8bca98a6ac19

sheluchin 2025-03-21T18:44:50.283399Z

yep same one

borkdude 2025-03-21T18:44:58.985799Z

weird, I don't even see a call on that line

sheluchin 2025-03-21T18:47:34.852699Z

and I didn't see any calls to do in https://github.com/tonsky/fast-edn/blob/main/test/fast_edn/test.clj but one of the usages shows:

{:filename
  "/home/alex/repos/clojure-stuff/fast-edn/test/fast_edn/test.clj",
  :row nil,
  :col nil,
  :from fast-edn.test,
  :to clojure.core,
  :name do,
  :arity 15,
  :from-var basics-test}

sheluchin 2025-03-21T18:48:20.002079Z

ah that's an expansion

borkdude 2025-03-21T18:49:34.548809Z

| :fixed-arities | :end-row | :name-end-col | :name-end-row | :name-row |        :name | :filename |            :from | :col | :name-col |             :from-var | :end-col | :arity | :row |              :to |
|----------------+----------+---------------+---------------+-----------+--------------+-----------+------------------+------+-----------+-----------------------+----------+--------+------+------------------|
|           #{0} |       27 |            12 |            27 |        27 |     skip-ws! |   <stdin> | fira-code.glyphs |    3 |         4 | parse-escaped-string! |       13 |      0 |   27 | fira-code.glyphs |
|           #{0} |       28 |            28 |            28 |        28 | current-char |   <stdin> | fira-code.glyphs |   15 |        16 | parse-escaped-string! |       29 |      0 |   28 | fira-code.glyphs |
|         #{1 2} |       28 |            11 |            28 |        28 |            = |   <stdin> | fira-code.glyphs |    9 |        10 | parse-escaped-string! |       30 |      2 |   28 |     clojure.core |
|           #{0} |       32 |            20 |            32 |        32 |     advance! |   <stdin> | fira-code.glyphs |   11 |        12 | parse-escaped-string! |       21 |      0 |   32 | fira-code.glyphs |
|           #{0} |       33 |            33 |            33 |        33 | current-char |   <stdin> | fira-code.glyphs |   20 |        21 | parse-escaped-string! |       34 |      0 |   33 | fira-code.glyphs |
|         #{1 2} |       35 |            17 |            35 |        35 |            = |   <stdin> | fira-code.glyphs |   15 |        16 | parse-escaped-string! |       24 |      2 |   35 |     clojure.core |
|                |       35 |               |               |           |            . |   <stdin> | fira-code.glyphs |   29 |           | parse-escaped-string! |       44 |      3 |   35 |     clojure.core |
|           #{0} |       35 |            54 |            35 |        35 |     advance! |   <stdin> | fira-code.glyphs |   45 |        46 | parse-escaped-string! |       55 |      0 |   35 | fira-code.glyphs |
|           #{0} |       35 |            81 |            35 |        35 | current-char |   <stdin> | fira-code.glyphs |   68 |        69 | parse-escaped-string! |       82 |      0 |   35 | fira-code.glyphs |
|                |       35 |               |               |           |            . |   <stdin> | fira-code.glyphs |   56 |           | parse-escaped-string! |       83 |      3 |   35 |     clojure.core |
|                |       35 |            90 |            35 |        35 |        recur |   <stdin> | fira-code.glyphs |   84 |        85 | parse-escaped-string! |       91 |      0 |   35 |     clojure.core |
|                |       35 |            28 |            35 |        35 |           do |   <stdin> | fira-code.glyphs |   25 |        26 | parse-escaped-string! |       92 |      4 |   35 |     clojure.core |
|         #{1 2} |       36 |            17 |            36 |        36 |            = |   <stdin> | fira-code.glyphs |   15 |        16 | parse-escaped-string! |       24 |      2 |   36 |     clojure.core |
|           #{0} |       36 |            38 |            36 |        36 |     advance! |   <stdin> | fira-code.glyphs |   29 |        30 | parse-escaped-string! |       39 |      0 |   36 | fira-code.glyphs |
|         #{0 1} |       36 |            44 |            36 |        36 |          str |   <stdin> | fira-code.glyphs |   40 |        41 | parse-escaped-string! |       48 |      1 |   36 |     clojure.core |
|                |       36 |            28 |            36 |        36 |           do |   <stdin> | fira-code.glyphs |   25 |        26 | parse-escaped-string! |       49 |      2 |   36 |     clojure.core |
|                |       37 |               |               |           |            . |   <stdin> | fira-code.glyphs |   29 |           | parse-escaped-string! |       44 |      3 |   37 |     clojure.core |
|                |       37 |            51 |            37 |        37 |        recur |   <stdin> | fira-code.glyphs |   45 |        46 | parse-escaped-string! |       52 |      0 |   37 |     clojure.core |
|                |       37 |            28 |            37 |        37 |           do |   <stdin> | fira-code.glyphs |   25 |        26 | parse-escaped-string! |       53 |      2 |   37 |     clojure.core |
|                |       37 |            18 |            34 |        34 |         cond |   <stdin> | fira-code.glyphs |   13 |        14 | parse-escaped-string! |       54 |      6 |   34 |     clojure.core |
|                |       37 |            15 |            33 |        33 |          let |   <stdin> | fira-code.glyphs |   11 |        12 | parse-escaped-string! |       55 |      2 |   33 |     clojure.core |
|                |       37 |            14 |            31 |        31 |         loop |   <stdin> | fira-code.glyphs |    9 |        10 | parse-escaped-string! |       56 |      3 |   31 |     clojure.core |
|           #{3} |       38 |            21 |            38 |        38 |      replace |   <stdin> | fira-code.glyphs |    9 |        10 | parse-escaped-string! |       35 |      3 |   38 |   clojure.string |
|           #{3} |       39 |            21 |            39 |        39 |      replace |   <stdin> | fira-code.glyphs |    9 |        10 | parse-escaped-string! |       34 |      3 |   39 |   clojure.string |
|           #{3} |       40 |            21 |            40 |        40 |      replace |   <stdin> | fira-code.glyphs |    9 |        10 | parse-escaped-string! |       34 |      3 |   40 |   clojure.string |
|                |       40 |            10 |            30 |        30 |           -> |   <stdin> | fira-code.glyphs |    7 |         8 | parse-escaped-string! |       35 |      4 |   30 |     clojure.core |
|                |       40 |             9 |            29 |        29 |          let |   <stdin> | fira-code.glyphs |    5 |         6 | parse-escaped-string! |       36 |      2 |   29 |     clojure.core |
|                |       40 |             8 |            28 |        28 |         when |   <stdin> | fira-code.glyphs |    3 |         4 | parse-escaped-string! |       37 |      2 |   28 |     clojure.core |

borkdude 2025-03-21T18:49:48.557109Z

This is from:

(->> (with-in-str (slurp "") (k/run! {:lint ["-"] :config {:analysis true}}) ) :analysis :var-usages (filter #(= (:from-var %) 'parse-escaped-string!)) (clojure.pprint/print-table))

borkdude 2025-03-21T18:50:58.929549Z

ah yes:

|                |       37 |               |               |           |            . |   <stdin> | fira-code.glyphs |   29 |           | parse-escaped-string! |       44 |      3 |   37 |     clojure.core |

borkdude 2025-03-21T18:51:07.721079Z

so the name is .

borkdude 2025-03-21T18:51:35.591559Z

which is an expansion of (StringBuilder.)

borkdude 2025-03-21T18:51:37.645189Z

makes sense

borkdude 2025-03-21T18:51:49.811769Z

so that one seems correct, it's an expansion

sheluchin 2025-03-21T18:58:58.075129Z

Here's an assoc example from babashka 4c6fe98236aa63e0044a7caff772510e441c2af0:

Name             |Value                |
-----------------+---------------------+
fixed-arities    |#{3}                 |
end-row          |828                  |
name-end-col     |                     |
name-end-row     |                     |
private          |                     |
name-row         |                     |
name             |assoc                |
defmethod        |                     |
dispatch-val-str |                     |
lang             |                     |
filename         |src/babashka/main.clj|
alias            |                     |
from             |babashka.main        |
macro            |                     |
col              |59                   |
deprecated       |                     |
name-col         |                     |
from-var         |readers-fn           |
end-col          |64                   |
arity            |3                    |
varargs-min-arity|3                    |
refer            |                     |
row              |828                  |
to               |clojure.core         |
basis-id         |375                  |

borkdude 2025-03-21T19:00:35.337629Z

I think that's from this line:

_ (swap! seen-urls assoc urls parsed-resources)

sheluchin 2025-03-21T19:00:55.752499Z

Yeah, that should have complete loc info, no?

borkdude 2025-03-21T19:01:05.451369Z

probably you can assume here that the name-col is the same as the call location since it's called as an argument

sheluchin 2025-03-21T19:02:31.789399Z

yep, that's where it lands

borkdude 2025-03-21T19:03:38.475089Z

I think clojure-lsp makes such assumptions too

sheluchin 2025-03-21T19:03:50.350609Z

Okay, so that's two edge cases: expansions and non-invocation usages

borkdude 2025-03-21T19:03:50.743039Z

perhaps we can just document this

borkdude 2025-03-21T19:04:19.577249Z

yes, but the second edge case does have full location information other than the name where the expansion had none right?

sheluchin 2025-03-21T19:04:49.868339Z

looks to be the case

sheluchin 2025-03-21T19:05:23.940529Z

Sounds like a bit of docs should suffice. I think in my case I will just filter out records that don't have this information and call it a day.

👍 1
sheluchin 2025-03-21T19:06:15.285639Z

Appreciate you diving into this. I'll add a PR with a mention of these cases for the docs if you don't prefer to do it yourself.

borkdude 2025-03-21T19:06:28.691439Z

thanks a lot, a PR would be appreciated!

🫡 1
sheluchin 2025-03-22T16:35:35.750619Z

ty for merge. link for ref: https://github.com/clj-kondo/clj-kondo/pull/2505