Fork me on GitHub
#clj-kondo
<
2023-02-05
>
orestis12:02:58

Does kondo support detecting keywords that could be mistyped? Eg. if a namespaced or even plain keyword appears only once in the codebase it might be a typo?

borkdude12:02:38

It doesn't do this, but clojure-lsp might be able to do this as it keeps all the keywords of the whole project in memory

orestis12:02:39

I produced an interesting bug the other day by doing search-replace from in-line strings to a var. There was a case statement that broke since case doesn’t support dereferencing vars.

borkdude12:02:51

This has come up before but it isn't possible to detect if the users intentionally matches the literal symbol or not

borkdude12:02:36

What we could however detect if when the argument is a certain type (string) and nothing in the case matches that type, we could warn

orestis12:02:21

Ah right. If this was an optional linter, it could be easily be circumvented by using the quoted form for the (rare?) cases you want to match against a symbol

orestis12:02:13

(assuming clj-kondo can detect quoted symbols?)

borkdude12:02:54

optional linter can be done for sure

borkdude12:02:15

does your case also have a default case? then the type idea would not pan out

orestis12:02:41

user=> (case 'foo-var foo-var "something" nil) ;; warn on this
"something"
user=> (case 'foo-var 'foo-var "something" nil) ;; but not on this
"something"

orestis12:02:04

My personal opinion (without any data to back it) is that using case as-designed (of not dereferencing symbols) is probably not that much used and a confusing bug (for beginners, at least).

borkdude12:02:07

huh, the second case is also not valid, quoted symbols in case is never what you want

orestis12:02:40

Why not? It seems to work exactly the same, right?

borkdude12:02:42

This is just co-incidental since 'foo-bar expands by the reader into (quote foo-bar) so it would also match the symbol quote:

user=> (case 'quote 'foo-var "something" nil)
"something"

orestis12:02:58

user=> (let [a "foo" b "bar"] (case "foo" 'a :a 'b :b))
Syntax error macroexpanding case at (REPL:1:24).
Duplicate case test constant: quote

orestis12:02:18

case is even more confusing than I thought 🙂

borkdude12:02:43

all you need to know about case is that the constants are not evaluated

borkdude12:02:00

and that you can list multiple constants in a list

orestis12:02:01

I used to know that, but when I did the refactoring I was scratching my head.

borkdude12:02:15

I agree that this often confuses people

orestis12:02:40

That is, I was away from the REPL - if I was writing new code I'd have caught it, but since it was old code that was being refactored, then it slipped under my radar.

orestis12:02:10

Can you detect if a symbol in the case expression is actually a valid var?

orestis12:02:28

or a local, I guess

borkdude12:02:10

yes, that can be detected

borkdude12:02:09

in clj-kondo's codebase there is actually a case statement which matches against the names of vars as symbol so that would be one counter-example, but agreed that this is probably an exception to the rule. also using class names in case is an often source of confusion

orestis12:02:03

I think in most "day-to-day" codebases that linter could be enabled, and then disabled for very specific instances of case statements (still should be an optional linter, I guess)

borkdude12:02:20

as an optional linter I wouldn't have a problem with it

orestis12:02:54

I wonder if an optional linter needs to be so accurate as to look into locals or vars, or instead just ban symbols altogether

borkdude12:02:36

I'd say just ban symbols. It would be good to scan existing codebases for such usages of case. You can do so using grasp: https://github.com/borkdude/grasp

orestis12:02:48

I always mean to use grasp but I alway forget what it's called. You should pack it in babashka 🙂

orestis12:02:25

So that it could be e.g. invoked by the command line. (Not really suggesting that, I've no idea about it 😄 )

borkdude12:02:02

I'll try to cook up a case example ;)

borkdude12:02:28

According to grasp here are some counter-examples:

| :line | :column |                                                                                                                                                                                                               :uri |
|-------+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|   176 |      17 |                                                                                jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/middleware/util/instrument.clj |
|    40 |       5 |                                                                                      jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/middleware/enlighten.clj |
|   176 |      20 |                                                                   jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/fipp/v0v6v26/fipp/clojure.cljc |
|   320 |       5 |                                                  jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader/edn.clj |
|   413 |       5 |                                                      jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader.clj |
|   325 |       5 |                                                    jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader/edn.cljs |
|   434 |       5 |                                                        jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader.cljs |
|   257 |       5 |                                                                                                                       jar:file:/Users/borkdude/.m2/repository/nrepl/nrepl/1.0.0/nrepl-1.0.0.jar!/nrepl/bencode.clj |
|   282 |      16 |                                                                                                                       jar:file:/Users/borkdude/.m2/repository/nrepl/nrepl/1.0.0/nrepl-1.0.0.jar!/nrepl/cmdline.clj |
|  1216 |      23 |                                                                                                               jar:file:/Users/borkdude/.m2/repository/org/babashka/sci/0.3.2/sci-0.3.2.jar!/sci/impl/analyzer.cljc |
|  1284 |      31 |                                                                                                               jar:file:/Users/borkdude/.m2/repository/org/babashka/sci/0.3.2/sci-0.3.2.jar!/sci/impl/analyzer.cljc |
|   144 |       3 |                                                                                                                  jar:file:/Users/borkdude/.m2/repository/org/babashka/sci/0.3.2/sci-0.3.2.jar!/sci/impl/utils.cljc |
|   358 |       4 |                                                                                                            jar:file:/Users/borkdude/.m2/repository/org/clojure/clojure/1.11.0/clojure-1.11.0.jar!/clojure/test.clj |
|   176 |       9 |                                                                                                     jar:file:/Users/borkdude/.m2/repository/org/clojure/clojure/1.11.0/clojure-1.11.0.jar!/clojure/core/server.clj |
|    46 |       6 |                                                                        jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/extract_definition.clj |
|    58 |       5 |                                                                        jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/extract_definition.clj |
|    20 |       7 |      jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzer/v1v1v0/clojure/tools/analyzer/passes/collect_closed_overs.clj |
|    50 |       5 |                jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzer/v1v1v0/clojure/tools/analyzer/passes/elide_meta.clj |
|    21 |       6 |          jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzer/v1v1v0/clojure/tools/analyzer/passes/add_binding_atom.clj |
|   809 |       4 |                                  jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzer/v1v1v0/clojure/tools/analyzer.clj |
|   320 |       5 |                                  jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader/edn.clj |
|   413 |       5 |                                      jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader.clj |
|   325 |       5 |                                    jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader/edn.cljs |
|   434 |       5 |                                        jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader.cljs |
|   427 |       4 |                           jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzerjvm/v1v2v2/clojure/tools/analyzer/jvm.clj |
|   151 |       3 |  jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzerjvm/v1v2v2/clojure/tools/analyzer/passes/jvm/analyze_host_expr.clj |
|    26 |       3 | jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzerjvm/v1v2v2/clojure/tools/analyzer/passes/jvm/annotate_host_info.clj |
|   327 |       5 |                                                                                          jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/cljs/tools/reader/edn.cljs |
|   436 |       5 |                                                                                              jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/cljs/tools/reader.cljs |
|   415 |       5 |                                                                                            jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/clojure/tools/reader.clj |
|   322 |       5 |                                                                                        jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/clojure/tools/reader/edn.clj |

orestis12:02:07

Ah nice, you're running this against .m2?

borkdude12:02:48

Here is the first counter-example: https://github.com/clojure-emacs/cider-nrepl/blob/d219ce610d9030108c273fab933538fb3b24d8be/src/cider/nrepl/middleware/util/instrument.clj#L176 You can go through the rest to learn more :) I've just matched against (System/getProperty "java.class.path")

borkdude12:02:01

I'll push the grasp code

orestis12:02:15

Thanks, I'll run it against our codebase to see

borkdude12:02:03

I found one case (!) where it matches the default case as a symbol which it shouldn't, let me fix that

borkdude12:02:41

Pushed, less cases now:

| :line | :column |                                                                                                                                                                                     :uri |
|-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|   176 |      17 |                                                      jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/middleware/util/instrument.clj |
|    40 |       5 |                                                            jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/middleware/enlighten.clj |
|   320 |       5 |                        jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader/edn.clj |
|   413 |       5 |                            jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader.clj |
|   325 |       5 |                          jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader/edn.cljs |
|   434 |       5 |                              jar:file:/Users/borkdude/.m2/repository/cider/cider-nrepl/0.28.6/cider-nrepl-0.28.6.jar!/cider/nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader.cljs |
|  1216 |      23 |                                                                                     jar:file:/Users/borkdude/.m2/repository/org/babashka/sci/0.3.2/sci-0.3.2.jar!/sci/impl/analyzer.cljc |
|  1284 |      31 |                                                                                     jar:file:/Users/borkdude/.m2/repository/org/babashka/sci/0.3.2/sci-0.3.2.jar!/sci/impl/analyzer.cljc |
|    46 |       6 |                                              jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/extract_definition.clj |
|    58 |       5 |                                              jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/extract_definition.clj |
|   809 |       4 |        jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzer/v1v1v0/clojure/tools/analyzer.clj |
|   320 |       5 |        jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader/edn.clj |
|   413 |       5 |            jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/clojure/tools/reader.clj |
|   325 |       5 |          jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader/edn.cljs |
|   434 |       5 |              jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsreader/v1v3v6/cljs/tools/reader.cljs |
|   427 |       4 | jar:file:/Users/borkdude/.m2/repository/refactor-nrepl/refactor-nrepl/3.5.5/refactor-nrepl-3.5.5.jar!/refactor_nrepl/inlined_deps/toolsanalyzerjvm/v1v2v2/clojure/tools/analyzer/jvm.clj |
|   327 |       5 |                                                                jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/cljs/tools/reader/edn.cljs |
|   436 |       5 |                                                                    jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/cljs/tools/reader.cljs |
|   415 |       5 |                                                                  jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/clojure/tools/reader.clj |
|   322 |       5 |                                                              jar:file:/Users/borkdude/.m2/repository/org/clojure/tools.reader/1.3.4/tools.reader-1.3.4.jar!/clojure/tools/reader/edn.clj |

borkdude12:02:11

It would be good to scan your entire .m2 folder and see if any non-tooling libraries have any examples :)

orestis12:02:01

| :line | :column |                                                                :uri |
|-------+---------+---------------------------------------------------------------------|
|    59 |      21 | file:/Users/orestis/dev/nosco/nosco-gamma/cljs/nosco/ui/inputs.cljs |
|   176 |      17 |                                                                     |
|    40 |       5 |                                                                     |
|   320 |       5 |                                                                     |
|   413 |       5 |                                                                     |
|   325 |       5 |                                                                     |
|   434 |       5 |                                                                     |
|    32 |       5 |                                                                     |
|  1087 |      22 |                                                                     |
|    58 |       6 |                                                                     |
|  1216 |      23 |                                                                     |
|  1284 |      31 |                                                                     |
|   427 |       4 |                                                                     |
|   327 |       5 |                                                                     |
|   436 |       5 |                                                                     |
|   415 |       5 |                                                                     |
|   322 |       5 |                                                                     |
|   415 |       5 |                                                                     |
|   322 |       5 |                                                                     |
|   809 |       4 |                                                                     |
I see this - the first file is the bug we found (the fix is in a branch) but the rest seems to not have any uris?

borkdude12:02:32

oh that's weird, hm

borkdude13:02:06

Do you have the newest version of carve?

orestis13:02:27

I put 0.0.3 in the deps as per README and copied the example code

borkdude13:02:56

eh carve, I meant grasp, sorry

borkdude13:02:20

The newest version is 0.1.4

orestis13:02:42

I went with the latest master for now

borkdude13:02:46

I'll update the README

orestis13:02:00

Everything else is tools.reader, tools.analyzer, clj-kondo plus variations of those vendored in other deps.

borkdude13:02:39

yeah, so it seems mostly clojure-related / low level libraries that tend to use this

borkdude13:02:04

or math (sicmutils for examples)

borkdude13:02:59

you mean, a false positive in carve?

orestis13:02:00

there's no first argument to the case, so the arguments shift around

orestis13:02:17

A bug in the java.time library

borkdude13:02:19

oh yes, that's funny

borkdude13:02:59

I guess clj-kondo could warn on the first argument to case being a constant

borkdude13:02:41

no this is not a bug

borkdude13:02:45

there's a threading involved

borkdude13:02:24

overuse of threading I'd say ;)

orestis13:02:05

clojure-lsp should have a refactoring to change an if to a cond - but in this case it's even a cond->

borkdude13:02:10

clj-kondo would have detected this since it expands -> and ->> and so on, but grasp is mostly for doing research on the feasibility of features for clj-kondo, SCI etc so it doesn't have to be 100% accurate

orestis13:02:02

Ah interesting. So I guess grasp is not meant to be used as e.g. a very custom lint step or for answering definitive questions about a codebase.

borkdude13:02:34

it can be but it should be used with this caution in mind

borkdude13:02:16

if you want to have it 100% accurate wrt macros (which are always the problem when it comes to static analysis) you could double-check with tools.analyzer or so.

borkdude13:02:36

I think grasp could have an option to expand ->, ->> etc as well, but so far I haven't found it worth the effort yet