Skip to content

Commit b638266

Browse files
authored
Rollup merge of #145474 - fmease:paren-use-bounds-fix, r=fee1-dead
Properly recover from parenthesized use-bounds (precise capturing lists) plus small cleanups Fixes #145470. First commit fixes the issue, second one performs some desperately needed cleanups. The fix shouldn't be a breaking change because IINM the parser always ensures that all brackets are balanced (via a buffer of brackets). Meaning even though we used to accept `(use<>` as a valid precise capturing list, it was guaranteed that we would fail in the end.
2 parents 9a5c00a + f8f7c27 commit b638266

File tree

10 files changed

+117
-103
lines changed

10 files changed

+117
-103
lines changed

compiler/rustc_parse/messages.ftl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -748,9 +748,6 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
748748
.suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters
749749
.suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly
750750
751-
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
752-
parse_parenthesized_lifetime_suggestion = remove the parentheses
753-
754751
parse_path_double_colon = path separator must be a double colon
755752
.suggestion = use a double colon instead
756753

compiler/rustc_parse/src/errors.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,27 +3163,6 @@ pub(crate) struct ModifierLifetime {
31633163
pub modifier: &'static str,
31643164
}
31653165

3166-
#[derive(Subdiagnostic)]
3167-
#[multipart_suggestion(
3168-
parse_parenthesized_lifetime_suggestion,
3169-
applicability = "machine-applicable"
3170-
)]
3171-
pub(crate) struct RemoveParens {
3172-
#[suggestion_part(code = "")]
3173-
pub lo: Span,
3174-
#[suggestion_part(code = "")]
3175-
pub hi: Span,
3176-
}
3177-
3178-
#[derive(Diagnostic)]
3179-
#[diag(parse_parenthesized_lifetime)]
3180-
pub(crate) struct ParenthesizedLifetime {
3181-
#[primary_span]
3182-
pub span: Span,
3183-
#[subdiagnostic]
3184-
pub sugg: RemoveParens,
3185-
}
3186-
31873166
#[derive(Diagnostic)]
31883167
#[diag(parse_underscore_literal_suffix)]
31893168
pub(crate) struct UnderscoreLiteralSuffix {

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,12 +2397,12 @@ impl<'a> Parser<'a> {
23972397
let before = self.prev_token;
23982398
let binder = if self.check_keyword(exp!(For)) {
23992399
let lo = self.token.span;
2400-
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
2400+
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
24012401
let span = lo.to(self.prev_token.span);
24022402

24032403
self.psess.gated_spans.gate(sym::closure_lifetime_binder, span);
24042404

2405-
ClosureBinder::For { span, generic_params: lifetime_defs }
2405+
ClosureBinder::For { span, generic_params: bound_vars }
24062406
} else {
24072407
ClosureBinder::NotPresent
24082408
};

compiler/rustc_parse/src/parser/generics.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,11 @@ impl<'a> Parser<'a> {
172172
})
173173
}
174174

175-
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
176-
/// a trailing comma and erroneous trailing attributes.
175+
/// Parse a (possibly empty) list of generic (lifetime, type, const) parameters.
176+
///
177+
/// ```ebnf
178+
/// GenericParams = (GenericParam ("," GenericParam)* ","?)?
179+
/// ```
177180
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::GenericParam>> {
178181
let mut params = ThinVec::new();
179182
let mut done = false;
@@ -520,15 +523,15 @@ impl<'a> Parser<'a> {
520523
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
521524
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
522525
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
523-
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
526+
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
524527

525528
// Parse type with mandatory colon and (possibly empty) bounds,
526529
// or with mandatory equality sign and the second type.
527530
let ty = self.parse_ty_for_where_clause()?;
528531
if self.eat(exp!(Colon)) {
529532
let bounds = self.parse_generic_bounds()?;
530533
Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
531-
bound_generic_params: lifetime_defs,
534+
bound_generic_params: bound_vars,
532535
bounded_ty: ty,
533536
bounds,
534537
}))

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 80 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,11 @@ impl<'a> Parser<'a> {
308308
// Function pointer type or bound list (trait object type) starting with a poly-trait.
309309
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
310310
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
311-
let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
311+
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
312312
if self.check_fn_front_matter(false, Case::Sensitive) {
313313
self.parse_ty_fn_ptr(
314314
lo,
315-
lifetime_defs,
315+
bound_vars,
316316
Some(self.prev_token.span.shrink_to_lo()),
317317
recover_return_sign,
318318
)?
@@ -326,7 +326,7 @@ impl<'a> Parser<'a> {
326326
let path = self.parse_path(PathStyle::Type)?;
327327
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
328328
let kind = self.parse_remaining_bounds_path(
329-
lifetime_defs,
329+
bound_vars,
330330
path,
331331
lo,
332332
parse_plus,
@@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
359359
let path = self.parse_path(PathStyle::Type)?;
360360
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
361361
self.parse_remaining_bounds_path(
362-
lifetime_defs,
362+
bound_vars,
363363
path,
364364
lo,
365365
parse_plus,
@@ -443,7 +443,7 @@ impl<'a> Parser<'a> {
443443
let ty = ts.into_iter().next().unwrap();
444444
let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus();
445445
match ty.kind {
446-
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
446+
// `"(" BareTraitBound ")" "+" Bound "+" ...`.
447447
TyKind::Path(None, path) if maybe_bounds => self.parse_remaining_bounds_path(
448448
ThinVec::new(),
449449
path,
@@ -853,10 +853,13 @@ impl<'a> Parser<'a> {
853853
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
854854
}
855855

856-
fn parse_precise_capturing_args(
857-
&mut self,
858-
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
859-
let lo = self.token.span;
856+
/// Parse a use-bound aka precise capturing list.
857+
///
858+
/// ```ebnf
859+
/// UseBound = "use" "<" (PreciseCapture ("," PreciseCapture)* ","?)? ">"
860+
/// PreciseCapture = "Self" | Ident | Lifetime
861+
/// ```
862+
fn parse_use_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
860863
self.expect_lt()?;
861864
let (args, _, _) = self.parse_seq_to_before_tokens(
862865
&[exp!(Gt)],
@@ -882,7 +885,13 @@ impl<'a> Parser<'a> {
882885
},
883886
)?;
884887
self.expect_gt()?;
885-
Ok((args, lo.to(self.prev_token.span)))
888+
889+
if let ast::Parens::Yes = parens {
890+
self.expect(exp!(CloseParen))?;
891+
self.report_parenthesized_bound(lo, self.prev_token.span, "precise capturing lists");
892+
}
893+
894+
Ok(GenericBound::Use(args, lo.to(self.prev_token.span)))
886895
}
887896

888897
/// Is a `dyn B0 + ... + Bn` type allowed here?
@@ -940,9 +949,10 @@ impl<'a> Parser<'a> {
940949
self.parse_generic_bounds_common(AllowPlus::Yes)
941950
}
942951

943-
/// Parses bounds of a type parameter `BOUND + BOUND + ...`, possibly with trailing `+`.
952+
/// Parse generic bounds.
944953
///
945-
/// See `parse_generic_bound` for the `BOUND` grammar.
954+
/// Only if `allow_plus` this parses a `+`-separated list of bounds (trailing `+` is admitted).
955+
/// Otherwise, this only parses a single bound or none.
946956
fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> {
947957
let mut bounds = Vec::new();
948958

@@ -988,48 +998,56 @@ impl<'a> Parser<'a> {
988998
|| self.check_keyword(exp!(Use))
989999
}
9901000

991-
/// Parses a bound according to the grammar:
1001+
/// Parse a bound.
1002+
///
9921003
/// ```ebnf
993-
/// BOUND = TY_BOUND | LT_BOUND
1004+
/// Bound = LifetimeBound | UseBound | TraitBound
9941005
/// ```
9951006
fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
996-
let lo = self.token.span;
9971007
let leading_token = self.prev_token;
1008+
let lo = self.token.span;
1009+
1010+
// We only admit parenthesized *trait* bounds. However, we want to gracefully recover from
1011+
// other kinds of parenthesized bounds, so parse the opening parenthesis *here*.
1012+
//
1013+
// In the future we might want to lift this syntactic restriction and
1014+
// introduce "`GenericBound::Paren(Box<GenericBound>)`".
9981015
let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No };
9991016

1000-
let bound = if self.token.is_lifetime() {
1001-
self.parse_generic_lt_bound(lo, parens)?
1017+
if self.token.is_lifetime() {
1018+
self.parse_lifetime_bound(lo, parens)
10021019
} else if self.eat_keyword(exp!(Use)) {
1003-
// parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
1004-
// lifetimes and ident params (including SelfUpper). These are validated later
1005-
// for order, duplication, and whether they actually reference params.
1006-
let use_span = self.prev_token.span;
1007-
let (args, args_span) = self.parse_precise_capturing_args()?;
1008-
GenericBound::Use(args, use_span.to(args_span))
1020+
self.parse_use_bound(lo, parens)
10091021
} else {
1010-
self.parse_generic_ty_bound(lo, parens, &leading_token)?
1011-
};
1012-
1013-
Ok(bound)
1022+
self.parse_trait_bound(lo, parens, &leading_token)
1023+
}
10141024
}
10151025

1016-
/// Parses a lifetime ("outlives") bound, e.g. `'a`, according to:
1026+
/// Parse a lifetime-bound aka outlives-bound.
1027+
///
10171028
/// ```ebnf
1018-
/// LT_BOUND = LIFETIME
1029+
/// LifetimeBound = Lifetime
10191030
/// ```
1020-
fn parse_generic_lt_bound(
1021-
&mut self,
1022-
lo: Span,
1023-
parens: ast::Parens,
1024-
) -> PResult<'a, GenericBound> {
1031+
fn parse_lifetime_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> {
10251032
let lt = self.expect_lifetime();
1026-
let bound = GenericBound::Outlives(lt);
1033+
10271034
if let ast::Parens::Yes = parens {
1028-
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
1029-
// possibly introducing `GenericBound::Paren(Box<GenericBound>)`?
1030-
self.recover_paren_lifetime(lo)?;
1035+
self.expect(exp!(CloseParen))?;
1036+
self.report_parenthesized_bound(lo, self.prev_token.span, "lifetime bounds");
10311037
}
1032-
Ok(bound)
1038+
1039+
Ok(GenericBound::Outlives(lt))
1040+
}
1041+
1042+
fn report_parenthesized_bound(&self, lo: Span, hi: Span, kind: &str) -> ErrorGuaranteed {
1043+
let mut diag =
1044+
self.dcx().struct_span_err(lo.to(hi), format!("{kind} may not be parenthesized"));
1045+
diag.multipart_suggestion(
1046+
"remove the parentheses",
1047+
vec![(lo, String::new()), (hi, String::new())],
1048+
Applicability::MachineApplicable,
1049+
);
1050+
diag.emit()
10331051
}
10341052

10351053
/// Emits an error if any trait bound modifiers were present.
@@ -1074,27 +1092,17 @@ impl<'a> Parser<'a> {
10741092
unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?")
10751093
}
10761094

1077-
/// Recover on `('lifetime)` with `(` already eaten.
1078-
fn recover_paren_lifetime(&mut self, lo: Span) -> PResult<'a, ()> {
1079-
self.expect(exp!(CloseParen))?;
1080-
let span = lo.to(self.prev_token.span);
1081-
let sugg = errors::RemoveParens { lo, hi: self.prev_token.span };
1082-
1083-
self.dcx().emit_err(errors::ParenthesizedLifetime { span, sugg });
1084-
Ok(())
1085-
}
1086-
10871095
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `[const] Trait`.
10881096
///
10891097
/// If no modifiers are present, this does not consume any tokens.
10901098
///
10911099
/// ```ebnf
1092-
/// CONSTNESS = [["["] "const" ["]"]]
1093-
/// ASYNCNESS = ["async"]
1094-
/// POLARITY = ["?" | "!"]
1100+
/// Constness = ("const" | "[" "const" "]")?
1101+
/// Asyncness = "async"?
1102+
/// Polarity = ("?" | "!")?
10951103
/// ```
10961104
///
1097-
/// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers.
1105+
/// See `parse_trait_bound` for more context.
10981106
fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
10991107
let modifier_lo = self.token.span;
11001108
let constness = self.parse_bound_constness()?;
@@ -1187,20 +1195,21 @@ impl<'a> Parser<'a> {
11871195
})
11881196
}
11891197

1190-
/// Parses a type bound according to:
1198+
/// Parse a trait bound.
1199+
///
11911200
/// ```ebnf
1192-
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
1193-
/// TY_BOUND_NOPAREN = [for<GENERIC_PARAMS> CONSTNESS ASYNCNESS | POLARITY] SIMPLE_PATH
1201+
/// TraitBound = BareTraitBound | "(" BareTraitBound ")"
1202+
/// BareTraitBound =
1203+
/// (HigherRankedBinder Constness Asyncness | Polarity)
1204+
/// TypePath
11941205
/// ```
1195-
///
1196-
/// For example, this grammar accepts `for<'a: 'b> [const] ?m::Trait<'a>`.
1197-
fn parse_generic_ty_bound(
1206+
fn parse_trait_bound(
11981207
&mut self,
11991208
lo: Span,
12001209
parens: ast::Parens,
12011210
leading_token: &Token,
12021211
) -> PResult<'a, GenericBound> {
1203-
let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
1212+
let (mut bound_vars, binder_span) = self.parse_higher_ranked_binder()?;
12041213

12051214
let modifiers_lo = self.token.span;
12061215
let modifiers = self.parse_trait_bound_modifiers()?;
@@ -1223,11 +1232,11 @@ impl<'a> Parser<'a> {
12231232
// e.g. `T: for<'a> 'a` or `T: [const] 'a`.
12241233
if self.token.is_lifetime() {
12251234
let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
1226-
return self.parse_generic_lt_bound(lo, parens);
1235+
return self.parse_lifetime_bound(lo, parens);
12271236
}
12281237

1229-
if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? {
1230-
lifetime_defs.extend(more_lifetime_defs);
1238+
if let (more_bound_vars, Some(binder_span)) = self.parse_higher_ranked_binder()? {
1239+
bound_vars.extend(more_bound_vars);
12311240
self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span });
12321241
}
12331242

@@ -1287,7 +1296,7 @@ impl<'a> Parser<'a> {
12871296
};
12881297

12891298
if self.may_recover() && self.token == TokenKind::OpenParen {
1290-
self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
1299+
self.recover_fn_trait_with_lifetime_params(&mut path, &mut bound_vars)?;
12911300
}
12921301

12931302
if let ast::Parens::Yes = parens {
@@ -1310,7 +1319,7 @@ impl<'a> Parser<'a> {
13101319
}
13111320

13121321
let poly_trait =
1313-
PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span), parens);
1322+
PolyTraitRef::new(bound_vars, path, modifiers, lo.to(self.prev_token.span), parens);
13141323
Ok(GenericBound::Trait(poly_trait))
13151324
}
13161325

@@ -1349,8 +1358,12 @@ impl<'a> Parser<'a> {
13491358
}
13501359
}
13511360

1352-
/// Optionally parses `for<$generic_params>`.
1353-
pub(super) fn parse_late_bound_lifetime_defs(
1361+
/// Parse an optional higher-ranked binder.
1362+
///
1363+
/// ```ebnf
1364+
/// HigherRankedBinder = ("for" "<" GenericParams ">")?
1365+
/// ```
1366+
pub(super) fn parse_higher_ranked_binder(
13541367
&mut self,
13551368
) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> {
13561369
if self.eat_keyword(exp!(For)) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Ensure that we forbid parenthesized use-bounds. In the future we might want
2+
// to lift this restriction but for now they bear no use whatsoever.
3+
4+
fn f() -> impl Sized + (use<>) {}
5+
//~^ ERROR precise capturing lists may not be parenthesized
6+
//~| HELP remove the parentheses
7+
8+
fn main() {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: precise capturing lists may not be parenthesized
2+
--> $DIR/parenthesized.rs:4:24
3+
|
4+
LL | fn f() -> impl Sized + (use<>) {}
5+
| ^^^^^^^
6+
|
7+
help: remove the parentheses
8+
|
9+
LL - fn f() -> impl Sized + (use<>) {}
10+
LL + fn f() -> impl Sized + use<> {}
11+
|
12+
13+
error: aborting due to 1 previous error
14+

0 commit comments

Comments
 (0)