nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: dtolnay/syn
Pull request #1625 (2024-04-17 19:02:52)

Improve how None-delimited groups are counted by peek


Patch:
diff --git a/src/buffer.rs b/src/buffer.rs
index 86dec46a..d4b0dd11 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -346,11 +346,13 @@ impl<'a> Cursor<'a> {
         self.span()
     }
 
-    /// Skip over the next token without cloning it. Returns `None` if this
-    /// cursor points to eof.
+    /// Skip over the next token that is not a None-delimited group, without
+    /// cloning it. Returns `None` if this cursor points to eof.
     ///
     /// This method treats `'lifetimes` as a single token.
-    pub(crate) fn skip(self) -> Option<Cursor<'a>> {
+    pub(crate) fn skip(mut self) -> Option<Cursor<'a>> {
+        self.ignore_none();
+
         let len = match self.entry() {
             Entry::End(_) => return None,
 
diff --git a/src/parse.rs b/src/parse.rs
index aafc1a96..5d0fbc43 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -614,11 +614,6 @@ impl<'a> ParseBuffer<'a> {
     /// ```
     pub fn peek2<T: Peek>(&self, token: T) -> bool {
         fn peek2(buffer: &ParseBuffer, peek: fn(Cursor) -> bool) -> bool {
-            if let Some(group) = buffer.cursor().group(Delimiter::None) {
-                if group.0.skip().map_or(false, peek) {
-                    return true;
-                }
-            }
             buffer.cursor().skip().map_or(false, peek)
         }
 
@@ -629,11 +624,6 @@ impl<'a> ParseBuffer<'a> {
     /// Looks at the third-next token in the parse stream.
     pub fn peek3<T: Peek>(&self, token: T) -> bool {
         fn peek3(buffer: &ParseBuffer, peek: fn(Cursor) -> bool) -> bool {
-            if let Some(group) = buffer.cursor().group(Delimiter::None) {
-                if group.0.skip().and_then(Cursor::skip).map_or(false, peek) {
-                    return true;
-                }
-            }
             buffer
                 .cursor()
                 .skip()


Test patch:
diff --git a/tests/test_parse_stream.rs b/tests/test_parse_stream.rs
index 11dc1a66..3a009b49 100644
--- a/tests/test_parse_stream.rs
+++ b/tests/test_parse_stream.rs
@@ -136,8 +136,8 @@ fn test_peek_groups() {
         assert!(!input.peek2(Token![::]));
         assert!(input.peek2(Token![!]));
         assert!(input.peek2(token::Group));
-        assert!(!input.peek3(Token![=])); // FIXME
-        assert!(input.peek3(Token![static])); // FIXME
+        assert!(input.peek3(Token![=]));
+        assert!(!input.peek3(Token![static]));
 
         let content;
         parenthesized!(content in input);
@@ -155,8 +155,8 @@ fn test_peek_groups() {
         assert!(input.peek(token::Group));
         assert!(input.peek(Token![!]));
         assert!(input.peek2(Token![=]));
-        assert!(!input.peek3(Token![static])); // FIXME
-        assert!(input.peek2(Token![static])); // FIXME
+        assert!(input.peek3(Token![static]));
+        assert!(!input.peek2(Token![static]));
 
         let _: Token![!] = input.parse()?;
Repository: dtolnay/syn
Pull request #1646 (2024-05-11 20:19:11)

Make ParseBuffer unwindsafe
ParseBuffer contains interior mutability, but there isn't any way to get it into some invalid state by panicking in user code.


Patch:
diff --git a/src/parse.rs b/src/parse.rs
index 5d0fbc43..825ffc6c 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -197,6 +197,7 @@ use std::hash::{Hash, Hasher};
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::Deref;
+use std::panic::{RefUnwindSafe, UnwindSafe};
 use std::rc::Rc;
 use std::str::FromStr;
 
@@ -283,6 +284,9 @@ impl<'a> Debug for ParseBuffer<'a> {
     }
 }
 
+impl<'a> UnwindSafe for ParseBuffer<'a> {}
+impl<'a> RefUnwindSafe for ParseBuffer<'a> {}
+
 /// Cursor state associated with speculative parsing.
 ///
 /// This type is the input of the closure provided to [`ParseStream::step`].


Test patch:
diff --git a/tests/test_parse_buffer.rs b/tests/test_parse_buffer.rs
index 1c5908bc..62abc6d2 100644
--- a/tests/test_parse_buffer.rs
+++ b/tests/test_parse_buffer.rs
@@ -1,6 +1,7 @@
 #![allow(clippy::non_ascii_literal)]
 
-use proc_macro2::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree};
+use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, TokenStream, TokenTree};
+use std::panic;
 use syn::parse::discouraged::Speculative as _;
 use syn::parse::{Parse, ParseStream, Parser, Result};
 use syn::{parenthesized, Token};
@@ -90,3 +91,13 @@ fn trailing_empty_none_group() {
 
     parse.parse2(tokens).unwrap();
 }
+
+#[test]
+fn test_unwind_safe() {
+    fn parse(input: ParseStream) -> Result<Ident> {
+        let thread_result = panic::catch_unwind(|| input.parse());
+        thread_result.unwrap()
+    }
+
+    parse.parse_str("throw").unwrap();
+}
Repository: dtolnay/syn
Pull request #1698 (2024-07-07 02:47:31)

Parenthesize ranges on left-hand side of assignment operator
Unparenthesized ranges are legal on the right of an assignment, but not on the left of an assignment.

```console
error: expected one of `;` or `}`, found `=`
 --> src/main.rs:3:12
  |
3 |         .. = true;
  |            ^ expected one of `;` or `}`

error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `=`
 --> src/main.rs:6:14
  |
6 |         a..b = true;
  |              ^ expected one of 8 possible tokens
```


Patch:
diff --git a/src/expr.rs b/src/expr.rs
index f92954ac..6d1df795 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -3157,7 +3157,7 @@ pub(crate) mod printing {
         outer_attrs_to_tokens(&e.attrs, tokens);
         print_subexpression(
             &e.left,
-            Precedence::of(&e.left) <= Precedence::Assign,
+            Precedence::of(&e.left) <= Precedence::Range,
             tokens,
             fixup.leftmost_subexpression(),
         );
@@ -3247,7 +3247,7 @@ pub(crate) mod printing {
 
         let binop_prec = Precedence::of_binop(&e.op);
         let (mut left_needs_group, right_needs_group) = if let Precedence::Assign = binop_prec {
-            (left_prec <= binop_prec, right_prec < binop_prec)
+            (left_prec <= Precedence::Range, right_prec < binop_prec)
         } else {
             (left_prec < binop_prec, right_prec <= binop_prec)
         };


Test patch:
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index f48f8ed1..3bd26877 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -677,6 +677,8 @@ fn test_fixup() {
         quote! { if let _ = ((break) - 1 || true) {} },
         quote! { if let _ = (break + 1 || true) {} },
         quote! { (break)() },
+        quote! { (..) = () },
+        quote! { (..) += () },
     ] {
         let original: Expr = syn::parse2(tokens).unwrap();
Repository: dtolnay/syn
Pull request #1699 (2024-07-07 02:54:43)

Parenthesize chained comparison on both sides
`a < b < c` is not valid syntax. Depending on whether the inner comparison is on the left or right of the outer one, syn needs to insert parentheses to print either `(a < b) < c` or `a < (b < c)`.


Patch:
diff --git a/src/expr.rs b/src/expr.rs
index 6d1df795..9493822f 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -3246,10 +3246,10 @@ pub(crate) mod printing {
         );
 
         let binop_prec = Precedence::of_binop(&e.op);
-        let (mut left_needs_group, right_needs_group) = if let Precedence::Assign = binop_prec {
-            (left_prec <= Precedence::Range, right_prec < binop_prec)
-        } else {
-            (left_prec < binop_prec, right_prec <= binop_prec)
+        let (mut left_needs_group, right_needs_group) = match binop_prec {
+            Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec),
+            Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec),
+            _ => (left_prec < binop_prec, right_prec <= binop_prec),
         };
 
         // These cases require parenthesization independently of precedence.


Test patch:
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 3bd26877..38492864 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -679,6 +679,7 @@ fn test_fixup() {
         quote! { (break)() },
         quote! { (..) = () },
         quote! { (..) += () },
+        quote! { (1 < 2) == (3 < 4) },
     ] {
         let original: Expr = syn::parse2(tokens).unwrap();
Repository: dtolnay/syn
Pull request #1700 (2024-07-07 03:08:06)

Parenthesize leading Expr::Let in statement
Printing Expr::Let at the start of a statement would cause it to be interpreted as Stmt::Local. This PR causes parentheses to be inserted in that scenario to preserve the Expr::Let.


Patch:
diff --git a/src/fixup.rs b/src/fixup.rs
index 9423e369..ddee906f 100644
--- a/src/fixup.rs
+++ b/src/fixup.rs
@@ -221,6 +221,7 @@ impl FixupContext {
     /// examples.
     pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool {
         (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr))
+            || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_)))
             || (self.leftmost_subexpression_in_match_arm
                 && !classify::requires_comma_to_be_match_arm(expr))
     }


Test patch:
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 38492864..11d0f86c 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -680,6 +680,7 @@ fn test_fixup() {
         quote! { (..) = () },
         quote! { (..) += () },
         quote! { (1 < 2) == (3 < 4) },
+        quote! { { (let _ = ()) } },
     ] {
         let original: Expr = syn::parse2(tokens).unwrap();
Repository: dtolnay/syn
Pull request #1707 (2024-07-13 19:28:08)

Parse `use<'a, T>` precise capturing bounds as verbatim
Parses the new `use<>` precise capturing bound (https://rust-lang.github.io/rfcs/3617-precise-capturing.html) as verbatim in all bound positions.

Not exactly sure what the process is for syn support for nightly features. I could alternatively prepare a PR adding full support for this.

cc https://github.com/rust-lang/rust/issues/123432


Patch:
diff --git a/src/generics.rs b/src/generics.rs
index c755151d..f2136aa5 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -772,6 +772,27 @@ pub(crate) mod parsing {
 
             let begin = input.fork();
 
+            if cfg!(feature = "full") && input.peek(Token![use]) {
+                input.parse::<Token![use]>()?;
+                input.parse::<Token![<]>()?;
+                while !input.peek(Token![>]) {
+                    if input.peek(Lifetime) {
+                        input.parse::<Lifetime>()?;
+                    } else if input.peek(Ident) {
+                        input.parse::<Ident>()?;
+                    } else {
+                        break;
+                    }
+                    if input.peek(Token![,]) {
+                        input.parse::<Token![,]>()?;
+                    } else {
+                        break;
+                    }
+                }
+                input.parse::<Token![>]>()?;
+                return Ok(TypeParamBound::Verbatim(verbatim::between(&begin, input)));
+            }
+
             let content;
             let (paren_token, content) = if input.peek(token::Paren) {
                 (Some(parenthesized!(content in input)), &content)


Test patch:
diff --git a/tests/test_ty.rs b/tests/test_ty.rs
index f1ced9ea..d85479de 100644
--- a/tests/test_ty.rs
+++ b/tests/test_ty.rs
@@ -395,3 +395,50 @@ fn test_tuple_comma() {
     }
     "###);
 }
+
+#[test]
+fn test_impl_trait_use() {
+    let tokens = quote! {
+        impl Sized + use<'_, 'a, A, Test>
+    };
+
+    snapshot!(tokens as Type, @r###"
+    Type::ImplTrait {
+        bounds: [
+            TypeParamBound::Trait(TraitBound {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Sized",
+                        },
+                    ],
+                },
+            }),
+            Token![+],
+            TypeParamBound::Verbatim(`use < '_ , 'a , A , Test >`),
+        ],
+    }
+    "###);
+
+    let trailing = quote! {
+        impl Sized + use<'_,>
+    };
+
+    snapshot!(trailing as Type, @r###"
+    Type::ImplTrait {
+        bounds: [
+            TypeParamBound::Trait(TraitBound {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Sized",
+                        },
+                    ],
+                },
+            }),
+            Token![+],
+            TypeParamBound::Verbatim(`use < '_ , >`),
+        ],
+    }
+    "###);
+}