Update to latest syn-1.0.16

* fill back missing NOTICE,METADATA,*LICENSE* files

Bug: 150877376
Test: make
Test: atest --host -c --include-subdirs external/rust/crates
Change-Id: Ib5df6b8fb97764214e701a888e44fab3a4245800
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index c053ad6..1339f73 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "b06fef4bd130b74cf47b44a68e76522a45bec18d"
+    "sha1": "b76945a91ff2e6cccbab7d5dd09dbdfea300571d"
   }
 }
diff --git a/Android.bp b/Android.bp
index e7bff6d..10f8444 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,3 +27,8 @@
         "libunicode_xid",
     ],
 }
+
+// dependent_library ["feature_list"]
+//   proc-macro2-1.0.9 "default,proc-macro"
+//   quote-1.0.3 "default,proc-macro"
+//   unicode-xid-0.2.0 "default"
diff --git a/Cargo.toml b/Cargo.toml
index 82f815b..e716835 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "syn"
-version = "1.0.7"
+version = "1.0.16"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 include = ["/benches/**", "/build.rs", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src/**", "/tests/**"]
 description = "Parser for Rust source code"
@@ -37,7 +37,7 @@
 name = "file"
 required-features = ["full", "parsing"]
 [dependencies.proc-macro2]
-version = "1.0"
+version = "1.0.7"
 default-features = false
 
 [dependencies.quote]
@@ -47,18 +47,31 @@
 
 [dependencies.unicode-xid]
 version = "0.2"
+[dev-dependencies.anyhow]
+version = "1.0"
+
+[dev-dependencies.flate2]
+version = "1.0"
+
 [dev-dependencies.insta]
-version = "0.11"
+version = "0.12"
 
 [dev-dependencies.rayon]
 version = "1.0"
 
 [dev-dependencies.ref-cast]
-version = "0.2"
+version = "1.0"
 
 [dev-dependencies.regex]
 version = "1.0"
 
+[dev-dependencies.reqwest]
+version = "0.10"
+features = ["blocking"]
+
+[dev-dependencies.tar]
+version = "0.4"
+
 [dev-dependencies.termcolor]
 version = "1.0"
 
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f050fac..79e5dc6 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "syn"
-version = "1.0.7" # don't forget to update html_root_url and syn.json
+version = "1.0.16" # don't forget to update html_root_url and syn.json
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 license = "MIT OR Apache-2.0"
 description = "Parser for Rust source code"
@@ -34,15 +34,19 @@
 proc-macro = ["proc-macro2/proc-macro", "quote/proc-macro"]
 
 [dependencies]
-proc-macro2 = { version = "1.0", default-features = false }
+proc-macro2 = { version = "1.0.7", default-features = false }
 quote = { version = "1.0", optional = true, default-features = false }
 unicode-xid = "0.2"
 
 [dev-dependencies]
-insta = "0.11"
+anyhow = "1.0"
+flate2 = "1.0"
+insta = "0.12"
 rayon = "1.0"
-ref-cast = "0.2"
+ref-cast = "1.0"
 regex = "1.0"
+reqwest = { version = "0.10", features = ["blocking"] }
+tar = "0.4"
 termcolor = "1.0"
 walkdir = "2.1"
 
diff --git a/METADATA b/METADATA
index b3cf575..314c2d7 100644
--- a/METADATA
+++ b/METADATA
@@ -1,8 +1,5 @@
 name: "syn"
-description:
-    "Syn is a parsing library for parsing a stream of Rust tokens into a syntax "
-    "tree of Rust source code."
-
+description: "Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code."
 third_party {
   url {
     type: HOMEPAGE
@@ -12,6 +9,10 @@
     type: GIT
     value: "https://github.com/dtolnay/syn"
   }
-  version: "1.0.7"
-  last_upgrade_date { year: 2020 month: 3 day: 5 }
+  version: "1.0.16"
+  last_upgrade_date {
+    year: 2020
+    month: 3
+    day: 20
+  }
 }
diff --git a/benches/file.rs b/benches/file.rs
index 08ecd90..58ab8df 100644
--- a/benches/file.rs
+++ b/benches/file.rs
@@ -1,9 +1,16 @@
 // $ cargo bench --features full --bench file
 
 #![feature(rustc_private, test)]
+#![recursion_limit = "1024"]
 
 extern crate test;
 
+#[macro_use]
+#[path = "../tests/macros/mod.rs"]
+mod macros;
+
+#[path = "../tests/common/mod.rs"]
+mod common;
 #[path = "../tests/repo/mod.rs"]
 pub mod repo;
 
diff --git a/benches/rust.rs b/benches/rust.rs
index e3d9cd2..941ecb9 100644
--- a/benches/rust.rs
+++ b/benches/rust.rs
@@ -4,7 +4,14 @@
 // $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust
 
 #![cfg_attr(not(syn_only), feature(rustc_private))]
+#![recursion_limit = "1024"]
 
+#[macro_use]
+#[path = "../tests/macros/mod.rs"]
+mod macros;
+
+#[path = "../tests/common/mod.rs"]
+mod common;
 #[path = "../tests/repo/mod.rs"]
 mod repo;
 
@@ -30,21 +37,25 @@
 #[cfg(not(syn_only))]
 mod libsyntax_parse {
     extern crate rustc_data_structures;
+    extern crate rustc_parse;
+    extern crate rustc_span;
     extern crate syntax;
-    extern crate syntax_pos;
 
     use rustc_data_structures::sync::Lrc;
+    use rustc_span::FileName;
     use syntax::edition::Edition;
-    use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
-    use syntax::parse::ParseSess;
+    use syntax::errors::{emitter::Emitter, Diagnostic, Handler};
+    use syntax::sess::ParseSess;
     use syntax::source_map::{FilePathMapping, SourceMap};
-    use syntax_pos::FileName;
 
     pub fn bench(content: &str) -> Result<(), ()> {
         struct SilentEmitter;
 
         impl Emitter for SilentEmitter {
-            fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
+            fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
+            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+                None
+            }
         }
 
         syntax::with_globals(Edition::Edition2018, || {
@@ -52,7 +63,7 @@
             let emitter = Box::new(SilentEmitter);
             let handler = Handler::with_emitter(false, None, emitter);
             let sess = ParseSess::with_span_handler(handler, cm);
-            if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
+            if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
                 FileName::Custom("bench".to_owned()),
                 content.to_owned(),
                 &sess,
diff --git a/build.rs b/build.rs
index c0f9ed3..c6040c1 100644
--- a/build.rs
+++ b/build.rs
@@ -57,7 +57,7 @@
     };
 
     Some(Compiler {
-        minor: minor,
+        minor,
         nightly: version.contains("nightly"),
     })
 }
diff --git a/src/attr.rs b/src/attr.rs
index 34009de..17b3793 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -111,6 +111,43 @@
     ///
     /// [`parse_meta()`]: Attribute::parse_meta
     /// [`parse_args()`]: Attribute::parse_args
+    ///
+    /// <p><br></p>
+    ///
+    /// # Doc comments
+    ///
+    /// The compiler transforms doc comments, such as `/// comment` and `/*!
+    /// comment */`, into attributes before macros are expanded. Each comment is
+    /// expanded into an attribute of the form `#[doc = r"comment"]`.
+    ///
+    /// As an example, the following `mod` items are expanded identically:
+    ///
+    /// ```
+    /// # use syn::{ItemMod, parse_quote};
+    /// let doc: ItemMod = parse_quote! {
+    ///     /// Single line doc comments
+    ///     /// We write so many!
+    ///     /**
+    ///      * Multi-line comments...
+    ///      * May span many lines
+    ///      */
+    ///     mod example {
+    ///         //! Of course, they can be inner too
+    ///         /*! And fit in a single line */
+    ///     }
+    /// };
+    /// let attr: ItemMod = parse_quote! {
+    ///     #[doc = r" Single line doc comments"]
+    ///     #[doc = r" We write so many!"]
+    ///     #[doc = r" Multi-line comments...
+    ///  May span many lines"]
+    ///     mod example {
+    ///         #![doc = r" Of course, they can be inner too"]
+    ///         #![doc = r" And fit in a single line "]
+    ///     }
+    /// };
+    /// assert_eq!(doc, attr);
+    /// ```
     pub struct Attribute #manual_extra_traits {
         pub pound_token: Token![#],
         pub style: AttrStyle,
@@ -247,7 +284,7 @@
 }
 
 #[cfg(feature = "parsing")]
-fn error_expected_args(attr: &Attribute) -> Error {
+fn expected_parentheses(attr: &Attribute) -> String {
     let style = match attr.style {
         AttrStyle::Outer => "#",
         AttrStyle::Inner(_) => "#!",
@@ -261,19 +298,23 @@
         path += &segment.ident.to_string();
     }
 
-    let msg = format!("expected attribute arguments: {}[{}(...)]", style, path);
-
-    #[cfg(feature = "printing")]
-    return Error::new_spanned(attr, msg);
-
-    #[cfg(not(feature = "printing"))]
-    return Error::new(attr.bracket_token.span, msg);
+    format!("{}[{}(...)]", style, path)
 }
 
 #[cfg(feature = "parsing")]
 fn enter_args<'a>(attr: &Attribute, input: ParseStream<'a>) -> Result<ParseBuffer<'a>> {
     if input.is_empty() {
-        return Err(error_expected_args(attr));
+        let expected = expected_parentheses(attr);
+        let msg = format!("expected attribute arguments in parentheses: {}", expected);
+        return Err(crate::error::new2(
+            attr.pound_token.span,
+            attr.bracket_token.span,
+            msg,
+        ));
+    } else if input.peek(Token![=]) {
+        let expected = expected_parentheses(attr);
+        let msg = format!("expected parentheses: {}", expected);
+        return Err(input.error(msg));
     };
 
     let content;
diff --git a/src/buffer.rs b/src/buffer.rs
index 551a5ac..5c2dd8a 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -221,7 +221,6 @@
 
     /// Checks whether the cursor is currently pointing at the end of its valid
     /// scope.
-    #[inline]
     pub fn eof(self) -> bool {
         // We're at eof if we're at the end of our scope.
         self.ptr == self.scope
@@ -342,6 +341,26 @@
             Entry::End(..) => Span::call_site(),
         }
     }
+
+    /// Skip over the next token 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>> {
+        match self.entry() {
+            Entry::End(..) => None,
+
+            // Treat lifetimes as a single tt for the purposes of 'skip'.
+            Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
+                let next = unsafe { self.bump() };
+                match next.entry() {
+                    Entry::Ident(_) => Some(unsafe { next.bump() }),
+                    _ => Some(next),
+                }
+            }
+            _ => Some(unsafe { self.bump() }),
+        }
+    }
 }
 
 pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
diff --git a/src/custom_keyword.rs b/src/custom_keyword.rs
index 200e847..d46e277 100644
--- a/src/custom_keyword.rs
+++ b/src/custom_keyword.rs
@@ -95,7 +95,7 @@
         }
 
         #[doc(hidden)]
-        #[allow(non_snake_case)]
+        #[allow(dead_code, non_snake_case)]
         pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>(
             span: __S,
         ) -> $ident {
diff --git a/src/custom_punctuation.rs b/src/custom_punctuation.rs
index 29fa448..8631b77 100644
--- a/src/custom_punctuation.rs
+++ b/src/custom_punctuation.rs
@@ -82,7 +82,7 @@
         }
 
         #[doc(hidden)]
-        #[allow(non_snake_case)]
+        #[allow(dead_code, non_snake_case)]
         pub fn $ident<__S: $crate::export::IntoSpans<custom_punctuation_repr!($($tt)+)>>(
             spans: __S,
         ) -> $ident {
diff --git a/src/data.rs b/src/data.rs
index 2554506..0d6b1b5 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -238,12 +238,15 @@
     use super::*;
 
     use crate::ext::IdentExt;
+    use crate::parse::discouraged::Speculative;
     use crate::parse::{Parse, ParseStream, Result};
 
     impl Parse for Variant {
         fn parse(input: ParseStream) -> Result<Self> {
+            let attrs = input.call(Attribute::parse_outer)?;
+            let _visibility: Visibility = input.parse()?;
             Ok(Variant {
-                attrs: input.call(Attribute::parse_outer)?,
+                attrs,
                 ident: input.parse()?,
                 fields: {
                     if input.peek(token::Brace) {
@@ -328,27 +331,39 @@
             let pub_token = input.parse::<Token![pub]>()?;
 
             if input.peek(token::Paren) {
-                // TODO: optimize using advance_to
                 let ahead = input.fork();
-                let mut content;
-                parenthesized!(content in ahead);
 
+                let content;
+                let paren_token = parenthesized!(content in ahead);
                 if content.peek(Token![crate])
                     || content.peek(Token![self])
                     || content.peek(Token![super])
                 {
-                    return Ok(Visibility::Restricted(VisRestricted {
-                        pub_token,
-                        paren_token: parenthesized!(content in input),
-                        in_token: None,
-                        path: Box::new(Path::from(content.call(Ident::parse_any)?)),
-                    }));
+                    let path = content.call(Ident::parse_any)?;
+
+                    // Ensure there are no additional tokens within `content`.
+                    // Without explicitly checking, we may misinterpret a tuple
+                    // field as a restricted visibility, causing a parse error.
+                    // e.g. `pub (crate::A, crate::B)` (Issue #720).
+                    if content.is_empty() {
+                        input.advance_to(&ahead);
+                        return Ok(Visibility::Restricted(VisRestricted {
+                            pub_token,
+                            paren_token,
+                            in_token: None,
+                            path: Box::new(Path::from(path)),
+                        }));
+                    }
                 } else if content.peek(Token![in]) {
+                    let in_token: Token![in] = content.parse()?;
+                    let path = content.call(Path::parse_mod_style)?;
+
+                    input.advance_to(&ahead);
                     return Ok(Visibility::Restricted(VisRestricted {
                         pub_token,
-                        paren_token: parenthesized!(content in input),
-                        in_token: Some(content.parse()?),
-                        path: Box::new(content.call(Path::parse_mod_style)?),
+                        paren_token,
+                        in_token: Some(in_token),
+                        path: Box::new(path),
                     }));
                 }
             }
diff --git a/src/discouraged.rs b/src/discouraged.rs
index 4d9ff93..29d1006 100644
--- a/src/discouraged.rs
+++ b/src/discouraged.rs
@@ -164,6 +164,30 @@
             panic!("Fork was not derived from the advancing parse stream");
         }
 
+        let (self_unexp, self_sp) = inner_unexpected(self);
+        let (fork_unexp, fork_sp) = inner_unexpected(fork);
+        if !Rc::ptr_eq(&self_unexp, &fork_unexp) {
+            match (fork_sp, self_sp) {
+                // Unexpected set on the fork, but not on `self`, copy it over.
+                (Some(span), None) => {
+                    self_unexp.set(Unexpected::Some(span));
+                }
+                // Unexpected unset. Use chain to propagate errors from fork.
+                (None, None) => {
+                    fork_unexp.set(Unexpected::Chain(self_unexp));
+
+                    // Ensure toplevel 'unexpected' tokens from the fork don't
+                    // bubble up the chain by replacing the root `unexpected`
+                    // pointer, only 'unexpected' tokens from existing group
+                    // parsers should bubble.
+                    fork.unexpected
+                        .set(Some(Rc::new(Cell::new(Unexpected::None))));
+                }
+                // Unexpected has been set on `self`. No changes needed.
+                (_, Some(_)) => {}
+            }
+        }
+
         // See comment on `cell` in the struct definition.
         self.cell
             .set(unsafe { mem::transmute::<Cursor, Cursor<'static>>(fork.cursor()) })
diff --git a/src/error.rs b/src/error.rs
index 146d652..dbcbc07 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,3 @@
-use std;
 use std::fmt::{self, Debug, Display};
 use std::iter::FromIterator;
 use std::slice;
@@ -250,6 +249,17 @@
     }
 }
 
+#[cfg(all(feature = "parsing", any(feature = "full", feature = "derive")))]
+pub fn new2<T: Display>(start: Span, end: Span, message: T) -> Error {
+    Error {
+        messages: vec![ErrorMessage {
+            start_span: ThreadBound::new(start),
+            end_span: ThreadBound::new(end),
+            message: message.to_string(),
+        }],
+    }
+}
+
 impl Debug for Error {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         if self.messages.len() == 1 {
diff --git a/src/expr.rs b/src/expr.rs
index 2874a46..b40f1ca 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -3,7 +3,10 @@
 #[cfg(feature = "extra-traits")]
 use crate::tt::TokenStreamHelper;
 use proc_macro2::{Span, TokenStream};
-#[cfg(feature = "extra-traits")]
+#[cfg(feature = "printing")]
+use quote::IdentFragment;
+#[cfg(feature = "printing")]
+use std::fmt::{self, Display};
 use std::hash::{Hash, Hasher};
 #[cfg(all(feature = "parsing", feature = "full"))]
 use std::mem;
@@ -998,7 +1001,8 @@
     ///
     /// *This type is available if Syn is built with the `"derive"` or `"full"`
     /// feature.*
-    pub enum Member {
+    #[derive(Eq, PartialEq, Hash)]
+    pub enum Member #manual_extra_traits {
         /// A named field like `self.x`.
         Named(Ident),
         /// An unnamed field like `self.0`.
@@ -1006,6 +1010,23 @@
     }
 }
 
+#[cfg(feature = "printing")]
+impl IdentFragment for Member {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Member::Named(m) => Display::fmt(m, formatter),
+            Member::Unnamed(m) => Display::fmt(&m.index, formatter),
+        }
+    }
+
+    fn span(&self) -> Option<Span> {
+        match self {
+            Member::Named(m) => Some(m.span()),
+            Member::Unnamed(m) => Some(m.span),
+        }
+    }
+}
+
 ast_struct! {
     /// The index of an unnamed tuple struct field.
     ///
@@ -1027,28 +1048,36 @@
     }
 }
 
-#[cfg(feature = "extra-traits")]
 impl Eq for Index {}
 
-#[cfg(feature = "extra-traits")]
 impl PartialEq for Index {
     fn eq(&self, other: &Self) -> bool {
         self.index == other.index
     }
 }
 
-#[cfg(feature = "extra-traits")]
 impl Hash for Index {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.index.hash(state);
     }
 }
 
+#[cfg(feature = "printing")]
+impl IdentFragment for Index {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(&self.index, formatter)
+    }
+
+    fn span(&self) -> Option<Span> {
+        Some(self.span)
+    }
+}
+
 #[cfg(feature = "full")]
 ast_struct! {
     #[derive(Default)]
     pub struct Reserved {
-        private: (),
+        _private: (),
     }
 }
 
@@ -1181,9 +1210,12 @@
 pub(crate) mod parsing {
     use super::*;
 
+    use crate::parse::discouraged::Speculative;
     use crate::parse::{Parse, ParseStream, Result};
     use crate::path;
 
+    crate::custom_keyword!(raw);
+
     // When we're parsing expressions which occur before blocks, like in an if
     // statement's condition, we cannot parse a struct literal.
     //
@@ -1436,24 +1468,41 @@
     // box <trailer>
     #[cfg(feature = "full")]
     fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
-        // TODO: optimize using advance_to
+        let begin = input.fork();
         let ahead = input.fork();
-        ahead.call(Attribute::parse_outer)?;
+        let attrs = ahead.call(Attribute::parse_outer)?;
         if ahead.peek(Token![&])
             || ahead.peek(Token![box])
             || ahead.peek(Token![*])
             || ahead.peek(Token![!])
             || ahead.peek(Token![-])
         {
-            let attrs = input.call(Attribute::parse_outer)?;
+            input.advance_to(&ahead);
             if input.peek(Token![&]) {
-                Ok(Expr::Reference(ExprReference {
-                    attrs,
-                    and_token: input.parse()?,
-                    raw: Reserved::default(),
-                    mutability: input.parse()?,
-                    expr: Box::new(unary_expr(input, allow_struct)?),
-                }))
+                let and_token: Token![&] = input.parse()?;
+                let raw: Option<raw> = if input.peek(raw)
+                    && (input.peek2(Token![mut]) || input.peek2(Token![const]))
+                {
+                    Some(input.parse()?)
+                } else {
+                    None
+                };
+                let mutability: Option<Token![mut]> = input.parse()?;
+                if raw.is_some() && mutability.is_none() {
+                    input.parse::<Token![const]>()?;
+                }
+                let expr = Box::new(unary_expr(input, allow_struct)?);
+                if raw.is_some() {
+                    Ok(Expr::Verbatim(verbatim::between(begin, input)))
+                } else {
+                    Ok(Expr::Reference(ExprReference {
+                        attrs,
+                        and_token,
+                        raw: Reserved::default(),
+                        mutability,
+                        expr,
+                    }))
+                }
             } else if input.peek(Token![box]) {
                 Ok(Expr::Box(ExprBox {
                     attrs,
@@ -1474,12 +1523,12 @@
 
     #[cfg(not(feature = "full"))]
     fn unary_expr(input: ParseStream, allow_struct: AllowStruct) -> Result<Expr> {
-        // TODO: optimize using advance_to
         let ahead = input.fork();
-        ahead.call(Attribute::parse_outer)?;
+        let attrs = ahead.call(Attribute::parse_outer)?;
         if ahead.peek(Token![*]) || ahead.peek(Token![!]) || ahead.peek(Token![-]) {
+            input.advance_to(&ahead);
             Ok(Expr::Unary(ExprUnary {
-                attrs: input.call(Attribute::parse_outer)?,
+                attrs,
                 op: input.parse()?,
                 expr: Box::new(unary_expr(input, allow_struct)?),
             }))
@@ -1905,7 +1954,7 @@
             return parse_expr(input, expr, allow_struct, Precedence::Any);
         };
 
-        if input.peek(Token![.]) || input.peek(Token![?]) {
+        if input.peek(Token![.]) && !input.peek(Token![..]) || input.peek(Token![?]) {
             expr = trailer_helper(input, expr)?;
 
             attrs.extend(expr.replace_attrs(Vec::new()));
@@ -2399,6 +2448,7 @@
     #[cfg(feature = "full")]
     impl Parse for FieldValue {
         fn parse(input: ParseStream) -> Result<Self> {
+            let attrs = input.call(Attribute::parse_outer)?;
             let member: Member = input.parse()?;
             let (colon_token, value) = if input.peek(Token![:]) || !member.is_named() {
                 let colon_token: Token![:] = input.parse()?;
@@ -2416,7 +2466,7 @@
             };
 
             Ok(FieldValue {
-                attrs: Vec::new(),
+                attrs,
                 member,
                 colon_token,
                 expr: value,
@@ -2433,24 +2483,22 @@
         let content;
         let brace_token = braced!(content in input);
         let inner_attrs = content.call(Attribute::parse_inner)?;
+        let attrs = private::attrs(outer_attrs, inner_attrs);
 
         let mut fields = Punctuated::new();
-        loop {
-            let attrs = content.call(Attribute::parse_outer)?;
-            // TODO: optimize using advance_to
-            if content.fork().parse::<Member>().is_err() {
-                if attrs.is_empty() {
-                    break;
-                } else {
-                    return Err(content.error("expected struct field"));
-                }
+        while !content.is_empty() {
+            if content.peek(Token![..]) {
+                return Ok(ExprStruct {
+                    attrs,
+                    brace_token,
+                    path,
+                    fields,
+                    dot2_token: Some(content.parse()?),
+                    rest: Some(Box::new(content.parse()?)),
+                });
             }
 
-            fields.push(FieldValue {
-                attrs,
-                ..content.parse()?
-            });
-
+            fields.push(content.parse()?);
             if !content.peek(Token![,]) {
                 break;
             }
@@ -2458,21 +2506,13 @@
             fields.push_punct(punct);
         }
 
-        let (dot2_token, rest) = if fields.empty_or_trailing() && content.peek(Token![..]) {
-            let dot2_token: Token![..] = content.parse()?;
-            let rest: Expr = content.parse()?;
-            (Some(dot2_token), Some(Box::new(rest)))
-        } else {
-            (None, None)
-        };
-
         Ok(ExprStruct {
-            attrs: private::attrs(outer_attrs, inner_attrs),
+            attrs,
             brace_token,
             path,
             fields,
-            dot2_token,
-            rest,
+            dot2_token: None,
+            rest: None,
         })
     }
 
diff --git a/src/generics.rs b/src/generics.rs
index d357cbb..9feeab9 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -28,9 +28,6 @@
     /// This type is a [syntax tree enum].
     ///
     /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
-    //
-    // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
-    // blocked on https://github.com/rust-lang/rust/issues/62833
     pub enum GenericParam {
         /// A generic type parameter: `T: Into<String>`.
         Type(TypeParam),
@@ -448,9 +445,6 @@
     /// This type is a [syntax tree enum].
     ///
     /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums
-    //
-    // TODO: change syntax-tree-enum link to an intra rustdoc link, currently
-    // blocked on https://github.com/rust-lang/rust/issues/62833
     pub enum WherePredicate {
         /// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`.
         Type(PredicateType),
diff --git a/src/item.rs b/src/item.rs
index a1ce7bf..83b82fd 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -7,6 +7,8 @@
 use crate::tt::TokenStreamHelper;
 #[cfg(feature = "extra-traits")]
 use std::hash::{Hash, Hasher};
+#[cfg(feature = "parsing")]
+use std::mem;
 
 ast_enum_of_structs! {
     /// Things that can appear directly inside of a module or scope.
@@ -435,6 +437,32 @@
     }
 }
 
+impl Item {
+    #[cfg(feature = "parsing")]
+    pub(crate) fn replace_attrs(&mut self, new: Vec<Attribute>) -> Vec<Attribute> {
+        match self {
+            Item::ExternCrate(ItemExternCrate { attrs, .. })
+            | Item::Use(ItemUse { attrs, .. })
+            | Item::Static(ItemStatic { attrs, .. })
+            | Item::Const(ItemConst { attrs, .. })
+            | Item::Fn(ItemFn { attrs, .. })
+            | Item::Mod(ItemMod { attrs, .. })
+            | Item::ForeignMod(ItemForeignMod { attrs, .. })
+            | Item::Type(ItemType { attrs, .. })
+            | Item::Struct(ItemStruct { attrs, .. })
+            | Item::Enum(ItemEnum { attrs, .. })
+            | Item::Union(ItemUnion { attrs, .. })
+            | Item::Trait(ItemTrait { attrs, .. })
+            | Item::TraitAlias(ItemTraitAlias { attrs, .. })
+            | Item::Impl(ItemImpl { attrs, .. })
+            | Item::Macro(ItemMacro { attrs, .. })
+            | Item::Macro2(ItemMacro2 { attrs, .. }) => mem::replace(attrs, new),
+            Item::Verbatim(_) => Vec::new(),
+            Item::__Nonexhaustive => unreachable!(),
+        }
+    }
+}
+
 #[cfg(feature = "extra-traits")]
 impl Eq for ItemMacro2 {}
 
@@ -1128,6 +1156,7 @@
     use crate::ext::IdentExt;
     use crate::parse::discouraged::Speculative;
     use crate::parse::{Parse, ParseStream, Result};
+    use crate::token::Brace;
     use proc_macro2::{Delimiter, Group, Punct, Spacing, TokenTree};
     use std::iter::{self, FromIterator};
 
@@ -1234,31 +1263,8 @@
                 Err(lookahead.error())
             }?;
 
-            {
-                let item_attrs = match &mut item {
-                    Item::ExternCrate(item) => &mut item.attrs,
-                    Item::Use(item) => &mut item.attrs,
-                    Item::Static(item) => &mut item.attrs,
-                    Item::Const(item) => &mut item.attrs,
-                    Item::Fn(item) => &mut item.attrs,
-                    Item::Mod(item) => &mut item.attrs,
-                    Item::ForeignMod(item) => &mut item.attrs,
-                    Item::Type(item) => &mut item.attrs,
-                    Item::Struct(item) => &mut item.attrs,
-                    Item::Enum(item) => &mut item.attrs,
-                    Item::Union(item) => &mut item.attrs,
-                    Item::Trait(item) => &mut item.attrs,
-                    Item::TraitAlias(item) => &mut item.attrs,
-                    Item::Impl(item) => &mut item.attrs,
-                    Item::Macro(item) => &mut item.attrs,
-                    Item::Macro2(item) => &mut item.attrs,
-                    Item::Verbatim(_) => return Ok(item),
-                    Item::__Nonexhaustive => unreachable!(),
-                };
-                attrs.extend(item_attrs.drain(..));
-                *item_attrs = attrs;
-            }
-
+            attrs.extend(item.replace_attrs(Vec::new()));
+            item.replace_attrs(attrs);
             Ok(item)
         }
     }
@@ -1463,6 +1469,54 @@
         }
     }
 
+    fn pop_variadic(args: &mut Punctuated<FnArg, Token![,]>) -> Option<Variadic> {
+        let trailing_punct = args.trailing_punct();
+
+        let last = match args.last_mut()? {
+            FnArg::Typed(last) => last,
+            _ => return None,
+        };
+
+        let ty = match last.ty.as_ref() {
+            Type::Verbatim(ty) => ty,
+            _ => return None,
+        };
+
+        let mut variadic = Variadic {
+            attrs: Vec::new(),
+            dots: parse2(ty.clone()).ok()?,
+        };
+
+        if let Pat::Verbatim(pat) = last.pat.as_ref() {
+            if pat.to_string() == "..." && !trailing_punct {
+                variadic.attrs = mem::replace(&mut last.attrs, Vec::new());
+                args.pop();
+            }
+        }
+
+        Some(variadic)
+    }
+
+    fn variadic_to_tokens(dots: &Token![...]) -> TokenStream {
+        TokenStream::from_iter(vec![
+            TokenTree::Punct({
+                let mut dot = Punct::new('.', Spacing::Joint);
+                dot.set_span(dots.spans[0]);
+                dot
+            }),
+            TokenTree::Punct({
+                let mut dot = Punct::new('.', Spacing::Joint);
+                dot.set_span(dots.spans[1]);
+                dot
+            }),
+            TokenTree::Punct({
+                let mut dot = Punct::new('.', Spacing::Alone);
+                dot.set_span(dots.spans[2]);
+                dot
+            }),
+        ])
+    }
+
     impl Parse for ItemFn {
         fn parse(input: ParseStream) -> Result<Self> {
             let outer_attrs = input.call(Attribute::parse_outer)?;
@@ -1477,22 +1531,8 @@
 
             let content;
             let paren_token = parenthesized!(content in input);
-            let inputs = content.parse_terminated(FnArg::parse)?;
-            let variadic = inputs.last().as_ref().and_then(get_variadic);
-
-            fn get_variadic(input: &&FnArg) -> Option<Variadic> {
-                if let FnArg::Typed(PatType { ty, .. }) = input {
-                    if let Type::Verbatim(tokens) = &**ty {
-                        if let Ok(dots) = parse2(tokens.clone()) {
-                            return Some(Variadic {
-                                attrs: Vec::new(),
-                                dots,
-                            });
-                        }
-                    }
-                }
-                None
-            }
+            let mut inputs = parse_fn_args(&content)?;
+            let variadic = pop_variadic(&mut inputs);
 
             let output: ReturnType = input.parse()?;
             let where_clause: Option<WhereClause> = input.parse()?;
@@ -1562,6 +1602,56 @@
         }
     }
 
+    fn parse_fn_args(input: ParseStream) -> Result<Punctuated<FnArg, Token![,]>> {
+        let mut args = Punctuated::new();
+        let mut has_receiver = false;
+
+        while !input.is_empty() {
+            let attrs = input.call(Attribute::parse_outer)?;
+
+            let arg = if let Some(dots) = input.parse::<Option<Token![...]>>()? {
+                FnArg::Typed(PatType {
+                    attrs,
+                    pat: Box::new(Pat::Verbatim(variadic_to_tokens(&dots))),
+                    colon_token: Token![:](dots.spans[0]),
+                    ty: Box::new(Type::Verbatim(variadic_to_tokens(&dots))),
+                })
+            } else {
+                let mut arg: FnArg = input.parse()?;
+                match &mut arg {
+                    FnArg::Receiver(receiver) if has_receiver => {
+                        return Err(Error::new(
+                            receiver.self_token.span,
+                            "unexpected second method receiver",
+                        ));
+                    }
+                    FnArg::Receiver(receiver) if !args.is_empty() => {
+                        return Err(Error::new(
+                            receiver.self_token.span,
+                            "unexpected method receiver",
+                        ));
+                    }
+                    FnArg::Receiver(receiver) => {
+                        has_receiver = true;
+                        receiver.attrs = attrs;
+                    }
+                    FnArg::Typed(arg) => arg.attrs = attrs,
+                }
+                arg
+            };
+            args.push_value(arg);
+
+            if input.is_empty() {
+                break;
+            }
+
+            let comma: Token![,] = input.parse()?;
+            args.push_punct(comma);
+        }
+
+        Ok(args)
+    }
+
     fn fn_arg_typed(input: ParseStream) -> Result<PatType> {
         // Hack to parse pre-2018 syntax in
         // test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs
@@ -1584,20 +1674,7 @@
             pat: input.parse()?,
             colon_token: input.parse()?,
             ty: Box::new(match input.parse::<Option<Token![...]>>()? {
-                Some(dot3) => {
-                    let args = vec![
-                        TokenTree::Punct(Punct::new('.', Spacing::Joint)),
-                        TokenTree::Punct(Punct::new('.', Spacing::Joint)),
-                        TokenTree::Punct(Punct::new('.', Spacing::Alone)),
-                    ];
-                    let tokens = TokenStream::from_iter(args.into_iter().zip(&dot3.spans).map(
-                        |(mut arg, span)| {
-                            arg.set_span(*span);
-                            arg
-                        },
-                    ));
-                    Type::Verbatim(tokens)
-                }
+                Some(dot3) => Type::Verbatim(variadic_to_tokens(&dot3)),
                 None => input.parse()?,
             }),
         })
@@ -1718,25 +1795,8 @@
 
             let content;
             let paren_token = parenthesized!(content in input);
-            let mut inputs = Punctuated::new();
-            let mut variadic = None;
-            while !content.is_empty() {
-                let attrs = content.call(Attribute::parse_outer)?;
-
-                if let Some(dots) = content.parse()? {
-                    variadic = Some(Variadic { attrs, dots });
-                    break;
-                }
-
-                let mut arg = content.call(fn_arg_typed)?;
-                arg.attrs = attrs;
-                inputs.push_value(FnArg::Typed(arg));
-                if content.is_empty() {
-                    break;
-                }
-
-                inputs.push_punct(content.parse()?);
-            }
+            let mut inputs = parse_fn_args(&content)?;
+            let variadic = pop_variadic(&mut inputs);
 
             let output: ReturnType = input.parse()?;
             let where_clause: Option<WhereClause> = input.parse()?;
@@ -2101,7 +2161,10 @@
 
     impl Parse for TraitItem {
         fn parse(input: ParseStream) -> Result<Self> {
+            let begin = input.fork();
             let mut attrs = input.call(Attribute::parse_outer)?;
+            let vis: Visibility = input.parse()?;
+            let defaultness: Option<Token![default]> = input.parse()?;
             let ahead = input.fork();
 
             let lookahead = ahead.lookahead1();
@@ -2139,18 +2202,20 @@
                 Err(lookahead.error())
             }?;
 
-            {
-                let item_attrs = match &mut item {
-                    TraitItem::Const(item) => &mut item.attrs,
-                    TraitItem::Method(item) => &mut item.attrs,
-                    TraitItem::Type(item) => &mut item.attrs,
-                    TraitItem::Macro(item) => &mut item.attrs,
-                    TraitItem::Verbatim(_) | TraitItem::__Nonexhaustive => unreachable!(),
-                };
-                attrs.extend(item_attrs.drain(..));
-                *item_attrs = attrs;
+            match (vis, defaultness) {
+                (Visibility::Inherited, None) => {}
+                _ => return Ok(TraitItem::Verbatim(verbatim::between(begin, input))),
             }
 
+            let item_attrs = match &mut item {
+                TraitItem::Const(item) => &mut item.attrs,
+                TraitItem::Method(item) => &mut item.attrs,
+                TraitItem::Type(item) => &mut item.attrs,
+                TraitItem::Macro(item) => &mut item.attrs,
+                TraitItem::Verbatim(_) | TraitItem::__Nonexhaustive => unreachable!(),
+            };
+            attrs.extend(item_attrs.drain(..));
+            *item_attrs = attrs;
             Ok(item)
         }
     }
@@ -2190,7 +2255,8 @@
 
             let content;
             let paren_token = parenthesized!(content in input);
-            let inputs = content.parse_terminated(FnArg::parse)?;
+            let mut inputs = parse_fn_args(&content)?;
+            let variadic = pop_variadic(&mut inputs);
 
             let output: ReturnType = input.parse()?;
             let where_clause: Option<WhereClause> = input.parse()?;
@@ -2221,7 +2287,7 @@
                     paren_token,
                     inputs,
                     output,
-                    variadic: None,
+                    variadic,
                     generics: Generics {
                         where_clause,
                         ..generics
@@ -2312,21 +2378,14 @@
                 Generics::default()
             };
 
-            let trait_ = {
-                // TODO: optimize using advance_to
+            let trait_ = (|| -> Option<_> {
                 let ahead = input.fork();
-                if ahead.parse::<Option<Token![!]>>().is_ok()
-                    && ahead.parse::<Path>().is_ok()
-                    && ahead.parse::<Token![for]>().is_ok()
-                {
-                    let polarity: Option<Token![!]> = input.parse()?;
-                    let path: Path = input.parse()?;
-                    let for_token: Token![for] = input.parse()?;
-                    Some((polarity, path, for_token))
-                } else {
-                    None
-                }
-            };
+                let polarity: Option<Token![!]> = ahead.parse().ok()?;
+                let path: Path = ahead.parse().ok()?;
+                let for_token: Token![for] = ahead.parse().ok()?;
+                input.advance_to(&ahead);
+                Some((polarity, path, for_token))
+            })();
             let self_ty: Type = input.parse()?;
             let where_clause: Option<WhereClause> = input.parse()?;
 
@@ -2358,6 +2417,7 @@
 
     impl Parse for ImplItem {
         fn parse(input: ParseStream) -> Result<Self> {
+            let begin = input.fork();
             let mut attrs = input.call(Attribute::parse_outer)?;
             let ahead = input.fork();
             let vis: Visibility = ahead.parse()?;
@@ -2372,10 +2432,30 @@
             };
 
             let mut item = if lookahead.peek(Token![const]) {
-                ahead.parse::<Token![const]>()?;
+                let const_token: Token![const] = ahead.parse()?;
                 let lookahead = ahead.lookahead1();
                 if lookahead.peek(Ident) {
-                    input.parse().map(ImplItem::Const)
+                    input.advance_to(&ahead);
+                    let ident: Ident = input.parse()?;
+                    let colon_token: Token![:] = input.parse()?;
+                    let ty: Type = input.parse()?;
+                    if let Some(eq_token) = input.parse()? {
+                        return Ok(ImplItem::Const(ImplItemConst {
+                            attrs,
+                            vis,
+                            defaultness,
+                            const_token,
+                            ident,
+                            colon_token,
+                            ty,
+                            eq_token,
+                            expr: input.parse()?,
+                            semi_token: input.parse()?,
+                        }));
+                    } else {
+                        input.parse::<Token![;]>()?;
+                        return Ok(ImplItem::Verbatim(verbatim::between(begin, input)));
+                    }
                 } else if lookahead.peek(Token![unsafe])
                     || lookahead.peek(Token![async])
                     || lookahead.peek(Token![extern])
@@ -2392,7 +2472,41 @@
             {
                 input.parse().map(ImplItem::Method)
             } else if lookahead.peek(Token![type]) {
-                input.parse().map(ImplItem::Type)
+                input.advance_to(&ahead);
+                let type_token: Token![type] = input.parse()?;
+                let ident: Ident = input.parse()?;
+                let mut generics: Generics = input.parse()?;
+                let colon_token: Option<Token![:]> = input.parse()?;
+                if colon_token.is_some() {
+                    let mut first = true;
+                    while !input.peek(Token![where])
+                        && !input.peek(Token![=])
+                        && !input.peek(Token![;])
+                    {
+                        if !first {
+                            input.parse::<Token![+]>()?;
+                        }
+                        input.parse::<TypeParamBound>()?;
+                        first = false;
+                    }
+                }
+                generics.where_clause = input.parse()?;
+                if let Some(eq_token) = input.parse()? {
+                    return Ok(ImplItem::Type(ImplItemType {
+                        attrs,
+                        vis,
+                        defaultness,
+                        type_token,
+                        ident,
+                        generics,
+                        eq_token,
+                        ty: input.parse()?,
+                        semi_token: input.parse()?,
+                    }));
+                } else {
+                    input.parse::<Token![;]>()?;
+                    return Ok(ImplItem::Verbatim(verbatim::between(begin, input)));
+                }
             } else if vis.is_inherited() && defaultness.is_none() && lookahead.peek(existential) {
                 input.call(item_existential).map(ImplItem::Verbatim)
             } else if vis.is_inherited()
@@ -2445,7 +2559,7 @@
 
     impl Parse for ImplItemMethod {
         fn parse(input: ParseStream) -> Result<Self> {
-            let outer_attrs = input.call(Attribute::parse_outer)?;
+            let mut attrs = input.call(Attribute::parse_outer)?;
             let vis: Visibility = input.parse()?;
             let defaultness: Option<Token![default]> = input.parse()?;
             let constness: Option<Token![const]> = input.parse()?;
@@ -2458,18 +2572,36 @@
 
             let content;
             let paren_token = parenthesized!(content in input);
-            let inputs = content.parse_terminated(FnArg::parse)?;
+            let mut inputs = parse_fn_args(&content)?;
+            let variadic = pop_variadic(&mut inputs);
 
             let output: ReturnType = input.parse()?;
             let where_clause: Option<WhereClause> = input.parse()?;
 
-            let content;
-            let brace_token = braced!(content in input);
-            let inner_attrs = content.call(Attribute::parse_inner)?;
-            let stmts = content.call(Block::parse_within)?;
+            let block = if let Some(semi) = input.parse::<Option<Token![;]>>()? {
+                // Accept methods without a body in an impl block because
+                // rustc's *parser* does not reject them (the compilation error
+                // is emitted later than parsing) and it can be useful for macro
+                // DSLs.
+                let mut punct = Punct::new(';', Spacing::Alone);
+                punct.set_span(semi.span);
+                let tokens = TokenStream::from_iter(vec![TokenTree::Punct(punct)]);
+                Block {
+                    brace_token: Brace::default(),
+                    stmts: vec![Stmt::Item(Item::Verbatim(tokens))],
+                }
+            } else {
+                let content;
+                let brace_token = braced!(content in input);
+                attrs.extend(content.call(Attribute::parse_inner)?);
+                Block {
+                    brace_token,
+                    stmts: content.call(Block::parse_within)?,
+                }
+            };
 
             Ok(ImplItemMethod {
-                attrs: private::attrs(outer_attrs, inner_attrs),
+                attrs,
                 vis,
                 defaultness,
                 sig: Signature {
@@ -2482,13 +2614,13 @@
                     paren_token,
                     inputs,
                     output,
-                    variadic: None,
+                    variadic,
                     generics: Generics {
                         where_clause,
                         ..generics
                     },
                 },
-                block: Block { brace_token, stmts },
+                block,
             })
         }
     }
@@ -2558,6 +2690,7 @@
 
     use crate::attr::FilterAttrs;
     use crate::print::TokensOrDefault;
+    use crate::punctuated::Pair;
 
     impl ToTokens for ItemExternCrate {
         fn to_tokens(&self, tokens: &mut TokenStream) {
@@ -2922,6 +3055,14 @@
             self.vis.to_tokens(tokens);
             self.defaultness.to_tokens(tokens);
             self.sig.to_tokens(tokens);
+            if self.block.stmts.len() == 1 {
+                if let Stmt::Item(Item::Verbatim(verbatim)) = &self.block.stmts[0] {
+                    if verbatim.to_string() == ";" {
+                        verbatim.to_tokens(tokens);
+                        return;
+                    }
+                }
+            }
             self.block.brace_token.surround(tokens, |tokens| {
                 tokens.append_all(self.attrs.inner());
                 tokens.append_all(&self.block.stmts);
@@ -2992,23 +3133,31 @@
         }
     }
 
-    fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
-        let last = match inputs.last() {
-            Some(last) => last,
-            None => return false,
+    fn maybe_variadic_to_tokens(arg: &FnArg, tokens: &mut TokenStream) -> bool {
+        let arg = match arg {
+            FnArg::Typed(arg) => arg,
+            FnArg::Receiver(receiver) => {
+                receiver.to_tokens(tokens);
+                return false;
+            }
         };
 
-        let pat = match last {
-            FnArg::Typed(pat) => pat,
-            FnArg::Receiver(_) => return false,
-        };
-
-        let tokens = match pat.ty.as_ref() {
-            Type::Verbatim(tokens) => tokens,
-            _ => return false,
-        };
-
-        tokens.to_string() == "..."
+        match arg.ty.as_ref() {
+            Type::Verbatim(ty) if ty.to_string() == "..." => {
+                match arg.pat.as_ref() {
+                    Pat::Verbatim(pat) if pat.to_string() == "..." => {
+                        tokens.append_all(arg.attrs.outer());
+                        pat.to_tokens(tokens);
+                    }
+                    _ => arg.to_tokens(tokens),
+                }
+                true
+            }
+            _ => {
+                arg.to_tokens(tokens);
+                false
+            }
+        }
     }
 
     impl ToTokens for Signature {
@@ -3021,8 +3170,19 @@
             self.ident.to_tokens(tokens);
             self.generics.to_tokens(tokens);
             self.paren_token.surround(tokens, |tokens| {
-                self.inputs.to_tokens(tokens);
-                if self.variadic.is_some() && !has_variadic(&self.inputs) {
+                let mut last_is_variadic = false;
+                for input in self.inputs.pairs() {
+                    match input {
+                        Pair::Punctuated(input, comma) => {
+                            maybe_variadic_to_tokens(input, tokens);
+                            comma.to_tokens(tokens);
+                        }
+                        Pair::End(input) => {
+                            last_is_variadic = maybe_variadic_to_tokens(input, tokens);
+                        }
+                    }
+                }
+                if self.variadic.is_some() && !last_is_variadic {
                     if !self.inputs.empty_or_trailing() {
                         <Token![,]>::default().to_tokens(tokens);
                     }
diff --git a/src/keyword.rs b/src/keyword.rs
deleted file mode 100644
index e69de29..0000000
--- a/src/keyword.rs
+++ /dev/null
diff --git a/src/lib.rs b/src/lib.rs
index 2a2ff61..c88a813 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -242,7 +242,7 @@
 //!   dynamic library libproc_macro from rustc toolchain.
 
 // Syn types in rustdoc of other crates get linked to here.
-#![doc(html_root_url = "https://docs.rs/syn/1.0.7")]
+#![doc(html_root_url = "https://docs.rs/syn/1.0.16")]
 #![deny(clippy::all, clippy::pedantic)]
 // Ignored clippy lints.
 #![allow(
@@ -265,6 +265,8 @@
     clippy::empty_enum,
     clippy::if_not_else,
     clippy::items_after_statements,
+    clippy::match_same_arms,
+    clippy::missing_errors_doc,
     clippy::module_name_repetitions,
     clippy::must_use_candidate,
     clippy::shadow_unrelated,
@@ -760,6 +762,8 @@
 mod custom_keyword;
 mod custom_punctuation;
 mod sealed;
+mod span;
+mod thread;
 
 #[cfg(feature = "parsing")]
 mod lookahead;
@@ -767,13 +771,12 @@
 #[cfg(feature = "parsing")]
 pub mod parse;
 
-mod span;
+#[cfg(all(feature = "parsing", feature = "full"))]
+mod verbatim;
 
 #[cfg(all(any(feature = "full", feature = "derive"), feature = "printing"))]
 mod print;
 
-mod thread;
-
 ////////////////////////////////////////////////////////////////////////////////
 
 #[allow(dead_code, non_camel_case_types)]
diff --git a/src/lit.rs b/src/lit.rs
index abc4ec2..0f271e4 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -403,18 +403,23 @@
 
 impl LitInt {
     pub fn new(repr: &str, span: Span) -> Self {
-        if let Some((digits, suffix)) = value::parse_lit_int(repr) {
-            let mut token = value::to_literal(repr);
-            token.set_span(span);
-            LitInt {
-                repr: Box::new(LitIntRepr {
-                    token,
-                    digits,
-                    suffix,
-                }),
-            }
-        } else {
-            panic!("Not an integer literal: `{}`", repr);
+        let (digits, suffix) = match value::parse_lit_int(repr) {
+            Some(parse) => parse,
+            None => panic!("Not an integer literal: `{}`", repr),
+        };
+
+        let mut token = match value::to_literal(repr, &digits, &suffix) {
+            Some(token) => token,
+            None => panic!("Unsupported integer literal: `{}`", repr),
+        };
+
+        token.set_span(span);
+        LitInt {
+            repr: Box::new(LitIntRepr {
+                token,
+                digits,
+                suffix,
+            }),
         }
     }
 
@@ -492,18 +497,23 @@
 
 impl LitFloat {
     pub fn new(repr: &str, span: Span) -> Self {
-        if let Some((digits, suffix)) = value::parse_lit_float(repr) {
-            let mut token = value::to_literal(repr);
-            token.set_span(span);
-            LitFloat {
-                repr: Box::new(LitFloatRepr {
-                    token,
-                    digits,
-                    suffix,
-                }),
-            }
-        } else {
-            panic!("Not a float literal: `{}`", repr);
+        let (digits, suffix) = match value::parse_lit_float(repr) {
+            Some(parse) => parse,
+            None => panic!("Not a float literal: `{}`", repr),
+        };
+
+        let mut token = match value::to_literal(repr, &digits, &suffix) {
+            Some(token) => token,
+            None => panic!("Unsupported float literal: `{}`", repr),
+        };
+
+        token.set_span(span);
+        LitFloat {
+            repr: Box::new(LitFloatRepr {
+                token,
+                digits,
+                suffix,
+            }),
         }
     }
 
@@ -691,7 +701,9 @@
 #[cfg(feature = "parsing")]
 pub mod parsing {
     use super::*;
+    use crate::buffer::Cursor;
     use crate::parse::{Parse, ParseStream, Result};
+    use proc_macro2::Punct;
 
     impl Parse for Lit {
         fn parse(input: ParseStream) -> Result<Self> {
@@ -699,25 +711,73 @@
                 if let Some((lit, rest)) = cursor.literal() {
                     return Ok((Lit::new(lit), rest));
                 }
-                while let Some((ident, rest)) = cursor.ident() {
-                    let value = if ident == "true" {
-                        true
-                    } else if ident == "false" {
-                        false
-                    } else {
-                        break;
-                    };
-                    let lit_bool = LitBool {
-                        value,
-                        span: ident.span(),
-                    };
-                    return Ok((Lit::Bool(lit_bool), rest));
+
+                if let Some((ident, rest)) = cursor.ident() {
+                    let value = ident == "true";
+                    if value || ident == "false" {
+                        let lit_bool = LitBool {
+                            value,
+                            span: ident.span(),
+                        };
+                        return Ok((Lit::Bool(lit_bool), rest));
+                    }
                 }
+
+                if let Some((punct, rest)) = cursor.punct() {
+                    if punct.as_char() == '-' {
+                        if let Some((lit, rest)) = parse_negative_lit(punct, rest) {
+                            return Ok((lit, rest));
+                        }
+                    }
+                }
+
                 Err(cursor.error("expected literal"))
             })
         }
     }
 
+    fn parse_negative_lit(neg: Punct, cursor: Cursor) -> Option<(Lit, Cursor)> {
+        let (lit, rest) = cursor.literal()?;
+
+        let mut span = neg.span();
+        span = span.join(lit.span()).unwrap_or(span);
+
+        let mut repr = lit.to_string();
+        repr.insert(0, '-');
+
+        if !(repr.ends_with("f32") || repr.ends_with("f64")) {
+            if let Some((digits, suffix)) = value::parse_lit_int(&repr) {
+                if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) {
+                    token.set_span(span);
+                    return Some((
+                        Lit::Int(LitInt {
+                            repr: Box::new(LitIntRepr {
+                                token,
+                                digits,
+                                suffix,
+                            }),
+                        }),
+                        rest,
+                    ));
+                }
+            }
+        }
+
+        let (digits, suffix) = value::parse_lit_float(&repr)?;
+        let mut token = value::to_literal(&repr, &digits, &suffix)?;
+        token.set_span(span);
+        Some((
+            Lit::Float(LitFloat {
+                repr: Box::new(LitFloatRepr {
+                    token,
+                    digits,
+                    suffix,
+                }),
+            }),
+            rest,
+        ))
+    }
+
     impl Parse for LitStr {
         fn parse(input: ParseStream) -> Result<Self> {
             let head = input.fork();
@@ -1372,11 +1432,33 @@
         }
     }
 
-    pub fn to_literal(s: &str) -> Literal {
-        let stream = s.parse::<TokenStream>().unwrap();
-        match stream.into_iter().next().unwrap() {
-            TokenTree::Literal(l) => l,
-            _ => unreachable!(),
+    pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option<Literal> {
+        if repr.starts_with('-') {
+            if suffix == "f64" {
+                digits.parse().ok().map(Literal::f64_suffixed)
+            } else if suffix == "f32" {
+                digits.parse().ok().map(Literal::f32_suffixed)
+            } else if suffix == "i64" {
+                digits.parse().ok().map(Literal::i64_suffixed)
+            } else if suffix == "i32" {
+                digits.parse().ok().map(Literal::i32_suffixed)
+            } else if suffix == "i16" {
+                digits.parse().ok().map(Literal::i16_suffixed)
+            } else if suffix == "i8" {
+                digits.parse().ok().map(Literal::i8_suffixed)
+            } else if !suffix.is_empty() {
+                None
+            } else if digits.contains('.') {
+                digits.parse().ok().map(Literal::f64_unsuffixed)
+            } else {
+                digits.parse().ok().map(Literal::i64_unsuffixed)
+            }
+        } else {
+            let stream = repr.parse::<TokenStream>().unwrap();
+            match stream.into_iter().next().unwrap() {
+                TokenTree::Literal(l) => Some(l),
+                _ => unreachable!(),
+            }
         }
     }
 }
diff --git a/src/parse.rs b/src/parse.rs
index 0dd698b..96eecce 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -261,13 +261,16 @@
     // the cell.
     cell: Cell<Cursor<'static>>,
     marker: PhantomData<Cursor<'a>>,
-    unexpected: Rc<Cell<Option<Span>>>,
+    unexpected: Cell<Option<Rc<Cell<Unexpected>>>>,
 }
 
 impl<'a> Drop for ParseBuffer<'a> {
     fn drop(&mut self) {
-        if !self.is_empty() && self.unexpected.get().is_none() {
-            self.unexpected.set(Some(self.cursor().span()));
+        if !self.is_empty() {
+            let (inner, old_span) = inner_unexpected(self);
+            if old_span.is_none() {
+                inner.set(Unexpected::Some(self.cursor().span()));
+            }
         }
     }
 }
@@ -371,36 +374,55 @@
     unsafe { mem::transmute::<Cursor<'c>, Cursor<'a>>(to) }
 }
 
-fn skip(input: ParseStream) -> bool {
-    input
-        .step(|cursor| {
-            if let Some((_lifetime, rest)) = cursor.lifetime() {
-                Ok((true, rest))
-            } else if let Some((_token, rest)) = cursor.token_tree() {
-                Ok((true, rest))
-            } else {
-                Ok((false, *cursor))
-            }
-        })
-        .unwrap()
-}
-
 pub(crate) fn new_parse_buffer(
     scope: Span,
     cursor: Cursor,
-    unexpected: Rc<Cell<Option<Span>>>,
+    unexpected: Rc<Cell<Unexpected>>,
 ) -> ParseBuffer {
     ParseBuffer {
         scope,
         // See comment on `cell` in the struct definition.
         cell: Cell::new(unsafe { mem::transmute::<Cursor, Cursor<'static>>(cursor) }),
         marker: PhantomData,
-        unexpected,
+        unexpected: Cell::new(Some(unexpected)),
     }
 }
 
-pub(crate) fn get_unexpected(buffer: &ParseBuffer) -> Rc<Cell<Option<Span>>> {
-    buffer.unexpected.clone()
+#[derive(Clone)]
+pub(crate) enum Unexpected {
+    None,
+    Some(Span),
+    Chain(Rc<Cell<Unexpected>>),
+}
+
+impl Default for Unexpected {
+    fn default() -> Self {
+        Unexpected::None
+    }
+}
+
+// We call this on Cell<Unexpected> and Cell<Option<T>> where temporarily
+// swapping in a None is cheap.
+fn cell_clone<T: Default + Clone>(cell: &Cell<T>) -> T {
+    let prev = cell.take();
+    let ret = prev.clone();
+    cell.set(prev);
+    ret
+}
+
+fn inner_unexpected(buffer: &ParseBuffer) -> (Rc<Cell<Unexpected>>, Option<Span>) {
+    let mut unexpected = get_unexpected(buffer);
+    loop {
+        match cell_clone(&unexpected) {
+            Unexpected::None => return (unexpected, None),
+            Unexpected::Some(span) => return (unexpected, Some(span)),
+            Unexpected::Chain(next) => unexpected = next,
+        }
+    }
+}
+
+pub(crate) fn get_unexpected(buffer: &ParseBuffer) -> Rc<Cell<Unexpected>> {
+    cell_clone(&buffer.unexpected).unwrap()
 }
 
 impl<'a> ParseBuffer<'a> {
@@ -562,14 +584,17 @@
     /// }
     /// ```
     pub fn peek2<T: Peek>(&self, token: T) -> bool {
-        let ahead = self.fork();
-        skip(&ahead) && ahead.peek(token)
+        let _ = token;
+        self.cursor().skip().map_or(false, T::Token::peek)
     }
 
     /// Looks at the third-next token in the parse stream.
     pub fn peek3<T: Peek>(&self, token: T) -> bool {
-        let ahead = self.fork();
-        skip(&ahead) && skip(&ahead) && ahead.peek(token)
+        let _ = token;
+        self.cursor()
+            .skip()
+            .and_then(Cursor::skip)
+            .map_or(false, T::Token::peek)
     }
 
     /// Parses zero or more occurrences of `T` separated by punctuation of type
@@ -841,8 +866,8 @@
             cell: self.cell.clone(),
             marker: PhantomData,
             // Not the parent's unexpected. Nothing cares whether the clone
-            // parses all the way.
-            unexpected: Rc::new(Cell::new(None)),
+            // parses all the way unless we `advance_to`.
+            unexpected: Cell::new(Some(Rc::new(Cell::new(Unexpected::None)))),
         }
     }
 
@@ -953,6 +978,18 @@
         Ok(node)
     }
 
+    /// Returns the `Span` of the next token in the parse stream, or
+    /// `Span::call_site()` if this parse stream has completely exhausted its
+    /// input `TokenStream`.
+    pub fn span(&self) -> Span {
+        let cursor = self.cursor();
+        if cursor.eof() {
+            self.scope
+        } else {
+            crate::buffer::open_span_of_group(cursor)
+        }
+    }
+
     /// Provides low-level access to the token representation underlying this
     /// parse stream.
     ///
@@ -963,7 +1000,7 @@
     }
 
     fn check_unexpected(&self) -> Result<()> {
-        match self.unexpected.get() {
+        match inner_unexpected(self).1 {
             Some(span) => Err(Error::new(span, "unexpected token")),
             None => Ok(()),
         }
@@ -1095,7 +1132,7 @@
 fn tokens_to_parse_buffer(tokens: &TokenBuffer) -> ParseBuffer {
     let scope = Span::call_site();
     let cursor = tokens.begin();
-    let unexpected = Rc::new(Cell::new(None));
+    let unexpected = Rc::new(Cell::new(Unexpected::None));
     new_parse_buffer(scope, cursor, unexpected)
 }
 
@@ -1121,7 +1158,7 @@
     fn __parse_scoped(self, scope: Span, tokens: TokenStream) -> Result<Self::Output> {
         let buf = TokenBuffer::new2(tokens);
         let cursor = buf.begin();
-        let unexpected = Rc::new(Cell::new(None));
+        let unexpected = Rc::new(Cell::new(Unexpected::None));
         let state = new_parse_buffer(scope, cursor, unexpected);
         let node = self(&state)?;
         state.check_unexpected()?;
diff --git a/src/parse_quote.rs b/src/parse_quote.rs
index 18a47b9..96e4765 100644
--- a/src/parse_quote.rs
+++ b/src/parse_quote.rs
@@ -56,8 +56,10 @@
 ///   or inner like `#![...]`
 /// - [`Punctuated<T, P>`] — parses zero or more `T` separated by punctuation
 ///   `P` with optional trailing punctuation
+/// - [`Vec<Stmt>`] — parses the same as `Block::parse_within`
 ///
 /// [`Punctuated<T, P>`]: punctuated::Punctuated
+/// [`Vec<Stmt>`]: Block::parse_within
 ///
 /// # Panics
 ///
@@ -112,6 +114,8 @@
 use crate::punctuated::Punctuated;
 #[cfg(any(feature = "full", feature = "derive"))]
 use crate::{attr, Attribute};
+#[cfg(feature = "full")]
+use crate::{Block, Stmt};
 
 #[cfg(any(feature = "full", feature = "derive"))]
 impl ParseQuote for Attribute {
@@ -129,3 +133,10 @@
         Self::parse_terminated(input)
     }
 }
+
+#[cfg(feature = "full")]
+impl ParseQuote for Vec<Stmt> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Block::parse_within(input)
+    }
+}
diff --git a/src/pat.rs b/src/pat.rs
index 262129b..fac651e 100644
--- a/src/pat.rs
+++ b/src/pat.rs
@@ -386,11 +386,12 @@
     use super::*;
 
     use crate::ext::IdentExt;
-    use crate::parse::{Parse, ParseStream, Result};
+    use crate::parse::{Parse, ParseBuffer, ParseStream, Result};
     use crate::path;
 
     impl Parse for Pat {
         fn parse(input: ParseStream) -> Result<Self> {
+            let begin = input.fork();
             let lookahead = input.lookahead1();
             if lookahead.peek(Ident)
                 && ({
@@ -434,7 +435,7 @@
             } else if lookahead.peek(token::Bracket) {
                 input.call(pat_slice).map(Pat::Slice)
             } else if lookahead.peek(Token![..]) && !input.peek(Token![...]) {
-                input.call(pat_rest).map(Pat::Rest)
+                pat_range_half_open(input, begin)
             } else {
                 Err(lookahead.error())
             }
@@ -442,10 +443,11 @@
     }
 
     fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result<Pat> {
+        let begin = input.fork();
         let (qself, path) = path::parsing::qpath(input, true)?;
 
         if input.peek(Token![..]) {
-            return pat_range(input, qself, path).map(Pat::Range);
+            return pat_range(input, begin, qself, path);
         }
 
         if qself.is_some() {
@@ -487,7 +489,7 @@
         } else if input.peek(token::Paren) {
             pat_tuple_struct(input, path).map(Pat::TupleStruct)
         } else if input.peek(Token![..]) {
-            pat_range(input, qself, path).map(Pat::Range)
+            pat_range(input, begin, qself, path)
         } else {
             Ok(Pat::Path(PatPath {
                 attrs: Vec::new(),
@@ -624,17 +626,44 @@
         })
     }
 
-    fn pat_range(input: ParseStream, qself: Option<QSelf>, path: Path) -> Result<PatRange> {
-        Ok(PatRange {
-            attrs: Vec::new(),
-            lo: Box::new(Expr::Path(ExprPath {
+    fn pat_range(
+        input: ParseStream,
+        begin: ParseBuffer,
+        qself: Option<QSelf>,
+        path: Path,
+    ) -> Result<Pat> {
+        let limits: RangeLimits = input.parse()?;
+        let hi = input.call(pat_lit_expr)?;
+        if let Some(hi) = hi {
+            Ok(Pat::Range(PatRange {
                 attrs: Vec::new(),
-                qself,
-                path,
-            })),
-            limits: input.parse()?,
-            hi: input.call(pat_lit_expr)?,
-        })
+                lo: Box::new(Expr::Path(ExprPath {
+                    attrs: Vec::new(),
+                    qself,
+                    path,
+                })),
+                limits,
+                hi,
+            }))
+        } else {
+            Ok(Pat::Verbatim(verbatim::between(begin, input)))
+        }
+    }
+
+    fn pat_range_half_open(input: ParseStream, begin: ParseBuffer) -> Result<Pat> {
+        let limits: RangeLimits = input.parse()?;
+        let hi = input.call(pat_lit_expr)?;
+        if hi.is_some() {
+            Ok(Pat::Verbatim(verbatim::between(begin, input)))
+        } else {
+            match limits {
+                RangeLimits::HalfOpen(dot2_token) => Ok(Pat::Rest(PatRest {
+                    attrs: Vec::new(),
+                    dot2_token,
+                })),
+                RangeLimits::Closed(_) => Err(input.error("expected range upper bound")),
+            }
+        }
     }
 
     fn pat_tuple(input: ParseStream) -> Result<PatTuple> {
@@ -669,14 +698,21 @@
     }
 
     fn pat_lit_or_range(input: ParseStream) -> Result<Pat> {
-        let lo = input.call(pat_lit_expr)?;
+        let begin = input.fork();
+        let lo = input.call(pat_lit_expr)?.unwrap();
         if input.peek(Token![..]) {
-            Ok(Pat::Range(PatRange {
-                attrs: Vec::new(),
-                lo,
-                limits: input.parse()?,
-                hi: input.call(pat_lit_expr)?,
-            }))
+            let limits: RangeLimits = input.parse()?;
+            let hi = input.call(pat_lit_expr)?;
+            if let Some(hi) = hi {
+                Ok(Pat::Range(PatRange {
+                    attrs: Vec::new(),
+                    lo,
+                    limits,
+                    hi,
+                }))
+            } else {
+                Ok(Pat::Verbatim(verbatim::between(begin, input)))
+            }
         } else {
             Ok(Pat::Lit(PatLit {
                 attrs: Vec::new(),
@@ -685,7 +721,17 @@
         }
     }
 
-    fn pat_lit_expr(input: ParseStream) -> Result<Box<Expr>> {
+    fn pat_lit_expr(input: ParseStream) -> Result<Option<Box<Expr>>> {
+        if input.is_empty()
+            || input.peek(Token![|])
+            || input.peek(Token![=>])
+            || input.peek(Token![:]) && !input.peek(Token![::])
+            || input.peek(Token![,])
+            || input.peek(Token![;])
+        {
+            return Ok(None);
+        }
+
         let neg: Option<Token![-]> = input.parse()?;
 
         let lookahead = input.lookahead1();
@@ -705,7 +751,7 @@
             return Err(lookahead.error());
         };
 
-        Ok(Box::new(if let Some(neg) = neg {
+        Ok(Some(Box::new(if let Some(neg) = neg {
             Expr::Unary(ExprUnary {
                 attrs: Vec::new(),
                 op: UnOp::Neg(neg),
@@ -713,7 +759,7 @@
             })
         } else {
             expr
-        }))
+        })))
     }
 
     fn pat_slice(input: ParseStream) -> Result<PatSlice> {
@@ -737,13 +783,6 @@
             elems,
         })
     }
-
-    fn pat_rest(input: ParseStream) -> Result<PatRest> {
-        Ok(PatRest {
-            attrs: Vec::new(),
-            dot2_token: input.parse()?,
-        })
-    }
 }
 
 #[cfg(feature = "printing")]
diff --git a/src/punctuated.rs b/src/punctuated.rs
index 38c7bf4..e02ac44 100644
--- a/src/punctuated.rs
+++ b/src/punctuated.rs
@@ -76,22 +76,19 @@
         self.iter().next()
     }
 
+    /// Mutably borrows the first element in this sequence.
+    pub fn first_mut(&mut self) -> Option<&mut T> {
+        self.iter_mut().next()
+    }
+
     /// Borrows the last element in this sequence.
     pub fn last(&self) -> Option<&T> {
-        if self.last.is_some() {
-            self.last.as_ref().map(Box::as_ref)
-        } else {
-            self.inner.last().map(|pair| &pair.0)
-        }
+        self.iter().next_back()
     }
 
     /// Mutably borrows the last element in this sequence.
     pub fn last_mut(&mut self) -> Option<&mut T> {
-        if self.last.is_some() {
-            self.last.as_mut().map(Box::as_mut)
-        } else {
-            self.inner.last_mut().map(|pair| &mut pair.0)
-        }
+        self.iter_mut().next_back()
     }
 
     /// Returns an iterator over borrowed syntax tree nodes of type `&T`.
diff --git a/src/stmt.rs b/src/stmt.rs
index e4277fd..acee5a3 100644
--- a/src/stmt.rs
+++ b/src/stmt.rs
@@ -47,8 +47,10 @@
 pub mod parsing {
     use super::*;
 
+    use crate::parse::discouraged::Speculative;
     use crate::parse::{Parse, ParseStream, Result};
     use crate::punctuated::Punctuated;
+    use proc_macro2::TokenStream;
 
     impl Block {
         /// Parse the body of a block as zero or more statements, possibly
@@ -106,8 +108,8 @@
         pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
             let mut stmts = Vec::new();
             loop {
-                while input.peek(Token![;]) {
-                    input.parse::<Token![;]>()?;
+                while let Some(semi) = input.parse::<Option<Token![;]>>()? {
+                    stmts.push(Stmt::Semi(Expr::Verbatim(TokenStream::new()), semi));
                 }
                 if input.is_empty() {
                     break;
@@ -146,55 +148,55 @@
     }
 
     fn parse_stmt(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
-        // TODO: optimize using advance_to
-        let ahead = input.fork();
-        ahead.call(Attribute::parse_outer)?;
+        let mut attrs = input.call(Attribute::parse_outer)?;
 
-        if {
-            let ahead = ahead.fork();
-            // Only parse braces here; paren and bracket will get parsed as
-            // expression statements
-            ahead.call(Path::parse_mod_style).is_ok()
-                && ahead.parse::<Token![!]>().is_ok()
-                && (ahead.peek(token::Brace) || ahead.peek(Ident))
-        } {
-            stmt_mac(input)
-        } else if ahead.peek(Token![let]) {
-            stmt_local(input).map(Stmt::Local)
-        } else if ahead.peek(Token![pub])
-            || ahead.peek(Token![crate]) && !ahead.peek2(Token![::])
-            || ahead.peek(Token![extern]) && !ahead.peek2(Token![::])
-            || ahead.peek(Token![use])
-            || ahead.peek(Token![static]) && (ahead.peek2(Token![mut]) || ahead.peek2(Ident))
-            || ahead.peek(Token![const])
-            || ahead.peek(Token![unsafe]) && !ahead.peek2(token::Brace)
-            || ahead.peek(Token![async])
-                && (ahead.peek2(Token![unsafe])
-                    || ahead.peek2(Token![extern])
-                    || ahead.peek2(Token![fn]))
-            || ahead.peek(Token![fn])
-            || ahead.peek(Token![mod])
-            || ahead.peek(Token![type])
-            || ahead.peek(item::parsing::existential) && ahead.peek2(Token![type])
-            || ahead.peek(Token![struct])
-            || ahead.peek(Token![enum])
-            || ahead.peek(Token![union]) && ahead.peek2(Ident)
-            || ahead.peek(Token![auto]) && ahead.peek2(Token![trait])
-            || ahead.peek(Token![trait])
-            || ahead.peek(Token![default])
-                && (ahead.peek2(Token![unsafe]) || ahead.peek2(Token![impl]))
-            || ahead.peek(Token![impl])
-            || ahead.peek(Token![macro])
+        // brace-style macros; paren and bracket macros get parsed as
+        // expression statements.
+        let ahead = input.fork();
+        if let Ok(path) = ahead.call(Path::parse_mod_style) {
+            if ahead.peek(Token![!]) && (ahead.peek2(token::Brace) || ahead.peek2(Ident)) {
+                input.advance_to(&ahead);
+                return stmt_mac(input, attrs, path);
+            }
+        }
+
+        if input.peek(Token![let]) {
+            stmt_local(input, attrs).map(Stmt::Local)
+        } else if input.peek(Token![pub])
+            || input.peek(Token![crate]) && !input.peek2(Token![::])
+            || input.peek(Token![extern]) && !input.peek2(Token![::])
+            || input.peek(Token![use])
+            || input.peek(Token![static]) && (input.peek2(Token![mut]) || input.peek2(Ident))
+            || input.peek(Token![const])
+            || input.peek(Token![unsafe]) && !input.peek2(token::Brace)
+            || input.peek(Token![async])
+                && (input.peek2(Token![unsafe])
+                    || input.peek2(Token![extern])
+                    || input.peek2(Token![fn]))
+            || input.peek(Token![fn])
+            || input.peek(Token![mod])
+            || input.peek(Token![type])
+            || input.peek(item::parsing::existential) && input.peek2(Token![type])
+            || input.peek(Token![struct])
+            || input.peek(Token![enum])
+            || input.peek(Token![union]) && input.peek2(Ident)
+            || input.peek(Token![auto]) && input.peek2(Token![trait])
+            || input.peek(Token![trait])
+            || input.peek(Token![default])
+                && (input.peek2(Token![unsafe]) || input.peek2(Token![impl]))
+            || input.peek(Token![impl])
+            || input.peek(Token![macro])
         {
-            input.parse().map(Stmt::Item)
+            let mut item: Item = input.parse()?;
+            attrs.extend(item.replace_attrs(Vec::new()));
+            item.replace_attrs(attrs);
+            Ok(Stmt::Item(item))
         } else {
-            stmt_expr(input, allow_nosemi)
+            stmt_expr(input, allow_nosemi, attrs)
         }
     }
 
-    fn stmt_mac(input: ParseStream) -> Result<Stmt> {
-        let attrs = input.call(Attribute::parse_outer)?;
-        let path = input.call(Path::parse_mod_style)?;
+    fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<Stmt> {
         let bang_token: Token![!] = input.parse()?;
         let ident: Option<Ident> = input.parse()?;
         let (delimiter, tokens) = mac::parse_delimiter(input)?;
@@ -213,9 +215,9 @@
         })))
     }
 
-    fn stmt_local(input: ParseStream) -> Result<Local> {
+    fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
         Ok(Local {
-            attrs: input.call(Attribute::parse_outer)?,
+            attrs,
             let_token: input.parse()?,
             pat: {
                 let leading_vert: Option<Token![|]> = input.parse()?;
@@ -265,8 +267,11 @@
         })
     }
 
-    fn stmt_expr(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
-        let mut attrs = input.call(Attribute::parse_outer)?;
+    fn stmt_expr(
+        input: ParseStream,
+        allow_nosemi: bool,
+        mut attrs: Vec<Attribute>,
+    ) -> Result<Stmt> {
         let mut e = expr::parsing::expr_early(input)?;
 
         attrs.extend(e.replace_attrs(Vec::new()));
diff --git a/src/token.rs b/src/token.rs
index 0b8c181..0b4ad88 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -88,7 +88,6 @@
 //! [Printing]: https://docs.rs/quote/1.0/quote/trait.ToTokens.html
 //! [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html
 
-use std;
 #[cfg(feature = "extra-traits")]
 use std::cmp;
 #[cfg(feature = "extra-traits")]
@@ -158,11 +157,12 @@
 #[cfg(any(feature = "full", feature = "derive"))]
 #[cfg(feature = "parsing")]
 fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
+    use crate::parse::Unexpected;
     use std::cell::Cell;
     use std::rc::Rc;
 
     let scope = Span::call_site();
-    let unexpected = Rc::new(Cell::new(None));
+    let unexpected = Rc::new(Cell::new(Unexpected::None));
     let buffer = crate::parse::new_parse_buffer(scope, cursor, unexpected);
     peek(&buffer)
 }
@@ -855,7 +855,7 @@
     }
 
     pub fn punct<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
-        let mut spans = [input.cursor().span(); 3];
+        let mut spans = [input.span(); 3];
         punct_helper(input, token, &mut spans)?;
         Ok(S::from_spans(&spans))
     }
diff --git a/src/ty.rs b/src/ty.rs
index 1e5b51f..b2e086b 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -784,9 +784,27 @@
     impl Parse for TypeTuple {
         fn parse(input: ParseStream) -> Result<Self> {
             let content;
+            let paren_token = parenthesized!(content in input);
+
+            if content.is_empty() {
+                return Ok(TypeTuple {
+                    paren_token,
+                    elems: Punctuated::new(),
+                });
+            }
+
+            let first: Type = content.parse()?;
             Ok(TypeTuple {
-                paren_token: parenthesized!(content in input),
-                elems: content.parse_terminated(Type::parse)?,
+                paren_token,
+                elems: {
+                    let mut elems = Punctuated::new();
+                    elems.push_value(first);
+                    elems.push_punct(content.parse()?);
+                    let rest: Punctuated<Type, Token![,]> =
+                        content.parse_terminated(Parse::parse)?;
+                    elems.extend(rest);
+                    elems
+                },
             })
         }
     }
diff --git a/src/verbatim.rs b/src/verbatim.rs
new file mode 100644
index 0000000..0686352
--- /dev/null
+++ b/src/verbatim.rs
@@ -0,0 +1,15 @@
+use crate::parse::{ParseBuffer, ParseStream};
+use proc_macro2::TokenStream;
+use std::iter;
+
+pub fn between<'a>(begin: ParseBuffer<'a>, end: ParseStream<'a>) -> TokenStream {
+    let end = end.cursor();
+    let mut cursor = begin.cursor();
+    let mut tokens = TokenStream::new();
+    while cursor != end {
+        let (tt, next) = cursor.token_tree().unwrap();
+        tokens.extend(iter::once(tt));
+        cursor = next;
+    }
+    tokens
+}
diff --git a/tests/clone.sh b/tests/clone.sh
deleted file mode 100755
index 03d0c22..0000000
--- a/tests/clone.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-REV=7979016aff545f7b41cc517031026020b340989d
-
-set -euo pipefail
-cd "$(dirname "${BASH_SOURCE[0]}")"
-mkdir -p rust
-touch rust/COMMIT
-
-if [ "$(cat rust/COMMIT)" != "$REV" ]; then
-    rm -rf rust
-    mkdir rust
-    curl -L "https://github.com/rust-lang/rust/archive/${REV}.tar.gz" \
-        | tar xz --directory rust --strip-components 1
-    echo "$REV" > rust/COMMIT
-fi
diff --git a/tests/common/eq.rs b/tests/common/eq.rs
index 199e0fe..e03b25b 100644
--- a/tests/common/eq.rs
+++ b/tests/common/eq.rs
@@ -1,36 +1,33 @@
 extern crate rustc_data_structures;
+extern crate rustc_span;
 extern crate rustc_target;
 extern crate syntax;
-extern crate syntax_pos;
 
 use std::mem;
 
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::thin_vec::ThinVec;
-use rustc_target::abi::FloatTy;
-use rustc_target::spec::abi::Abi;
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span, Symbol, SyntaxContext, DUMMY_SP};
 use syntax::ast::{
-    AngleBracketedArgs, AnonConst, Arm, AsmDialect, AssocTyConstraint, AssocTyConstraintKind,
-    AttrId, AttrItem, AttrStyle, Attribute, BareFnTy, BinOpKind, BindingMode, Block,
-    BlockCheckMode, CaptureBy, Constness, Crate, CrateSugar, Defaultness, EnumDef, Expr, ExprKind,
-    Field, FieldPat, FnDecl, FnHeader, ForeignItem, ForeignItemKind, ForeignMod, FunctionRetTy,
-    GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, GlobalAsm,
-    Ident, ImplItem, ImplItemKind, ImplPolarity, InlineAsm, InlineAsmOutput, IntTy, IsAsync,
-    IsAuto, Item, ItemKind, Label, Lifetime, Lit, LitIntType, LitKind, Local, Mac, MacDelimiter,
-    MacStmtStyle, MacroDef, MethodSig, Mod, Movability, MutTy, Mutability, NodeId, Param,
-    ParenthesizedArgs, Pat, PatKind, Path, PathSegment, PolyTraitRef, QSelf, RangeEnd, RangeLimits,
-    RangeSyntax, Stmt, StmtKind, StrStyle, StructField, TraitBoundModifier, TraitItem,
-    TraitItemKind, TraitObjectSyntax, TraitRef, Ty, TyKind, UintTy, UnOp, UnsafeSource, Unsafety,
-    UseTree, UseTreeKind, Variant, VariantData, VisibilityKind, WhereBoundPredicate, WhereClause,
+    AngleBracketedArgs, AnonConst, Arm, AsmDialect, AssocItem, AssocItemKind, AssocTyConstraint,
+    AssocTyConstraintKind, Async, AttrId, AttrItem, AttrKind, AttrStyle, Attribute, BareFnTy,
+    BinOpKind, BindingMode, Block, BlockCheckMode, BorrowKind, CaptureBy, Const, Crate, CrateSugar,
+    Defaultness, EnumDef, Expr, ExprKind, Extern, Field, FieldPat, FloatTy, FnDecl, FnHeader,
+    FnRetTy, FnSig, ForeignMod, GenericArg, GenericArgs, GenericBound, GenericParam,
+    GenericParamKind, Generics, GlobalAsm, Ident, ImplPolarity, InlineAsm, InlineAsmOutput, IntTy,
+    IsAuto, Item, ItemKind, Label, Lifetime, Lit, LitFloatType, LitIntType, LitKind, Local, Mac,
+    MacArgs, MacDelimiter, MacStmtStyle, MacroDef, Mod, Movability, MutTy, Mutability, NodeId,
+    Param, ParenthesizedArgs, Pat, PatKind, Path, PathSegment, PolyTraitRef, QSelf, RangeEnd,
+    RangeLimits, RangeSyntax, Stmt, StmtKind, StrLit, StrStyle, StructField, TraitBoundModifier,
+    TraitObjectSyntax, TraitRef, Ty, TyKind, UintTy, UnOp, Unsafe, UnsafeSource, UseTree,
+    UseTreeKind, Variant, VariantData, VisibilityKind, WhereBoundPredicate, WhereClause,
     WhereEqPredicate, WherePredicate, WhereRegionPredicate,
 };
-use syntax::parse::lexer::comments;
-use syntax::parse::token::{self, DelimToken, Token, TokenKind};
 use syntax::ptr::P;
-use syntax::source_map::Spanned;
-use syntax::symbol::{sym, Symbol};
+use syntax::token::{self, DelimToken, Token, TokenKind};
 use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
-use syntax_pos::{Span, SyntaxContext, DUMMY_SP};
+use syntax::util::comments;
 
 pub trait SpanlessEq {
     fn eq(&self, other: &Self) -> bool;
@@ -127,16 +124,15 @@
 spanless_eq_partial_eq!(usize);
 spanless_eq_partial_eq!(char);
 spanless_eq_partial_eq!(Symbol);
-spanless_eq_partial_eq!(Abi);
 spanless_eq_partial_eq!(DelimToken);
 
 macro_rules! spanless_eq_struct {
     {
-        $name:ident;
+        $name:ident $(<$param:ident>)?;
         $([$field:ident $other:ident])*
         $(![$ignore:ident])*
     } => {
-        impl SpanlessEq for $name {
+        impl $(<$param: SpanlessEq>)* SpanlessEq for $name $(<$param>)* {
             fn eq(&self, other: &Self) -> bool {
                 let $name { $($field,)* $($ignore: _,)* } = self;
                 let $name { $($field: $other,)* $($ignore: _,)* } = other;
@@ -146,14 +142,14 @@
     };
 
     {
-        $name:ident;
+        $name:ident $(<$param:ident>)?;
         $([$field:ident $other:ident])*
         $next:ident
         $($rest:ident)*
         $(!$ignore:ident)*
     } => {
         spanless_eq_struct! {
-            $name;
+            $name $(<$param>)*;
             $([$field $other])*
             [$next other]
             $($rest)*
@@ -162,14 +158,14 @@
     };
 
     {
-        $name:ident;
+        $name:ident $(<$param:ident>)?;
         $([$field:ident $other:ident])*
         $(![$ignore:ident])*
         !$next:ident
         $(!$rest:ident)*
     } => {
         spanless_eq_struct! {
-            $name;
+            $name $(<$param>)*;
             $([$field $other])*
             $(![$ignore])*
             ![$next]
@@ -266,34 +262,33 @@
 spanless_eq_struct!(AngleBracketedArgs; span args constraints);
 spanless_eq_struct!(AnonConst; id value);
 spanless_eq_struct!(Arm; attrs pat guard body span id is_placeholder);
+spanless_eq_struct!(AssocItem; attrs id span vis ident defaultness kind !tokens);
 spanless_eq_struct!(AssocTyConstraint; id ident kind span);
-spanless_eq_struct!(AttrItem; path tokens);
-spanless_eq_struct!(Attribute; item id style span !is_sugared_doc);
-spanless_eq_struct!(BareFnTy; unsafety abi generic_params decl);
+spanless_eq_struct!(AttrItem; path args);
+spanless_eq_struct!(Attribute; kind id style span);
+spanless_eq_struct!(BareFnTy; unsafety ext generic_params decl);
 spanless_eq_struct!(Block; stmts id rules span);
-spanless_eq_struct!(Crate; module attrs span);
+spanless_eq_struct!(Crate; module attrs span proc_macros);
 spanless_eq_struct!(EnumDef; variants);
 spanless_eq_struct!(Expr; id kind span attrs);
-spanless_eq_struct!(Field; ident expr span is_shorthand attrs id is_placeholder);
+spanless_eq_struct!(Field; attrs id span ident expr is_shorthand is_placeholder);
 spanless_eq_struct!(FieldPat; ident pat is_shorthand attrs id span is_placeholder);
 spanless_eq_struct!(FnDecl; inputs output);
-spanless_eq_struct!(FnHeader; constness asyncness unsafety abi);
-spanless_eq_struct!(ForeignItem; ident attrs kind id span vis);
+spanless_eq_struct!(FnHeader; constness asyncness unsafety ext);
+spanless_eq_struct!(FnSig; header decl);
 spanless_eq_struct!(ForeignMod; abi items);
 spanless_eq_struct!(GenericParam; id ident attrs bounds is_placeholder kind);
 spanless_eq_struct!(Generics; params where_clause span);
 spanless_eq_struct!(GlobalAsm; asm);
-spanless_eq_struct!(ImplItem; id ident vis defaultness attrs generics kind span !tokens);
 spanless_eq_struct!(InlineAsm; asm asm_str_style outputs inputs clobbers volatile alignstack dialect);
 spanless_eq_struct!(InlineAsmOutput; constraint expr is_rw is_indirect);
-spanless_eq_struct!(Item; ident attrs id kind vis span !tokens);
+spanless_eq_struct!(Item<K>; attrs id span vis ident kind !tokens);
 spanless_eq_struct!(Label; ident);
 spanless_eq_struct!(Lifetime; id ident);
 spanless_eq_struct!(Lit; token kind span);
 spanless_eq_struct!(Local; pat ty init id span attrs);
-spanless_eq_struct!(Mac; path delim tts span prior_type_ascription);
-spanless_eq_struct!(MacroDef; tokens legacy);
-spanless_eq_struct!(MethodSig; header decl);
+spanless_eq_struct!(Mac; path args prior_type_ascription);
+spanless_eq_struct!(MacroDef; body legacy);
 spanless_eq_struct!(Mod; inner items inline);
 spanless_eq_struct!(MutTy; ty mutbl);
 spanless_eq_struct!(Param; attrs ty pat id span is_placeholder);
@@ -304,56 +299,59 @@
 spanless_eq_struct!(PolyTraitRef; bound_generic_params trait_ref span);
 spanless_eq_struct!(QSelf; ty path_span position);
 spanless_eq_struct!(Stmt; id kind span);
-spanless_eq_struct!(StructField; span ident vis id ty attrs is_placeholder);
+spanless_eq_struct!(StrLit; style symbol suffix span symbol_unescaped);
+spanless_eq_struct!(StructField; attrs id span vis ident ty is_placeholder);
 spanless_eq_struct!(Token; kind span);
-spanless_eq_struct!(TraitItem; id ident attrs generics kind span !tokens);
 spanless_eq_struct!(TraitRef; path ref_id);
 spanless_eq_struct!(Ty; id kind span);
 spanless_eq_struct!(UseTree; prefix kind span);
-spanless_eq_struct!(Variant; ident attrs id data disr_expr span is_placeholder);
+spanless_eq_struct!(Variant; attrs id span vis ident data disr_expr is_placeholder);
 spanless_eq_struct!(WhereBoundPredicate; span bound_generic_params bounded_ty bounds);
 spanless_eq_struct!(WhereClause; predicates span);
 spanless_eq_struct!(WhereEqPredicate; id span lhs_ty rhs_ty);
 spanless_eq_struct!(WhereRegionPredicate; span lifetime bounds);
 spanless_eq_enum!(AsmDialect; Att Intel);
+spanless_eq_enum!(AssocItemKind; Const(0 1) Static(0 1 2) Fn(0 1 2) TyAlias(0 1 2) Macro(0));
 spanless_eq_enum!(AssocTyConstraintKind; Equality(ty) Bound(bounds));
+spanless_eq_enum!(Async; Yes(span closure_id return_impl_trait_id) No);
+spanless_eq_enum!(AttrKind; Normal(0) DocComment(0));
 spanless_eq_enum!(AttrStyle; Outer Inner);
 spanless_eq_enum!(BinOpKind; Add Sub Mul Div Rem And Or BitXor BitAnd BitOr Shl Shr Eq Lt Le Ne Ge Gt);
 spanless_eq_enum!(BindingMode; ByRef(0) ByValue(0));
 spanless_eq_enum!(BlockCheckMode; Default Unsafe(0));
+spanless_eq_enum!(BorrowKind; Ref Raw);
 spanless_eq_enum!(CaptureBy; Value Ref);
-spanless_eq_enum!(Constness; Const NotConst);
+spanless_eq_enum!(Const; Yes(0) No);
 spanless_eq_enum!(CrateSugar; PubCrate JustCrate);
 spanless_eq_enum!(Defaultness; Default Final);
+spanless_eq_enum!(Extern; None Implicit Explicit(0));
 spanless_eq_enum!(FloatTy; F32 F64);
-spanless_eq_enum!(ForeignItemKind; Fn(0 1) Static(0 1) Ty Macro(0));
-spanless_eq_enum!(FunctionRetTy; Default(0) Ty(0));
+spanless_eq_enum!(FnRetTy; Default(0) Ty(0));
 spanless_eq_enum!(GenericArg; Lifetime(0) Type(0) Const(0));
 spanless_eq_enum!(GenericArgs; AngleBracketed(0) Parenthesized(0));
 spanless_eq_enum!(GenericBound; Trait(0 1) Outlives(0));
 spanless_eq_enum!(GenericParamKind; Lifetime Type(default) Const(ty));
-spanless_eq_enum!(ImplItemKind; Const(0 1) Method(0 1) TyAlias(0) OpaqueTy(0) Macro(0));
 spanless_eq_enum!(ImplPolarity; Positive Negative);
 spanless_eq_enum!(IntTy; Isize I8 I16 I32 I64 I128);
-spanless_eq_enum!(IsAsync; Async(closure_id return_impl_trait_id) NotAsync);
 spanless_eq_enum!(IsAuto; Yes No);
+spanless_eq_enum!(LitFloatType; Suffixed(0) Unsuffixed);
 spanless_eq_enum!(LitIntType; Signed(0) Unsigned(0) Unsuffixed);
+spanless_eq_enum!(MacArgs; Empty Delimited(0 1 2) Eq(0 1));
 spanless_eq_enum!(MacDelimiter; Parenthesis Bracket Brace);
 spanless_eq_enum!(MacStmtStyle; Semicolon Braces NoBraces);
 spanless_eq_enum!(Movability; Static Movable);
-spanless_eq_enum!(Mutability; Mutable Immutable);
+spanless_eq_enum!(Mutability; Mut Not);
 spanless_eq_enum!(RangeEnd; Included(0) Excluded);
 spanless_eq_enum!(RangeLimits; HalfOpen Closed);
 spanless_eq_enum!(StmtKind; Local(0) Item(0) Expr(0) Semi(0) Mac(0));
 spanless_eq_enum!(StrStyle; Cooked Raw(0));
 spanless_eq_enum!(TokenTree; Token(0) Delimited(0 1 2));
-spanless_eq_enum!(TraitBoundModifier; None Maybe);
-spanless_eq_enum!(TraitItemKind; Const(0 1) Method(0 1) Type(0 1) Macro(0));
+spanless_eq_enum!(TraitBoundModifier; None Maybe MaybeConst MaybeConstMaybe);
 spanless_eq_enum!(TraitObjectSyntax; Dyn None);
 spanless_eq_enum!(UintTy; Usize U8 U16 U32 U64 U128);
 spanless_eq_enum!(UnOp; Deref Not Neg);
+spanless_eq_enum!(Unsafe; Yes(0) No);
 spanless_eq_enum!(UnsafeSource; CompilerGenerated UserProvided);
-spanless_eq_enum!(Unsafety; Unsafe Normal);
 spanless_eq_enum!(UseTreeKind; Simple(0 1 2) Nested(0) Glob);
 spanless_eq_enum!(VariantData; Struct(0 1) Tuple(0 1) Unit(0));
 spanless_eq_enum!(VisibilityKind; Public Crate(0) Restricted(path id) Inherited);
@@ -361,16 +359,17 @@
 spanless_eq_enum!(ExprKind; Box(0) Array(0) Call(0 1) MethodCall(0 1) Tup(0)
     Binary(0 1 2) Unary(0 1) Lit(0) Cast(0 1) Type(0 1) Let(0 1) If(0 1 2)
     While(0 1 2) ForLoop(0 1 2 3) Loop(0 1) Match(0 1) Closure(0 1 2 3 4 5)
-    Block(0 1) Async(0 1 2) Await(0) TryBlock(0) Assign(0 1) AssignOp(0 1 2)
-    Field(0 1) Index(0 1) Range(0 1 2) Path(0 1) AddrOf(0 1) Break(0 1)
+    Block(0 1) Async(0 1 2) Await(0) TryBlock(0) Assign(0 1 2) AssignOp(0 1 2)
+    Field(0 1) Index(0 1) Range(0 1 2) Path(0 1) AddrOf(0 1 2) Break(0 1)
     Continue(0) Ret(0) InlineAsm(0) Mac(0) Struct(0 1 2) Repeat(0 1) Paren(0)
     Try(0) Yield(0) Err);
 spanless_eq_enum!(ItemKind; ExternCrate(0) Use(0) Static(0 1 2) Const(0 1)
-    Fn(0 1 2 3) Mod(0) ForeignMod(0) GlobalAsm(0) TyAlias(0 1) OpaqueTy(0 1)
-    Enum(0 1) Struct(0 1) Union(0 1) Trait(0 1 2 3 4) TraitAlias(0 1)
-    Impl(0 1 2 3 4 5 6) Mac(0) MacroDef(0));
+    Fn(0 1 2) Mod(0) ForeignMod(0) GlobalAsm(0) TyAlias(0 1 2) Enum(0 1)
+    Struct(0 1) Union(0 1) Trait(0 1 2 3 4) TraitAlias(0 1)
+    Impl(unsafety polarity defaultness constness generics of_trait self_ty items)
+    Mac(0) MacroDef(0));
 spanless_eq_enum!(LitKind; Str(0 1) ByteStr(0) Byte(0) Char(0) Int(0 1)
-    Float(0 1) FloatUnsuffixed(0) Bool(0) Err(0));
+    Float(0 1) Bool(0) Err(0));
 spanless_eq_enum!(PatKind; Wild Ident(0 1 2) Struct(0 1 2) TupleStruct(0 1)
     Or(0) Path(0 1) Tuple(0) Box(0) Ref(0 1) Lit(0) Range(0 1 2) Slice(0) Rest
     Paren(0) Mac(0));
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index 8b784be..3dd2552 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -12,3 +12,8 @@
         Err(_) => usize::max_value(),
     }
 }
+
+/// Are we running in travis-ci.org.
+pub fn travis_ci() -> bool {
+    env::var_os("TRAVIS").is_some()
+}
diff --git a/tests/common/parse.rs b/tests/common/parse.rs
index e6425b7..acdd172 100644
--- a/tests/common/parse.rs
+++ b/tests/common/parse.rs
@@ -1,19 +1,20 @@
+extern crate rustc_expand;
+extern crate rustc_parse as parse;
+extern crate rustc_session;
+extern crate rustc_span;
 extern crate syntax;
-extern crate syntax_pos;
 
+use rustc_session::parse::ParseSess;
+use rustc_span::source_map::FilePathMapping;
+use rustc_span::FileName;
 use syntax::ast;
-use syntax::parse;
 use syntax::ptr::P;
-use syntax::sess::ParseSess;
-use syntax::source_map::FilePathMapping;
-use syntax_pos::FileName;
 
 use std::panic;
 
 pub fn libsyntax_expr(input: &str) -> Option<P<ast::Expr>> {
     match panic::catch_unwind(|| {
         let sess = ParseSess::new(FilePathMapping::empty());
-        sess.span_diagnostic.set_continue_after_error(false);
         let e = parse::new_parser_from_source_str(
             &sess,
             FileName::Custom("test_precedence".to_string()),
diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs
index 02b4e32..6ab8d2e 100644
--- a/tests/repo/mod.rs
+++ b/tests/repo/mod.rs
@@ -1,7 +1,16 @@
-use std::process::Command;
+mod progress;
 
+use self::progress::Progress;
+use crate::common;
+use anyhow::Result;
+use flate2::read::GzDecoder;
+use std::fs;
+use std::path::Path;
+use tar::Archive;
 use walkdir::DirEntry;
 
+const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";
+
 pub fn base_dir_filter(entry: &DirEntry) -> bool {
     let path = entry.path();
     if path.is_dir() {
@@ -10,51 +19,159 @@
     if path.extension().map(|e| e != "rs").unwrap_or(true) {
         return false;
     }
-    let path_string = path.to_string_lossy();
-    let path_string = if cfg!(windows) {
-        path_string.replace('\\', "/").into()
-    } else {
-        path_string
-    };
+
+    let mut path_string = path.to_string_lossy();
+    if cfg!(windows) {
+        path_string = path_string.replace('\\', "/").into();
+    }
+    assert!(path_string.starts_with("tests/rust/src/"));
+    let path = &path_string["tests/rust/src/".len()..];
+
     // TODO assert that parsing fails on the parse-fail cases
-    if path_string.starts_with("tests/rust/src/test/parse-fail")
-        || path_string.starts_with("tests/rust/src/test/compile-fail")
-        || path_string.starts_with("tests/rust/src/test/rustfix")
+    if path.starts_with("test/parse-fail")
+        || path.starts_with("test/compile-fail")
+        || path.starts_with("test/rustfix")
     {
         return false;
     }
 
-    if path_string.starts_with("tests/rust/src/test/ui") {
-        let stderr_path = path.with_extension("stderr");
+    if path.starts_with("test/ui") {
+        let stderr_path = entry.path().with_extension("stderr");
         if stderr_path.exists() {
             // Expected to fail in some way
             return false;
         }
     }
 
-    match path_string.as_ref() {
+    match path {
+        // TODO: or-patterns patterns: `Some(1 | 8)`
+        // https://github.com/dtolnay/syn/issues/758
+        "test/mir-opt/exponential-or.rs" |
+        "test/ui/or-patterns/basic-switch.rs" |
+        "test/ui/or-patterns/basic-switchint.rs" |
+        "test/ui/or-patterns/bindings-runpass-1.rs" |
+        "test/ui/or-patterns/bindings-runpass-2.rs" |
+        "test/ui/or-patterns/consistent-bindings.rs" |
+        "test/ui/or-patterns/exhaustiveness-pass.rs" |
+        "test/ui/or-patterns/for-loop.rs" |
+        "test/ui/or-patterns/if-let-while-let.rs" |
+        "test/ui/or-patterns/issue-67514-irrefutable-param.rs" |
+        "test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs" |
+        "test/ui/or-patterns/let-pattern.rs" |
+        "test/ui/or-patterns/mix-with-wild.rs" |
+        "test/ui/or-patterns/or-patterns-default-binding-modes.rs" |
+        "test/ui/or-patterns/or-patterns-syntactic-pass.rs" |
+        "test/ui/or-patterns/search-via-bindings.rs" |
+        "test/ui/or-patterns/struct-like.rs" |
+
+        // TODO: inner attr in traits: `trait Foo { #![...] }`
+        // https://github.com/dtolnay/syn/issues/759
+        "test/pretty/trait-inner-attr.rs" |
+        "test/ui/parser/inner-attr-in-trait-def.rs" |
+
+        // TODO: const underscore in traits: `trait A { const _: (); }`
+        // https://github.com/dtolnay/syn/issues/760
+        "test/ui/parser/assoc-const-underscore-syntactic-pass.rs" |
+
+        // TODO: top level fn without body: `fn f();`
+        // https://github.com/dtolnay/syn/issues/761
+        "test/ui/parser/fn-body-optional-syntactic-pass.rs" |
+        "test/ui/parser/fn-header-syntactic-pass.rs" |
+
+        // TODO: extern static with value: `extern { static X: u8 = 0; }`
+        // https://github.com/dtolnay/syn/issues/762
+        "test/ui/parser/foreign-static-syntactic-pass.rs" |
+
+        // TODO: extern type with bound: `extern { type A: Ord; }`
+        // https://github.com/dtolnay/syn/issues/763
+        "test/ui/parser/foreign-ty-syntactic-pass.rs" |
+
+        // TODO: top level const/static without value: `const X: u8;`
+        // https://github.com/dtolnay/syn/issues/764
+        "test/ui/parser/item-free-const-no-body-syntactic-pass.rs" |
+        "test/ui/parser/item-free-static-no-body-syntactic-pass.rs" |
+
+        // TODO: mut receiver in fn pointer type: `fn(mut self)`
+        // https://github.com/dtolnay/syn/issues/765
+        "test/ui/parser/self-param-syntactic-pass.rs" |
+
+        // TODO: const trait impls and bounds
+        // https://github.com/dtolnay/syn/issues/766
+        // https://github.com/dtolnay/syn/issues/767
+        "test/ui/rfc-2632-const-trait-impl/assoc-type.rs" |
+        "test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs" |
+        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs" |
+        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs" |
+        "test/ui/rfc-2632-const-trait-impl/feature-gate.rs" |
+        "test/ui/rfc-2632-const-trait-impl/generic-bound.rs" |
+        "test/ui/rfc-2632-const-trait-impl/syntax.rs" |
+
         // Deprecated placement syntax
-        "tests/rust/src/test/ui/obsolete-in-place/bad.rs" |
+        "test/ui/obsolete-in-place/bad.rs" |
+
         // Deprecated anonymous parameter syntax in traits
-        "tests/rust/src/test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
-        "tests/rust/src/test/ui/issues/issue-13105.rs" |
-        "tests/rust/src/test/ui/issues/issue-13775.rs" |
-        "tests/rust/src/test/ui/issues/issue-34074.rs" |
-        // Deprecated await macro syntax
-        "tests/rust/src/test/ui/async-await/await-macro.rs" |
+        "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
+        "test/ui/issues/issue-13105.rs" |
+        "test/ui/issues/issue-13775.rs" |
+        "test/ui/issues/issue-34074.rs" |
+
         // 2015-style dyn that libsyntax rejects
-        "tests/rust/src/test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |
+        "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |
+
         // not actually test cases
-        "tests/rust/src/test/ui/include-single-expr-helper.rs" |
-        "tests/rust/src/test/ui/include-single-expr-helper-1.rs" |
-        "tests/rust/src/test/ui/issues/auxiliary/issue-21146-inc.rs" |
-        "tests/rust/src/test/ui/macros/auxiliary/macro-comma-support.rs" |
-        "tests/rust/src/test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,
+        "test/rustdoc-ui/test-compile-fail2.rs" |
+        "test/rustdoc-ui/test-compile-fail3.rs" |
+        "test/ui/include-single-expr-helper.rs" |
+        "test/ui/include-single-expr-helper-1.rs" |
+        "test/ui/issues/auxiliary/issue-21146-inc.rs" |
+        "test/ui/macros/auxiliary/macro-comma-support.rs" |
+        "test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,
+
         _ => true,
     }
 }
 
 pub fn clone_rust() {
-    let result = Command::new("tests/clone.sh").status().unwrap();
-    assert!(result.success());
+    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
+        Err(_) => true,
+        Ok(contents) => contents.trim() != REVISION,
+    };
+    if needs_clone {
+        download_and_unpack().unwrap();
+    }
+}
+
+fn download_and_unpack() -> Result<()> {
+    let url = format!(
+        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
+        REVISION
+    );
+    let response = reqwest::blocking::get(&url)?.error_for_status()?;
+    let progress = Progress::new(response);
+    let decoder = GzDecoder::new(progress);
+    let mut archive = Archive::new(decoder);
+    let prefix = format!("rust-{}", REVISION);
+
+    let tests_rust = Path::new("tests/rust");
+    if tests_rust.exists() {
+        fs::remove_dir_all(tests_rust)?;
+    }
+
+    for entry in archive.entries()? {
+        let mut entry = entry?;
+        let path = entry.path()?;
+        if path == Path::new("pax_global_header") {
+            continue;
+        }
+        let relative = path.strip_prefix(&prefix)?;
+        let out = tests_rust.join(relative);
+        entry.unpack(&out)?;
+        if common::travis_ci() {
+            // Something about this makes the travis build not deadlock...
+            errorf!(".");
+        }
+    }
+
+    fs::write("tests/rust/COMMIT", REVISION)?;
+    Ok(())
 }
diff --git a/tests/repo/progress.rs b/tests/repo/progress.rs
new file mode 100644
index 0000000..28c8a44
--- /dev/null
+++ b/tests/repo/progress.rs
@@ -0,0 +1,37 @@
+use std::io::{Read, Result};
+use std::time::{Duration, Instant};
+
+pub struct Progress<R> {
+    bytes: usize,
+    tick: Instant,
+    stream: R,
+}
+
+impl<R> Progress<R> {
+    pub fn new(stream: R) -> Self {
+        Progress {
+            bytes: 0,
+            tick: Instant::now() + Duration::from_millis(2000),
+            stream,
+        }
+    }
+}
+
+impl<R: Read> Read for Progress<R> {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        let num = self.stream.read(buf)?;
+        self.bytes += num;
+        let now = Instant::now();
+        if now > self.tick {
+            self.tick = now + Duration::from_millis(500);
+            errorf!("downloading... {} bytes\n", self.bytes);
+        }
+        Ok(num)
+    }
+}
+
+impl<R> Drop for Progress<R> {
+    fn drop(&mut self) {
+        errorf!("done ({} bytes)\n", self.bytes);
+    }
+}
diff --git a/tests/test_attribute.rs b/tests/test_attribute.rs
index d77c0c0..e1b0142 100644
--- a/tests/test_attribute.rs
+++ b/tests/test_attribute.rs
@@ -286,6 +286,48 @@
     "###);
 }
 
+#[test]
+fn test_negative_lit() {
+    let meta = test("#[form(min = -1, max = 200)]");
+
+    snapshot!(meta, @r###"
+    Meta::List {
+        path: Path {
+            segments: [
+                PathSegment {
+                    ident: "form",
+                    arguments: None,
+                },
+            ],
+        },
+        nested: [
+            Meta(Meta::NameValue {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "min",
+                            arguments: None,
+                        },
+                    ],
+                },
+                lit: -1,
+            }),
+            Meta(Meta::NameValue {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "max",
+                            arguments: None,
+                        },
+                    ],
+                },
+                lit: 200,
+            }),
+        ],
+    }
+    "###);
+}
+
 fn test(input: &str) -> Meta {
     let attrs = Attribute::parse_outer.parse_str(input).unwrap();
 
diff --git a/tests/test_lit.rs b/tests/test_lit.rs
index 0b300c4..d4495ce 100644
--- a/tests/test_lit.rs
+++ b/tests/test_lit.rs
@@ -1,9 +1,9 @@
 mod features;
 
-use proc_macro2::{TokenStream, TokenTree};
+use proc_macro2::{Span, TokenStream, TokenTree};
 use quote::ToTokens;
 use std::str::FromStr;
-use syn::Lit;
+use syn::{Lit, LitFloat, LitInt};
 
 fn lit(s: &str) -> Lit {
     match TokenStream::from_str(s)
@@ -182,3 +182,16 @@
     test_float("1.0__3e-12", 1.03e-12, "");
     test_float("1.03e+12", 1.03e12, "");
 }
+
+#[test]
+fn negative() {
+    let span = Span::call_site();
+    assert_eq!("-1", LitInt::new("-1", span).to_string());
+    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
+    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
+    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
+    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
+    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
+    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
+    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
+}
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
index 582c0fd..80f5130 100644
--- a/tests/test_precedence.rs
+++ b/tests/test_precedence.rs
@@ -15,19 +15,17 @@
 //! 5. Compare the expressions with one another, if they are not equal fail.
 
 extern crate rustc_data_structures;
-extern crate smallvec;
+extern crate rustc_span;
 extern crate syntax;
-extern crate syntax_pos;
 
 mod features;
 
 use quote::quote;
 use rayon::iter::{IntoParallelIterator, ParallelIterator};
 use regex::Regex;
-use smallvec::smallvec;
+use rustc_span::edition::Edition;
 use syntax::ast;
 use syntax::ptr::P;
-use syntax_pos::edition::Edition;
 use walkdir::{DirEntry, WalkDir};
 
 use std::fs::File;
@@ -86,7 +84,6 @@
 
 /// Test expressions from rustc, like in `test_round_trip`.
 #[test]
-#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
 fn test_rustc_precedence() {
     repo::clone_rust();
     let abort_after = common::abort_after();
@@ -212,16 +209,58 @@
 /// This method operates on libsyntax objects.
 fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
     use rustc_data_structures::thin_vec::ThinVec;
-    use smallvec::SmallVec;
+    use rustc_span::DUMMY_SP;
     use std::mem;
-    use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
-    use syntax::mut_visit::{noop_visit_expr, MutVisitor};
-    use syntax_pos::DUMMY_SP;
+    use syntax::ast::{Block, BorrowKind, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
+    use syntax::mut_visit::MutVisitor;
+    use syntax::util::map_in_place::MapInPlace;
 
     struct BracketsVisitor {
         failed: bool,
     };
 
+    fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
+        if f.is_shorthand {
+            noop_visit_expr(&mut f.expr, vis);
+        } else {
+            vis.visit_expr(&mut f.expr);
+        }
+        vec![f]
+    }
+
+    fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
+        let kind = match stmt.kind {
+            // Don't wrap toplevel expressions in statements.
+            StmtKind::Expr(mut e) => {
+                noop_visit_expr(&mut e, vis);
+                StmtKind::Expr(e)
+            }
+            StmtKind::Semi(mut e) => {
+                noop_visit_expr(&mut e, vis);
+                StmtKind::Semi(e)
+            }
+            s => s,
+        };
+
+        vec![Stmt { kind, ..stmt }]
+    }
+
+    fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
+        use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs};
+        match &mut e.kind {
+            ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
+            ExprKind::Struct(path, fields, expr) => {
+                vis.visit_path(path);
+                fields.flat_map_in_place(|field| flat_map_field(field, vis));
+                visit_opt(expr, |expr| vis.visit_expr(expr));
+                vis.visit_id(&mut e.id);
+                vis.visit_span(&mut e.span);
+                visit_thin_attrs(&mut e.attrs, vis);
+            }
+            _ => noop_visit_expr(e, vis),
+        }
+    }
+
     impl MutVisitor for BracketsVisitor {
         fn visit_expr(&mut self, e: &mut P<Expr>) {
             noop_visit_expr(e, self);
@@ -242,13 +281,12 @@
             }
         }
 
-        fn flat_map_field(&mut self, mut f: Field) -> SmallVec<[Field; 1]> {
-            if f.is_shorthand {
-                noop_visit_expr(&mut f.expr, self);
-            } else {
-                self.visit_expr(&mut f.expr);
-            }
-            SmallVec::from([f])
+        fn visit_block(&mut self, block: &mut P<Block>) {
+            self.visit_id(&mut block.id);
+            block
+                .stmts
+                .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
+            self.visit_span(&mut block.span);
         }
 
         // We don't want to look at expressions that might appear in patterns or
@@ -262,23 +300,6 @@
             let _ = ty;
         }
 
-        fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
-            let kind = match stmt.kind {
-                // Don't wrap toplevel expressions in statements.
-                StmtKind::Expr(mut e) => {
-                    noop_visit_expr(&mut e, self);
-                    StmtKind::Expr(e)
-                }
-                StmtKind::Semi(mut e) => {
-                    noop_visit_expr(&mut e, self);
-                    StmtKind::Semi(e)
-                }
-                s => s,
-            };
-
-            smallvec![Stmt { kind, ..stmt }]
-        }
-
         fn visit_mac(&mut self, mac: &mut Mac) {
             // By default when folding over macros, libsyntax panics. This is
             // because it's usually not what you want, you want to run after
@@ -355,7 +376,10 @@
     struct CollectExprs(Vec<Expr>);
     impl Fold for CollectExprs {
         fn fold_expr(&mut self, expr: Expr) -> Expr {
-            self.0.push(expr);
+            match expr {
+                Expr::Verbatim(tokens) if tokens.is_empty() => {}
+                _ => self.0.push(expr),
+            }
 
             Expr::Tuple(ExprTuple {
                 attrs: vec![],
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index b6e8ee1..a7e48c8 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -2,19 +2,23 @@
 #![recursion_limit = "1024"]
 #![feature(rustc_private)]
 
+extern crate rustc_errors;
+extern crate rustc_expand;
+extern crate rustc_parse as parse;
+extern crate rustc_session;
+extern crate rustc_span;
 extern crate syntax;
-extern crate syntax_pos;
 
 mod features;
 
 use quote::quote;
 use rayon::iter::{IntoParallelIterator, ParallelIterator};
+use rustc_errors::PResult;
+use rustc_session::parse::ParseSess;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::FilePathMapping;
+use rustc_span::FileName;
 use syntax::ast;
-use syntax::parse::{self, PResult};
-use syntax::sess::ParseSess;
-use syntax::source_map::FilePathMapping;
-use syntax_pos::edition::Edition;
-use syntax_pos::FileName;
 use walkdir::{DirEntry, WalkDir};
 
 use std::fs::File;
@@ -35,7 +39,6 @@
 use common::eq::SpanlessEq;
 
 #[test]
-#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
 fn test_round_trip() {
     repo::clone_rust();
     let abort_after = common::abort_after();
diff --git a/tests/test_stmt.rs b/tests/test_stmt.rs
new file mode 100644
index 0000000..d68b47f
--- /dev/null
+++ b/tests/test_stmt.rs
@@ -0,0 +1,44 @@
+#[macro_use]
+mod macros;
+
+use syn::Stmt;
+
+#[test]
+fn test_raw_operator() {
+    let stmt = syn::parse_str::<Stmt>("let _ = &raw const x;").unwrap();
+
+    snapshot!(stmt, @r###"
+    Local(Local {
+        pat: Pat::Wild,
+        init: Some(Verbatim(`& raw const x`)),
+    })
+    "###);
+}
+
+#[test]
+fn test_raw_variable() {
+    let stmt = syn::parse_str::<Stmt>("let _ = &raw;").unwrap();
+
+    snapshot!(stmt, @r###"
+    Local(Local {
+        pat: Pat::Wild,
+        init: Some(Expr::Reference {
+            expr: Expr::Path {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "raw",
+                            arguments: None,
+                        },
+                    ],
+                },
+            },
+        }),
+    })
+    "###);
+}
+
+#[test]
+fn test_raw_invalid() {
+    assert!(syn::parse_str::<Stmt>("let _ = &raw x;").is_err());
+}
diff --git a/tests/test_visibility.rs b/tests/test_visibility.rs
new file mode 100644
index 0000000..21c49c9
--- /dev/null
+++ b/tests/test_visibility.rs
@@ -0,0 +1,99 @@
+mod features;
+
+use proc_macro2::TokenStream;
+use syn::parse::{Parse, ParseStream};
+use syn::{Result, Visibility};
+
+#[derive(Debug)]
+struct VisRest {
+    vis: Visibility,
+    rest: TokenStream,
+}
+
+impl Parse for VisRest {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok(VisRest {
+            vis: input.parse()?,
+            rest: input.parse()?,
+        })
+    }
+}
+
+macro_rules! assert_vis_parse {
+    ($input:expr, Ok($p:pat)) => {
+        assert_vis_parse!($input, Ok($p) + "");
+    };
+
+    ($input:expr, Ok($p:pat) + $rest:expr) => {
+        let expected = $rest.parse::<TokenStream>().unwrap();
+        let parse: VisRest = syn::parse_str($input).unwrap();
+
+        match parse.vis {
+            $p => {}
+            _ => panic!("Expected {}, got {:?}", stringify!($p), parse.vis),
+        }
+
+        // NOTE: Round-trips through `to_string` to avoid potential whitespace
+        // diffs.
+        assert_eq!(parse.rest.to_string(), expected.to_string());
+    };
+
+    ($input:expr, Err) => {
+        syn::parse2::<VisRest>($input.parse().unwrap()).unwrap_err();
+    };
+}
+
+#[test]
+fn test_pub() {
+    assert_vis_parse!("pub", Ok(Visibility::Public(_)));
+}
+
+#[test]
+fn test_crate() {
+    assert_vis_parse!("crate", Ok(Visibility::Crate(_)));
+}
+
+#[test]
+fn test_inherited() {
+    assert_vis_parse!("", Ok(Visibility::Inherited));
+}
+
+#[test]
+fn test_in() {
+    assert_vis_parse!("pub(in foo::bar)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_crate() {
+    assert_vis_parse!("pub(crate)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_self() {
+    assert_vis_parse!("pub(self)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_pub_super() {
+    assert_vis_parse!("pub(super)", Ok(Visibility::Restricted(_)));
+}
+
+#[test]
+fn test_missing_in() {
+    assert_vis_parse!("pub(foo::bar)", Ok(Visibility::Public(_)) + "(foo::bar)");
+}
+
+#[test]
+fn test_missing_in_path() {
+    assert_vis_parse!("pub(in)", Err);
+}
+
+#[test]
+fn test_crate_path() {
+    assert_vis_parse!("pub(crate::A, crate::B)", Ok(Visibility::Public(_)) + "(crate::A, crate::B)");
+}
+
+#[test]
+fn test_junk_after_in() {
+    assert_vis_parse!("pub(in some::path @@garbage)", Err);
+}