Reader attaches collection metadata to data, not a with-meta form

The reader lowered ^meta on a vector/map/set literal to a runtime
(with-meta form meta) list, so read-string/edn of data with metadata
returned the form and lost the metadata. Attach it to the value instead,
as Clojure does; the analyzer re-emits (with-meta coll meta) for a
meta-carrying collection literal in code, so a literal still carries its
metadata at runtime and ^Type/^long arglist hints (consumed by
analyze-arity directly) are unaffected.

Also: pr honors *print-meta*, and clojure.walk/clojure.edn re-attach
metadata to the collections they rebuild (matches Clojure; a
metadata-driven config lib like aero relies on it).
This commit is contained in:
Yogthos 2026-06-24 12:04:36 -04:00
parent 091d2c4b62
commit 473d1002b7
8 changed files with 763 additions and 716 deletions

View file

@ -8,12 +8,15 @@
;; The reader yields set literals as a FORM ({:jolt/type :jolt/set :value [...]})
;; rather than a constructed set, so build the actual values, recursing into
;; maps/vectors/lists. (Lists stay lists — EDN never evaluates them as code.)
;; Re-attach the source value's metadata to each rebuilt collection — read-string
;; preserves reader metadata (^:ref […]) but this rebuild would otherwise drop it,
;; which a metadata-driven config lib (aero/integrant) relies on.
(defn- edn->value [opts x]
(cond
;; Reader FORMS are detected by :jolt/type tag, never by map? — strict map?
;; (correctly) excludes tagged structs, so the old (and (map? x) ...) guard
;; would skip them.
(= :jolt/set (get x :jolt/type)) (set (map (fn [v] (edn->value opts v)) (get x :value)))
(= :jolt/set (get x :jolt/type)) (with-meta (set (map (fn [v] (edn->value opts v)) (get x :value))) (meta x))
;; Tagged elements: a reader from the :readers opt wins, then the built-in
;; data readers (#uuid/#inst + registered); an unknown tag falls to the
;; :default opt fn (called with tag and value, as in Clojure) or throws.
@ -31,9 +34,9 @@
(get opts :default) ((get opts :default) tag-sym v)
:else (__read-tagged tag v)))
(map? x)
(into {} (map (fn [e] [(edn->value opts (key e)) (edn->value opts (val e))]) x))
(vector? x) (mapv (fn [v] (edn->value opts v)) x)
(seq? x) (map (fn [v] (edn->value opts v)) x)
(with-meta (into {} (map (fn [e] [(edn->value opts (key e)) (edn->value opts (val e))]) x)) (meta x))
(vector? x) (with-meta (mapv (fn [v] (edn->value opts v)) x) (meta x))
(seq? x) (with-meta (map (fn [v] (edn->value opts v)) x) (meta x))
:else x))
;; Private helper, NOT named read-string: an unqualified (read-string …) call