All notable changes to this project will be documented in this file.
:and, :tuple, :vector,
:sequential, :set (with :min/:max parsed and dropped), :map
with required keys, {:optional true} keys, and the :closed true
property (default is open, matching Malli's semantics — extra keyword
keys are admitted with Any values); multi-arity :function with
per-arm FnMethodT; :multi with {:dispatch :kw} tagged dispatch
(later branches are narrowed by negation of earlier tags); :=;
:schema with optional {:registry {...}} properties carrying a
local registry; :ref resolved through the active registry with
cycle detection (recursive positions emit InfCycleT rather than
diverging); and the primitive leaves :double, :float,
:qualified-keyword, and :qualified-symbol. Sequence/regex
combinators outside the :=> head remain experimental.--cljs-enable). Skeptic discovers .cljs and .cljc files in
deps.edn, Leiningen, and Shadow-CLJS projects and admits Plumatic
Schema (s/defn / s/def / s/defschema) and Malli
(:malli/schema) declarations on cljs vars. .cljc files are
admitted twice (once per host language); identical findings from
both passes are deduped with lang set to ["clj","cljs"].clj -T:skeptic check deps.edn tool entrypoint, so Skeptic is now
usable from a deps.edn :tools/usage alias in addition to the
Leiningen plugin. Supported tool arg-map keys include :project-dir,
:paths (override discovered source paths), and :alias (merge
additional deps.edn aliases when resolving the project's basis).
clojure -M:skeptic and clj -X:skeptic print a message redirecting
to the -T form.tools.analyzer, and any other library are what drive checking.
Skeptic's own declared dependency versions no longer collide with
the project's.--plumatic-disable and --malli-disable flags to switch off either
intake stream entirely. A disabled stream contributes no entries and
no findings whose source matches that stream. --plumatic-disable
additionally suppresses :skeptic/type-overrides, since overrides
are a Plumatic-domain construct. A Var declared via both streams is
still admitted via the enabled one; combining both flags leaves only
Skeptic's built-in native-fn declarations.lang field on every JSONL location object identifying the host
language the reported type was admitted under: "clj", "cljs", or
a sorted JSON array ["clj","cljs"] when both passes of a .cljc
file produced the same finding.Runnable, Callable, Comparator, and the
java.util.function.* single-abstract-method interfaces).
Previously a :- Runnable parameter produced a confusing
(=> Any) but expected java.lang.Runnable mismatch for any Clojure
fn; now Skeptic checks the source fn's arity against the interface's
abstract-method arity and casts its declared return type to Bool
for Predicate / BiPredicate, Int for Comparator, and Dyn
for the rest.{(s/required-key "a") s/Int}) now check correctly: correct
call sites are no longer flagged with a spurious unexpected-key
finding, and missing-required-key cases are now reported instead of
being silently dropped. Keyword required keys were already handled
by Plumatic's short-circuit to bare keywords; the bug only affected
string and other non-keyword keys.Expected typed entry on Malli :map or other
unsupported Malli forms encountered while building the per-namespace
declaration dict.(assoc nilable-map k v) (and update on a nilable map) no longer
carries a Maybe wrapper through the result, since (assoc nil k v)
returns {k v} — not nil. The nil-branch is captured by downgrading
the inner map's required keys to optional, so callers that previously
saw a spurious nullability finding now get a clean check, while callers
that depended on inner keys being required see a :map-nullable-key
finding instead of a false pass.(when (or (nil? a) (nil? b) ...) (throw ...)) now narrows every
guarded local in subsequent statements, not just the single-variable
shape. Previously a disjunctive throw-guard's De-Morgan negation was
treated as unsupported, so the rest of the enclosing do continued to
see each local as (maybe T) — yielding a spurious nullability
finding on a correct program. The conjunctive shape
(when (and (nil? a) (nil? b)) (throw)) was already handled and is
unchanged.wc.[(s/one s/Int 'a) (s/one s/Str 'b) s/Keyword]) are now
checked correctly. Previously Skeptic treated every collection
schema as either fully fixed-arity or fully homogeneous (#8).cond / case branches now restrict later ones. A
predicate test that succeeds on one arm narrows the remaining
arms by negation, so a (some? v) arm following (vector? v)
sees v as a non-vector non-nil, and downstream sum-type
variants flagged exhausted are removed from the residual (#9).:malli/schema Var metadata
and projects the compile-time (malli.core/function-schemas)
registry (which captures m/=>). The first batch of admitted
forms covers single-arity [:=> [:cat ...] out] function schemas,
:maybe, :or, :enum, the primitive leaves :int, :string,
:keyword, :symbol, :boolean, :nil, and :any, and the bare
predicate symbols recognized by Skeptic's predicate registry.
Unsupported Malli forms admit as dynamic. Broader Malli shapes are
added in subsequent releases.--explain-full flag to print fully expanded structural forms in
type-mismatch output. Without the flag, declared Schema names are
preferred so reports stay compact.[source: schema|malli|native|type-override|inferred]
and JSONL findings include the same source on the location object,
so consumers can tell which intake stream produced the expected
type. The corresponding Provenance is recorded on every admitted
Type and is used to render the :source field.--explain-full. Reports for s/maybe, s/eq, declared
map schemas, and similar shapes are noticeably shorter.lein skeptic runs the analysis pass under
schema.core/without-fn-validation, avoiding Plumatic Schema
function-validation overhead in projects that have runtime Schema
validation enabled. Runtime validation is restored after Skeptic's
pass completes.defn output is now checked per arity against the
declared return type for that specific arity, instead of comparing
every analyzed method against the first arity's declared output.(some? n) or (string? s) guard now
refines the local in subsequent expressions even when the local
reaches the test through a structured-origin path.and/or paths so
branch tests on a let-bound local refine subsequent reads of that
local (previously the refinement was lost across the let boundary).string?, int?, keyword?, etc.), so a Dyn value tested
positively against a recognized predicate is treated as the
corresponding ground type for the remainder of the then-branch.-o/--output OUTPUT_FILE on lein skeptic so skeptic's findings, summary, or JSONL stream can be written to a file while lein/JVM chatter stays on stdout (#2).(or x fallback) no longer reports a spurious nullability error when fallback is truthy.(str/blank? a) or (some? a) guard on a {:keys [a]} destructure refines
the local a itself, not only the parent map, so downstream reads of a
see the narrowed type.org.clojars.nomicflux/skeptic and org.clojars.nomicflux/lein-skeptic, :deploy-repositories for Leiningen, CI checks that keep the library and plugin versions aligned before publish, and GitHub Actions for Release lifecycle (orchestrates phases), Change project versions (reusable version bump), and Publish to Clojars (reusable deploy to Clojars).lein skeptic -p / --porcelain for newline-delimited JSON output (one
JSON object per line), documented in the README.lein skeptic --profile for optional CPU, memory, and wall-clock profiling;
when combined with --porcelain, the profile summary is written to stderr so
stdout stays JSONL-only..skeptic/config.edn with :exclude-files (root-relative
globs that skip loading and checking matched paths) and :type-overrides
(Plumatic Schema forms evaluated with schema.core in scope and merged into
collected declarations, including :output-only overrides).:skeptic/ignore-body and :skeptic/opaque on
s/defn attribute maps, and ^{:skeptic/type T} on expressions (see README
Suppressing checks).README.md that explains what Skeptic checks, how
the plugin works, how to install it, how to interpret its reports, where to
find the algorithm reference, and how to use configuration, JSONL output,
suppressions, and a short “building from source” section.--analyzer CLI flag to print analyzer output while inspecting a
namespace.get
and merge.clojure.tools.analyzer ASTs
instead of the older macroexpansion-driven pipeline.clojure.tools.reader with source logging so
reports can preserve source text and location metadata.throw, case, and cond; numeric tower comparisons; clojure.string/blank?;
conditional branches; maybe / eq / nil edge cases; invoke analysis without
redundant walks; cyclic type graphs; preservation of map projections through
checks; and a single consolidated boundary for schema-side compatibility checks
against inferred types.Can you improve this documentation? These fine people already did:
Michael A. & Michael AndersonEdit on GitHub
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 |