clj-kondo

borkdude 2024-08-29T15:41:01.172469Z

https://clojurians.slack.com/archives/C06MAR553/p1724946046971999

❤️ 1
stijn 2024-08-29T17:33:24.212009Z

Is it possible that https://github.com/clj-kondo/clj-kondo/issues/1157 resurfaced again with 2024.08.01? Haven't seen this for a while, but when upgrading github actions this is failing again. The declaration is on a cljc file:

(:require
    #?(:clj  [clojure.spec.gen.alpha :as gen]
       :cljs [cljs.spec.gen.alpha :as gen])

borkdude 2024-08-29T18:51:22.565879Z

let me check

borkdude 2024-08-29T18:52:56.629859Z

When I lint this:

(ns dude
  (:require
   [clojure.spec.alpha :as s]
   [clojure.spec.gen.alpha :as sgen]))

(s/def ::uuid-str
  (s/with-gen string? (fn [] (sgen/fmap str (s/gen uuid?)))))
I get
$ ./clj-kondo --lint /tmp/dude.clj
linting took 16ms, errors: 0, warnings: 0

stijn 2024-08-29T18:53:44.997329Z

let me create a minimal example

stijn 2024-08-29T18:59:18.181399Z

❯ clj-kondo --lint break.cljc
break.cljc:10:7: warning: Unresolved var: gen/fmap
linting took 15ms, errors: 0, warnings: 1
❯ cat break.cljc                 
(ns break
  (:require
    [clojure.spec.alpha :as s]
    #?(:clj  [clojure.spec.gen.alpha :as gen]
       :cljs [cljs.spec.gen.alpha :as gen])))

(s/def ::break
  (s/with-gen
    nat-int?
    #(gen/fmap
       inc
       (s/gen integer?))))
so just to be sure, this is only with cljc

stijn 2024-08-29T19:01:08.138529Z

❯ clj-kondo --lint works.clj
linting took 9ms, errors: 0, warnings: 0

21:00:45 in /tmp …
❯ cat works.clj             
(ns works 
  (:require
    [clojure.spec.alpha :as s]
    [clojure.spec.gen.alpha :as gen]))

(s/def ::works
  (s/with-gen
    nat-int?
    #(gen/fmap
       inc
       (s/gen integer?))))

borkdude 2024-08-29T19:02:48.916529Z

I can reproduce this with .cljc yes. I don't think this is a recurring error but it never worked in .cljc, for some reason. Issue welcome

borkdude 2024-08-29T19:03:49.086359Z

a workaround can be found in the other issue

👍 1
stijn 2024-08-29T19:12:00.515129Z

thanks for the quick response, created an issue https://github.com/clj-kondo/clj-kondo/issues/2384

arohner 2024-08-29T20:16:12.495599Z

Is there a linter for discouraged java static method call?

arohner 2024-08-29T20:16:50.100569Z

i.e. (foo/bar) where bar is a static method on class foo

borkdude 2024-08-29T20:57:14.442899Z

There isn't a :discouraged-static-method linter yet, but since the latest version you can write an :analyze-call hook and supply a fully qualified method for which you can then write a hook and warn

borkdude 2024-08-29T20:58:02.008379Z

Feel free to submit an issue for this though. Also, FWIW you can use the clj-kondo analysis as well, to find the static method call: https://github.com/clj-kondo/clj-kondo/blob/master/analysis/README.md

arohner 2024-08-29T20:58:30.800079Z

Thanks

Alex Miller (Clojure team) 2024-08-29T20:43:19.797259Z

why would you want to discourage that?

2024-08-30T13:42:05.030809Z

Y'all decided that continuing to allow the junk was a necessary evil for compatibility reasons?

borkdude 2024-08-30T13:44:52.988669Z

of course

borkdude 2024-08-30T13:45:00.720219Z

it will break many programs if you don't do that

2024-08-30T13:45:55.877499Z

Well, they've tightened rules on undefined behavior before, such as 1.9 disallowing symbols in ns forms due to the introduction of core specs. a lot of old zach tellman libraries no longer work because of that

borkdude 2024-08-30T13:50:55.234079Z

symbols in ns?

dpsutton 2024-08-30T13:52:53.343729Z

I do remember some ns forms failing in 1.9+ but i thought it was from a way that imports were used? Might have been a no-op in 1.8 and below

borkdude 2024-08-30T13:54:43.664799Z

An example of what would break when junk would not be allowed, which is preeeetty common:

user=> (defprotocol MyDude (foobar [_]))
user=> (ns foo)
foo=> (require '[user :as u])
foo=> (defmacro extend! [] `(extend-type String u/MyDude (foobar [_#] :str))) ;; looks pretty innocent right?
foo=> (extend!)
nil
foo=> (u/foobar "dude")
:str
yeah all good but:
foo=> (macroexpand-1 '(extend!))
(clojure.core/extend-type java.lang.String user/MyDude (foo/foobar [___189__auto__] :str))
you see, foo/foobar?

borkdude 2024-08-30T13:55:04.928749Z

well, this is not about interop, but a similar case can be easily constructed with interop

borkdude 2024-08-30T13:55:31.065629Z

and such things have become widespread because the compiler allowed this junk from 1.0 or earlier

2024-08-30T13:58:07.916019Z

https://github.com/clj-commons/seesaw/blob/38695ea1a590d84d877a50df8792f58e04fcbd02/test/seesaw/test/examples/j18n.clj

(ns seesaw.test.examples.j18n
  (use [seesaw.core]
       seesaw.test.examples.example))
throws the error
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
In: [1] val: ((use [seesaw.core] seesaw.test.examples.example)) fails spec: :clojure.core.specs.alpha/ns-form at: [:args] predicate: (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs.alpha/ns-clauses),  Extra input

2024-08-30T14:04:35.334189Z

i know i fundamentally disagree with the core team's approach to "what is worth breaking or not breaking", but i'm still surprised that something as important as "interop syntax" is codified instead of "garbage in, garbage out"

borkdude 2024-08-30T14:07:09.298789Z

@nbtheduke if you ask me it's because of syntax-quote, but I'm not the core team. I think I agree on this one (also on the ns garbage, because that's obvious to spot with your naked eyes).

user=> (defmacro dude [] `(fn [x#] (. x# endsWith "d")))
#'user/dude
user=> (dude)
#object[user$eval151$fn__152 0x1ded7b14 "user$eval151$fn__152@1ded7b14"]
user=> ((dude) "odd")
true
user=> (macroexpand '(dude))
(fn* ([x__145__auto__] (. x__145__auto__ user/endsWith "d")))

2024-08-30T14:08:16.856619Z

oh huh, that's... yeah, i don't have a good answer for that one

borkdude 2024-08-30T14:08:38.317189Z

of course clj-kondo could help spot this garbage, but only if it's slated to be removed from the compiler one day, like (System/out) which it now spots

2024-08-30T14:10:00.285799Z

heh i've complained to you about auto-qualifying symbols without existing vars before, even wrote a whole linter we ended up throwing away over this

borkdude 2024-08-30T14:10:58.086719Z

(hopefully for good reasons, I don't remember them)

2024-08-30T14:12:59.181509Z

oh, it was very legitimate to not merge it, due to the prevalence of ` to auto-qualify symbols outside of macros in core

borkdude 2024-08-30T14:13:33.678109Z

yeah, I guess clj-kondo could help spot "interop" garbage specifically, but ship sailed too long ago probably

2024-08-30T17:23:00.221159Z

I think a linter that fully expands code like eastwood might be more effective at finding these issues.

arohner 2024-08-30T21:07:52.133849Z

@alexmiller to answer your original question: https://clojurians.slack.com/archives/C03S1KBA2/p1724947307123279 I want to create “zones” where code is totally deterministic, so I want to deprecate certain function calls and java methods

arohner 2024-08-30T21:11:32.011119Z

I guess the other option is to load my code in https://github.com/corda/djvm, but that has a pretty big RAM implication, and interop between the classloaders gets tricky

borkdude 2024-08-29T20:55:40.658939Z

I think he's not looking to discourage that in general, but a specific one

👍 1
2024-08-29T21:44:09.087509Z

this got me thinking, what do the new changes in 1.12 do for static fields and methods with the same name? for example

class HelloWorld {
    public static int a = 1;
    public static int a() {
        return 2;
    }

    public static void main(String[] args) {
        System.out.println(HelloWorld.a);
        System.out.println(HelloWorld.a());
    }
}
To sketch my idea, imagine the expression (do HelloWorld/a) . Is this a disambiguated field reference in Clojure 1.11? And does it become a method in 1.12?

borkdude 2024-08-29T21:45:53.706459Z

This was discussed in #clojure recently, the static field has precedence

2024-08-29T21:46:38.040729Z

thanks

2024-08-29T21:50:04.613159Z

perhaps worth documenting in Clojure's changes.md? currently says Classname/method always returns a method, but might be clearer to say "unless there's a static field called method".

2024-08-29T21:51:34.747749Z

I guess there's no syntax to force it to return a method?

2024-08-29T21:51:56.431399Z

or is that where :param-tags comes in?

borkdude 2024-08-29T21:52:24.328399Z

> Note: Static fields are values and should be referenced without parens unless they are intended as function calls, e.g (System/out) should be System/out. Future Clojure releases will treat the field's value as something invokable and invoke it.

Alex Miller (Clojure team) 2024-08-29T21:53:08.688379Z

we are working on updates to the Clojure reference docs, including the evaluation page, which will list things in precedence order

🎉 1
1
borkdude 2024-08-29T21:53:37.040779Z

Also there's the edge case where the field contains an IFn and you want to call that directly, using (Foo/bar) , and an edge-edge case when there's also a method bar in addition to the field + IFn

😄 1
Alex Miller (Clojure team) 2024-08-29T21:54:24.530869Z

https://github.com/clojure/clojure-site/blob/1.12-doc-updates/content/reference/evaluation.adoc is the very much wip evaluation docs

👍 1
Alex Miller (Clojure team) 2024-08-29T21:55:44.707149Z

notably, static fields were missing from this before 1.12

Alex Miller (Clojure team) 2024-08-29T21:57:28.717129Z

we have retained support for (C/staticField) == C/staticField as it is annoyingly common in the wild, even though this is a true problem re substitutability

➕ 1
borkdude 2024-08-29T21:58:09.876949Z

clj-kondo will warn about it since a while though

🙏 2
Alex Miller (Clojure team) 2024-08-29T21:58:10.173919Z

I can imagine a further phasing out with an annoying warning being the next step

➕ 1
Alex Miller (Clojure team) 2024-08-29T21:58:34.280219Z

we opted not to do that yet :)

2024-08-29T22:00:16.944909Z

impressive amount of features with only one breaking change, that you haven't even implemented yet 🙂

Alex Miller (Clojure team) 2024-08-29T22:01:05.443229Z

we were surprised to discover a) that it existed and b) that it was widespread

2024-08-29T22:02:17.438869Z

did you look into the history of clojure's syntax? I wasn't around early enough, but how long was (. Class foo) the only interop (if ever?) and was (Class/foo) and Class/foo always concurrently supported?

borkdude 2024-08-29T22:02:18.966419Z

Just double-checked:

$ clj-kondo --lint - <<< '(System/out)'
<stdin>:1:1: error: Static fields should be referenced without parens unless they are intended as function calls

Alex Miller (Clojure team) 2024-08-29T22:02:49.498059Z

. has always been the bottom

Alex Miller (Clojure team) 2024-08-29T22:03:25.786789Z

really, it would have been better if the bottom syntax was explicit in some way

Alex Miller (Clojure team) 2024-08-29T22:03:29.570449Z

oops

2024-08-29T22:03:45.152119Z

@borkdude I can confirm this linter works in the wild, a coworker suppressed the warning for it and it was a great opportunity to advertise the new 1.12 features.

Alex Miller (Clojure team) 2024-08-29T22:04:14.113409Z

we hit walls multiple times with issues in .

❓ 1
borkdude 2024-08-29T22:04:29.968049Z

> a coworker suppressed the warning for it 😨

borkdude 2024-08-29T22:05:16.944489Z

@ambrosebs subtitle: with issues in .

Alex Miller (Clojure team) 2024-08-29T22:05:21.222719Z

that the qualifier is ignored in instance methods and down to . has allowed much broken code to "work"

🤔 1
Alex Miller (Clojure team) 2024-08-29T22:06:04.175609Z

user=> (. Integer java.util.Date/valueOf "1")
1

😂 1
😮 2
borkdude 2024-08-29T22:06:19.597789Z

it's also allowed syntax-quote to be used more sloppily in macros though ;)

➕ 1
2024-08-29T22:06:42.017389Z

yeah I think the ship has sailed on that one xD

Alex Miller (Clojure team) 2024-08-29T22:06:43.634469Z

I'm taking that as a negative :)

borkdude 2024-08-29T22:07:15.396889Z

I remember having to support the more sloppy interop in SCI for compatibility with JVM Clojure ;)

Alex Miller (Clojure team) 2024-08-29T22:07:52.182099Z

it prevented us from leveraging that space for several syntax things we tried b/c it was already allowing junk there

☠️ 1
2