core: one alias store (jolt-ark); '.' classified as the special form it is

require-:as wrote the string-keyed :imports table (which resolution reads)
while ns-aliases read the symbol-keyed :aliases table (which nothing wrote)
— so (ns-aliases) was always empty and the alias fn had to write both as a
bridge. :aliases (alias-name string -> ns-name string) is THE store now:
require :as and the alias fn write it, both resolution paths read it first
(falling back to :imports for class imports, which is all that table holds
now), ns-unalias removes one entry, and ns-aliases presents Clojure's
{alias-symbol -> namespace object} shape built from it. ns-resolve's
qualified path goes through the same lookup.

Also: the coverage dashboard's last 'resolvable-not-interned' entry was '.'
— which (resolve '.) returns nil for on the JVM too; the tool now classifies
it as the special form it is, and that category reads ZERO.

7 new unified-alias spec rows (require/alias/ns-unalias round-trips through
both the resolution and introspection views); the white-box namespace test
tracks the accessor rename. Gate exit 0.
This commit is contained in:
Yogthos 2026-06-10 18:36:06 -04:00
parent 7c2d556dc5
commit e311018d55
6 changed files with 55 additions and 22 deletions

View file

@ -47,7 +47,7 @@
(assert (= "my.app" (ctx-current-ns ctx)) "ns with require sets current namespace")
# Alias should be registered
(let [ns (ctx-find-ns ctx "my.app")
aliased (ns-import-lookup ns "o")]
aliased (ns-alias-lookup ns "o")]
(assert (= "other.lib" aliased) "alias o -> other.lib registered")))
(print " passed")
@ -55,7 +55,7 @@
(let [ctx (fresh-ctx)]
(eval-form ctx @{} (parse-string "(require '[other.lib :as o])"))
(let [ns (ctx-find-ns ctx "user")
aliased (ns-import-lookup ns "o")]
aliased (ns-alias-lookup ns "o")]
(assert (= "other.lib" aliased) "standalone require registers alias")))
(print " passed")

View file

@ -53,3 +53,21 @@
"(do (require (quote [clojure.string :as s9])) (try (s9/join nil nil nil) (catch Exception e nil)) (s9/upper-case \"a\"))"]
["*ns* restored after throw" "\"user\""
"(do (require (quote [clojure.walk :as w9])) (try (w9/postwalk nil nil nil) (catch Exception e nil)) (str *ns*))"])
# Alias bookkeeping is unified (jolt-ark): one string-keyed :aliases store,
# read by resolution AND ns-aliases (which presents Clojure's
# {alias-symbol -> namespace} shape); :imports holds class imports only.
(defspec "namespaces / unified alias store"
["require :as registers the alias" "1"
"(do (require (quote [clojure.string :as st1])) (count (filter (fn [[a n]] (= (str a) \"st1\")) (ns-aliases))))"]
["aliased call resolves" "\"A\""
"(do (require (quote [clojure.string :as st2])) (st2/upper-case \"a\"))"]
["alias fn registers + resolves" "\"B\""
"(do (require (quote [clojure.string])) (alias (quote st3) (quote clojure.string)) (st3/upper-case \"b\"))"]
["alias fn visible to ns-aliases" "true"
"(do (require (quote [clojure.string])) (alias (quote st4) (quote clojure.string)) (pos? (count (filter (fn [[a n]] (= (str a) \"st4\")) (ns-aliases)))))"]
["ns-unalias removes both views" "[0 false]"
"(do (require (quote [clojure.string :as st5])) (ns-unalias (quote user) (quote st5)) [(count (filter (fn [[a n]] (= (str a) \"st5\")) (ns-aliases))) (boolean (resolve (quote st5/upper-case)))])"]
["ns-resolve through alias" "true"
"(do (require (quote [clojure.string :as st6])) (var? (ns-resolve (quote user) (quote st6/upper-case))))"]
["empty ns-aliases is a map" "true" "(map? (ns-aliases (quote clojure.core)))"])