- or, at minimum, alert the owners of those crates to the problem and
direct them to the tracking issue
4. Once the change has been in the wild for at least one cycle, we can
**stabilize the change**, converting those warnings into errors.
Finally, for changes to `rustc_ast` that will affect plugins, the general policy
is to batch these changes. That is discussed below in more detail.
### Tracking issue
Every breaking change should be accompanied by a **dedicated tracking issue**
for that change. The main text of this issue should describe the change being
made, with a focus on what users must do to fix their code. The issue should be
approachable and practical; it may make sense to direct users to an RFC or some
other issue for the full details. The issue also serves as a place where users
can comment with questions or other concerns.
A template for these breaking-change tracking issues can be found
[here][template]. An example of how such an issue should look can be [found
here][breaking-change-issue].
### Issuing future compatibility warnings
The best way to handle a breaking change is to begin by issuing
future-compatibility warnings. These are a special category of lint warning.
Adding a new future-compatibility warning can be done as follows.
```rust
// 1. Define the lint in `compiler/rustc_middle/src/lint/builtin.rs`:
declare_lint! {
pub YOUR_ERROR_HERE,
Warn,
"illegal use of foo bar baz"
}
// 2. Add to the list of HardwiredLints in the same file:
impl LintPass for HardwiredLints {
fn get_lints(&self) -> LintArray {
lint_array!(
..,
YOUR_ERROR_HERE
)
}
}
// 3. Register the lint in `compiler/rustc_lint/src/lib.rs`:
store.register_future_incompatible(sess, vec![
...,
FutureIncompatibleInfo {
id: LintId::of(YOUR_ERROR_HERE),
reference: "issue #1234", // your tracking issue here!
},
]);
// 4. Report the lint:
tcx.lint_node(
lint::builtin::YOUR_ERROR_HERE,
path_id,
binding.span,
format!("some helper message here"));
```
#### Helpful techniques
It can often be challenging to filter out new warnings from older, pre-existing
errors. One technique that has been used in the past is to run the older code
unchanged and collect the errors it would have reported. You can then issue
warnings for any errors you would give which do not appear in that original set.
Another option is to abort compilation after the original code completes if
errors are reported: then you know that your new code will only execute when
there were no errors before.
#### Crater and crates.io
[Crater] is a bot that will compile all crates.io crates and many
public github repos with the compiler with your changes. A report will then be
generated with crates that ceased to compile with or began to compile with your
changes. Crater runs can take a few days to complete.
We should always do a crater run to assess impact. It is polite and considerate
to at least notify the authors of affected crates the breaking change. If we can
submit PRs to fix the problem, so much the better.
#### Is it ever acceptable to go directly to issuing errors?
Changes that are believed to have negligible impact can go directly to issuing
an error. One rule of thumb would be to check against `crates.io`: if fewer than
10 **total** affected projects are found (**not** root errors), we can move
straight to an error. In such cases, we should still make the "breaking change"
page as before, and we should ensure that the error directs users to this page.
In other words, everything should be the same except that users are getting an
error, and not a warning. Moreover, we should submit PRs to the affected
projects (ideally before the PR implementing the change lands in rustc).
If the impact is not believed to be negligible (e.g., more than 10 crates are
affected), then warnings are required (unless the compiler team agrees to grant
a special exemption in some particular case). If implementing warnings is not