No vars found in this namespace.
ClojureScript-only AST op handlers. Mirrors skeptic.analysis.annotate.jvm:
each handler recurses children via the step trampoline and assigns a :type
derived from the cljs :tag (translated via av/cljs-tag->type), falling
back to Dyn when the tag is any or unrecognized.
ClojureScript-only AST op handlers. Mirrors `skeptic.analysis.annotate.jvm`: each handler recurses children via the step trampoline and assigns a `:type` derived from the cljs `:tag` (translated via `av/cljs-tag->type`), falling back to `Dyn` when the tag is `any` or unrecognized.
Trampoline for the annotate helpers.
End state (Phase 7): every annotate helper has signature
(Ctx, AnnotatedNode) -> Step, where Step is the sum type below.
[:done annotated-node]
[:call helper-fn ctx node k]
run is a loop over a heap-allocated stack of continuations. During
the Phase 2-6 migration window, some helpers still return plain
AnnotatedNode values; step? / normalize plus the [:done v]
auto-wrap branch inside run carry those through. All three are
migration-window cruft and are deleted in Phase 7 contraction.
Trampoline for the annotate helpers.
End state (Phase 7): every annotate helper has signature
`(Ctx, AnnotatedNode) -> Step`, where `Step` is the sum type below.
[:done annotated-node]
[:call helper-fn ctx node k]
`run` is a loop over a heap-allocated stack of continuations. During
the Phase 2-6 migration window, some helpers still return plain
AnnotatedNode values; `step?` / `normalize` plus the [:done v]
auto-wrap branch inside `run` carry those through. All three are
migration-window cruft and are deleted in Phase 7 contraction.Shared tools.analyzer child listing and preorder traversal. Delegates to clojure.tools.analyzer.ast so all callers stay aligned.
Shared tools.analyzer child listing and preorder traversal. Delegates to clojure.tools.analyzer.ast so all callers stay aligned.
Per-form descriptor extraction for Plumatic source forms stored in the
project-wide form-refs IdentityHashMap (built once in
skeptic.checking.pipeline/project-state, threaded through bridge ctx).
Pipeline puts raw (s/defn ...) / (s/def ...) / (s/defschema ...) lists
into the map; bridge.clj normalizes them via raw->descriptor on demand.
Pipeline-side filtering (only Plumatic-discovered Vars get stored) means a head-name match is sufficient at consumer time — alias resolution already happened in skeptic.schema.discovery. Heads with name 'defn' / 'def' / 'defschema' under any alias of schema.core resolve to the matching extractor.
Descriptor shapes: :defn → {:kind :defn :output-form form :arglists {k {:input-forms [...]}}} :def → {:kind :def :schema-form form} :defschema → {:kind :defschema :schema-form form}
Per-form descriptor extraction for Plumatic source forms stored in the
project-wide form-refs IdentityHashMap (built once in
`skeptic.checking.pipeline/project-state`, threaded through bridge ctx).
Pipeline puts raw `(s/defn ...)` / `(s/def ...)` / `(s/defschema ...)` lists
into the map; bridge.clj normalizes them via `raw->descriptor` on demand.
Pipeline-side filtering (only Plumatic-discovered Vars get stored) means a
head-name match is sufficient at consumer time — alias resolution already
happened in skeptic.schema.discovery. Heads with name 'defn' / 'def' /
'defschema' under any alias of schema.core resolve to the matching
extractor.
Descriptor shapes:
:defn → {:kind :defn :output-form form :arglists {k {:input-forms [...]}}}
:def → {:kind :def :schema-form form}
:defschema → {:kind :defschema :schema-form form}Native typing for clojure.lang.Numbers static calls and selected clojure.core invokes. Toolchain reference (Clojure 1.11.1, tools.analyzer.jvm 1.2.3):
Native typing for clojure.lang.Numbers static calls and selected clojure.core invokes. Toolchain reference (Clojure 1.11.1, tools.analyzer.jvm 1.2.3): - Numbers: inc/dec argc 1; add/multiply argc 2 (n-ary +/* is nested static calls); minus argc 1 or 2; isPos/isNeg argc 1 -> bool. - Invoke (native-fn-dict): (+) (+ x) (*) (* x); str; format; even? odd?; inc when not inlined. - Literal (+ 1 2 3) never yields top-level :invoke; test 3-arg + with ((resolve '+) 1 2 3).
Shared registry of clojure.core predicates that Skeptic can type. Two consumers:
Shared registry of clojure.core predicates that Skeptic can type.
Two consumers:
- native_fns: each predicate is typed Dyn -> Bool when invoked
- bridge admission: (s/pred f) and Malli's bare-predicate-as-schema
both convert to the predicate's witness type.JDK 9+/Leiningen-bootclasspath workaround for clojure.instant.
Leiningen's launcher appends leiningen-<v>-standalone.jar to the JVM's
-Xbootclasspath/a: (unless LEIN_USE_BOOTCLASSPATH=no is set in the
environment — which we cannot require of end users). Clojure ends up
loaded by the JVM bootstrap classloader. When any code transitively
requires clojure.instant, its AOT-compiled <clinit> pushes
clojure.lang.Compiler/LOADER = (.getClassLoader thisLoadingFn) = null
(bootstrap). RT.classForNameNonLoading("java.sql.Timestamp") then
calls Class.forName("java.sql.Timestamp", false, null) against the
bootstrap loader. On JDK 9+, java.sql is defined to
PlatformClassLoader, NOT bootstrap, so the call raises
ClassNotFoundException. The four sibling java.util.* imports just
before it succeed because java.util lives in java.base, which IS in
bootstrap.
Skeptic transitively pulls clojure.instant via cljs.analyzer.api →
cljs.tagged-literals → cljs.instant → clojure.instant, so this triggers
at plugin-load time and blocks the skeptic task from running at all.
Fix: clojure.lang.RT/classForName bytecode short-circuits to
DynamicClassLoader/findInMemoryClass BEFORE invoking
Class.forName(name, false, null) when the passed loader is not itself a
DynamicClassLoader. That short-circuit reads
DynamicClassLoader/classCache, a static ConcurrentHashMap normally
used by Clojure's in-memory class generation. We pre-populate it with
"java.sql.Timestamp" → java.sql.Timestamp.class, then eagerly
(require 'clojure.instant). Its <clinit> runs immediately, hits our
cache entry through the short-circuit, and never falls through to the
broken Class.forName(name, false, null) path. The namespace is
registered in *loaded-libs*, so the later transitive require from
cljs.instant is a no-op and the unsafe <clinit> does not re-run.
This namespace is intentionally required FIRST by
skeptic.cljs.analyzer-driver — the earliest namespace on the cljs
intake chain — so the cache is populated before any cljs require can
drag clojure.instant in.
JDK 9+/Leiningen-bootclasspath workaround for `clojure.instant`.
Leiningen's launcher appends `leiningen-<v>-standalone.jar` to the JVM's
`-Xbootclasspath/a:` (unless LEIN_USE_BOOTCLASSPATH=no is set in the
environment — which we cannot require of end users). Clojure ends up
loaded by the JVM bootstrap classloader. When any code transitively
requires `clojure.instant`, its AOT-compiled `<clinit>` pushes
`clojure.lang.Compiler/LOADER = (.getClassLoader thisLoadingFn) = null`
(bootstrap). `RT.classForNameNonLoading("java.sql.Timestamp")` then
calls `Class.forName("java.sql.Timestamp", false, null)` against the
bootstrap loader. On JDK 9+, `java.sql` is defined to
`PlatformClassLoader`, NOT bootstrap, so the call raises
`ClassNotFoundException`. The four sibling `java.util.*` imports just
before it succeed because `java.util` lives in `java.base`, which IS in
bootstrap.
Skeptic transitively pulls clojure.instant via cljs.analyzer.api →
cljs.tagged-literals → cljs.instant → clojure.instant, so this triggers
at plugin-load time and blocks the `skeptic` task from running at all.
Fix: `clojure.lang.RT/classForName` bytecode short-circuits to
`DynamicClassLoader/findInMemoryClass` BEFORE invoking
`Class.forName(name, false, null)` when the passed loader is not itself a
`DynamicClassLoader`. That short-circuit reads
`DynamicClassLoader/classCache`, a static `ConcurrentHashMap` normally
used by Clojure's in-memory class generation. We pre-populate it with
`"java.sql.Timestamp" → java.sql.Timestamp.class`, then eagerly
`(require 'clojure.instant)`. Its `<clinit>` runs immediately, hits our
cache entry through the short-circuit, and never falls through to the
broken `Class.forName(name, false, null)` path. The namespace is
registered in `*loaded-libs*`, so the later transitive require from
cljs.instant is a no-op and the unsafe `<clinit>` does not re-run.
This namespace is intentionally required FIRST by
`skeptic.cljs.analyzer-driver` — the earliest namespace on the cljs
intake chain — so the cache is populated before any cljs require can
drag clojure.instant in.No vars found in this namespace.
Source discovery for cljs/cljc files in a deps.edn project. Reuses
skeptic.cli.paths/discover-paths to obtain the project's resolved
source paths from clojure.tools.deps/create-basis, then walks them
for cljs/cljc files. discover-paths returns paths verbatim from the
deps.edn (relative strings like "src"); they are absolutized against
root here before walking.
Source discovery for cljs/cljc files in a deps.edn project. Reuses `skeptic.cli.paths/discover-paths` to obtain the project's resolved source paths from `clojure.tools.deps/create-basis`, then walks them for cljs/cljc files. `discover-paths` returns paths verbatim from the deps.edn (relative strings like "src"); they are absolutized against `root` here before walking.
Walk a collection of source roots and partition the matching files into cljs and cljc buckets. Paths that do not exist are skipped silently; paths that point at a regular file are included if their extension matches.
Walk a collection of source roots and partition the matching files into cljs and cljc buckets. Paths that do not exist are skipped silently; paths that point at a regular file are included if their extension matches.
Source discovery for cljs/cljc files in a Leiningen project. Reads :source-paths and :test-paths from the project map, plus any cljsbuild build :source-paths if present, and walks them for cljs/cljc files.
Source discovery for cljs/cljc files in a Leiningen project. Reads :source-paths and :test-paths from the project map, plus any cljsbuild build :source-paths if present, and walks them for cljs/cljc files.
Source discovery for cljs/cljc files in a shadow-cljs project. Reads
shadow-cljs.edn at root as plain EDN and walks the top-level
:source-paths key for cljs/cljc files.
Source discovery for cljs/cljc files in a shadow-cljs project. Reads shadow-cljs.edn at `root` as plain EDN and walks the top-level :source-paths key for cljs/cljc files.
deps.edn-side entrypoint for Skeptic. Invoked as: clojure -M:skeptic [flags] (calls -main) clojure -X:skeptic (calls run with EDN args) Library code lives elsewhere; this namespace only handles CLI parsing, source-path discovery, and the call into skeptic.core/check-project.
deps.edn-side entrypoint for Skeptic. Invoked as: clojure -M:skeptic [flags] (calls -main) clojure -X:skeptic (calls run with EDN args) Library code lives elsewhere; this namespace only handles CLI parsing, source-path discovery, and the call into skeptic.core/check-project.
Shared CLI option vector and parser for both the Leiningen plugin (leiningen.skeptic) and the deps.edn entrypoint (skeptic.cli.main). Keys produced here land directly in the opts map consumed by skeptic.core/check-project.
Shared CLI option vector and parser for both the Leiningen plugin (leiningen.skeptic) and the deps.edn entrypoint (skeptic.cli.main). Keys produced here land directly in the opts map consumed by skeptic.core/check-project.
Source-path discovery for the deps.edn entrypoint. Reads the project's deps.edn through the official tools.deps API and returns the merged :paths vector for the given alias selection. The Leiningen plugin does not use this; it gets paths from the lein project map.
Source-path discovery for the deps.edn entrypoint. Reads the project's deps.edn through the official tools.deps API and returns the merged :paths vector for the given alias selection. The Leiningen plugin does not use this; it gets paths from the lein project map.
cljs analyzer entrypoints via a file-local cljs compiler state.
Each cljs source file is read and analyzed inside one non-leaking compiler
state. This lets cljs.analyzer.api/forms-seq read with the analyzer's
current cljs namespace, cljs data readers, and alias map, then immediately
analyzes each top-level form against the same state. The file pass loads
macros but does not analyze required cljs dependencies; the state is local
to the source file and is not threaded through the checker.
cljs ASTs carry :type on :binding/:fn-method nodes that conflicts
with skeptic's :type slot (SemanticType), and :binding nodes lack
:form. analyze-form strips and synthesizes via normalize-cljs-node
so the skeptic annotate pipeline starts from a clean shape.
cljs analyzer entrypoints via a file-local cljs compiler state. Each cljs source file is read and analyzed inside one non-leaking compiler state. This lets `cljs.analyzer.api/forms-seq` read with the analyzer's current cljs namespace, cljs data readers, and alias map, then immediately analyzes each top-level form against the same state. The file pass loads macros but does not analyze required cljs dependencies; the state is local to the source file and is not threaded through the checker. cljs ASTs carry `:type` on `:binding`/`:fn-method` nodes that conflicts with skeptic's `:type` slot (SemanticType), and `:binding` nodes lack `:form`. `analyze-form` strips and synthesizes via `normalize-cljs-node` so the skeptic annotate pipeline starts from a clean shape.
sci-sandboxed interpretation of post-macroexpansion Plumatic Schema forms collected from cljs ASTs.
The sci context exposes schema.core as the only allowlisted user
namespace; sci interprets the form by applying Plumatic's real JVM
functions and returns real Plumatic Schema records. Symbols outside
the allowlist (and sci's default clojure.core surface) cannot be
resolved, so the interpreter cannot execute arbitrary user code.
sci-sandboxed interpretation of post-macroexpansion Plumatic Schema forms collected from cljs ASTs. The sci context exposes `schema.core` as the only allowlisted user namespace; sci interprets the form by applying Plumatic's real JVM functions and returns real Plumatic Schema records. Symbols outside the allowlist (and sci's default clojure.core surface) cannot be resolved, so the interpreter cannot execute arbitrary user code.
Dependency ordering for cljs/cljc source files. Returns files in an order where each file's project-local :require'd dependencies appear before it. When a cycle blocks standard topo progress, the next pick is chosen by tiebreaker: nss without :require-macros / :use-macros first, then fewest :requires, then ns-sym alphabetical.
Dependency ordering for cljs/cljc source files. Returns files in an order where each file's project-local :require'd dependencies appear before it. When a cycle blocks standard topo progress, the next pick is chosen by tiebreaker: nss without :require-macros / :use-macros first, then fewest :requires, then ns-sym alphabetical.
No vars found in this namespace.
Malli intake stream. Hermetic from Plumatic by construction:
[:=> ...]) and rejects non-function shapes that the
bridge admits as Dyn (e.g. :map Var-meta on a value Var).Malli intake stream. Hermetic from Plumatic by construction: - reads :malli/schema Var-meta directly (a Malli-only convention; Plumatic never writes :malli/schema). - projects (malli.core/function-schemas) for compile-time-registered m/=> and mx/defn entries. Does NOT call malli.instrument/collect!, which validates each schema as a function schema (`[:=> ...]`) and rejects non-function shapes that the bridge admits as Dyn (e.g. `:map` Var-meta on a value Var).
ClojureScript admission for Malli function schemas.
Mirrors skeptic.malli-spec.collect/ns-malli-spec-results for cljs source
files. Two channels:
:def AST has [:var :info :meta]
holding the source-form metadata. The :malli/schema key on that map
holds the literal Malli spec vector (self-evaluating; no quote wrapper).(malli.core/=> sym SPEC) macroexpands to a
top-level :op :do whose form is
(do (malli.core/-register-function-schema! 'ns 'sym SPEC nil :cljs id) 'ns/sym).
Classification walks the outer :form directly.No cenv reads, no caller-managed compiler state.
ClojureScript admission for Malli function schemas. Mirrors `skeptic.malli-spec.collect/ns-malli-spec-results` for cljs source files. Two channels: - Var-meta channel: each per-form `:def` AST has `[:var :info :meta]` holding the source-form metadata. The `:malli/schema` key on that map holds the literal Malli spec vector (self-evaluating; no quote wrapper). - Registration channel: `(malli.core/=> sym SPEC)` macroexpands to a top-level `:op :do` whose form is `(do (malli.core/-register-function-schema! 'ns 'sym SPEC nil :cljs id) 'ns/sym)`. Classification walks the outer `:form` directly. No cenv reads, no caller-managed compiler state.
Coerce a Clojure value to a JSON-safe form for the --debug wire-tap. The only job is to make the value printable through clojure.data.json. No type dispatch, no projection, no filtering. The value is printed by Clojure's own printer under locked bindings — the string it produces is what ships.
Coerce a Clojure value to a JSON-safe form for the --debug wire-tap. The only job is to make the value printable through clojure.data.json. No type dispatch, no projection, no filtering. The value is printed by Clojure's own printer under locked bindings — the string it produces is what ships.
ClojureScript admission for Plumatic Schema declarations.
Mirrors skeptic.schema.collect/ns-schema-results for cljs source files.
Operates on per-form analyzed ASTs from skeptic.cljs.analyzer-driver.
Reads alias requires from the parsed ns AST (output of
cljs.analyzer.api/parse-ns) and Var metadata directly from each
:def AST node's [:var :info :meta] slot — no cenv reads, no caller-
managed compiler state.
Three top-level shapes are recognized:
s/def :op :let, single binding output-schema__*__auto__,
inner :def at [:body :ret].s/defschema :op :def, :init :form starts with vary-meta.s/defn :op :let, multi-binding outer let with output-schema*
and input-schema* gensyms.The cljs :meta :schema value is a symbolic form (the cljs analyzer
does not evaluate JVM-side); resolution to a real Schema record goes
through skeptic.cljs.schema-interpreter, which interprets the form
in a sci-sandboxed context that exposes only schema.core.
ClojureScript admission for Plumatic Schema declarations.
Mirrors `skeptic.schema.collect/ns-schema-results` for cljs source files.
Operates on per-form analyzed ASTs from `skeptic.cljs.analyzer-driver`.
Reads alias requires from the parsed ns AST (output of
`cljs.analyzer.api/parse-ns`) and Var metadata directly from each
`:def` AST node's `[:var :info :meta]` slot — no cenv reads, no caller-
managed compiler state.
Three top-level shapes are recognized:
- `s/def` :op :let, single binding `output-schema__*__auto__`,
inner :def at [:body :ret].
- `s/defschema` :op :def, :init :form starts with vary-meta.
- `s/defn` :op :let, multi-binding outer let with `output-schema*`
and `input-schema*` gensyms.
The cljs `:meta :schema` value is a symbolic form (the cljs analyzer
does not evaluate JVM-side); resolution to a real Schema record goes
through `skeptic.cljs.schema-interpreter`, which interprets the form
in a sci-sandboxed context that exposes only `schema.core`.Plumatic source-form discovery. Reads top-level forms from the namespace's source file, resolves each form's head symbol against the namespace's aliases via ns-resolve, and tags matches with a producer role.
Data verified at /tmp/skeptic-research/intake-data-dump.out: source-forms arrive pre-macro-expansion, so heads like 's/defn / schema.core/defn / schemy/defn all resolve to #'schema.core/defn after binding ns.
Plumatic source-form discovery. Reads top-level forms from the namespace's source file, resolves each form's head symbol against the namespace's aliases via ns-resolve, and tags matches with a producer role. Data verified at /tmp/skeptic-research/intake-data-dump.out: source-forms arrive pre-macro-expansion, so heads like 's/defn / schema.core/defn / schemy/defn all resolve to #'schema.core/defn after binding *ns*.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |