Skip to content

Commit e9e8414

Browse files
committed
Suggest removing parenss around leading lifetime "bound" in "trait object type"
1 parent 6242335 commit e9e8414

File tree

8 files changed

+139
-22
lines changed

8 files changed

+139
-22
lines changed

compiler/rustc_lint/src/unused.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,7 @@ impl EarlyLintPass for UnusedParens {
12541254
}
12551255
ast::TyKind::Paren(r) => {
12561256
match &r.kind {
1257+
ast::TyKind::Err(_) => { /* we don't know anything about the shape */ }
12571258
ast::TyKind::TraitObject(..) => {}
12581259
ast::TyKind::BareFn(b)
12591260
if self.with_self_ty_parens && b.generic_params.len() > 0 => {}

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,8 @@ parse_recover_import_as_use = expected item, found {$token_name}
718718
parse_remove_let = expected pattern, found `let`
719719
.suggestion = remove the unnecessary `let` keyword
720720
721+
parse_remove_paren = remove these parentheses
722+
721723
parse_repeated_mut_in_pattern = `mut` on a binding may not be repeated
722724
.suggestion = remove the additional `mut`s
723725

compiler/rustc_parse/src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ pub(crate) enum BadTypePlusSub {
5656
#[primary_span]
5757
span: Span,
5858
},
59+
#[multipart_suggestion(parse_remove_paren, applicability = "machine-applicable")]
60+
RemoveParen {
61+
#[suggestion_part(code = "")]
62+
lo: Span,
63+
#[suggestion_part(code = "")]
64+
hi: Span,
65+
},
5966
#[label(parse_expect_path)]
6067
ExpectPath {
6168
#[primary_span]

compiler/rustc_parse/src/parser/diagnostics.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1628,22 +1628,36 @@ impl<'a> Parser<'a> {
16281628
Ok(())
16291629
}
16301630

1631-
pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
1631+
pub(super) fn maybe_recover_from_bad_type_plus(
1632+
&mut self,
1633+
paren_ty_plus: bool,
1634+
ty: &Ty,
1635+
) -> PResult<'a, ()> {
16321636
// Do not add `+` to expected tokens.
16331637
if !self.token.is_like_plus() {
16341638
return Ok(());
16351639
}
16361640

16371641
self.bump(); // `+`
16381642
let _bounds = self.parse_generic_bounds()?;
1643+
let sum_span = ty.span.to(self.prev_token.span);
1644+
16391645
let sub = match &ty.kind {
16401646
TyKind::Ref(_lifetime, mut_ty) => {
16411647
let lo = mut_ty.ty.span.shrink_to_lo();
16421648
let hi = self.prev_token.span.shrink_to_hi();
16431649
BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } }
16441650
}
1645-
TyKind::Ptr(..) | TyKind::BareFn(..) => {
1646-
BadTypePlusSub::ForgotParen { span: ty.span.to(self.prev_token.span) }
1651+
TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
1652+
TyKind::Paren(inner_ty)
1653+
if let TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &inner_ty.kind
1654+
&& let [ast::GenericBound::Outlives(_)] = bounds.as_slice()
1655+
&& !paren_ty_plus /* no trailing `+` */ =>
1656+
{
1657+
BadTypePlusSub::RemoveParen {
1658+
lo: ty.span.shrink_to_lo().to(inner_ty.span.shrink_to_lo()),
1659+
hi: inner_ty.span.shrink_to_hi().to(ty.span.shrink_to_hi()),
1660+
}
16471661
}
16481662
_ => BadTypePlusSub::ExpectPath { span: ty.span },
16491663
};

compiler/rustc_parse/src/parser/ty.rs

+15-14
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,12 @@ impl<'a> Parser<'a> {
256256
return Ok(ty);
257257
}
258258

259-
let lo = self.token.span;
260259
let mut impl_dyn_multi = false;
260+
let mut paren_ty_plus = false;
261+
262+
let lo = self.token.span;
261263
let kind = if self.check(exp!(OpenParen)) {
262-
self.parse_ty_tuple_or_parens(lo, allow_plus)?
264+
self.parse_ty_tuple_or_parens(lo, allow_plus, &mut paren_ty_plus)?
263265
} else if self.eat(exp!(Bang)) {
264266
// Never type `!`
265267
TyKind::Never
@@ -370,7 +372,7 @@ impl<'a> Parser<'a> {
370372

371373
// Try to recover from use of `+` with incorrect priority.
372374
match allow_plus {
373-
AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?,
375+
AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(paren_ty_plus, &ty)?,
374376
AllowPlus::No => self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty),
375377
}
376378
if let RecoverQuestionMark::Yes = recover_question_mark {
@@ -395,7 +397,12 @@ impl<'a> Parser<'a> {
395397
/// Parses either:
396398
/// - `(TYPE)`, a parenthesized type.
397399
/// - `(TYPE,)`, a tuple with a single field of type TYPE.
398-
fn parse_ty_tuple_or_parens(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> {
400+
fn parse_ty_tuple_or_parens(
401+
&mut self,
402+
lo: Span,
403+
allow_plus: AllowPlus,
404+
paren_ty_plus: &mut bool,
405+
) -> PResult<'a, TyKind> {
399406
let mut trailing_plus = false;
400407
let (ts, trailing) = self.parse_paren_comma_seq(|p| {
401408
let ty = p.parse_ty()?;
@@ -404,20 +411,14 @@ impl<'a> Parser<'a> {
404411
})?;
405412

406413
if ts.len() == 1 && matches!(trailing, Trailing::No) {
414+
*paren_ty_plus = trailing_plus;
407415
let ty = ts.into_iter().next().unwrap().into_inner();
408-
let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus();
409416
match ty.kind {
410417
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
411-
TyKind::Path(None, path) if maybe_bounds => {
412-
self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true)
413-
}
414-
// For `('a) + …`, we know that `'a` in type position already lead to an error being
415-
// emitted. To reduce output, let's indirectly suppress E0178 (bad `+` in type) and
416-
// other irrelevant consequential errors.
417-
TyKind::TraitObject(bounds, TraitObjectSyntax::None)
418-
if maybe_bounds && bounds.len() == 1 && !trailing_plus =>
418+
TyKind::Path(None, path)
419+
if allow_plus == AllowPlus::Yes && self.token.is_like_plus() =>
419420
{
420-
self.parse_remaining_bounds(bounds, true)
421+
self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true)
421422
}
422423
// `(TYPE)`
423424
_ => Ok(TyKind::Paren(P(ty))),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
error: parenthesized lifetime bounds are not supported
2+
--> $DIR/trait-object-lifetime-parens.rs:9:21
3+
|
4+
LL | fn f<'a, T: Trait + ('a)>() {}
5+
| ^^^^
6+
|
7+
help: remove the parentheses
8+
|
9+
LL - fn f<'a, T: Trait + ('a)>() {}
10+
LL + fn f<'a, T: Trait + 'a>() {}
11+
|
12+
13+
error: parenthesized lifetime bounds are not supported
14+
--> $DIR/trait-object-lifetime-parens.rs:12:24
15+
|
16+
LL | let _: Box<Trait + ('a)>;
17+
| ^^^^
18+
|
19+
help: remove the parentheses
20+
|
21+
LL - let _: Box<Trait + ('a)>;
22+
LL + let _: Box<Trait + 'a>;
23+
|
24+
25+
error: lifetimes must be followed by `+` to form a trait object type
26+
--> $DIR/trait-object-lifetime-parens.rs:16:17
27+
|
28+
LL | let _: Box<('a) + Trait>;
29+
| ^^
30+
|
31+
help: consider adding a trait bound after the potential lifetime bound
32+
|
33+
LL | let _: Box<('a + /* Trait */) + Trait>;
34+
| +++++++++++++
35+
36+
error[E0178]: expected a path on the left-hand side of `+`
37+
--> $DIR/trait-object-lifetime-parens.rs:16:16
38+
|
39+
LL | let _: Box<('a) + Trait>;
40+
| ^^^^
41+
|
42+
help: remove these parentheses
43+
|
44+
LL - let _: Box<('a) + Trait>;
45+
LL + let _: Box<'a + Trait>;
46+
|
47+
48+
warning: trait objects without an explicit `dyn` are deprecated
49+
--> $DIR/trait-object-lifetime-parens.rs:12:16
50+
|
51+
LL | let _: Box<Trait + ('a)>;
52+
| ^^^^^^^^^^^^
53+
|
54+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
55+
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
56+
= note: `#[warn(bare_trait_objects)]` on by default
57+
help: if this is a dyn-compatible trait, use `dyn`
58+
|
59+
LL | let _: Box<dyn Trait + ('a)>;
60+
| +++
61+
62+
error[E0224]: at least one trait is required for an object type
63+
--> $DIR/trait-object-lifetime-parens.rs:16:17
64+
|
65+
LL | let _: Box<('a) + Trait>;
66+
| ^^
67+
68+
error: aborting due to 5 previous errors; 1 warning emitted
69+
70+
Some errors have detailed explanations: E0178, E0224.
71+
For more information about an error, try `rustc --explain E0178`.

tests/ui/parser/trait-object-lifetime-parens.e2015.stderr

+21-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,25 @@ help: consider adding a trait bound after the potential lifetime bound
3333
LL | let _: Box<('a + /* Trait */) + Trait>;
3434
| +++++++++++++
3535

36-
error: aborting due to 3 previous errors
36+
error[E0178]: expected a path on the left-hand side of `+`
37+
--> $DIR/trait-object-lifetime-parens.rs:16:16
38+
|
39+
LL | let _: Box<('a) + Trait>;
40+
| ^^^^
41+
|
42+
help: remove these parentheses
43+
|
44+
LL - let _: Box<('a) + Trait>;
45+
LL + let _: Box<'a + Trait>;
46+
|
47+
48+
error[E0224]: at least one trait is required for an object type
49+
--> $DIR/trait-object-lifetime-parens.rs:16:17
50+
|
51+
LL | let _: Box<('a) + Trait>;
52+
| ^^
53+
54+
error: aborting due to 5 previous errors
3755

56+
Some errors have detailed explanations: E0178, E0224.
57+
For more information about an error, try `rustc --explain E0178`.

tests/ui/parser/trait-object-lifetime-parens.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s
1111
fn check<'a>() {
1212
let _: Box<Trait + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
1313
//[e2021]~^ ERROR expected a type, found a trait
14-
// FIXME: It'd be great if we could suggest removing the parentheses here too.
15-
//[e2015]~v ERROR lifetimes must be followed by `+` to form a trait object type
14+
15+
//~v ERROR expected a path on the left-hand side of `+`
1616
let _: Box<('a) + Trait>;
17-
//[e2021]~^ ERROR expected type, found lifetime
18-
//[e2021]~| ERROR expected a path on the left-hand side of `+`
17+
//[e2015]~^ ERROR lifetimes must be followed by `+` to form a trait object type
18+
//[e2015]~| ERROR at least one trait is required for an object type
19+
//[e2021]~^^^ ERROR expected type, found lifetime
1920
}
2021

2122
fn main() {}

0 commit comments

Comments
 (0)