Scan another MoonBit project
riantr/moonbit_static_analysis is a three-inspection static-analysis pipeline
(structural walk → type/symbol inference → abstract interpretation → merged
report) covering every MoonBit toolchain file kind: .mbt, .mbtx, .mbti,
.mbt.md, .mbtp.
Run it
moonx riantr/moonbit_static_analysis@latest riantr/moonbit_doubleML@latest
The target is a registry coordinate (author/module, optionally @version or
@latest) or a filesystem path; omit it to scan the current directory. A
coordinate is materialised with moon fetch into .repos/ and then walked.
The scanned project neither depends on this module nor is modified. Its
sources are only READ, at scan time, and the dependency runs one-way:
analyzer → registry → target source.
Pin a version when reproducibility matters:
moonx riantr/moonbit_static_analysis@0.3.5 riantr/moonbit_doubleML
moonx is the recommended invocation. It downloads the prebuilt wasm that
mooncakes built for the published version, caches it under
~/.moon/registry/cache/assets/<author>/<module>/<version>/, and runs it with
moonrun — no clone, no local build. The very same artifact is what the
"Download wasm" button serves on skills.mooncakes.io.
moon runwasm … is deprecated (the toolchain says removal after
2026-09-14) but still works. Prefer moonx.
moonx with no package path resolves the module's ROOT package, which is why
the command needs no /src/sa suffix.
Reading the output
The headline is parse coverage, not the finding count. A scan reports what it
understood and what it refused to guess at:
EXCLUDE _qa_verify
TARGET D:\src\...\ML\CI D:\src\...\ML\CI
SCANNED 85 files under D:\src\...\ML\CI
CLEAN .mbti D:\src\...\CI/pof/pkg.generated.mbti
FOUND .mbt 2 D:\src\...\CI/scm/dag.mbt
12:3 - error: ... (UndefinedName) [behavior]
PARSED 23/85 files understood (27.0%)
NOTPARSE 62 files, 17940 finding(s) withheld — they measure what this analyzer
does not understand, not what your code does
55x unexpected token
e.g. D:\src\...\CI/scm/dag.mbt
SUMMARY files=85 parsed=23 actionable=0 withheld=17940 total=17940
- CLEAN / FOUND lines cover only files the frontend read completely.
- Unparsed files are grouped by reason, not listed one per line, with example
paths — 62 unparsed files cost 9 lines, not 62.
- actionable + withheld = total: nothing is silently dropped.
Read the output, not the exit code. MoonBit exposes no process-exit entry
point in the available packages, so a target that could not be located still
exits 0 — it prints
ERROR<TAB>cannot locate source for target: <target>. A successful run always
prints a SUMMARY line.
Excluding directories
moonx riantr/moonbit_static_analysis@latest <target> --exclude <dir> [--exclude <dir>...]
--exclude matches any path segment, and is repeatable. Use it when the tree
keeps copies of other people's code under an ordinary directory name: on one
such tree those copies were 1020 files and 126,687 findings — 88% of everything
the scan reported, none of it the target's own code. Dot-directories
(.mooncakes, .repos) and _build are already skipped.
What the numbers mean, and what they do not
.mbti, .mbt.md and .mbtp are audited properly: on a real project they
report 0 findings unless something is actually wrong.
.mbt is different. The program frontend covers a subset of MoonBit — no
struct literals, no match, no @alias calls, no type annotations in general
position, no lambdas or interpolation. On real code it therefore produces a large
number of findings that are all subset edges, not defects.
That is why the scan now withholds them. An unparsed struct body turns its
fields into undefined names and its * into operator mismatches, so the
downstream families on an unread file are noise with a real-looking shape. On a
63-file module moon check reports 8 unused values where this pipeline
reported 529 "unused locals" — the difference is the analyzer not seeing the
struct bodies. So: quote PARSED first, and treat actionable as the only
count that describes the code. Do not report withheld to a user as "problems
found".
Instead of the CLI
For the analyzer's own repository, or when you want structured output, depend
on the module and call the API — @pipeline.run, @moonfiles.literate /
.iface / .proof, @statecheck.audit. See the adjacent
README.md and EXTENSIONS.md.