about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2025-08-15 22:14:37 +0200
committerLeón Orell Valerian Liehr <me@fmease.dev>2025-08-16 01:21:35 +0200
commiteb3e0d4c8a5599db0a4624662e5ab59380be8cd2 (patch)
tree237a3761291924c77aadde70402e17b275d4d629 /compiler/rustc_parse/src
parent577166503aee7290e09374da21f4045c455acfd5 (diff)
downloadrust-eb3e0d4c8a5599db0a4624662e5ab59380be8cd2.tar.gz
rust-eb3e0d4c8a5599db0a4624662e5ab59380be8cd2.zip
Properly recover from parenthesized use-bounds (precise capturing)
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs42
1 files changed, 26 insertions, 16 deletions
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 0d479731e73..b02eeeb93a8 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -849,8 +849,9 @@ impl<'a> Parser<'a> {
 
     fn parse_precise_capturing_args(
         &mut self,
-    ) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
-        let lo = self.token.span;
+        lo: Span,
+        parens: ast::Parens,
+    ) -> PResult<'a, GenericBound> {
         self.expect_lt()?;
         let (args, _, _) = self.parse_seq_to_before_tokens(
             &[exp!(Gt)],
@@ -876,7 +877,22 @@ impl<'a> Parser<'a> {
             },
         )?;
         self.expect_gt()?;
-        Ok((args, lo.to(self.prev_token.span)))
+
+        if let ast::Parens::Yes = parens {
+            self.expect(exp!(CloseParen))?;
+            let hi = self.prev_token.span;
+            let mut diag = self
+                .dcx()
+                .struct_span_err(lo.to(hi), "precise capturing lists may not be parenthesized");
+            diag.multipart_suggestion(
+                "remove the parentheses",
+                vec![(lo, String::new()), (hi, String::new())],
+                Applicability::MachineApplicable,
+            );
+            diag.emit();
+        }
+
+        Ok(GenericBound::Use(args, lo.to(self.prev_token.span)))
     }
 
     /// Is a `dyn B0 + ... + Bn` type allowed here?
@@ -987,24 +1003,18 @@ impl<'a> Parser<'a> {
     /// BOUND = TY_BOUND | LT_BOUND
     /// ```
     fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> {
-        let lo = self.token.span;
         let leading_token = self.prev_token;
+        let lo = self.token.span;
+
         let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No };
 
-        let bound = if self.token.is_lifetime() {
-            self.parse_generic_lt_bound(lo, parens)?
+        if self.token.is_lifetime() {
+            self.parse_generic_lt_bound(lo, parens)
         } else if self.eat_keyword(exp!(Use)) {
-            // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
-            // lifetimes and ident params (including SelfUpper). These are validated later
-            // for order, duplication, and whether they actually reference params.
-            let use_span = self.prev_token.span;
-            let (args, args_span) = self.parse_precise_capturing_args()?;
-            GenericBound::Use(args, use_span.to(args_span))
+            self.parse_precise_capturing_args(lo, parens)
         } else {
-            self.parse_generic_ty_bound(lo, parens, &leading_token)?
-        };
-
-        Ok(bound)
+            self.parse_generic_ty_bound(lo, parens, &leading_token)
+        }
     }
 
     /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: