Setting Lint Levels

Once your crate is configured to run some lints, it quickly becomes important to control the lint levels within your code.

Marker provides the ability to use normal lint control attributes #[allow(...)], #[deny(...)], and #[warn(....)] to change the behavior of marker lints. One caveat is that you need to put these attributes under a conditionally-compiled attribute #[cfg_attr(marker, ...)].

Example:

#![allow(unused)]
fn main() {
#[cfg_attr(marker, allow(marker::lint_crate::lint_name))]
fn foo() {}
}

Lints namespacing

Marker uses the marker:: tool prefix for lints. This is to make sure that your lints never collide with the native rustc lints and lints from any other linting tools. This is similar to how clippy puts all of its lints under clippy:: prefix.

After marker:: there must be the name of the lint crate. This is to make sure that lints from different lint crates never collide with each other. The name of the lint crate must be in snake case. The rule is the same as when you reference the crate in your code as a dependency. If the name of the crate contains dashes, they will be replaced with underscores.

The last segment is the name of the lint itself, which is the lowercaed name of the static variable that defines it in the lint crate.

Conditional compilation

There is a problem that a regular cargo check/build knows nothing about Marker and it will complain about unknown lints unless marker-specific attributes are compiled-out. To work around this Marker passes a --cfg=marker flag that you can use in your code.

This allows you to conditionally include allow/warn/deny attributes such that only cargo marker sees them, and regular builds ignore them allowing you to continue using the version of the Rust toolchain you are using in your project.

Per-lint-crate cfg

Marker also passes --cfg=marker="lint_crate" for each loaded lint crate that participates in linting. You may use this flag to conditionally control the level of lints from a specific lint crate.

This is intended to be used only for lints that are time consuming. For example, you could group lints that take a lot of time to run in a separate lint crate. This way you can run Marker with these additional lints enabled on CI, but without them locally.

Example:

Run lints from a heavy_lint_crate only on CI.

cargo marker --lints 'heavy_lint_crate = 1.0.0'

Use the default lints from Cargo.toml metadata locally.

cargo marker

Conditionally ignore the lint from the heavy_lint_crate, but only if it actually participates in linting.

#![allow(unused)]
fn main() {
#[cfg_attr(marker = "heavy_lint_crate", allow(marker::heavy_lint_crate::lint_name))]
fn foo() {}
}

This way you'll avoid triggering an "unknown lint" error if the heavy_lint_crate isn't included during the linting.

The reason for doing this kind of conditional compilation instead of just including heavy_lint_crate in linting unconditionally is a bit intricate. If you put any allow attributes in your code for the heavy lints then this won't prevent them from running. Of course, the lints for code under an allow will not be emitted, but the lints logic still walks through that code. Therefore, if you want to prevent a heavy lint from running you should just not include it in the lints configured in Cargo.toml or with --lints CLI parameter.