Say what every dart call is talking to
Forty-four DYNAMIC WARNINGs, and none left. Each one is ClojureDart saying it could not resolve a member statically and fell back to dynamic dispatch, because the target's type was lost — through `defonce`, through `await`, or through an `or`. A type hint at the site is the whole fix. One of them was not cosmetic, and is the reason to do this rather than silence the output. `frq.hiccup`'s scroll follow reads `.pixels` off a ScrollPosition; hinting the *binding* rather than the uses made a non-nullable cast out of a `when` that is nil whenever the controller has no clients, which is most of the time — "type 'Null' is not a subtype of type 'ScrollPosition'", thrown before the `(nil? pos)` two lines down could run. The hint belongs where the `or` has already ruled nil out, and the comment there says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a036bb4 parent: 6ca9b5a modified
flutter/src/frq/atproto/dart.cljd +4 -2 | @@ -26,13 +26,15 @@ | ||
| 26 | 26 | ;; resolveHandle path carries its query string, and Uri.https would escape |
| 27 | 27 | ;; the ? into the path. |
| 28 | 28 | (let [uri (Uri.parse (str "https://" host path)) |
| 29 | - req (await (if body (.postUrl client uri) (.getUrl client uri)))] | |
| 29 | + ^io/HttpClientRequest req (await (if body | |
| 30 | + (.postUrl ^io/HttpClient client uri) | |
| 31 | + (.getUrl ^io/HttpClient client uri)))] | |
| 30 | 32 | (.set (.-headers req) "user-agent" "frq") |
| 31 | 33 | (.set (.-headers req) "accept" "application/json") |
| 32 | 34 | (when body |
| 33 | 35 | (.set (.-headers req) "content-type" "application/json") |
| 34 | 36 | (.write req body)) |
| 35 | - (let [resp (await (.close req))] | |
| 37 | + (let [^io/HttpClientResponse resp (await (.close req))] | |
| 36 | 38 | (await (.join (.transform resp (.-decoder conv/utf8))))))) |
| 37 | 39 | |
| 38 | 40 | (defn ^:async resolve-handle |
| @@ -26,13 +26,15 @@ | |||
| 26 | ;; resolveHandle path carries its query string, and Uri.https would escape | 26 | ;; resolveHandle path carries its query string, and Uri.https would escape |
| 27 | ;; the ? into the path. | 27 | ;; the ? into the path. |
| 28 | (let [uri (Uri.parse (str "https://" host path)) | 28 | (let [uri (Uri.parse (str "https://" host path)) |
| 29 | - req (await (if body (.postUrl client uri) (.getUrl client uri)))] | 29 | + ^io/HttpClientRequest req (await (if body |
| 30 | + (.postUrl ^io/HttpClient client uri) | ||
| 31 | + (.getUrl ^io/HttpClient client uri)))] | ||
| 30 | (.set (.-headers req) "user-agent" "frq") | 32 | (.set (.-headers req) "user-agent" "frq") |
| 31 | (.set (.-headers req) "accept" "application/json") | 33 | (.set (.-headers req) "accept" "application/json") |
| 32 | (when body | 34 | (when body |
| 33 | (.set (.-headers req) "content-type" "application/json") | 35 | (.set (.-headers req) "content-type" "application/json") |
| 34 | (.write req body)) | 36 | (.write req body)) |
| 35 | - (let [resp (await (.close req))] | 37 | + (let [^io/HttpClientResponse resp (await (.close req))] |
| 36 | (await (.join (.transform resp (.-decoder conv/utf8))))))) | 38 | (await (.join (.transform resp (.-decoder conv/utf8))))))) |
| 37 | 39 | ||
| 38 | (defn ^:async resolve-handle | 40 | (defn ^:async resolve-handle |
modified
flutter/src/frq/crypto/dart.cljd +1 -1 | @@ -34,7 +34,7 @@ | ||
| 34 | 34 | |
| 35 | 35 | (defn- random-bytes [n] |
| 36 | 36 | (let [out (td/Uint8List. n)] |
| 37 | - (dotimes [i n] (aset out i (.nextInt rng 256))) | |
| 37 | + (dotimes [i n] (aset out i (.nextInt ^math/Random rng 256))) | |
| 38 | 38 | (vec out))) |
| 39 | 39 | |
| 40 | 40 | (defn- sha256 [bs] |
| @@ -34,7 +34,7 @@ | |||
| 34 | 34 | ||
| 35 | (defn- random-bytes [n] | 35 | (defn- random-bytes [n] |
| 36 | (let [out (td/Uint8List. n)] | 36 | (let [out (td/Uint8List. n)] |
| 37 | - (dotimes [i n] (aset out i (.nextInt rng 256))) | 37 | + (dotimes [i n] (aset out i (.nextInt ^math/Random rng 256))) |
| 38 | (vec out))) | 38 | (vec out))) |
| 39 | 39 | ||
| 40 | (defn- sha256 [bs] | 40 | (defn- sha256 [bs] |
modified
flutter/src/frq/hiccup.cljd +9 -3 | @@ -60,7 +60,7 @@ | ||
| 60 | 60 | |
| 61 | 61 | (defn- controller-for [k text] |
| 62 | 62 | (let [k (or k ::anonymous) |
| 63 | - c (or (get @controllers k) | |
| 63 | + ^m/TextEditingController c (or (get @controllers k) | |
| 64 | 64 | (let [c (m/TextEditingController .text (or text ""))] |
| 65 | 65 | (swap! controllers assoc k c) |
| 66 | 66 | c))] |
| @@ -186,12 +186,18 @@ | ||
| 186 | 186 | |
| 187 | 187 | After the frame, because the extent being scrolled to is the height of |
| 188 | 188 | content that has not been laid out yet at the point this is called." |
| 189 | - [k ctrl token] | |
| 189 | + [k ^m/ScrollController ctrl token] | |
| 190 | 190 | (let [mark (get @scroll-marks k ::fresh) |
| 191 | + ;; Not hinted here: a controller with no clients has no position, so | |
| 192 | + ;; this is nil as often as not, and a non-nullable hint on the binding | |
| 193 | + ;; is a cast that fails before `(nil? pos)` below ever gets to run. | |
| 194 | + ;; The hint goes on the uses instead, where the `or` has already ruled | |
| 195 | + ;; nil out. | |
| 191 | 196 | pos (when (.-hasClients ctrl) (.-position ctrl)) |
| 192 | 197 | at-end (or (= mark ::fresh) |
| 193 | 198 | (nil? pos) |
| 194 | - (>= (.-pixels pos) (- (.-maxScrollExtent pos) 24.0)))] | |
| 199 | + (>= (.-pixels ^m/ScrollPosition pos) | |
| 200 | + (- (.-maxScrollExtent ^m/ScrollPosition pos) 24.0)))] | |
| 195 | 201 | (when (or (not= mark token) at-end) |
| 196 | 202 | (swap! scroll-marks assoc k token) |
| 197 | 203 | (.addPostFrameCallback |
| @@ -60,7 +60,7 @@ | |||
| 60 | 60 | ||
| 61 | (defn- controller-for [k text] | 61 | (defn- controller-for [k text] |
| 62 | (let [k (or k ::anonymous) | 62 | (let [k (or k ::anonymous) |
| 63 | - c (or (get @controllers k) | 63 | + ^m/TextEditingController c (or (get @controllers k) |
| 64 | (let [c (m/TextEditingController .text (or text ""))] | 64 | (let [c (m/TextEditingController .text (or text ""))] |
| 65 | (swap! controllers assoc k c) | 65 | (swap! controllers assoc k c) |
| 66 | c))] | 66 | c))] |
| @@ -186,12 +186,18 @@ | |||
| 186 | 186 | ||
| 187 | After the frame, because the extent being scrolled to is the height of | 187 | After the frame, because the extent being scrolled to is the height of |
| 188 | content that has not been laid out yet at the point this is called." | 188 | content that has not been laid out yet at the point this is called." |
| 189 | - [k ctrl token] | 189 | + [k ^m/ScrollController ctrl token] |
| 190 | (let [mark (get @scroll-marks k ::fresh) | 190 | (let [mark (get @scroll-marks k ::fresh) |
| 191 | + ;; Not hinted here: a controller with no clients has no position, so | ||
| 192 | + ;; this is nil as often as not, and a non-nullable hint on the binding | ||
| 193 | + ;; is a cast that fails before `(nil? pos)` below ever gets to run. | ||
| 194 | + ;; The hint goes on the uses instead, where the `or` has already ruled | ||
| 195 | + ;; nil out. | ||
| 191 | pos (when (.-hasClients ctrl) (.-position ctrl)) | 196 | pos (when (.-hasClients ctrl) (.-position ctrl)) |
| 192 | at-end (or (= mark ::fresh) | 197 | at-end (or (= mark ::fresh) |
| 193 | (nil? pos) | 198 | (nil? pos) |
| 194 | - (>= (.-pixels pos) (- (.-maxScrollExtent pos) 24.0)))] | 199 | + (>= (.-pixels ^m/ScrollPosition pos) |
| 200 | + (- (.-maxScrollExtent ^m/ScrollPosition pos) 24.0)))] | ||
| 195 | (when (or (not= mark token) at-end) | 201 | (when (or (not= mark token) at-end) |
| 196 | (swap! scroll-marks assoc k token) | 202 | (swap! scroll-marks assoc k token) |
| 197 | (.addPostFrameCallback | 203 | (.addPostFrameCallback |
modified
flutter/src/frq/io/dart.cljd +2 -2 | @@ -109,7 +109,7 @@ | ||
| 109 | 109 | :config-dir (fn [] dir) |
| 110 | 110 | :file-exists? (fn [p] (.existsSync (io/File. p))) |
| 111 | 111 | :directory? (fn [p] (.existsSync (io/Directory. p))) |
| 112 | - :list-dir (fn [p] (mapv #(.-path %) (.listSync (io/Directory. p)))) | |
| 112 | + :list-dir (fn [p] (mapv #(.-path ^io/FileSystemEntity %) (.listSync (io/Directory. p)))) | |
| 113 | 113 | :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true)) |
| 114 | 114 | :delete-file! (fn [p] (.deleteSync (io/File. p))) |
| 115 | 115 | :slurp slurp* |
| @@ -118,5 +118,5 @@ | ||
| 118 | 118 | :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s)))) |
| 119 | 119 | :utf8-string utf8-string |
| 120 | 120 | :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now)))) |
| 121 | - :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed uptime)))) | |
| 121 | + :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed ^Stopwatch uptime)))) | |
| 122 | 122 | :local-offset-seconds local-offset-seconds})) |
| @@ -109,7 +109,7 @@ | |||
| 109 | :config-dir (fn [] dir) | 109 | :config-dir (fn [] dir) |
| 110 | :file-exists? (fn [p] (.existsSync (io/File. p))) | 110 | :file-exists? (fn [p] (.existsSync (io/File. p))) |
| 111 | :directory? (fn [p] (.existsSync (io/Directory. p))) | 111 | :directory? (fn [p] (.existsSync (io/Directory. p))) |
| 112 | - :list-dir (fn [p] (mapv #(.-path %) (.listSync (io/Directory. p)))) | 112 | + :list-dir (fn [p] (mapv #(.-path ^io/FileSystemEntity %) (.listSync (io/Directory. p)))) |
| 113 | :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true)) | 113 | :mkdirs! (fn [p] (.createSync (io/Directory. p) .recursive true)) |
| 114 | :delete-file! (fn [p] (.deleteSync (io/File. p))) | 114 | :delete-file! (fn [p] (.deleteSync (io/File. p))) |
| 115 | :slurp slurp* | 115 | :slurp slurp* |
| @@ -118,5 +118,5 @@ | |||
| 118 | :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s)))) | 118 | :utf8-bytes (fn [s] (vec (.encode conv/utf8 (str s)))) |
| 119 | :utf8-string utf8-string | 119 | :utf8-string utf8-string |
| 120 | :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now)))) | 120 | :wall-nanos (fn [] (* 1000 (.-microsecondsSinceEpoch (DateTime/now)))) |
| 121 | - :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed uptime)))) | 121 | + :mono-nanos (fn [] (* 1000 (.-inMicroseconds (.-elapsed ^Stopwatch uptime)))) |
| 122 | :local-offset-seconds local-offset-seconds})) | 122 | :local-offset-seconds local-offset-seconds})) |
modified
flutter/src/frq/main.cljd +2 -2 | @@ -558,7 +558,7 @@ | ||
| 558 | 558 | ;; build, so nothing can catch them, and what they leave is a blank screen |
| 559 | 559 | ;; and an empty log. Flutter routes them here instead. |
| 560 | 560 | (set! (.-onError m/FlutterError) |
| 561 | - (fn [details] | |
| 561 | + (fn [^m/FlutterErrorDetails details] | |
| 562 | 562 | (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details))) |
| 563 | 563 | (m/debugPrint (str "frq: LIBRARY " (.-library details) |
| 564 | 564 | " CONTEXT " (.-context details))))) |
| @@ -573,7 +573,7 @@ | ||
| 573 | 573 | ;; when to ask and remembers the answer; only the awaiting is here. |
| 574 | 574 | (profile/install-fetch! |
| 575 | 575 | (fn [actor] |
| 576 | - (.then (atproto/fetch (profile/profile-req actor)) | |
| 576 | + (.then ^async/Future (atproto/fetch (profile/profile-req actor)) | |
| 577 | 577 | (fn [body] (profile/deliver-profile! actor body) nil) |
| 578 | 578 | .onError (fn [_ _] (profile/deliver-profile! actor nil) nil)))) |
| 579 | 579 | ;; Before any widget is built: a cell that changes before its watch is on |
| @@ -558,7 +558,7 @@ | |||
| 558 | ;; build, so nothing can catch them, and what they leave is a blank screen | 558 | ;; build, so nothing can catch them, and what they leave is a blank screen |
| 559 | ;; and an empty log. Flutter routes them here instead. | 559 | ;; and an empty log. Flutter routes them here instead. |
| 560 | (set! (.-onError m/FlutterError) | 560 | (set! (.-onError m/FlutterError) |
| 561 | - (fn [details] | 561 | + (fn [^m/FlutterErrorDetails details] |
| 562 | (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details))) | 562 | (m/debugPrint (str "frq: FLUTTER ERROR " (.-exception details))) |
| 563 | (m/debugPrint (str "frq: LIBRARY " (.-library details) | 563 | (m/debugPrint (str "frq: LIBRARY " (.-library details) |
| 564 | " CONTEXT " (.-context details))))) | 564 | " CONTEXT " (.-context details))))) |
| @@ -573,7 +573,7 @@ | |||
| 573 | ;; when to ask and remembers the answer; only the awaiting is here. | 573 | ;; when to ask and remembers the answer; only the awaiting is here. |
| 574 | (profile/install-fetch! | 574 | (profile/install-fetch! |
| 575 | (fn [actor] | 575 | (fn [actor] |
| 576 | - (.then (atproto/fetch (profile/profile-req actor)) | 576 | + (.then ^async/Future (atproto/fetch (profile/profile-req actor)) |
| 577 | (fn [body] (profile/deliver-profile! actor body) nil) | 577 | (fn [body] (profile/deliver-profile! actor body) nil) |
| 578 | .onError (fn [_ _] (profile/deliver-profile! actor nil) nil)))) | 578 | .onError (fn [_ _] (profile/deliver-profile! actor nil) nil)))) |
| 579 | ;; Before any widget is built: a cell that changes before its watch is on | 579 | ;; Before any widget is built: a cell that changes before its watch is on |
modified
flutter/src/frq/net/dart.cljd +2 -2 | @@ -67,8 +67,8 @@ | ||
| 67 | 67 | .cancelOnError true)) |
| 68 | 68 | sock)) |
| 69 | 69 | |
| 70 | -(defn send-line! [sock line] | |
| 70 | +(defn send-line! [^io/Socket sock line] | |
| 71 | 71 | (.write sock (str line "\r\n"))) |
| 72 | 72 | |
| 73 | -(defn close! [sock] | |
| 73 | +(defn close! [^io/Socket sock] | |
| 74 | 74 | (try (.destroy sock) (catch Exception _ nil))) |
| @@ -67,8 +67,8 @@ | |||
| 67 | .cancelOnError true)) | 67 | .cancelOnError true)) |
| 68 | sock)) | 68 | sock)) |
| 69 | 69 | ||
| 70 | -(defn send-line! [sock line] | 70 | +(defn send-line! [^io/Socket sock line] |
| 71 | (.write sock (str line "\r\n"))) | 71 | (.write sock (str line "\r\n"))) |
| 72 | 72 | ||
| 73 | -(defn close! [sock] | 73 | +(defn close! [^io/Socket sock] |
| 74 | (try (.destroy sock) (catch Exception _ nil))) | 74 | (try (.destroy sock) (catch Exception _ nil))) |
modified
flutter/src/frq/oauth/dart.cljd +4 -4 | @@ -54,8 +54,8 @@ | ||
| 54 | 54 | (try |
| 55 | 55 | (on-url (core/login-url broker handle (str "http://127.0.0.1:" port))) |
| 56 | 56 | (.listen server |
| 57 | - (fn [req] | |
| 58 | - (let [resp (.-response req)] | |
| 57 | + (fn [^io/HttpRequest req] | |
| 58 | + (let [^io/HttpResponse resp (.-response req)] | |
| 59 | 59 | (if (= "POST" (.-method req)) |
| 60 | 60 | ;; The fragment the page posted back. A POST carrying |
| 61 | 61 | ;; nothing usable is not the end of the wait — the real |
| @@ -77,10 +77,10 @@ | ||
| 77 | 77 | .onDone |
| 78 | 78 | (fn [] |
| 79 | 79 | (let [tokens (try (core/tokens-of |
| 80 | - (.trim (host/utf8-string @chunks))) | |
| 80 | + (.trim ^String (host/utf8-string @chunks))) | |
| 81 | 81 | (catch Object _ nil))] |
| 82 | 82 | (.write resp (if tokens "ok" "bad payload")) |
| 83 | - (.then (.close resp) | |
| 83 | + (.then ^async/Future (.close resp) | |
| 84 | 84 | (fn [_] |
| 85 | 85 | (when (and tokens |
| 86 | 86 | (not (.-isCompleted done))) |
| @@ -54,8 +54,8 @@ | |||
| 54 | (try | 54 | (try |
| 55 | (on-url (core/login-url broker handle (str "http://127.0.0.1:" port))) | 55 | (on-url (core/login-url broker handle (str "http://127.0.0.1:" port))) |
| 56 | (.listen server | 56 | (.listen server |
| 57 | - (fn [req] | 57 | + (fn [^io/HttpRequest req] |
| 58 | - (let [resp (.-response req)] | 58 | + (let [^io/HttpResponse resp (.-response req)] |
| 59 | (if (= "POST" (.-method req)) | 59 | (if (= "POST" (.-method req)) |
| 60 | ;; The fragment the page posted back. A POST carrying | 60 | ;; The fragment the page posted back. A POST carrying |
| 61 | ;; nothing usable is not the end of the wait — the real | 61 | ;; nothing usable is not the end of the wait — the real |
| @@ -77,10 +77,10 @@ | |||
| 77 | .onDone | 77 | .onDone |
| 78 | (fn [] | 78 | (fn [] |
| 79 | (let [tokens (try (core/tokens-of | 79 | (let [tokens (try (core/tokens-of |
| 80 | - (.trim (host/utf8-string @chunks))) | 80 | + (.trim ^String (host/utf8-string @chunks))) |
| 81 | (catch Object _ nil))] | 81 | (catch Object _ nil))] |
| 82 | (.write resp (if tokens "ok" "bad payload")) | 82 | (.write resp (if tokens "ok" "bad payload")) |
| 83 | - (.then (.close resp) | 83 | + (.then ^async/Future (.close resp) |
| 84 | (fn [_] | 84 | (fn [_] |
| 85 | (when (and tokens | 85 | (when (and tokens |
| 86 | (not (.-isCompleted done))) | 86 | (not (.-isCompleted done))) |
modified
flutter/src/frq/theme.cljd +1 -1 | @@ -51,7 +51,7 @@ | ||
| 51 | 51 | (def dim |
| 52 | 52 | "`:dim-label` is libcosmic's caption class, which is the body colour stepped |
| 53 | 53 | back rather than a colour of its own." |
| 54 | - (.withValues on-bg .alpha 0.7)) | |
| 54 | + (.withValues ^m/Color on-bg .alpha 0.7)) | |
| 55 | 55 | |
| 56 | 56 | (def brightness (if c/dark? m/Brightness.dark m/Brightness.light)) |
| 57 | 57 | |
| @@ -51,7 +51,7 @@ | |||
| 51 | (def dim | 51 | (def dim |
| 52 | "`:dim-label` is libcosmic's caption class, which is the body colour stepped | 52 | "`:dim-label` is libcosmic's caption class, which is the body colour stepped |
| 53 | back rather than a colour of its own." | 53 | back rather than a colour of its own." |
| 54 | - (.withValues on-bg .alpha 0.7)) | 54 | + (.withValues ^m/Color on-bg .alpha 0.7)) |
| 55 | 55 | ||
| 56 | (def brightness (if c/dark? m/Brightness.dark m/Brightness.light)) | 56 | (def brightness (if c/dark? m/Brightness.dark m/Brightness.light)) |
| 57 | 57 | ||