[`rustc_lint_defs`]. You should prefer to place lints in `rustc_lint` if
possible. One benefit is that it is close to the dependency root, so it can be
much faster to work on.
[builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html
[`rustc_lint_defs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/index.html
Every lint is implemented via a `struct` that implements the `LintPass` `trait`
(you can also implement one of the more specific lint pass traits, either
`EarlyLintPass` or `LateLintPass` depending on when is best for your lint to run).
The trait implementation allows you to check certain syntactic constructs
as the linter walks the AST. You can then choose to emit lints in a
very similar way to compile errors.
You also declare the metadata of a particular lint via the [`declare_lint!`]
macro. This macro includes the name, the default level, a short description, and some
more details.
Note that the lint and the lint pass must be registered with the compiler.
For example, the following lint checks for uses
of `while true { ... }` and suggests using `loop { ... }` instead.
```rust,ignore
// Declare a lint called `WHILE_TRUE`
declare_lint! {
WHILE_TRUE,
// warn-by-default
Warn,
// This string is the lint description
"suggest using `loop { }` instead of `while true { }`"
}
// This declares a struct and a lint pass, providing a list of associated lints. The
// compiler currently doesn't use the associated lints directly (e.g., to not
// run the pass or otherwise check that the pass emits the appropriate set of
// lints). However, it's good to be accurate here as it's possible that we're
// going to register the lints via the get_lints method on our lint pass (that
// this macro generates).
declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
// Helper function for `WhileTrue` lint.
// Traverse through any amount of parenthesis and return the first non-parens expression.
fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
while let ast::ExprKind::Paren(sub) = &expr.kind {
expr = sub;
}
expr
}
// `EarlyLintPass` has lots of methods. We only override the definition of
// `check_expr` for this lint because that's all we need, but you could
// override other methods for your own lint. See the rustc docs for a full
// list of methods.
impl EarlyLintPass for WhileTrue {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::While(cond, ..) = &e.kind
&& let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind
&& let ast::LitKind::Bool(true) = lit.kind
&& !lit.span.from_expansion()
{
let condition_span = cx.sess.source_map().guess_head_span(e.span);
cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
lint.build(fluent::example::use_loop)
.span_suggestion_short(
condition_span,
fluent::example::suggestion,
"loop".to_owned(),
Applicability::MachineApplicable,
)
.emit();
})
}
}
}
```
```fluent
example-use-loop = denote infinite loops with `loop {"{"} ... {"}"}`
.suggestion = use `loop`
```
### Edition-gated lints
Sometimes we want to change the behavior of a lint in a new edition. To do this,
we just add the transition to our invocation of `declare_lint!`:
```rust,ignore
declare_lint! {
pub ANONYMOUS_PARAMETERS,
Allow,
"detects anonymous parameters",
Edition::Edition2018 => Warn,
}
```
This makes the `ANONYMOUS_PARAMETERS` lint allow-by-default in the 2015 edition
but warn-by-default in the 2018 edition.
See [Edition-specific lints](./guides/editions.md#edition-specific-lints) for more information.
### Feature-gated lints
Lints belonging to a feature should only be usable if the feature is enabled in the
crate. To support this, lint declarations can contain a feature gate like so: