rfcs/text/1193-cap-lints.md

4.9 KiB

Summary

Add a new flag to the compiler, --cap-lints, which set the maximum possible lint level for the entire crate (and cannot be overridden). Cargo will then pass --cap-lints allow to all upstream dependencies when compiling code.

Motivation

Note: this RFC represents issue #1029

Currently any modification to a lint in the compiler is strictly speaking a breaking change. All crates are free to place #![deny(warnings)] at the top of their crate, turning any new warnings into compilation errors. This means that if a future version of Rust starts to emit new warnings it may fail to compile some previously written code (a breaking change).

We would very much like to be able to modify lints, however. For example rust-lang/rust#26473 updated the missing_docs lint to also look for missing documentation on const items. This ended up breaking some crates in the ecosystem due to their usage of #![deny(missing_docs)].

The mechanism proposed in this RFC is aimed at providing a method to compile upstream dependencies in a way such that they are resilient to changes in the behavior of the standard lints in the compiler. A new lint warning or error will never represent a memory safety issue (otherwise it'd be a real error) so it should be safe to ignore any new instances of a warning that didn't show up before.

Detailed design

There are two primary changes propsed by this RFC, the first of which is a new flag to the compiler:

    --cap-lints LEVEL   Set the maximum lint level for this compilation, cannot
                        be overridden by other flags or attributes.

For example when --cap-lints allow is passed, all instances of #[warn], #[deny], and #[forbid] are ignored. If, however --cap-lints warn is passed only deny and forbid directives are ignored.

The acceptable values for LEVEL will be allow, warn, deny, or forbid.

The second change proposed is to have Cargo pass --cap-lints allow to all upstream dependencies. Cargo currently passes -A warnings to all upstream dependencies (allow all warnings by default), so this would just be guaranteeing that no lints could be fired for upstream dependencies.

With these two pieces combined together it is now possible to modify lints in the compiler in a backwards compatible fashion. Modifications to existing lints to emit new warnings will not get triggered, and new lints will also be entirely suppressed only for upstream dependencies.

Cargo Backwards Compatibility

This flag would be first non-1.0 flag that Cargo would be passing to the compiler. This means that Cargo can no longer drive a 1.0 compiler, but only a 1.N+ compiler which has the --cap-lints flag. To handle this discrepancy Cargo will detect whether --cap-lints is a valid flag to the compiler.

Cargo already runs rustc -vV to learn about the compiler (e.g. a "unique string" that's opaque to Cargo) and it will instead start passing rustc -vV --cap-lints allow to the compiler instead. This will allow Cargo to simultaneously detect whether the flag is valid and learning about the version string. If this command fails and rustc -vV succeeds then Cargo will fall back to the old behavior of passing -A warnings.

Drawbacks

This RFC adds surface area to the command line of the compiler with a relatively obscure option --cap-lints. The option will almost never be passed by anything other than Cargo, so having it show up here is a little unfortunate.

Some crates may inadvertently rely on memory safety through lints, or otherwise very much not want lints to be turned off. For example if modifications to a new lint to generate more warnings caused an upstream dependency to fail to compile, it could represent a serious bug indicating the dependency needs to be updated. This system would paper over this issue by forcing compilation to succeed. This use case seems relatively rare, however, and lints are also perhaps not the best method to ensure the safety of a crate.

Cargo may one day grow configuration to not pass this flag by default (e.g. go back to passing -Awarnings by default), which is yet again more expansion of API surface area.

Alternatives

  • Modifications to lints or additions to lints could be considered backwards-incompatible changes.
  • The meaning of the -A flag could be reinterpreted as "this cannot be overridden"
  • A new "meta lint" could be introduced to represent the maximum cap, for example -A everything. This is semantically different enough from -A foo that it seems worth having a new flag.

Unresolved questions

None yet.