https://clojurians.slack.com/archives/C06MAR553/p1724946046971999
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])let me check
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
let me create a minimal example
❯ 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❯ 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?))))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
a workaround can be found in the other issue
thanks for the quick response, created an issue https://github.com/clj-kondo/clj-kondo/issues/2384
Is there a linter for discouraged java static method call?
i.e. (foo/bar) where bar is a static method on class foo
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
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
Thanks
why would you want to discourage that?
Y'all decided that continuing to allow the junk was a necessary evil for compatibility reasons?
of course
it will break many programs if you don't do that
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
symbols in ns?
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
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?well, this is not about interop, but a similar case can be easily constructed with interop
and such things have become widespread because the compiler allowed this junk from 1.0 or earlier
(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
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"
@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")))oh huh, that's... yeah, i don't have a good answer for that one
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
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
(hopefully for good reasons, I don't remember them)
oh, it was very legitimate to not merge it, due to the prevalence of ` to auto-qualify symbols outside of macros in core
yeah, I guess clj-kondo could help spot "interop" garbage specifically, but ship sailed too long ago probably
I think a linter that fully expands code like eastwood might be more effective at finding these issues.
@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
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
I think he's not looking to discourage that in general, but a specific one
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?https://clojurians.slack.com/archives/C03S1KBA2/p1723737971842419
This was discussed in #clojure recently, the static field has precedence
thanks
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".
I guess there's no syntax to force it to return a method?
or is that where :param-tags comes in?
> 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.
we are working on updates to the Clojure reference docs, including the evaluation page, which will list things in precedence order
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
https://github.com/clojure/clojure-site/blob/1.12-doc-updates/content/reference/evaluation.adoc is the very much wip evaluation docs
notably, static fields were missing from this before 1.12
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
clj-kondo will warn about it since a while though
I can imagine a further phasing out with an annoying warning being the next step
we opted not to do that yet :)
impressive amount of features with only one breaking change, that you haven't even implemented yet 🙂
we were surprised to discover a) that it existed and b) that it was widespread
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?
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. has always been the bottom
really, it would have been better if the bottom syntax was explicit in some way
oops
@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.
we hit walls multiple times with issues in .
> a coworker suppressed the warning for it 😨
@ambrosebs subtitle: with issues in .
that the qualifier is ignored in instance methods and down to . has allowed much broken code to "work"
user=> (. Integer java.util.Date/valueOf "1")
1it's also allowed syntax-quote to be used more sloppily in macros though ;)
yeah I think the ship has sailed on that one xD
I'm taking that as a negative :)
I remember having to support the more sloppy interop in SCI for compatibility with JVM Clojure ;)
it prevented us from leveraging that space for several syntax things we tried b/c it was already allowing junk there