Snap for 6533464 from dfc35a2c85654d53298514ffc85469e7024a082a to sdk-release

Change-Id: I76f507336f58158c70f3629fcd705bcc0da2a15f
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 28d7b30..1347e27 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "5feb37605c9ec2d237651dbd669e3527566a839e"
+    "sha1": "157559c3faf524ae24f8329537d2a763f6e18931"
   }
 }
diff --git a/Cargo.toml b/Cargo.toml
index 90aff3f..97a4cab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "paste-impl"
-version = "0.1.10"
+version = "0.1.14"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 description = "Implementation detail of the `paste` crate"
 license = "MIT OR Apache-2.0"
@@ -34,5 +34,3 @@
 
 [dependencies.syn]
 version = "1.0"
-[badges.travis-ci]
-repository = "dtolnay/paste"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 9b7a04e..80d2071 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "paste-impl"
-version = "0.1.10"
+version = "0.1.14"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
@@ -10,9 +10,6 @@
 [lib]
 proc-macro = true
 
-[badges]
-travis-ci = { repository = "dtolnay/paste" }
-
 [dependencies]
 proc-macro-hack = "0.5"
 proc-macro2 = "1.0"
diff --git a/METADATA b/METADATA
index e289934..2b0d5de 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
     type: GIT
     value: "https://github.com/dtolnay/paste"
   }
-  version: "0.1.10"
+  version: "0.1.14"
   license_type: NOTICE
   last_upgrade_date {
     year: 2020
-    month: 4
-    day: 17
+    month: 5
+    day: 25
   }
 }
diff --git a/src/lib.rs b/src/lib.rs
index b2c1e41..1c762d9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,31 +40,57 @@
 
 impl Parse for PasteInput {
     fn parse(input: ParseStream) -> Result<Self> {
-        let mut expanded = TokenStream::new();
-        while !input.is_empty() {
-            match input.parse()? {
-                TokenTree::Group(group) => {
-                    let delimiter = group.delimiter();
-                    let content = group.stream();
-                    let span = group.span();
-                    if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
-                        let segments = parse_bracket_as_segments.parse2(content)?;
-                        let pasted = paste_segments(span, &segments)?;
-                        pasted.to_tokens(&mut expanded);
-                    } else if delimiter == Delimiter::None && is_single_ident(&content) {
-                        content.to_tokens(&mut expanded);
-                    } else {
-                        let nested = PasteInput::parse.parse2(content)?;
-                        let mut group = Group::new(delimiter, nested.expanded);
+        let mut contains_paste = false;
+        let expanded = parse(input, &mut contains_paste)?;
+        Ok(PasteInput { expanded })
+    }
+}
+
+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()? {
+            TokenTree::Group(group) => {
+                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)?;
+                    pasted.to_tokens(&mut expanded);
+                    *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)?;
+                    let group = if group_contains_paste {
+                        let mut group = Group::new(delimiter, nested);
                         group.set_span(span);
+                        *contains_paste = true;
+                        group
+                    } else {
+                        group.clone()
+                    };
+                    if in_path && delimiter == Delimiter::None {
+                        group.stream().to_tokens(&mut expanded);
+                        *contains_paste = true;
+                    } else {
                         group.to_tokens(&mut expanded);
                     }
                 }
-                other => other.to_tokens(&mut expanded),
             }
+            other => other.to_tokens(&mut expanded),
         }
-        Ok(PasteInput { expanded })
+        prev_colons = colons;
+        colons = save.peek(Token![::]);
     }
+    Ok(expanded)
 }
 
 fn is_paste_operation(input: &TokenStream) -> bool {
@@ -72,15 +98,30 @@
     parse_bracket_as_segments.parse2(input).is_ok()
 }
 
-fn is_single_ident(input: &TokenStream) -> bool {
-    let mut has_ident = false;
-    for tt in input.clone() {
-        match tt {
-            TokenTree::Ident(_) if !has_ident => has_ident = true,
-            _ => return false,
-        }
+// https://github.com/dtolnay/paste/issues/26
+fn is_none_delimited_single_ident_or_lifetime(delimiter: Delimiter, input: &TokenStream) -> bool {
+    if delimiter != Delimiter::None {
+        return false;
     }
-    has_ident
+
+    #[derive(PartialEq)]
+    enum State {
+        Init,
+        Ident,
+        Apostrophe,
+        Lifetime,
+    }
+
+    let mut state = State::Init;
+    for tt in input.clone() {
+        state = match (state, &tt) {
+            (State::Init, TokenTree::Ident(_)) => State::Ident,
+            (State::Init, TokenTree::Punct(punct)) if punct.as_char() == '\'' => State::Apostrophe,
+            (State::Apostrophe, TokenTree::Ident(_)) => State::Lifetime,
+            _ => return false,
+        };
+    }
+    state == State::Ident || state == State::Lifetime
 }
 
 enum Segment {
@@ -194,6 +235,26 @@
                         prev = ch;
                     }
                     evaluated.push(acc.to_lowercase());
+                } else if ident == "camel" {
+                    let mut acc = String::new();
+                    let mut prev = '_';
+                    for ch in last.chars() {
+                        if ch != '_' {
+                            if prev == '_' {
+                                for chu in ch.to_uppercase() {
+                                    acc.push(chu);
+                                }
+                            } else if prev.is_uppercase() {
+                                for chl in ch.to_lowercase() {
+                                    acc.push(chl);
+                                }
+                            } else {
+                                acc.push(ch);
+                            }
+                        }
+                        prev = ch;
+                    }
+                    evaluated.push(acc);
                 } else {
                     return Err(Error::new_spanned(span, "unsupported modifier"));
                 }