Reference

torb check

torb check resolves every module, import and name in a type position, types every expression, and reports one block per diagnostic - the gate every other command trusts.

check is the front end alone, with nothing built and nothing run: it is what torb build and the self-hosted torb run call before they do anything else, and it is safe to run on its own as often as you like.

Synopsis#

torb check [path]...     Check projects, workspaces or single files (default: the current directory)
    --statistics          How many expressions of every file have a type, and how many are deferred
    --timings             The wall time of every pass, in the order they ran
    --every-target        Lower every program and test once per target, without a C compiler
    --partial             Every operation that can panic and that the compiler did not prove
    --deny deprecated     A use of a deprecated declaration is a problem, not a warning

What it does#

Finding what to check#

A path on the command line can be a single file or a directory. check walks up from it looking for a project.trb; the outermost one it finds is the workspace root, and every .trb file below that root is read, because that is where the standard library the path depends on lives - except in a hidden directory and in a build directory, which holds what a build, a test run or a docs check wrote and never a source. A file with no project.trb above it at all is checked alone, against the default prelude.

The files of a package are every .trb file below its directory that no nested project.trb claims, so a file beside src/ - tools/migrate.trb - is one of them. A file that is named directly is checked even where no package sweeps it in, in a hidden or a build directory. A workspace without a standard library of its own - a file below no project.trb, a project that is nobody's member - gets the toolchain's: the std of the nearest directory above the named path, then above the working directory, then above torb itself, that has std/prelude/project.trb, or $TORB_STD.

Passing several paths checks several targets in one run; each is resolved this way independently. A path that reaches no file at all is an error, not a clean run.

What is checked#

Every module, every use, the visibility of every name, every name in a type position, and the type of every expression - and every project.trb on the way: a static setting that is computed, a program line the tree contradicts and a profile block that is no profile are errors at their line (project.trb). A clean run answers <n> files, no problems, where <n> counts only the files the given paths asked for - the files of the standard library and of other workspace members that had to be read along the way are not counted, even though they were type checked too.

$ torb check examples/tour
14 files, no problems

A problem is one block per diagnostic: the file, the line and column, a caret under the span, and one message, the same rendering torb build uses for a program that does not check.

$ torb check scratch.trb
error: Expected `Int64`, found `String`
 --> ../scratch.trb:1:20
  |
1 | const total: Int = "not a number"
  |                    ^^^^^^^^^^^^^^

1 problems in 1 of 1 files

check exits with 1 when it reports a problem and 0 when it does not, so it composes with a shell's && and with a continuous-integration step that only continues on success.

--statistics#

Prints how many expressions of every requested file have a type and how many are deferred - waiting for a later milestone of the checker - then the total across every requested file.

$ torb check --statistics examples/tour
../examples/tour/src/01-bindings-and-values.trb: 84 typed, 0 deferred
../examples/tour/src/02-functions.trb: 260 typed, 0 deferred
../examples/tour/project.trb: 6 typed, 0 deferred
3139 of 3139 expressions typed (100%), 0 deferred
14 files, no problems

0 deferred over the whole repository is what the checker's own milestone is measured by.

--timings#

Prints the wall time of every pass of the checker, in the order it ran, then the total. The first pass - finding the workspace, lexing and parsing every file the target depends on, and building the module graph - reads and parses the whole workspace the target belongs to, not only the files the run reports on, which is why it dominates the total for a target that is a small part of a large workspace.

$ torb check --timings .
finding the workspace: 53 ms
name resolution: 415 ms
names in type positions: 453 ms
bodies: 14796 ms
all passes: 41069 ms
255 files, no problems

--every-target#

After the check, lowers every program and test of the paths - every entry file, script and test file - once per target of the toolchain's list (windows, linux, macos and freebsd, each on x64 and arm64, and browser on wasm64) and stops before C, so no C compiler is needed. The checker already checked every arm of every match OperatingSystem.current on this machine; what only a target's lowering sees is which arm that target keeps, and what the arm reaches. A native that exists on some systems only, reached on another, is the error it finds, printed under the target that has it (Compile-time branches).

$ torb check --every-target tools/probe.trb
1 file, no problems
linux-x64:
error: `Windows.tickCount` exists only on Windows, and this program is built for Linux
...
7 problems on 7 targets of 9: linux-x64, linux-arm64, macos-x64, macos-arm64, freebsd-x64, freebsd-arm64, browser-wasm64

--deny deprecated#

A use of a declaration marked deprecated(...) - or a call that passes a deprecated parameter, by label or by position - is a warning, and a warning is no problem (Deprecation): check prints it after the problems, counts it in the last line and exits with 0. --deny deprecated turns every one of them into a problem for this run: the same message, position and note, printed as an error: block in its place among the others, counted as a problem, and check exits with 1. A file with such a use counts as a file with problems.

$ torb check --deny deprecated src
error: `parse` is deprecated since 0.4: the name says too little
 --> src/main.trb:3:15
  |
3 | const point = parse("abc")
  |               ^^^^^
  = Write `parsePoint` instead; `torb lint --fix --rule deprecated` rewrites it

1 problems in 1 of 3 files

A run with no use of a deprecated declaration answers 3 files, no problems as before. The value is the next argument (--deny deprecated, not --deny=deprecated) and deprecated is the only value: any other value, or --deny as the last argument, is an error that names it, and exits with 2 like an unknown flag. It changes what a run counts as a problem and nothing about what is valid, and it is no setting of a project: a pipeline that wants it says so. It combines with --statistics, --timings, --partial and --every-target, which still runs only when there is no problem - a denied use is one.

A false positive is a bug in the checker#

check is the one command whose answer is never argued with: a program that should type check and does not, or the reverse, is reported as a defect of the checker rather than worked around. See What a model trained on other languages gets wrong for the mistakes that most often produce a diagnostic worth reading twice before assuming it is wrong.

Examples#

The whole repository, workspace and all, checked in one run:

$ torb check .
255 files, no problems
Esc