Cargo.toml Validator
A Cargo.toml can be flawless TOML and still be wrong: edition as a number, a feature that names a dependency which is not optional, a branch that cargo silently ignores because there is no git next to it. This checks the manifest against cargo's rules and gives you the line number.
The mistakes that cost the most time
Cargo's manifest errors are better than most, but the expensive ones are the mistakes it does not report — keys it reads past because they are not keys it knows.
edition = 2021. Without the quotes it is a TOML integer, and cargo
answers invalid type: integer `2021`, expected a string. It is the most common
Cargo.toml error there is, and the fix is two characters.
default_features = false. The key cargo reads is
default-features, with a hyphen. Written with an underscore it is an unknown key on
a dependency, so the default features stay switched on — and the build that was supposed to be
no_std quietly is not. Cargo has warned about this since 1.68; in a large manifest
the warning scrolls past.
version = "1.0" in [package]. A package version is
SemVer and SemVer has three numbers. A dependency requirement may be written
"1.0" — that is a different grammar, and it means ^1.0, not
=1.0.
A git dependency with a branch and a tag. Only one of
branch, tag or rev is allowed, and either of them without
a git key is ignored outright — the dependency resolves from crates.io while the
manifest says otherwise.
A feature that enables something that is not there.
json = ["serde/derive"] is fine if serde is a dependency;
["missing/feature"] fails at build time with a message about a feature you did not
write. Optional dependencies are the mirror image: a feature can only switch on a dependency
marked optional = true, and in [dev-dependencies] that word is not
allowed at all.
Features, and the implicit ones
Every optional dependency creates a feature with the same name unless some feature refers to it
as dep:name. That is convenient until you rename the crate or want the feature to
mean something slightly different, at which point the implicit feature is in the way. The
validator points out optional dependencies that no feature mentions explicitly, because that is
the moment to decide whether the implicit feature is what you want.
Cycles are the other feature trap. fast = ["simd"] with simd = ["fast"]
is an error, and cargo's message names only one of the two. The report here prints the whole loop.
Workspaces
Inheritance — version.workspace = true, serde = { workspace = true } —
is checked against a [workspace] table in the same file, which means it is checked
when you paste the workspace root and skipped when you paste a member. Alongside an inherited
dependency only features, optional and default-features
may be added; anything else, a version in particular, is an error.
A virtual manifest — [workspace] with no [package] — should set
resolver = "2" or "3". Without it cargo uses resolver 1 no matter what
edition the members declare, and features unify across build-dependencies and target-specific
dependencies in ways that produce a build failure in one member and not another.
What it deliberately does not do
It resolves nothing. No request leaves the page, so it cannot tell you that a crate name is misspelled, that a version does not exist, or that two requirements cannot both be satisfied. What it can tell you is that a string is not a version requirement, that a key is not one cargo reads, and that a feature points at nothing — which is the whole class of failures you can fix without a network.