Summary

Rust currently only allows configuring lint levels on the command line or via crate level attributes. This proposes an additional way to configure lint levels and possibly lint-specific settings in a configuration file.

Motivation

Most people use the same lint configuration for all of their crates. Currently there is no convenient way to specify this configuration. Instead, for every crate a

#![warn(
    long_list,
    of_lint,
    clippy::names,
    ...
)]

header has to be copied to the lib.rs/main.rs of every crate. This doesn't scale and is really hard to maintain. Having a file which can be copied to each crate (e.g. from a template repository) would simplify both, the definition of default lint sets and the maintainability of the same.

Guide-level explanation

Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means:

  • Introducing new named concepts.
  • Explaining the feature largely in terms of examples.
  • Explaining how Rust programmers should think about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible.
  • If applicable, provide sample error messages, deprecation warnings, or migration guidance.
  • If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers.

For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms.

Reference-level explanation

This is the technical portion of the RFC. Explain the design in sufficient detail that:

  • Its interaction with other features is clear.
  • It is reasonably clear how the feature would be implemented.
  • Corner cases are dissected by example.

The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work.

Drawbacks

Why should we not do this?

Rationale and alternatives

  • Why is this design the best in the space of possible designs?
  • What other designs have been considered and what is the rationale for not choosing them?
  • What is the impact of not doing this?

Prior art

There have been previous attempts to achieve this in the past. Always from the design perspective, never a PR nor similar. An RFC proposal has never been attempted neither(at least that we're aware of). And almost all of the discussions that have taken place have been collected or cited here.

Indeed, there have been 3 major threads of discussion and proposals about this feature.

First proposal/discussion thread

The first time this feature was suggested in an issue was in this clippy issue where it was introduced the fact of introducing the deny, warn and other lint-levels support to clippy.toml.

It was mentioned that as it is possible to do:

#.cargo/config
[build]
rustflags = ["-W", "missing_docs"]

It could also be done something similar for Clippy:

#clippy.toml
warn = ["absurd_extreme_comparisons", "almost_swapped"]
allow = ["assign_ops", "needless_return"]

Second discussion

Later on, the issue jumped into the rust-lang repo since almost everyone uses the Clippy though cargo and then, there was no way for Clippy to parse the lints and pass them to Rustc on any way.

In this new issue, @oli-obk compiled the info of the previous issue.
And @kennytm stated that Cargo can parse the lints.toml and pass the -D, -A, -W flags to rustc directly. On that way, rustc doesn't need to be changed or aware of anything in respect of that new feature and Cargo could take the responsability instead.
This was indeed a good point. Cargo is much more flexible than rustc in terms of changes, aditions and contributions. Therefore it makes sense also for this reason to move the impl there.

A good point was raised in this issue which is a reference to this rollup PR on which it's shown how much easier would have been to use a lints.toml file rather than track on each repo/workspace/crate component the lints themseleves.

First serious and more acurated proposal w/ discussion

So the issue was finally moved to Cargo. By this time, more issues like this one started to appear. This is one of the first times where adding the lint config to Cargo.toml is considered and supported by quite a few contributors.
On this issue @Rantanen purposed the following:

  • A configuration lints.toml (or [lints] in Cargo.toml?) file that specifies the lints.
# Cargo.toml
[lints]
allow_unused = "allow"
non_snake_case = "allow"

Suggests to differ from @oli-obk's proposal for the toml structure. Introduces this new way of declaring the lints advocating for:
- Diff benefits
- Ability for the user to group logically similar lints together seems more useful than packaging inside allow or deny blocks.

  • The lints can be specified on the workspace level and for individual packages. Anything on the package level will override the workspace setup on per lint basis.
    This essentially follows the way on which Cargo treats the Manifest config when there's more than one on the workspace scope + $CARGO_HOME(if set).
  • The configuration file is cfg/feature-aware and can specify different lints depending on features.
#Cargo.toml
[lints.'cfg(feature = "clippy")']
cyclomatic_compexity = "deny"

Following Cargo's plataform's specific dependencies support purposes to have the same for the lints.

  • Lint configurations
    This goes far beyond this RFC but if at some point, lint config was also enabled for lints, an example was provided:

Unresolved questions

  • lints.toml vs Cargo.toml(lints section).
    Needs investigation and go more deeeply through the issues.
  • Support for cfg'd lints(I think so)?
    We should be compatible with the cfg dependency Manifest syntax.
  • Leave the door open for lint config means adopting the verbose format probably. Is there an alternative?
allow = ["cyclomatic_complexity(How do we ad threshold?)"]
cyclomatic_complexity = { state = "allow", threshold = 30 }

Future possibilities

Think about what the natural extension and evolution of your proposal would
be and how it would affect the language and project as a whole in a holistic
way. Try to use this section as a tool to more fully consider all possible
interactions with the project and language in your proposal.
Also consider how this all fits into the roadmap for the project
and of the relevant sub-team.

This is also a good place to "dump ideas", if they are out of scope for the
RFC you are writing but otherwise related.

If you have tried and cannot think of any future possibilities,
you may simply state that you cannot think of anything.

Note that having something written down in the future-possibilities section
is not a reason to accept the current or a future RFC; such notes should be
in the section on motivation or rationale in this or subsequent RFCs.
The section merely provides additional information.