Repository: dtolnay/paste (rust)

Issue:
paste 0.1.13 is not backwards compatible
The new version released yesterday (0.1.13) breaks dependent modules. For example jemalloc-ctl compiles with paste 0.1.12, but not with 0.1.13:
```
   Compiling jemalloc-ctl v0.3.3
error: expected identifier, found `Mib<[usize; 2]>`
##[error]  --> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/jemalloc-ctl-0.3.3/src/macros.rs:8:9
   |
8  | /          paste::item! {
9  | |              $(#[$doc])*
10 | |              #[allow(non_camel_case_types)]
11 | |              pub struct $id;
...  |
39 | |              pub struct [<$id _mib>](pub ::keys::$mib);
40 | |          }
   | |__________^
   | 
  ::: /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/jemalloc-ctl-0.3.3/src/arenas.rs:3:1
   |
3  |  / option! {
4  |  |     narenas[ str: b"arenas.narenas\0", non_str: 2 ] => libc::c_uint |
5  |  |     ops: r |
6  |  |     docs:
...   |
26 |  |     mib_docs: /// See [`narenas`].
27 |  | }
   |  |_- in this macro invocation
```

The above compiler error was fixed by forcing Cargo to select version 0.1.12 of paste instead of 0.1.13.

Based on cargo's scheme to by default select the "maximum compatible" version based semantic versioning, if a backwards-incompatible change needs to be made, a new minor version should be used (e.g., 0.2.0) so that dependent crates do not break. Thanks!

Pull request:
Flatten None-delimited group inside of path
Closes #31.

Patch:
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index ebf8da8..1c762d9 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -48,6 +48,7 @@ impl Parse for PasteInput {
 
 fn parse(input: ParseStream, contains_paste: &mut bool) -> Result<TokenStream> {
     let mut expanded = TokenStream::new();
+    let (mut prev_colons, mut colons) = (false, false);
     while !input.is_empty() {
         let save = input.fork();
         match input.parse()? {
@@ -55,6 +56,7 @@ fn parse(input: ParseStream, contains_paste: &mut bool) -> Result<TokenStream> {
                 let delimiter = group.delimiter();
                 let content = group.stream();
                 let span = group.span();
+                let in_path = prev_colons || input.peek(Token![::]);
                 if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
                     let segments = parse_bracket_as_segments.parse2(content)?;
                     let pasted = paste_segments(span, &segments)?;
@@ -62,22 +64,31 @@ fn parse(input: ParseStream, contains_paste: &mut bool) -> Result<TokenStream> {
                     *contains_paste = true;
                 } else if is_none_delimited_single_ident_or_lifetime(delimiter, &content) {
                     content.to_tokens(&mut expanded);
+                    *contains_paste |= in_path;
                 } else {
                     let mut group_contains_paste = false;
                     let nested = (|input: ParseStream| parse(input, &mut group_contains_paste))
                         .parse2(content)?;
-                    if group_contains_paste {
+                    let group = if group_contains_paste {
                         let mut group = Group::new(delimiter, nested);
                         group.set_span(span);
-                        group.to_tokens(&mut expanded);
-                        *contains_paste |= group_contains_paste;
+                        *contains_paste = true;
+                        group
+                    } else {
+                        group.clone()
+                    };
+                    if in_path && delimiter == Delimiter::None {
+                        group.stream().to_tokens(&mut expanded);
+                        *contains_paste = true;
                     } else {
-                        save.parse::<TokenTree>()?.to_tokens(&mut expanded);
+                        group.to_tokens(&mut expanded);
                     }
                 }
             }
             other => other.to_tokens(&mut expanded),
         }
+        prev_colons = colons;
+        colons = save.peek(Token![::]);
     }
     Ok(expanded)
 }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index b8547b6..8e6ea75 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -325,3 +325,42 @@ mod test_doc_expr {
         let _: S;
     }
 }
+
+mod test_type_in_path {
+    // https://github.com/dtolnay/paste/issues/31
+
+    mod keys {
+        #[derive(Default)]
+        pub struct Mib<T = ()>(std::marker::PhantomData<T>);
+    }
+
+    macro_rules! types {
+        ($mib:ty) => {
+            paste::item! {
+                #[derive(Default)]
+                pub struct S(pub keys::$mib);
+            }
+        };
+    }
+
+    macro_rules! write {
+        ($fn:ident, $field:ty) => {
+            paste::item! {
+                pub fn $fn() -> $field {
+                    $field::default()
+                }
+            }
+        };
+    }
+
+    types! {Mib<[usize; 2]>}
+    write! {get_a, keys::Mib}
+    write! {get_b, usize}
+
+    #[test]
+    fn test_type_in_path() {
+        let _: S;
+        let _ = get_a;
+        let _ = get_b;
+    }
+}