(ns com.tailrecursion.blacktide.annotations
"Discover compiler namespaces and register their language and pass contracts.
New source files participate in checking without a second handwritten list."
(:require [clojure.java.io :as io]
[typed.clojure]
[clojure.core.typed :as ct]
[com.tailrecursion.nanopass.pipeline :as pipeline]
[com.tailrecursion.nanopass :as np]))
(def compiler-namespaces
(->> (cons (io/file "src/com/tailrecursion/blacktide/compiler.clj")
(file-seq (io/file "src/com/tailrecursion/blacktide/compiler")))
(filter #(.endsWith (.getName ^java.io.File %) ".clj"))
(map (fn [file]
(with-open [reader (java.io.PushbackReader. (io/reader file))]
(binding [*read-eval* false] (second (read reader))))))
sort vec))
(doseq [namespace compiler-namespaces] (require namespace))
(defmacro annotate-compiler []
(let [vars (mapcat (comp vals ns-interns) compiler-namespaces)
names (fn [key]
(->> vars (filter #(key (meta %))) (map #(symbol %)) sort))]
`(do (np/ann-languages ~@(names ::np/language))
(np/ann-passes ~@(names ::np/signature)))))
(annotate-compiler)
(np/ann-passes pipeline/map-value)
;; Var.toSymbol delegates to Symbol.create; its successful result is non-null.
(ct/non-nil-return clojure.lang.Var/toSymbol :all)
;; Java reflection guarantees these successful results are non-null.
(ct/non-nil-return java.lang.Class/getName :all)
(ct/non-nil-return java.lang.Object/getClass :all)
(ct/non-nil-return java.lang.Class/getConstructors :all)
(ct/non-nil-return java.lang.Class/getMethods :all)
(ct/non-nil-return java.lang.reflect.Method/getName :all)
(ct/non-nil-return java.lang.reflect.Executable/getDeclaringClass :all)
(ct/non-nil-return java.lang.reflect.Executable/getParameterTypes :all)
(ct/non-nil-return java.lang.reflect.Constructor/getDeclaringClass :all)
(ct/non-nil-return java.lang.reflect.Method/getReturnType :all)
(ct/non-nil-return java.lang.reflect.Method/getDeclaringClass :all)
(ct/non-nil-return java.lang.reflect.Method/getParameterTypes :all)