Skip to content

Commit 6af1bdd

Browse files
committed
Auto merge of #73595 - SNCPlay42:lifetime-after-mut, r=Mark-Simulacrum
improve diagnostics for lifetime after `&mut` If, when parsing a borrow pointee type, we see a lifetime after `mut`, suggest placing the lifetime before `mut` and eat the lifetime to avoid a large number of unhelpful diagnostics. There are some subtleties to avoid false positives in cases like `&mut 'a + Trait`, where `&mut ('a + Trait)` is a better suggestion. fixes #73568
2 parents 07ece44 + 4de9a53 commit 6af1bdd

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

compiler/rustc_parse/src/parser/ty.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,34 @@ impl<'a> Parser<'a> {
276276
}
277277

278278
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
279-
let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
279+
let and_span = self.prev_token.span;
280+
let mut opt_lifetime =
281+
if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
280282
let mutbl = self.parse_mutability();
283+
if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
284+
// A lifetime is invalid here: it would be part of a bare trait bound, which requires
285+
// it to be followed by a plus, but we disallow plus in the pointee type.
286+
// So we can handle this case as an error here, and suggest `'a mut`.
287+
// If there *is* a plus next though, handling the error later provides better suggestions
288+
// (like adding parentheses)
289+
if !self.look_ahead(1, |t| t.is_like_plus()) {
290+
let lifetime_span = self.token.span;
291+
let span = and_span.to(lifetime_span);
292+
293+
let mut err = self.struct_span_err(span, "lifetime must precede `mut`");
294+
if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) {
295+
err.span_suggestion(
296+
span,
297+
"place the lifetime before `mut`",
298+
format!("&{} mut", lifetime_src),
299+
Applicability::MaybeIncorrect,
300+
);
301+
}
302+
err.emit();
303+
304+
opt_lifetime = Some(self.expect_lifetime());
305+
}
306+
}
281307
let ty = self.parse_ty_no_plus()?;
282308
Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }))
283309
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![crate_type="lib"]
2+
fn x<'a>(x: &mut 'a i32){} //~ ERROR lifetime must precede `mut`
3+
4+
macro_rules! mac {
5+
($lt:lifetime) => {
6+
fn w<$lt>(w: &mut $lt i32) {}
7+
//~^ ERROR lifetime must precede `mut`
8+
}
9+
}
10+
11+
mac!('a);
12+
13+
// avoid false positives
14+
fn y<'a>(y: &mut 'a + Send) {
15+
//~^ ERROR expected a path on the left-hand side of `+`, not `&mut 'a`
16+
//~| WARNING trait objects without an explicit `dyn` are deprecated
17+
//~| ERROR at least one trait is required for an object type
18+
let z = y as &mut 'a + Send;
19+
//~^ ERROR expected value, found trait `Send`
20+
//~| WARNING trait objects without an explicit `dyn` are deprecated
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
error: lifetime must precede `mut`
2+
--> $DIR/issue-73568-lifetime-after-mut.rs:2:13
3+
|
4+
LL | fn x<'a>(x: &mut 'a i32){}
5+
| ^^^^^^^ help: place the lifetime before `mut`: `&'a mut`
6+
7+
error[E0178]: expected a path on the left-hand side of `+`, not `&mut 'a`
8+
--> $DIR/issue-73568-lifetime-after-mut.rs:14:13
9+
|
10+
LL | fn y<'a>(y: &mut 'a + Send) {
11+
| ^^^^^^^^^^^^^^ help: try adding parentheses: `&mut ('a + Send)`
12+
13+
error: lifetime must precede `mut`
14+
--> $DIR/issue-73568-lifetime-after-mut.rs:6:22
15+
|
16+
LL | fn w<$lt>(w: &mut $lt i32) {}
17+
| ^^^^^^^^ help: place the lifetime before `mut`: `&$lt mut`
18+
...
19+
LL | mac!('a);
20+
| --------- in this macro invocation
21+
|
22+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
23+
24+
error[E0423]: expected value, found trait `Send`
25+
--> $DIR/issue-73568-lifetime-after-mut.rs:18:28
26+
|
27+
LL | let z = y as &mut 'a + Send;
28+
| ^^^^ not a value
29+
30+
warning: trait objects without an explicit `dyn` are deprecated
31+
--> $DIR/issue-73568-lifetime-after-mut.rs:14:18
32+
|
33+
LL | fn y<'a>(y: &mut 'a + Send) {
34+
| ^^ help: use `dyn`: `dyn 'a`
35+
|
36+
= note: `#[warn(bare_trait_objects)]` on by default
37+
38+
warning: trait objects without an explicit `dyn` are deprecated
39+
--> $DIR/issue-73568-lifetime-after-mut.rs:18:23
40+
|
41+
LL | let z = y as &mut 'a + Send;
42+
| ^^ help: use `dyn`: `dyn 'a`
43+
44+
error[E0224]: at least one trait is required for an object type
45+
--> $DIR/issue-73568-lifetime-after-mut.rs:14:18
46+
|
47+
LL | fn y<'a>(y: &mut 'a + Send) {
48+
| ^^
49+
50+
error: aborting due to 5 previous errors; 2 warnings emitted
51+
52+
Some errors have detailed explanations: E0178, E0224, E0423.
53+
For more information about an error, try `rustc --explain E0178`.

0 commit comments

Comments
 (0)