Liking cljdoc? Tell your friends :D

skeptic.analysis

No vars found in this namespace.

skeptic.analysis.annotate.cljs

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.
raw docstring

skeptic.analysis.annotate.runner

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.
raw docstring

skeptic.analysis.ast-children

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.
raw docstring

skeptic.analysis.bridge.descriptors

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}
raw docstring

skeptic.analysis.native-fns

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).
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).
raw docstring

skeptic.analysis.predicates

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.
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.
raw docstring

skeptic.classloader-fix

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.
raw docstring

No vars found in this namespace.

skeptic.cli.cljs.deps

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.
raw docstring

skeptic.cli.cljs.discover

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.
raw docstring

skeptic.cli.cljs.lein

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.
raw docstring

skeptic.cli.cljs.shadow

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.
raw docstring

skeptic.cli.main

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.
raw docstring

skeptic.cli.options

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.
raw docstring

skeptic.cli.paths

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.
raw docstring

skeptic.cljs.analyzer-driver

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.
raw docstring

skeptic.cljs.schema-interpreter

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.
raw docstring

skeptic.cljs.topo

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.
raw docstring

skeptic.core-fns

No vars found in this namespace.

skeptic.malli-spec.collect

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).
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).
raw docstring

skeptic.malli-spec.collect.cljs

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.

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.
raw docstring

skeptic.output.serialize

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.
raw docstring

skeptic.schema.collect.cljs

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`.
raw docstring

skeptic.schema.discovery

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*.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close