Rules
Forty-six rules across five families. Every one can be disabled, reweighted, or given a different severity in configuration.
Rules are namespaced by their category, so doc addresses the whole family and doc/work-marker addresses one rule.
rust
Idiomaticity, error handling, and abstractions with no second user.
| Rule | Severity | Default | What it reports |
|---|---|---|---|
rust/unwrap-in-library | high | on | A fallible value is unwrapped outside tests, so the failure path is unhandled |
rust/panic-in-library | high | on | Library code panics instead of returning an error |
rust/expect-in-library | medium | on | A fallible value is resolved with expect outside tests |
rust/panicking-index | medium | on | Indexing can panic on out-of-range input |
rust/single-implementor-trait | medium | on | A crate-private trait has one implementor, so the indirection has no second user |
rust/oversized-function | medium | on | A function body is longer than the configured maximum |
rust/deep-nesting | medium | on | A block is nested more deeply than the configured maximum |
rust/single-use-wrapper | low | on | A wrapper type only forwards to its inner value |
rust/over-exposed-visibility | low | on | An item is public but referenced only within its own module |
rust/manual-index-loop | low | on | A range loop only indexes a collection, where an iterator would read more clearly |
rust/match-on-bool | low | on | A match on a boolean where a conditional would read more clearly |
rust/needless-return | low | on | An explicit return at the end of a function body |
rust/oversized-file | low | on | A file is longer than the configured maximum |
rust/single-instantiation-generic | low | off | A generic parameter is instantiated at exactly one type |
rust/single-caller-indirection | low | off | A function exists solely to forward to another and has one caller |
Error-handling rules are exempt in test code, whether that is an inline #[cfg(test)] module, an integration test target, or a file reached only through a test-only module declaration.
typescript
What a declaration earns its keep doing, and what discards the checking around it.
| Rule | Severity | Default | What it reports |
|---|---|---|---|
typescript/cast-through-unknown | high | on | A cast passes through unknown to reach an unrelated type, defeating the check a single cast would have had |
typescript/single-implementor-interface | medium | on | An unexported interface has one implementor, so the indirection has no second user |
typescript/explicit-any | medium | on | A position is written as any, discarding the checking the rest of the file relies on |
typescript/non-null-assertion | medium | on | An expression asserts a value is present rather than establishing it |
typescript/unexplained-suppression | medium | on | A suppression comment carries no reason for suppressing anything |
typescript/aliasing-type | low | on | A type alias renames a single named type without adding meaning |
typescript/optional-heavy-type | low | on | Most of a type's properties are optional, so it states little about its values |
typescript/barrel-module | low | on | A module only forwards names declared elsewhere |
typescript/duplicate-shape | low | on | Two declarations describe the same shape under different names |
typescript/console-in-library | low | on | Library code writes to the console, which its callers did not ask for |
typescript/default-export | low | on | A module exports a value whose name every importing site chooses for itself |
.d.ts files are not analyzed. A declaration file states an interface without implementing it and is usually written by tsc, so the rules about implementors, forwarding, and console calls have nothing to read. node_modules is never walked, whether or not the project ignores it.
Six of these are also linter rules
explicit-any, non-null-assertion, unexplained-suppression, console-in-library and default-export have equivalents in Biome or typescript-eslint. cast-through-unknown has none.
They ship because TypeScript linting is opted into twice, by installing a tool and by turning the individual rule on. A project with no configuration is not one that decided these were acceptable; it is one where nothing is checking them. That differs from Rust, where clippy comes with the toolchain, and where rules duplicating it were removed during calibration.
Each therefore asks first whether the project already enforces it:
| What the project has | What the rule does |
|---|---|
biome.json or .eslintrc.json enabling the equivalent, directly or through a preset | stays silent |
| No linter configuration at all | reports, confirmed |
Configuration only as JavaScript, such as a flat eslint.config.ts | reports, unconfirmed |
Configuration is read as data and never executed, for the same reason the analyzer never builds the project it is pointed at.
c
What a declaration does with the reach it is given.
| Rule | Severity | Default | What it reports |
|---|---|---|---|
c/missing-internal-linkage | medium | on | A function is exposed to the whole program while only its own file uses it |
c/definition-in-header | medium | on | A header carries a function body, so every including file gets a copy |
c/orphan-declaration | medium | on | A header declares a function no analyzed file defines |
c/file-scope-mutable-state | medium | on | Mutable state at file scope is readable and writable from every file in the program |
c/unincluded-header | low | on | An interface file states an interface no analyzed file includes |
.c and .h are both read as C. A header written for C++ produces a parse diagnostic naming the file rather than being silently reported as understood.
Nothing is preprocessed
No macro is expanded, no include resolved, and no condition evaluated, even where the definition needed sits in the analyzed tree. Expanding a macro means resolving include paths and evaluating conditionals, which is running the build.
The cost is measured and large. Across twenty widely deployed C projects, 97.6 percent of c findings came back unconfirmed and nineteen of the twenty carried no score, because every one of them invokes macros at file scope. Read C findings individually; do not expect a c score. See calibration.
doc
Whether the prose informs anyone.
| Rule | Severity | Default | What it reports |
|---|---|---|---|
doc/missing-on-public-item | medium | on | A public item carries no documentation |
doc/documented-parameter-absent | medium | on | Documentation describes a parameter the signature does not declare |
doc/commented-out-code | medium | on | Commented-out source was left in place |
doc/restates-signature | low | on | Documentation adds nothing beyond the item name and signature |
doc/undocumented-parameter | low | on | A parameter is undocumented while its siblings are documented |
doc/restating-comment | low | on | A comment paraphrases the statement below it |
doc/work-marker | low | on | An unresolved work marker was left in source |
doc/unresolved-reference | medium | off | Documentation links to an identifier that does not resolve |
doc/contentless-banner | low | off | A banner or separator comment carries no content |
"Public" means reachable from outside the crate, not merely written pub. An item inside a private module, or one marked #[doc(hidden)], is not part of the documented API and is left alone.
A comment above an unsafe operation is exempt from restating-comment: that is where the reasoning making the operation sound gets written. Code introduced by prose ending in a colon is read as illustration rather than as leftovers.
filler
Volume without meaning. Every rule here is low severity by default, because each pattern has legitimate uses.
| Rule | Severity | Default | What it reports |
|---|---|---|---|
filler/narrating-comments | low | on | Most statements in a function carry a comment restating them |
filler/defensive-redundancy | low | on | A check cannot fail given a guarantee already established |
filler/templated-documentation | low | on | Many items share one documentation template with only the name substituted |
filler/repeated-wrapper | low | on | Structurally identical wrappers repeat across a module |
filler/unimplemented-marker | low | on | A non-test function body consists only of an unimplemented marker |
filler/simplification-disclaimer | low | on | Prose states that the surrounding code is simplified or not production ready |
Nothing in this family makes any claim about who or what wrote the code. It describes patterns, and a test asserts that no message, category name, or serialized field expresses an opinion about authorship.
Rules that ship switched off
Four rules are disabled by default because measurement showed them reporting their own imprecision rather than sloppiness. Each carries its reason in the catalog, and calibration has the numbers.
Turn any of them on for a run:
slopometer . --enable doc/contentless-bannerOr in configuration:
[rules."doc/contentless-banner"]
enabled = true