Repository: dtolnay/syn
Pull request #952 (2021-01-06 13:17:07)

Parse empty supertrait list
Rustc parses all of the following. [(playground)](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=00b4ab96698af6d42f37be4f27c2aaba)

```rust
trait Trait where {}
trait Trait: where {}
trait Trait: Sized where {}
trait Trait: Sized + where {}
```

Syn was failing to parse if there wasn't at least one supertrait after the `:`.


Patch:
diff --git a/src/item.rs b/src/item.rs
index 883ecc79..c5083d05 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -2032,14 +2032,14 @@ pub mod parsing {
         let mut supertraits = Punctuated::new();
         if colon_token.is_some() {
             loop {
-                supertraits.push_value(input.parse()?);
                 if input.peek(Token![where]) || input.peek(token::Brace) {
                     break;
                 }
-                supertraits.push_punct(input.parse()?);
+                supertraits.push_value(input.parse()?);
                 if input.peek(Token![where]) || input.peek(token::Brace) {
                     break;
                 }
+                supertraits.push_punct(input.parse()?);
             }
         }
 


Test patch:
diff --git a/tests/test_item.rs b/tests/test_item.rs
index c28fc87f..7695f199 100644
--- a/tests/test_item.rs
+++ b/tests/test_item.rs
@@ -4,7 +4,7 @@ mod macros;
 use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
 use quote::quote;
 use std::iter::FromIterator;
-use syn::Item;
+use syn::{Item, ItemTrait};
 
 #[test]
 fn test_macro_variable_attr() {
@@ -159,3 +159,85 @@ fn test_macro_variable_impl() {
     }
     "###);
 }
+
+#[test]
+fn test_supertraits() {
+    // Rustc parses all of the following.
+
+    #[rustfmt::skip]
+    let tokens = quote!(trait Trait where {});
+    snapshot!(tokens as ItemTrait, @r###"
+    ItemTrait {
+        vis: Inherited,
+        ident: "Trait",
+        generics: Generics {
+            where_clause: Some(WhereClause),
+        },
+    }
+    "###);
+
+    #[rustfmt::skip]
+    let tokens = quote!(trait Trait: where {});
+    snapshot!(tokens as ItemTrait, @r###"
+    ItemTrait {
+        vis: Inherited,
+        ident: "Trait",
+        generics: Generics {
+            where_clause: Some(WhereClause),
+        },
+        colon_token: Some,
+    }
+    "###);
+
+    #[rustfmt::skip]
+    let tokens = quote!(trait Trait: Sized where {});
+    snapshot!(tokens as ItemTrait, @r###"
+    ItemTrait {
+        vis: Inherited,
+        ident: "Trait",
+        generics: Generics {
+            where_clause: Some(WhereClause),
+        },
+        colon_token: Some,
+        supertraits: [
+            Trait(TraitBound {
+                modifier: None,
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Sized",
+                            arguments: None,
+                        },
+                    ],
+                },
+            }),
+        ],
+    }
+    "###);
+
+    #[rustfmt::skip]
+    let tokens = quote!(trait Trait: Sized + where {});
+    snapshot!(tokens as ItemTrait, @r###"
+    ItemTrait {
+        vis: Inherited,
+        ident: "Trait",
+        generics: Generics {
+            where_clause: Some(WhereClause),
+        },
+        colon_token: Some,
+        supertraits: [
+            Trait(TraitBound {
+                modifier: None,
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Sized",
+                            arguments: None,
+                        },
+                    ],
+                },
+            }),
+        ],
+    }
+    "###);
+}